Skip to main content

zebra_network/protocol/external/
types.rs

1use std::{cmp::max, fmt};
2
3use zebra_chain::{
4    block,
5    parameters::{
6        Network::{self, *},
7        NetworkUpgrade::{self, *},
8    },
9};
10
11use crate::constants::{self, CURRENT_NETWORK_PROTOCOL_VERSION};
12
13#[cfg(any(test, feature = "proptest-impl"))]
14use proptest_derive::Arbitrary;
15
16/// A protocol version number.
17#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
18pub struct Version(pub u32);
19
20impl fmt::Display for Version {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        f.write_str(&self.0.to_string())
23    }
24}
25
26impl Version {
27    /// Returns the minimum remote node network protocol version for `network` and
28    /// `height`. Zebra disconnects from peers with lower versions.
29    ///
30    /// # Panics
31    ///
32    /// If we are incompatible with our own minimum remote protocol version.
33    pub fn min_remote_for_height(
34        network: &Network,
35        height: impl Into<Option<block::Height>>,
36    ) -> Version {
37        let height = height.into().unwrap_or(block::Height(0));
38        let min_spec = Version::min_specified_for_height(network, height);
39
40        // shut down if our own version is too old
41        assert!(
42            constants::CURRENT_NETWORK_PROTOCOL_VERSION >= min_spec,
43            "Zebra does not implement the minimum specified {:?} protocol version for {:?} at {:?}",
44            NetworkUpgrade::current(network, height),
45            network,
46            height,
47        );
48
49        max(min_spec, Version::initial_min_for_network(network))
50    }
51
52    /// Returns the minimum supported network protocol version for `network`.
53    ///
54    /// This is the minimum peer version when Zebra is significantly behind current tip:
55    /// - during the initial block download,
56    /// - after Zebra restarts, and
57    /// - after Zebra's local network is slow or shut down.
58    fn initial_min_for_network(network: &Network) -> Version {
59        *constants::INITIAL_MIN_NETWORK_PROTOCOL_VERSION
60            .get(&network.kind())
61            .expect("We always have a value for testnet or mainnet")
62    }
63
64    /// Returns the minimum specified network protocol version for `network` and
65    /// `height`.
66    ///
67    /// This is the minimum peer version when Zebra is close to the current tip.
68    fn min_specified_for_height(network: &Network, height: block::Height) -> Version {
69        let network_upgrade = NetworkUpgrade::current(network, height);
70        Version::min_specified_for_upgrade(network, network_upgrade)
71    }
72
73    /// Returns the minimum specified network protocol version for `network` and
74    /// `network_upgrade`.
75    ///
76    /// ## ZIP-253
77    ///
78    /// > Nodes compatible with a network upgrade activation MUST advertise a network protocol
79    /// > version that is greater than or equal to the MIN_NETWORK_PROTOCOL_VERSION for that
80    /// > activation.
81    ///
82    /// <https://zips.z.cash/zip-0253>
83    ///
84    /// ### Notes
85    ///
86    /// - The citation above is a generalization of a statement in ZIP-253 since that ZIP is
87    ///   concerned only with NU6 on Mainnet and Testnet.
88    pub(crate) fn min_specified_for_upgrade(
89        network: &Network,
90        network_upgrade: NetworkUpgrade,
91    ) -> Version {
92        Version(match (network, network_upgrade) {
93            (_, Genesis) | (_, BeforeOverwinter) => 170_002,
94            (Testnet(params), Overwinter) if params.is_default_testnet() => 170_003,
95            (Mainnet, Overwinter) => 170_005,
96            (Testnet(params), Sapling) if params.is_default_testnet() => 170_007,
97            (Testnet(params), Sapling) if params.is_regtest() => 170_006,
98            (Mainnet, Sapling) => 170_007,
99            (Testnet(params), Blossom) if params.is_default_testnet() || params.is_regtest() => {
100                170_008
101            }
102            (Mainnet, Blossom) => 170_009,
103            (Testnet(params), Heartwood) if params.is_default_testnet() || params.is_regtest() => {
104                170_010
105            }
106            (Mainnet, Heartwood) => 170_011,
107            (Testnet(params), Canopy) if params.is_default_testnet() || params.is_regtest() => {
108                170_012
109            }
110            (Mainnet, Canopy) => 170_013,
111            (Testnet(params), Nu5) if params.is_default_testnet() || params.is_regtest() => 170_050,
112            (Mainnet, Nu5) => 170_100,
113            (Testnet(params), Nu6) if params.is_default_testnet() || params.is_regtest() => 170_110,
114            (Mainnet, Nu6) => 170_120,
115            (Testnet(params), Nu6_1) if params.is_default_testnet() || params.is_regtest() => {
116                170_130
117            }
118            (Mainnet, Nu6_1) => 170_140,
119            (Testnet(params), Nu6_2) if params.is_default_testnet() || params.is_regtest() => {
120                170_150
121            }
122            (Mainnet, Nu6_2) => 170_150,
123            // TODO: these NU6.3 (Ironwood) and Nu7 protocol versions are provisional, bumped above
124            // Nu6_2's 170_150. Update them when the real values are specified.
125            (Testnet(params), Nu6_3) if params.is_default_testnet() || params.is_regtest() => {
126                170_160
127            }
128            (Mainnet, Nu6_3) => 170_160,
129            (Testnet(params), Nu7) if params.is_default_testnet() || params.is_regtest() => 170_170,
130            (Mainnet, Nu7) => 170_180,
131
132            // It should be fine to reject peers with earlier network protocol versions on custom testnets for now.
133            (Testnet(_), _) => CURRENT_NETWORK_PROTOCOL_VERSION.0,
134
135            #[cfg(zcash_unstable = "zfuture")]
136            (Mainnet, ZFuture) => {
137                panic!("ZFuture network upgrade should not be active on Mainnet")
138            }
139        })
140    }
141}
142
143bitflags! {
144    /// A bitflag describing services advertised by a node in the network.
145    ///
146    /// Note that bits 24-31 are reserved for temporary experiments; other
147    /// service bits should be allocated via the ZIP process.
148    #[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
149    pub struct PeerServices: u64 {
150        /// NODE_NETWORK means that the node is a full node capable of serving
151        /// blocks, as opposed to a light client that makes network requests but
152        /// does not provide network services.
153        const NODE_NETWORK = 1;
154    }
155}
156
157/// A nonce used in the networking layer to identify messages.
158#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
159#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
160pub struct Nonce(pub u64);
161
162impl Default for Nonce {
163    fn default() -> Self {
164        use rand::{thread_rng, Rng};
165        Self(thread_rng().gen())
166    }
167}
168
169/// A random value to add to the seed value in a hash function.
170#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
171#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
172pub struct Tweak(pub u32);
173
174impl Default for Tweak {
175    fn default() -> Self {
176        use rand::{thread_rng, Rng};
177        Self(thread_rng().gen())
178    }
179}
180
181/// A Bloom filter consisting of a bit field of arbitrary byte-aligned
182/// size, maximum size is 36,000 bytes.
183#[derive(Clone, Debug, Eq, PartialEq)]
184#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
185pub struct Filter(pub Vec<u8>);
186
187#[cfg(test)]
188mod test {
189    use super::*;
190
191    #[test]
192    fn version_extremes_mainnet() {
193        version_extremes(&Mainnet)
194    }
195
196    #[test]
197    fn version_extremes_testnet() {
198        version_extremes(&Network::new_default_testnet())
199    }
200
201    /// Test the min_specified_for_upgrade and min_specified_for_height functions for `network` with
202    /// extreme values.
203    fn version_extremes(network: &Network) {
204        let _init_guard = zebra_test::init();
205
206        assert_eq!(
207            Version::min_specified_for_height(network, block::Height(0)),
208            Version::min_specified_for_upgrade(network, BeforeOverwinter),
209        );
210
211        // We assume that the last version we know about continues forever
212        // (even if we suspect that won't be true)
213        assert_ne!(
214            Version::min_specified_for_height(network, block::Height::MAX),
215            Version::min_specified_for_upgrade(network, BeforeOverwinter),
216        );
217    }
218
219    #[test]
220    fn version_consistent_mainnet() {
221        version_consistent(&Mainnet)
222    }
223
224    #[test]
225    fn version_consistent_testnet() {
226        version_consistent(&Network::new_default_testnet())
227    }
228
229    /// Check that the min_specified_for_upgrade and min_specified_for_height functions
230    /// are consistent for `network`.
231    fn version_consistent(network: &Network) {
232        let _init_guard = zebra_test::init();
233
234        let highest_network_upgrade = NetworkUpgrade::current(network, block::Height::MAX);
235        assert!(
236            matches!(highest_network_upgrade, Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7),
237            "expected coverage of all network upgrades: \
238            add the new network upgrade to the list in this test"
239        );
240
241        for &network_upgrade in &[
242            BeforeOverwinter,
243            Overwinter,
244            Sapling,
245            Blossom,
246            Heartwood,
247            Canopy,
248            Nu5,
249            Nu6,
250            Nu6_1,
251            Nu6_2,
252            Nu6_3,
253            Nu7,
254        ] {
255            let height = network_upgrade.activation_height(network);
256            if let Some(height) = height {
257                assert_eq!(
258                    Version::min_specified_for_upgrade(network, network_upgrade),
259                    Version::min_specified_for_height(network, height)
260                );
261            }
262        }
263    }
264}