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)]
15#[cfg_attr(with_testing, derive(PartialEq))]
16pub struct CrossChainConfig {
17 #[arg(long = "cross-chain-queue-size", default_value = "1000")]
19 pub(crate) queue_size: usize,
20
21 #[arg(long = "cross-chain-max-retries", default_value = "10")]
23 pub(crate) max_retries: u32,
24
25 #[arg(long = "cross-chain-retry-delay-ms", default_value = "2000")]
27 pub(crate) retry_delay_ms: u64,
28
29 #[arg(long = "cross-chain-max-backoff-ms", default_value = "30000")]
31 pub(crate) max_backoff_ms: u64,
32
33 #[arg(long = "cross-chain-sender-delay-ms", default_value = "0")]
35 pub(crate) sender_delay_ms: u64,
36
37 #[arg(long = "cross-chain-sender-failure-rate", default_value = "0.0")]
39 pub(crate) sender_failure_rate: f32,
40}
41
42impl Default for CrossChainConfig {
43 fn default() -> Self {
44 CrossChainConfig::parse_from::<[OsString; 1], OsString>(["".into()])
45 }
46}
47
48impl CrossChainConfig {
49 pub fn to_args(&self) -> Vec<String> {
51 vec![
52 "--cross-chain-queue-size".to_string(),
53 self.queue_size.to_string(),
54 "--cross-chain-max-retries".to_string(),
55 self.max_retries.to_string(),
56 "--cross-chain-retry-delay-ms".to_string(),
57 self.retry_delay_ms.to_string(),
58 "--cross-chain-max-backoff-ms".to_string(),
59 self.max_backoff_ms.to_string(),
60 "--cross-chain-sender-delay-ms".to_string(),
61 self.sender_delay_ms.to_string(),
62 "--cross-chain-sender-failure-rate".to_string(),
63 self.sender_failure_rate.to_string(),
64 ]
65 }
66}
67
68#[derive(Clone, Debug, Parser)]
70pub struct NotificationConfig {
71 #[arg(long = "notification-queue-size", default_value = "1000")]
73 pub notification_queue_size: usize,
74
75 #[arg(long = "notification-batch-size", default_value = "100")]
77 pub notification_batch_size: usize,
78
79 #[arg(long = "notification-max-in-flight", default_value = "8")]
81 pub notification_max_in_flight: usize,
82}
83
84pub type ShardId = usize;
86
87#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
89pub struct ShardConfig {
90 pub host: String,
92 pub port: u16,
94 pub metrics_port: Option<u16>,
96}
97
98impl ShardConfig {
99 pub fn address(&self) -> String {
101 format!("{}:{}", self.host, self.port)
102 }
103
104 pub fn http_address(&self) -> String {
106 format!("http://{}:{}", self.host, self.port)
107 }
108}
109
110#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
112pub struct ProxyConfig {
113 pub host: String,
115 pub public_port: u16,
117 pub private_port: u16,
119 pub metrics_port: u16,
121}
122
123impl ProxyConfig {
124 pub fn internal_address(&self, protocol: &NetworkProtocol) -> String {
126 format!(
127 "{}://{}:{}",
128 protocol.scheme(),
129 self.host,
130 self.private_port
131 )
132 }
133}
134
135#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
137pub enum NetworkProtocol {
138 #[cfg(with_simple_network)]
140 Simple(simple::TransportProtocol),
141 Grpc(TlsConfig),
143}
144
145#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
147pub enum TlsConfig {
148 ClearText,
150 Tls,
152}
153
154impl NetworkProtocol {
155 fn scheme(&self) -> &'static str {
156 match self {
157 #[cfg(with_simple_network)]
158 NetworkProtocol::Simple(transport) => transport.scheme(),
159 NetworkProtocol::Grpc(tls) => match tls {
160 TlsConfig::ClearText => "http",
161 TlsConfig::Tls => "https",
162 },
163 }
164 }
165}
166
167pub type ValidatorInternalNetworkConfig = ValidatorInternalNetworkPreConfig<NetworkProtocol>;
169
170pub type ValidatorPublicNetworkConfig = ValidatorPublicNetworkPreConfig<NetworkProtocol>;
172
173#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
175pub struct ValidatorInternalNetworkPreConfig<P> {
176 pub public_key: ValidatorPublicKey,
178 pub protocol: P,
180 pub shards: Vec<ShardConfig>,
183 pub block_exporters: Vec<ExporterServiceConfig>,
187 pub proxies: Vec<ProxyConfig>,
189}
190
191impl<P> ValidatorInternalNetworkPreConfig<P> {
192 pub fn clone_with_protocol<Q>(&self, protocol: Q) -> ValidatorInternalNetworkPreConfig<Q> {
194 ValidatorInternalNetworkPreConfig {
195 public_key: self.public_key,
196 protocol,
197 shards: self.shards.clone(),
198 block_exporters: self.block_exporters.clone(),
199 proxies: self.proxies.clone(),
200 }
201 }
202}
203
204impl ValidatorInternalNetworkConfig {
205 pub fn exporter_addresses(&self) -> Vec<String> {
207 self.block_exporters
208 .iter()
209 .map(|ExporterServiceConfig { host, port }| {
210 format!("{}://{}:{}", self.protocol.scheme(), host, port)
211 })
212 .collect::<Vec<_>>()
213 }
214}
215
216impl ValidatorPublicNetworkConfig {
217 pub fn http_address(&self) -> String {
219 format!("{}://{}:{}", self.protocol.scheme(), self.host, self.port)
220 }
221}
222
223#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
225pub struct ValidatorPublicNetworkPreConfig<P> {
226 pub protocol: P,
228 pub host: String,
230 pub port: u16,
232}
233
234impl<P> ValidatorPublicNetworkPreConfig<P> {
235 pub fn clone_with_protocol<Q>(&self, protocol: Q) -> ValidatorPublicNetworkPreConfig<Q> {
237 ValidatorPublicNetworkPreConfig {
238 protocol,
239 host: self.host.clone(),
240 port: self.port,
241 }
242 }
243}
244
245impl<P> std::fmt::Display for ValidatorPublicNetworkPreConfig<P>
246where
247 P: std::fmt::Display,
248{
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 write!(f, "{}:{}:{}", self.protocol, self.host, self.port)
251 }
252}
253
254impl std::fmt::Display for NetworkProtocol {
255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256 match self {
257 #[cfg(with_simple_network)]
258 NetworkProtocol::Simple(protocol) => write!(f, "{protocol:?}"),
259 NetworkProtocol::Grpc(tls) => match tls {
260 TlsConfig::ClearText => write!(f, "grpc"),
261 TlsConfig::Tls => write!(f, "grpcs"),
262 },
263 }
264 }
265}
266
267impl<P> std::str::FromStr for ValidatorPublicNetworkPreConfig<P>
268where
269 P: std::str::FromStr,
270 P::Err: std::fmt::Display,
271{
272 type Err = anyhow::Error;
273
274 fn from_str(s: &str) -> Result<Self, Self::Err> {
275 let parts = s.split(':').collect::<Vec<_>>();
276 anyhow::ensure!(
277 parts.len() == 3,
278 "Expecting format `(tcp|udp|grpc|grpcs):host:port`"
279 );
280 let protocol = parts[0].parse().map_err(|s| anyhow::anyhow!("{s}"))?;
281 let host = parts[1].to_owned();
282 let port = parts[2].parse()?;
283 Ok(ValidatorPublicNetworkPreConfig {
284 protocol,
285 host,
286 port,
287 })
288 }
289}
290
291impl std::str::FromStr for NetworkProtocol {
292 type Err = String;
293
294 fn from_str(s: &str) -> Result<Self, Self::Err> {
295 let protocol = match s {
296 "grpc" => Self::Grpc(TlsConfig::ClearText),
297 "grpcs" => Self::Grpc(TlsConfig::Tls),
298 #[cfg(with_simple_network)]
299 s => Self::Simple(simple::TransportProtocol::from_str(s)?),
300 #[cfg(not(with_simple_network))]
301 s => return Err(format!("unsupported protocol: {s:?}")),
302 };
303 Ok(protocol)
304 }
305}
306
307impl<P> ValidatorInternalNetworkPreConfig<P> {
308 #[expect(
310 clippy::cast_possible_truncation,
311 reason = "result is reduced modulo shards.len(), so any truncation is irrelevant"
312 )]
313 pub fn get_shard_id(&self, chain_id: ChainId) -> ShardId {
314 use std::hash::{Hash, Hasher};
315 let mut s = std::collections::hash_map::DefaultHasher::new();
316 self.public_key.hash(&mut s);
318 chain_id.hash(&mut s);
319 (s.finish() as ShardId) % self.shards.len()
320 }
321
322 pub fn shard(&self, shard_id: ShardId) -> &ShardConfig {
324 &self.shards[shard_id]
325 }
326
327 pub fn get_shard_for(&self, chain_id: ChainId) -> &ShardConfig {
329 self.shard(self.get_shard_id(chain_id))
330 }
331}
332
333#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
334pub struct ExporterServiceConfig {
336 pub host: String,
338 pub port: u16,
340}
341
342impl ExporterServiceConfig {
343 pub fn new(host: String, port: u16) -> ExporterServiceConfig {
345 ExporterServiceConfig { host, port }
346 }
347}
348
349#[test]
350fn cross_chain_config_to_args() {
351 let config = CrossChainConfig::default();
352 let args = config.to_args();
353 let mut cmd = vec![String::new()];
354 cmd.extend(args.clone());
355 let config2 = CrossChainConfig::parse_from(cmd);
356 let args2 = config2.to_args();
357 assert_eq!(config, config2);
358 assert_eq!(args, args2);
359}