diem_config/config/
network_config.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::{
9    config::{Error, SecureBackend},
10    keys::ConfigKey,
11    network_id::NetworkId,
12    utils,
13};
14use diem_crypto::{x25519, Uniform, ValidCryptoMaterial};
15use diem_secure_storage::{CryptoStorage, KVStorage, Storage};
16use diem_types::{
17    network_address::NetworkAddress,
18    transaction::authenticator::AuthenticationKey, PeerId,
19};
20use rand::{
21    rngs::{OsRng, StdRng},
22    Rng, SeedableRng,
23};
24use serde::{Deserialize, Serialize};
25use short_hex_str::AsShortHexStr;
26use std::{
27    collections::{HashMap, HashSet},
28    convert::TryFrom,
29    string::ToString,
30};
31
32// TODO: We could possibly move these constants somewhere else, but since they
33// are defaults for the   configurations of the system, we'll leave it here for
34// now.
35/// Current supported protocol negotiation handshake version. See
36/// `network::protocols::wire::v1`(../../network/protocols/wire/handshake/v1/
37/// index.html).
38pub const HANDSHAKE_VERSION: u8 = 0;
39pub const NETWORK_CHANNEL_SIZE: usize = 1024;
40pub const PING_INTERVAL_MS: u64 = 1000;
41pub const PING_TIMEOUT_MS: u64 = 10_000;
42pub const PING_FAILURES_TOLERATED: u64 = 5;
43pub const CONNECTIVITY_CHECK_INTERVAL_MS: u64 = 5000;
44pub const MAX_CONCURRENT_NETWORK_REQS: usize = 100;
45pub const MAX_CONNECTION_DELAY_MS: u64 = 60_000; /* 1 minute */
46pub const MAX_FULLNODE_OUTBOUND_CONNECTIONS: usize = 3;
47pub const MAX_INBOUND_CONNECTIONS: usize = 100;
48pub const MAX_FRAME_SIZE: usize = 8 * 1024 * 1024; /* 8 MiB */
49pub const CONNECTION_BACKOFF_BASE: u64 = 2;
50pub const IP_BYTE_BUCKET_RATE: usize = 102400 /* 100 KiB */;
51pub const IP_BYTE_BUCKET_SIZE: usize = IP_BYTE_BUCKET_RATE;
52
53#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
54#[serde(default, deny_unknown_fields)]
55pub struct NetworkConfig {
56    // Maximum backoff delay for connecting outbound to peers
57    pub max_connection_delay_ms: u64,
58    // Base for outbound connection backoff
59    pub connection_backoff_base: u64,
60    // Rate to check connectivity to connected peers
61    pub connectivity_check_interval_ms: u64,
62    // Size of all network channels
63    pub network_channel_size: usize,
64    // Maximum number of concurrent network requests
65    pub max_concurrent_network_reqs: usize,
66    // Choose a protocol to discover and dial out to other peers on this
67    // network. `DiscoveryMethod::None` disables discovery and dialing out
68    // (unless you have seed peers configured).
69    pub discovery_method: DiscoveryMethod,
70    pub identity: Identity,
71    // TODO: Add support for multiple listen/advertised addresses in config.
72    // The address that this node is listening on for new connections.
73    pub listen_address: NetworkAddress,
74    // Select this to enforce that both peers should authenticate each other,
75    // otherwise authentication only occurs for outgoing connections.
76    pub mutual_authentication: bool,
77    // Used to store network address encryption keys for validator nodes
78    pub network_address_key_backend: Option<SecureBackend>,
79    pub network_id: NetworkId,
80    // Addresses of initial peers to connect to. In a mutual_authentication
81    // network, we will extract the public keys from these addresses to set
82    // our initial trusted peers set.  TODO: Replace usage in configs with
83    // `seeds` this is for backwards compatibility
84    pub seed_addrs: HashMap<PeerId, Vec<NetworkAddress>>,
85    // The initial peers to connect to prior to onchain discovery
86    pub seeds: PeerSet,
87    // The maximum size of an inbound or outbound request frame
88    pub max_frame_size: usize,
89    // Enables proxy protocol on incoming connections to get original source
90    // addresses
91    pub enable_proxy_protocol: bool,
92    // Interval to send healthcheck pings to peers
93    pub ping_interval_ms: u64,
94    // Timeout until a healthcheck ping is rejected
95    pub ping_timeout_ms: u64,
96    // Number of failed healthcheck pings until a peer is marked unhealthy
97    pub ping_failures_tolerated: u64,
98    // Maximum number of outbound connections, limited by ConnectivityManager
99    pub max_outbound_connections: usize,
100    // Maximum number of outbound connections, limited by PeerManager
101    pub max_inbound_connections: usize,
102    // Inbound rate limiting configuration, if not specified, no rate limiting
103    pub inbound_rate_limit_config: Option<RateLimitConfig>,
104    // Outbound rate limiting configuration, if not specified, no rate limiting
105    pub outbound_rate_limit_config: Option<RateLimitConfig>,
106}
107
108impl Default for NetworkConfig {
109    fn default() -> Self {
110        NetworkConfig::network_with_id(NetworkId::default())
111    }
112}
113
114impl NetworkConfig {
115    pub fn network_with_id(network_id: NetworkId) -> NetworkConfig {
116        let mut config = Self {
117            discovery_method: DiscoveryMethod::None,
118            identity: Identity::None,
119            listen_address: "/ip4/0.0.0.0/tcp/6180".parse().unwrap(),
120            mutual_authentication: false,
121            network_address_key_backend: None,
122            network_id,
123            seed_addrs: HashMap::new(),
124            seeds: PeerSet::default(),
125            max_frame_size: MAX_FRAME_SIZE,
126            enable_proxy_protocol: false,
127            max_connection_delay_ms: MAX_CONNECTION_DELAY_MS,
128            connectivity_check_interval_ms: CONNECTIVITY_CHECK_INTERVAL_MS,
129            network_channel_size: NETWORK_CHANNEL_SIZE,
130            max_concurrent_network_reqs: MAX_CONCURRENT_NETWORK_REQS,
131            connection_backoff_base: CONNECTION_BACKOFF_BASE,
132            ping_interval_ms: PING_INTERVAL_MS,
133            ping_timeout_ms: PING_TIMEOUT_MS,
134            ping_failures_tolerated: PING_FAILURES_TOLERATED,
135            max_outbound_connections: MAX_FULLNODE_OUTBOUND_CONNECTIONS,
136            max_inbound_connections: MAX_INBOUND_CONNECTIONS,
137            inbound_rate_limit_config: None,
138            outbound_rate_limit_config: None,
139        };
140        config.prepare_identity();
141        config
142    }
143}
144
145impl NetworkConfig {
146    pub fn identity_key(&self) -> x25519::PrivateKey {
147        let key = match &self.identity {
148            Identity::FromConfig(config) => Some(config.key.clone().key),
149            Identity::FromStorage(config) => {
150                let storage: Storage = (&config.backend).into();
151                let key = storage
152                    .export_private_key(&config.key_name)
153                    .expect("Unable to read key");
154                let key = x25519::PrivateKey::from_ed25519_private_bytes(
155                    &key.to_bytes(),
156                )
157                .expect("Unable to convert key");
158                Some(key)
159            }
160            Identity::None => None,
161        };
162        key.expect("identity key should be present")
163    }
164
165    pub fn identity_from_storage(&self) -> IdentityFromStorage {
166        if let Identity::FromStorage(identity) = self.identity.clone() {
167            identity
168        } else {
169            panic!("Invalid identity found, expected a storage identity.");
170        }
171    }
172
173    /// Per convenience, so that NetworkId isn't needed to be specified for
174    /// `validator_networks`
175    pub fn load_validator_network(&mut self) -> Result<(), Error> {
176        self.network_id = NetworkId::Validator;
177        self.load()
178    }
179
180    pub fn load_fullnode_network(&mut self) -> Result<(), Error> {
181        if self.network_id.is_validator_network() {
182            return Err(Error::InvariantViolation(format!(
183                "Set {} network for a non-validator network",
184                self.network_id
185            )));
186        }
187        self.load()
188    }
189
190    fn load(&mut self) -> Result<(), Error> {
191        if self.listen_address.to_string().is_empty() {
192            self.listen_address = utils::get_local_ip().ok_or_else(|| {
193                Error::InvariantViolation("No local IP".to_string())
194            })?;
195        }
196
197        self.prepare_identity();
198        Ok(())
199    }
200
201    pub fn peer_id(&self) -> PeerId {
202        match &self.identity {
203            Identity::FromConfig(config) => Some(config.peer_id),
204            Identity::FromStorage(config) => {
205                let storage: Storage = (&config.backend).into();
206                let peer_id = storage
207                    .get::<PeerId>(&config.peer_id_name)
208                    .expect("Unable to read peer id")
209                    .value;
210                Some(peer_id)
211            }
212            Identity::None => None,
213        }
214        .expect("peer id should be present")
215    }
216
217    fn prepare_identity(&mut self) {
218        match &mut self.identity {
219            Identity::FromStorage(_) => (),
220            Identity::None => {
221                let mut rng = StdRng::from_seed(OsRng.gen());
222                let key = x25519::PrivateKey::generate(&mut rng);
223                let peer_id =
224                    diem_types::account_address::from_identity_public_key(
225                        key.public_key(),
226                    );
227                self.identity = Identity::from_config(key, peer_id);
228            }
229            Identity::FromConfig(config) => {
230                let peer_id =
231                    diem_types::account_address::from_identity_public_key(
232                        config.key.public_key(),
233                    );
234                if config.peer_id == PeerId::ZERO {
235                    config.peer_id = peer_id;
236                }
237            }
238        };
239    }
240
241    pub fn random(&mut self, rng: &mut StdRng) {
242        self.random_with_peer_id(rng, None);
243    }
244
245    pub fn random_with_peer_id(
246        &mut self, rng: &mut StdRng, peer_id: Option<PeerId>,
247    ) {
248        let identity_key = x25519::PrivateKey::generate(rng);
249        let peer_id = if let Some(peer_id) = peer_id {
250            peer_id
251        } else {
252            AuthenticationKey::try_from(identity_key.public_key().as_slice())
253                .unwrap()
254                .derived_address()
255        };
256        self.identity = Identity::from_config(identity_key, peer_id);
257    }
258
259    fn verify_address(
260        peer_id: &PeerId, addr: &NetworkAddress,
261    ) -> Result<(), Error> {
262        crate::config::invariant(
263            addr.is_diemnet_addr(),
264            format!(
265                "Unexpected seed peer address format: peer_id: {}, addr: '{}'",
266                peer_id.short_str(),
267                addr,
268            ),
269        )
270    }
271
272    // Verifies both the `seed_addrs` and `seeds` before they're merged
273    pub fn verify_seeds(&self) -> Result<(), Error> {
274        for (peer_id, addrs) in self.seed_addrs.iter() {
275            for addr in addrs {
276                Self::verify_address(peer_id, addr)?;
277            }
278        }
279
280        for (peer_id, seed) in self.seeds.iter() {
281            for addr in seed.addresses.iter() {
282                Self::verify_address(peer_id, addr)?;
283            }
284
285            // Require there to be a pubkey somewhere, either in the address
286            // (assumed by `is_diemnet_addr`)
287            crate::config::invariant(
288                !seed.keys.is_empty() || !seed.addresses.is_empty(),
289                format!("Seed peer {} has no pubkeys", peer_id.short_str()),
290            )?;
291        }
292        Ok(())
293    }
294}
295
296#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
297#[serde(rename_all = "snake_case")]
298pub enum DiscoveryMethod {
299    Onchain,
300    None,
301}
302
303#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
304#[serde(rename_all = "snake_case", tag = "type")]
305pub enum Identity {
306    FromConfig(IdentityFromConfig),
307    FromStorage(IdentityFromStorage),
308    None,
309}
310
311impl Identity {
312    pub fn from_config(key: x25519::PrivateKey, peer_id: PeerId) -> Self {
313        let key = ConfigKey::new(key);
314        Identity::FromConfig(IdentityFromConfig { key, peer_id })
315    }
316
317    pub fn from_storage(
318        key_name: String, peer_id_name: String, backend: SecureBackend,
319    ) -> Self {
320        Identity::FromStorage(IdentityFromStorage {
321            backend,
322            key_name,
323            peer_id_name,
324        })
325    }
326}
327
328/// The identity is stored within the config.
329#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
330#[serde(deny_unknown_fields)]
331pub struct IdentityFromConfig {
332    #[serde(flatten)]
333    pub key: ConfigKey<x25519::PrivateKey>,
334    pub peer_id: PeerId,
335}
336
337/// This represents an identity in a secure-storage as defined in
338/// NodeConfig::secure.
339#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
340#[serde(deny_unknown_fields)]
341pub struct IdentityFromStorage {
342    pub backend: SecureBackend,
343    pub key_name: String,
344    pub peer_id_name: String,
345}
346
347#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
348pub struct RateLimitConfig {
349    /// Maximum number of bytes/s for an IP
350    pub ip_byte_bucket_rate: usize,
351    /// Maximum burst of bytes for an IP
352    pub ip_byte_bucket_size: usize,
353    /// Initial amount of tokens initially in the bucket
354    pub initial_bucket_fill_percentage: u8,
355    /// Allow for disabling the throttles
356    pub enabled: bool,
357}
358
359impl Default for RateLimitConfig {
360    fn default() -> Self {
361        Self {
362            ip_byte_bucket_rate: IP_BYTE_BUCKET_RATE,
363            ip_byte_bucket_size: IP_BYTE_BUCKET_SIZE,
364            initial_bucket_fill_percentage: 25,
365            enabled: true,
366        }
367    }
368}
369
370pub type PeerSet = HashMap<PeerId, Peer>;
371
372// TODO: Combine with RoleType?
373/// Represents the Role that a peer plays in the network ecosystem rather than
374/// the type of node. Determines how nodes are connected to other nodes, and how
375/// discovery views them.
376///
377/// Rules for upstream nodes via Peer Role:
378///
379/// Validator -> Always upstream if not Validator else P2P
380/// PreferredUpstream -> Always upstream, overriding any other discovery
381/// ValidatorFullNode -> Always upstream for incoming connections (including
382/// other ValidatorFullNodes) Upstream -> Upstream, if no ValidatorFullNode or
383/// PreferredUpstream.  Useful for initial seed discovery Downstream ->
384/// Downstream, defining a controlled downstream that I always want to connect
385/// Known -> A known peer, but it has no particular role assigned to it
386/// Unknown -> Undiscovered peer, likely due to a non-mutually authenticated
387/// connection always downstream
388#[derive(
389    Clone,
390    Copy,
391    Debug,
392    Deserialize,
393    Eq,
394    Hash,
395    Ord,
396    PartialEq,
397    PartialOrd,
398    Serialize,
399)]
400pub enum PeerRole {
401    Validator = 0,
402    PreferredUpstream,
403    Upstream,
404    ValidatorFullNode,
405    Downstream,
406    Known,
407    Unknown,
408}
409
410impl Default for PeerRole {
411    /// Default to least trusted
412    fn default() -> Self { PeerRole::Unknown }
413}
414
415/// Represents a single seed configuration for a seed peer
416#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
417#[serde(default)]
418pub struct Peer {
419    pub addresses: Vec<NetworkAddress>,
420    pub keys: HashSet<x25519::PublicKey>,
421    pub role: PeerRole,
422}
423
424impl Peer {
425    /// Combines `Vec<NetworkAddress>` keys with the `HashSet` given
426    pub fn new(
427        addresses: Vec<NetworkAddress>, mut keys: HashSet<x25519::PublicKey>,
428        role: PeerRole,
429    ) -> Peer {
430        let addr_keys = addresses
431            .iter()
432            .filter_map(NetworkAddress::find_noise_proto);
433        keys.extend(addr_keys);
434        Peer {
435            addresses,
436            keys,
437            role,
438        }
439    }
440
441    /// Combines two `Peer`.  Note: Does not merge duplicate addresses
442    /// TODO: Instead of rejecting, maybe pick one of the roles?
443    pub fn extend(&mut self, other: Peer) -> Result<(), Error> {
444        crate::config::invariant(
445            self.role != other.role,
446            format!(
447                "Roles don't match self {:?} vs other {:?}",
448                self.role, other.role
449            ),
450        )?;
451        self.addresses.extend(other.addresses);
452        self.keys.extend(other.keys);
453        Ok(())
454    }
455
456    pub fn from_addrs(role: PeerRole, addresses: Vec<NetworkAddress>) -> Peer {
457        let keys: HashSet<x25519::PublicKey> = addresses
458            .iter()
459            .filter_map(NetworkAddress::find_noise_proto)
460            .collect();
461        Peer::new(addresses, keys, role)
462    }
463}