1use std::ffi::OsString;
5
6use clap::Parser;
7use linera_base::{crypto::ValidatorPublicKey, identifiers::ChainId};
8use serde::{Deserialize, Serialize};
9
10#[cfg(with_simple_network)]
11use crate::simple;
12
13#[derive(Clone, Debug, Parser)]
14#[cfg_attr(with_testing, derive(PartialEq))]
15pub struct CrossChainConfig {
16 #[arg(long = "cross-chain-queue-size", default_value = "1000")]
18 pub(crate) queue_size: usize,
19
20 #[arg(long = "cross-chain-max-retries", default_value = "10")]
22 pub(crate) max_retries: u32,
23
24 #[arg(long = "cross-chain-retry-delay-ms", default_value = "2000")]
26 pub(crate) retry_delay_ms: u64,
27
28 #[arg(long = "cross-chain-max-backoff-ms", default_value = "30000")]
30 pub(crate) max_backoff_ms: u64,
31
32 #[arg(long = "cross-chain-sender-delay-ms", default_value = "0")]
34 pub(crate) sender_delay_ms: u64,
35
36 #[arg(long = "cross-chain-sender-failure-rate", default_value = "0.0")]
38 pub(crate) sender_failure_rate: f32,
39}
40
41impl Default for CrossChainConfig {
42 fn default() -> Self {
43 CrossChainConfig::parse_from::<[OsString; 1], OsString>(["".into()])
44 }
45}
46
47impl CrossChainConfig {
48 pub fn to_args(&self) -> Vec<String> {
49 vec![
50 "--cross-chain-queue-size".to_string(),
51 self.queue_size.to_string(),
52 "--cross-chain-max-retries".to_string(),
53 self.max_retries.to_string(),
54 "--cross-chain-retry-delay-ms".to_string(),
55 self.retry_delay_ms.to_string(),
56 "--cross-chain-max-backoff-ms".to_string(),
57 self.max_backoff_ms.to_string(),
58 "--cross-chain-sender-delay-ms".to_string(),
59 self.sender_delay_ms.to_string(),
60 "--cross-chain-sender-failure-rate".to_string(),
61 self.sender_failure_rate.to_string(),
62 ]
63 }
64}
65
66#[derive(Clone, Debug, Parser)]
67pub struct NotificationConfig {
68 #[arg(long = "notification-queue-size", default_value = "1000")]
70 pub notification_queue_size: usize,
71
72 #[arg(long = "notification-batch-size", default_value = "100")]
74 pub notification_batch_size: usize,
75
76 #[arg(long = "notification-max-in-flight", default_value = "8")]
78 pub notification_max_in_flight: usize,
79}
80
81pub type ShardId = usize;
82
83#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
85pub struct ShardConfig {
86 pub host: String,
88 pub port: u16,
90 pub metrics_port: Option<u16>,
92}
93
94impl ShardConfig {
95 pub fn address(&self) -> String {
96 format!("{}:{}", self.host, self.port)
97 }
98
99 pub fn http_address(&self) -> String {
100 format!("http://{}:{}", self.host, self.port)
101 }
102}
103
104#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
106pub struct ProxyConfig {
107 pub host: String,
109 pub public_port: u16,
111 pub private_port: u16,
113 pub metrics_port: u16,
115}
116
117impl ProxyConfig {
118 pub fn internal_address(&self, protocol: &NetworkProtocol) -> String {
119 format!(
120 "{}://{}:{}",
121 protocol.scheme(),
122 self.host,
123 self.private_port
124 )
125 }
126}
127
128#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
130pub enum NetworkProtocol {
131 #[cfg(with_simple_network)]
132 Simple(simple::TransportProtocol),
133 Grpc(TlsConfig),
134}
135
136#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
137pub enum TlsConfig {
138 ClearText,
139 Tls,
140}
141
142impl NetworkProtocol {
143 fn scheme(&self) -> &'static str {
144 match self {
145 #[cfg(with_simple_network)]
146 NetworkProtocol::Simple(transport) => transport.scheme(),
147 NetworkProtocol::Grpc(tls) => match tls {
148 TlsConfig::ClearText => "http",
149 TlsConfig::Tls => "https",
150 },
151 }
152 }
153}
154
155pub type ValidatorInternalNetworkConfig = ValidatorInternalNetworkPreConfig<NetworkProtocol>;
157
158pub type ValidatorPublicNetworkConfig = ValidatorPublicNetworkPreConfig<NetworkProtocol>;
160
161#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
163pub struct ValidatorInternalNetworkPreConfig<P> {
164 pub public_key: ValidatorPublicKey,
166 pub protocol: P,
168 pub shards: Vec<ShardConfig>,
171 pub block_exporters: Vec<ExporterServiceConfig>,
175 pub proxies: Vec<ProxyConfig>,
177}
178
179impl<P> ValidatorInternalNetworkPreConfig<P> {
180 pub fn clone_with_protocol<Q>(&self, protocol: Q) -> ValidatorInternalNetworkPreConfig<Q> {
181 ValidatorInternalNetworkPreConfig {
182 public_key: self.public_key,
183 protocol,
184 shards: self.shards.clone(),
185 block_exporters: self.block_exporters.clone(),
186 proxies: self.proxies.clone(),
187 }
188 }
189}
190
191impl ValidatorInternalNetworkConfig {
192 pub fn exporter_addresses(&self) -> Vec<String> {
193 self.block_exporters
194 .iter()
195 .map(|ExporterServiceConfig { host, port }| {
196 format!("{}://{}:{}", self.protocol.scheme(), host, port)
197 })
198 .collect::<Vec<_>>()
199 }
200}
201
202impl ValidatorPublicNetworkConfig {
203 pub fn http_address(&self) -> String {
204 format!("{}://{}:{}", self.protocol.scheme(), self.host, self.port)
205 }
206}
207
208#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
210pub struct ValidatorPublicNetworkPreConfig<P> {
211 pub protocol: P,
213 pub host: String,
215 pub port: u16,
217}
218
219impl<P> ValidatorPublicNetworkPreConfig<P> {
220 pub fn clone_with_protocol<Q>(&self, protocol: Q) -> ValidatorPublicNetworkPreConfig<Q> {
221 ValidatorPublicNetworkPreConfig {
222 protocol,
223 host: self.host.clone(),
224 port: self.port,
225 }
226 }
227}
228
229impl<P> std::fmt::Display for ValidatorPublicNetworkPreConfig<P>
230where
231 P: std::fmt::Display,
232{
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 write!(f, "{}:{}:{}", self.protocol, self.host, self.port)
235 }
236}
237
238impl std::fmt::Display for NetworkProtocol {
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240 match self {
241 #[cfg(with_simple_network)]
242 NetworkProtocol::Simple(protocol) => write!(f, "{:?}", protocol),
243 NetworkProtocol::Grpc(tls) => match tls {
244 TlsConfig::ClearText => write!(f, "grpc"),
245 TlsConfig::Tls => write!(f, "grpcs"),
246 },
247 }
248 }
249}
250
251impl<P> std::str::FromStr for ValidatorPublicNetworkPreConfig<P>
252where
253 P: std::str::FromStr,
254 P::Err: std::fmt::Display,
255{
256 type Err = anyhow::Error;
257
258 fn from_str(s: &str) -> Result<Self, Self::Err> {
259 let parts = s.split(':').collect::<Vec<_>>();
260 anyhow::ensure!(
261 parts.len() == 3,
262 "Expecting format `(tcp|udp|grpc|grpcs):host:port`"
263 );
264 let protocol = parts[0].parse().map_err(|s| anyhow::anyhow!("{}", s))?;
265 let host = parts[1].to_owned();
266 let port = parts[2].parse()?;
267 Ok(ValidatorPublicNetworkPreConfig {
268 protocol,
269 host,
270 port,
271 })
272 }
273}
274
275impl std::str::FromStr for NetworkProtocol {
276 type Err = String;
277
278 fn from_str(s: &str) -> Result<Self, Self::Err> {
279 let protocol = match s {
280 "grpc" => Self::Grpc(TlsConfig::ClearText),
281 "grpcs" => Self::Grpc(TlsConfig::Tls),
282 #[cfg(with_simple_network)]
283 s => Self::Simple(simple::TransportProtocol::from_str(s)?),
284 #[cfg(not(with_simple_network))]
285 s => return Err(format!("unsupported protocol: {s:?}")),
286 };
287 Ok(protocol)
288 }
289}
290
291impl<P> ValidatorInternalNetworkPreConfig<P> {
292 pub fn get_shard_id(&self, chain_id: ChainId) -> ShardId {
294 use std::hash::{Hash, Hasher};
295 let mut s = std::collections::hash_map::DefaultHasher::new();
296 self.public_key.hash(&mut s);
298 chain_id.hash(&mut s);
299 (s.finish() as ShardId) % self.shards.len()
300 }
301
302 pub fn shard(&self, shard_id: ShardId) -> &ShardConfig {
303 &self.shards[shard_id]
304 }
305
306 pub fn get_shard_for(&self, chain_id: ChainId) -> &ShardConfig {
308 self.shard(self.get_shard_id(chain_id))
309 }
310}
311
312#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
313pub struct ExporterServiceConfig {
315 pub host: String,
317 pub port: u16,
319}
320
321impl ExporterServiceConfig {
322 pub fn new(host: String, port: u16) -> ExporterServiceConfig {
323 ExporterServiceConfig { host, port }
324 }
325}
326
327#[test]
328fn cross_chain_config_to_args() {
329 let config = CrossChainConfig::default();
330 let args = config.to_args();
331 let mut cmd = vec![String::new()];
332 cmd.extend(args.clone());
333 let config2 = CrossChainConfig::parse_from(cmd);
334 let args2 = config2.to_args();
335 assert_eq!(config, config2);
336 assert_eq!(args, args2);
337}