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    /// Introduce a delay before sending every cross-chain message (e.g. for testing purpose).
29    #[arg(long = "cross-chain-sender-delay-ms", default_value = "0")]
30    pub(crate) sender_delay_ms: u64,
31
32    /// Drop cross-chain messages randomly at the given rate (0 <= rate < 1) (meant for testing).
33    #[arg(long = "cross-chain-sender-failure-rate", default_value = "0.0")]
34    pub(crate) sender_failure_rate: f32,
35}
36
37impl Default for CrossChainConfig {
38    fn default() -> Self {
39        CrossChainConfig::parse_from::<[OsString; 1], OsString>(["".into()])
40    }
41}
42
43impl CrossChainConfig {
44    pub fn to_args(&self) -> Vec<String> {
45        vec![
46            "--cross-chain-queue-size".to_string(),
47            self.queue_size.to_string(),
48            "--cross-chain-max-retries".to_string(),
49            self.max_retries.to_string(),
50            "--cross-chain-retry-delay-ms".to_string(),
51            self.retry_delay_ms.to_string(),
52            "--cross-chain-sender-delay-ms".to_string(),
53            self.sender_delay_ms.to_string(),
54            "--cross-chain-sender-failure-rate".to_string(),
55            self.sender_failure_rate.to_string(),
56        ]
57    }
58}
59
60#[derive(Clone, Debug, Parser)]
61pub struct NotificationConfig {
62    /// Number of notifications allowed before blocking the main server loop
63    #[arg(long = "notification-queue-size", default_value = "1000")]
64    pub(crate) notification_queue_size: usize,
65}
66
67pub type ShardId = usize;
68
69/// The network configuration of a shard.
70#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
71pub struct ShardConfig {
72    /// The host name (e.g., an IP address).
73    pub host: String,
74    /// The port.
75    pub port: u16,
76    /// The port on which metrics are served.
77    pub metrics_port: Option<u16>,
78}
79
80impl ShardConfig {
81    pub fn address(&self) -> String {
82        format!("{}:{}", self.host, self.port)
83    }
84
85    pub fn http_address(&self) -> String {
86        format!("http://{}:{}", self.host, self.port)
87    }
88}
89
90/// The network configuration of a proxy.
91#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
92pub struct ProxyConfig {
93    /// The hostname (e.g., an IP address).
94    pub host: String,
95    /// The public facing port. Receives incoming connections from clients.
96    pub public_port: u16,
97    /// The private port. Used for communicating with shards.
98    pub private_port: u16,
99    /// The port on which metrics are served.
100    pub metrics_port: u16,
101}
102
103impl ProxyConfig {
104    pub fn internal_address(&self, protocol: &NetworkProtocol) -> String {
105        format!(
106            "{}://{}:{}",
107            protocol.scheme(),
108            self.host,
109            self.private_port
110        )
111    }
112}
113
114/// The network protocol.
115#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
116pub enum NetworkProtocol {
117    #[cfg(with_simple_network)]
118    Simple(simple::TransportProtocol),
119    Grpc(TlsConfig),
120}
121
122#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
123pub enum TlsConfig {
124    ClearText,
125    Tls,
126}
127
128impl NetworkProtocol {
129    fn scheme(&self) -> &'static str {
130        match self {
131            #[cfg(with_simple_network)]
132            NetworkProtocol::Simple(transport) => transport.scheme(),
133            NetworkProtocol::Grpc(tls) => match tls {
134                TlsConfig::ClearText => "http",
135                TlsConfig::Tls => "https",
136            },
137        }
138    }
139}
140
141/// The network configuration for all shards.
142pub type ValidatorInternalNetworkConfig = ValidatorInternalNetworkPreConfig<NetworkProtocol>;
143
144/// The public network configuration for a validator.
145pub type ValidatorPublicNetworkConfig = ValidatorPublicNetworkPreConfig<NetworkProtocol>;
146
147/// The network configuration for all shards.
148#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
149pub struct ValidatorInternalNetworkPreConfig<P> {
150    /// The public key of the validator.
151    pub public_key: ValidatorPublicKey,
152    /// The network protocol to use internally.
153    pub protocol: P,
154    /// The available shards. Each chain UID is mapped to a unique shard in the vector in
155    /// a static way.
156    pub shards: Vec<ShardConfig>,
157    /// The server configurations for the linera-exporter.
158    /// They can be used as optional locations to forward notifications to destinations other than
159    /// the proxy, by the workers.
160    pub block_exporters: Vec<ExporterServiceConfig>,
161    /// The available proxies.
162    pub proxies: Vec<ProxyConfig>,
163}
164
165impl<P> ValidatorInternalNetworkPreConfig<P> {
166    pub fn clone_with_protocol<Q>(&self, protocol: Q) -> ValidatorInternalNetworkPreConfig<Q> {
167        ValidatorInternalNetworkPreConfig {
168            public_key: self.public_key,
169            protocol,
170            shards: self.shards.clone(),
171            block_exporters: self.block_exporters.clone(),
172            proxies: self.proxies.clone(),
173        }
174    }
175}
176
177impl ValidatorInternalNetworkConfig {
178    pub fn exporter_addresses(&self) -> Vec<String> {
179        self.block_exporters
180            .iter()
181            .map(|ExporterServiceConfig { host, port }| {
182                format!("{}://{}:{}", self.protocol.scheme(), host, port)
183            })
184            .collect::<Vec<_>>()
185    }
186}
187
188impl ValidatorPublicNetworkConfig {
189    pub fn http_address(&self) -> String {
190        format!("{}://{}:{}", self.protocol.scheme(), self.host, self.port)
191    }
192}
193
194/// The public network configuration for a validator.
195#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
196pub struct ValidatorPublicNetworkPreConfig<P> {
197    /// The network protocol to use for the validator frontend.
198    pub protocol: P,
199    /// The host name of the validator (IP or hostname).
200    pub host: String,
201    /// The port the validator listens on.
202    pub port: u16,
203}
204
205impl<P> ValidatorPublicNetworkPreConfig<P> {
206    pub fn clone_with_protocol<Q>(&self, protocol: Q) -> ValidatorPublicNetworkPreConfig<Q> {
207        ValidatorPublicNetworkPreConfig {
208            protocol,
209            host: self.host.clone(),
210            port: self.port,
211        }
212    }
213}
214
215impl<P> std::fmt::Display for ValidatorPublicNetworkPreConfig<P>
216where
217    P: std::fmt::Display,
218{
219    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220        write!(f, "{}:{}:{}", self.protocol, self.host, self.port)
221    }
222}
223
224impl std::fmt::Display for NetworkProtocol {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        match self {
227            #[cfg(with_simple_network)]
228            NetworkProtocol::Simple(protocol) => write!(f, "{:?}", protocol),
229            NetworkProtocol::Grpc(tls) => match tls {
230                TlsConfig::ClearText => write!(f, "grpc"),
231                TlsConfig::Tls => write!(f, "grpcs"),
232            },
233        }
234    }
235}
236
237impl<P> std::str::FromStr for ValidatorPublicNetworkPreConfig<P>
238where
239    P: std::str::FromStr,
240    P::Err: std::fmt::Display,
241{
242    type Err = anyhow::Error;
243
244    fn from_str(s: &str) -> Result<Self, Self::Err> {
245        let parts = s.split(':').collect::<Vec<_>>();
246        anyhow::ensure!(
247            parts.len() == 3,
248            "Expecting format `(tcp|udp|grpc|grpcs):host:port`"
249        );
250        let protocol = parts[0].parse().map_err(|s| anyhow::anyhow!("{}", s))?;
251        let host = parts[1].to_owned();
252        let port = parts[2].parse()?;
253        Ok(ValidatorPublicNetworkPreConfig {
254            protocol,
255            host,
256            port,
257        })
258    }
259}
260
261impl std::str::FromStr for NetworkProtocol {
262    type Err = String;
263
264    fn from_str(s: &str) -> Result<Self, Self::Err> {
265        let protocol = match s {
266            "grpc" => Self::Grpc(TlsConfig::ClearText),
267            "grpcs" => Self::Grpc(TlsConfig::Tls),
268            #[cfg(with_simple_network)]
269            s => Self::Simple(simple::TransportProtocol::from_str(s)?),
270            #[cfg(not(with_simple_network))]
271            s => return Err(format!("unsupported protocol: {s:?}")),
272        };
273        Ok(protocol)
274    }
275}
276
277impl<P> ValidatorInternalNetworkPreConfig<P> {
278    /// Static shard assignment
279    pub fn get_shard_id(&self, chain_id: ChainId) -> ShardId {
280        use std::hash::{Hash, Hasher};
281        let mut s = std::collections::hash_map::DefaultHasher::new();
282        // Use the validator public key to randomise shard assignment.
283        self.public_key.hash(&mut s);
284        chain_id.hash(&mut s);
285        (s.finish() as ShardId) % self.shards.len()
286    }
287
288    pub fn shard(&self, shard_id: ShardId) -> &ShardConfig {
289        &self.shards[shard_id]
290    }
291
292    /// Gets the [`ShardConfig`] of the shard assigned to the `chain_id`.
293    pub fn get_shard_for(&self, chain_id: ChainId) -> &ShardConfig {
294        self.shard(self.get_shard_id(chain_id))
295    }
296}
297
298#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
299/// The server configuration for the linera-exporter.
300pub struct ExporterServiceConfig {
301    /// The host name of the server (IP or hostname).
302    pub host: String,
303    /// The port for the server to listen on.
304    pub port: u16,
305}
306
307#[test]
308fn cross_chain_config_to_args() {
309    let config = CrossChainConfig::default();
310    let args = config.to_args();
311    let mut cmd = vec![String::new()];
312    cmd.extend(args.clone());
313    let config2 = CrossChainConfig::parse_from(cmd);
314    let args2 = config2.to_args();
315    assert_eq!(config, config2);
316    assert_eq!(args, args2);
317}