diem_config/
network_id.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/
7use crate::config::{PeerRole, RoleType};
8use diem_types::PeerId;
9use serde::{Deserialize, Serialize, Serializer};
10use short_hex_str::AsShortHexStr;
11use std::{cmp::Ordering, fmt};
12
13/// A grouping of common information between all networking code for logging.
14/// This should greatly reduce the groupings between these given everywhere, and
15/// will allow for logging accordingly.
16#[derive(Clone, Eq, PartialEq, Serialize)]
17pub struct NetworkContext {
18    /// The type of node
19    role: RoleType,
20    #[serde(serialize_with = "NetworkId::serialize_str")]
21    network_id: NetworkId,
22    peer_id: PeerId,
23}
24
25impl fmt::Debug for NetworkContext {
26    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27        write!(f, "{}", self)
28    }
29}
30
31impl fmt::Display for NetworkContext {
32    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33        write!(
34            f,
35            "[{},{},{}]",
36            self.role,
37            self.network_id.as_str(),
38            self.peer_id.short_str(),
39        )
40    }
41}
42
43impl NetworkContext {
44    pub fn new(
45        role: RoleType, network_id: NetworkId, peer_id: PeerId,
46    ) -> NetworkContext {
47        NetworkContext {
48            role,
49            network_id,
50            peer_id,
51        }
52    }
53
54    pub fn role(&self) -> RoleType { self.role }
55
56    pub fn network_id(&self) -> &NetworkId { &self.network_id }
57
58    pub fn peer_id(&self) -> PeerId { self.peer_id }
59
60    #[cfg(any(test, feature = "testing", feature = "fuzzing"))]
61    pub fn mock_with_peer_id(peer_id: PeerId) -> std::sync::Arc<Self> {
62        std::sync::Arc::new(Self::new(
63            RoleType::Validator,
64            NetworkId::Validator,
65            peer_id,
66        ))
67    }
68
69    #[cfg(any(test, feature = "testing", feature = "fuzzing"))]
70    pub fn mock() -> std::sync::Arc<Self> {
71        std::sync::Arc::new(Self::new(
72            RoleType::Validator,
73            NetworkId::Validator,
74            PeerId::random(),
75        ))
76    }
77}
78
79/// A representation of the network being used in communication.
80/// There should only be one of each NetworkId used for a single node (except
81/// for NetworkId::Public), and handshakes should verify that the NetworkId
82/// being used is the same during a handshake, to effectively ensure
83/// communication is restricted to a network.  Network should be checked that it
84/// is not the `DEFAULT_NETWORK`
85#[derive(Clone, Deserialize, Eq, Hash, PartialEq, Serialize)]
86#[serde(rename = "NetworkId", rename_all = "snake_case")]
87pub enum NetworkId {
88    Validator,
89    Public,
90    Private(String),
91}
92
93impl PartialOrd for NetworkId {
94    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
95        Some(self.cmp(other))
96    }
97}
98
99impl Ord for NetworkId {
100    /// Generalized ordering for determining which network is the most
101    /// important. The lower the ordering, the higher the importance (i.e.,
102    /// the validator network is less than all other networks because it has
103    /// the highest priority).
104    fn cmp(&self, other: &Self) -> Ordering {
105        // To simplify logic below, if it's the same it's equal
106        if self == other {
107            Ordering::Equal
108        } else {
109            // Everywhere below assumes that equal has already been covered
110            match self {
111                NetworkId::Validator => Ordering::Less,
112                NetworkId::Public => Ordering::Greater,
113                NetworkId::Private(_) => match other {
114                    NetworkId::Validator => Ordering::Greater,
115                    NetworkId::Public => Ordering::Less,
116                    NetworkId::Private(_) => {
117                        if self.is_vfn_network() {
118                            Ordering::Less
119                        } else {
120                            Ordering::Greater
121                        }
122                    }
123                },
124            }
125        }
126    }
127}
128
129/// An intra-node identifier for a network of a node unique for a network
130/// This extra layer on top of `NetworkId` mainly exists for the
131/// application-layer (e.g. mempool, state sync) to differentiate between
132/// multiple public networks that a node may belong to
133#[derive(Clone, Deserialize, Eq, Hash, PartialEq, Serialize)]
134pub struct NodeNetworkId(NetworkId, usize);
135
136impl NodeNetworkId {
137    pub fn new(network_id: NetworkId, num_id: usize) -> Self {
138        Self(network_id, num_id)
139    }
140
141    pub fn network_id(&self) -> NetworkId { self.0.clone() }
142}
143
144impl fmt::Debug for NodeNetworkId {
145    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146        write!(f, "{}", self)
147    }
148}
149
150impl fmt::Display for NodeNetworkId {
151    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152        write!(f, "{}:{}", self.0, self.1)
153    }
154}
155
156/// Default needed to handle downstream structs that use `Default`
157impl Default for NetworkId {
158    fn default() -> NetworkId { NetworkId::Public }
159}
160
161impl fmt::Debug for NetworkId {
162    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163        f.write_str(self.as_str())
164    }
165}
166
167impl fmt::Display for NetworkId {
168    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169        f.write_str(self.as_str())
170    }
171}
172
173const VFN_NETWORK: &str = "vfn";
174
175impl NetworkId {
176    /// Convenience function to specify the VFN network
177    pub fn vfn_network() -> NetworkId {
178        NetworkId::Private(VFN_NETWORK.to_string())
179    }
180
181    pub fn is_vfn_network(&self) -> bool {
182        matches!(self, NetworkId::Private(network) if network == VFN_NETWORK)
183    }
184
185    pub fn is_validator_network(&self) -> bool {
186        matches!(self, NetworkId::Validator)
187    }
188
189    /// Roles for a prioritization of relative upstreams
190    pub fn upstream_roles(&self, role: &RoleType) -> &'static [PeerRole] {
191        match self {
192            NetworkId::Validator => &[PeerRole::Validator],
193            NetworkId::Public => &[
194                PeerRole::PreferredUpstream,
195                PeerRole::Upstream,
196                PeerRole::ValidatorFullNode,
197            ],
198            NetworkId::Private(_) => {
199                if self.is_vfn_network() {
200                    match role {
201                        RoleType::Validator => &[],
202                        RoleType::FullNode => &[PeerRole::Validator],
203                    }
204                } else {
205                    &[PeerRole::PreferredUpstream, PeerRole::Upstream]
206                }
207            }
208        }
209    }
210
211    /// Roles for a prioritization of relative downstreams
212    pub fn downstream_roles(&self, role: &RoleType) -> &'static [PeerRole] {
213        match self {
214            NetworkId::Validator => &[PeerRole::Validator],
215            // In order to allow fallbacks, we must allow for nodes to accept
216            // ValidatorFullNodes
217            NetworkId::Public => &[
218                PeerRole::ValidatorFullNode,
219                PeerRole::Downstream,
220                PeerRole::Known,
221                PeerRole::Unknown,
222            ],
223            NetworkId::Private(_) => {
224                if self.is_vfn_network() {
225                    match role {
226                        RoleType::Validator => &[PeerRole::ValidatorFullNode],
227                        RoleType::FullNode => &[],
228                    }
229                } else {
230                    // It's a private network, disallow unknown peers
231                    &[PeerRole::Downstream, PeerRole::Known]
232                }
233            }
234        }
235    }
236
237    pub fn as_str(&self) -> &str {
238        match self {
239            NetworkId::Validator => "Validator",
240            NetworkId::Public => "Public",
241            // We used to return "Private({info})" here; however, it's important
242            // to get the network id str without temp allocs, as this is in the
243            // metrics/logging hot path. In theory, someone could set their
244            // network id as `Private("Validator")`, which would result in
245            // confusing metrics/logging output for them, but would not affect
246            // correctness.
247            NetworkId::Private(info) => info.as_ref(),
248        }
249    }
250
251    fn serialize_str<S>(
252        &self, serializer: S,
253    ) -> std::result::Result<S::Ok, S::Error>
254    where S: Serializer {
255        self.as_str().serialize(serializer)
256    }
257}
258
259#[cfg(test)]
260mod test {
261    use super::*;
262
263    #[test]
264    fn test_ensure_network_id_order() {
265        assert!(NetworkId::Validator < NetworkId::vfn_network());
266        assert!(NetworkId::vfn_network() < NetworkId::Public);
267        assert!(NetworkId::Validator < NetworkId::Public);
268    }
269
270    #[test]
271    fn test_serialization() {
272        let id = NetworkId::vfn_network();
273        let encoded = yaml_serde::to_string(&id).unwrap();
274        let decoded: NetworkId =
275            yaml_serde::from_str(encoded.as_str()).unwrap();
276        assert_eq!(id, decoded);
277        let encoded = yaml_serde::to_string(&id).unwrap().into_bytes();
278        let decoded: NetworkId =
279            yaml_serde::from_slice(encoded.as_slice()).unwrap();
280        assert_eq!(id, decoded);
281
282        let id = NetworkId::Validator;
283        let encoded = yaml_serde::to_string(&id).unwrap();
284        let decoded: NetworkId =
285            yaml_serde::from_str(encoded.as_str()).unwrap();
286        assert_eq!(id, decoded);
287        let encoded = yaml_serde::to_string(&id).unwrap().into_bytes();
288        let decoded: NetworkId =
289            yaml_serde::from_slice(encoded.as_slice()).unwrap();
290        assert_eq!(id, decoded);
291    }
292
293    #[test]
294    fn test_network_context_serialization() {
295        let peer_id = PeerId::random();
296        let context = NetworkContext::new(
297            RoleType::Validator,
298            NetworkId::vfn_network(),
299            peer_id,
300        );
301        let expected = format!(
302            "role: {}\nnetwork_id: {}\npeer_id: {:x}\n",
303            RoleType::Validator,
304            VFN_NETWORK,
305            peer_id
306        );
307        assert_eq!(expected, yaml_serde::to_string(&context).unwrap());
308    }
309}