linera_rpc/
config.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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    /// Number of cross-chain messages allowed before dropping them.
17    #[arg(long = "cross-chain-queue-size", default_value = "1000")]
18    pub(crate) queue_size: usize,
19
20    /// Maximum number of retries for a cross-chain message.
21    #[arg(long = "cross-chain-max-retries", default_value = "10")]
22    pub(crate) max_retries: u32,
23
24    /// Delay before retrying of cross-chain message.
25    #[arg(long = "cross-chain-retry-delay-ms", default_value = "2000")]
26    pub(crate) retry_delay_ms: u64,
27
28    /// Maximum backoff delay for cross-chain message retries.
29    #[arg(long = "cross-chain-max-backoff-ms", default_value = "30000")]
30    pub(crate) max_backoff_ms: u64,
31
32    /// Introduce a delay before sending every cross-chain message (e.g. for testing purpose).
33    #[arg(long = "cross-chain-sender-delay-ms", default_value = "0")]
34    pub(crate) sender_delay_ms: u64,
35
36    /// Drop cross-chain messages randomly at the given rate (0 <= rate < 1) (meant for testing).
37    #[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    /// Size of the broadcast channel buffer for notifications
69    #[arg(long = "notification-queue-size", default_value = "1000")]
70    pub notification_queue_size: usize,
71
72    /// Maximum number of notifications per batch sent to proxy
73    #[arg(long = "notification-batch-size", default_value = "100")]
74    pub notification_batch_size: usize,
75
76    /// Maximum number of concurrent batch send tasks per proxy
77    #[arg(long = "notification-max-in-flight", default_value = "8")]
78    pub notification_max_in_flight: usize,
79}
80
81pub type ShardId = usize;
82
83/// The network configuration of a shard.
84#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
85pub struct ShardConfig {
86    /// The host name (e.g., an IP address).
87    pub host: String,
88    /// The port.
89    pub port: u16,
90    /// The port on which metrics are served.
91    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/// The network configuration of a proxy.
105#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
106pub struct ProxyConfig {
107    /// The hostname (e.g., an IP address).
108    pub host: String,
109    /// The public facing port. Receives incoming connections from clients.
110    pub public_port: u16,
111    /// The private port. Used for communicating with shards.
112    pub private_port: u16,
113    /// The port on which metrics are served.
114    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/// The network protocol.
129#[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
155/// The network configuration for all shards.
156pub type ValidatorInternalNetworkConfig = ValidatorInternalNetworkPreConfig<NetworkProtocol>;
157
158/// The public network configuration for a validator.
159pub type ValidatorPublicNetworkConfig = ValidatorPublicNetworkPreConfig<NetworkProtocol>;
160
161/// The network configuration for all shards.
162#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
163pub struct ValidatorInternalNetworkPreConfig<P> {
164    /// The public key of the validator.
165    pub public_key: ValidatorPublicKey,
166    /// The network protocol to use internally.
167    pub protocol: P,
168    /// The available shards. Each chain UID is mapped to a unique shard in the vector in
169    /// a static way.
170    pub shards: Vec<ShardConfig>,
171    /// The server configurations for the linera-exporter.
172    /// They can be used as optional locations to forward notifications to destinations other than
173    /// the proxy, by the workers.
174    pub block_exporters: Vec<ExporterServiceConfig>,
175    /// The available proxies.
176    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/// The public network configuration for a validator.
209#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
210pub struct ValidatorPublicNetworkPreConfig<P> {
211    /// The network protocol to use for the validator frontend.
212    pub protocol: P,
213    /// The host name of the validator (IP or hostname).
214    pub host: String,
215    /// The port the validator listens on.
216    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    /// Static shard assignment
293    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        // Use the validator public key to randomise shard assignment.
297        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    /// Gets the [`ShardConfig`] of the shard assigned to the `chain_id`.
307    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)]
313/// The server configuration for the linera-exporter.
314pub struct ExporterServiceConfig {
315    /// The host name of the server (IP or hostname).
316    pub host: String,
317    /// The port for the server to listen on.
318    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}