zebra_network/peer/
minimum_peer_version.rs

1//! Watches for chain tip height updates to determine the minimum supported peer protocol version.
2
3use std::fmt;
4
5use zebra_chain::{block::Height, chain_tip::ChainTip, parameters::Network};
6
7use crate::protocol::external::types::Version;
8
9#[cfg(any(test, feature = "proptest-impl"))]
10mod tests;
11
12/// A helper type to monitor the chain tip in order to determine the minimum peer protocol version
13/// that is currently supported.
14pub struct MinimumPeerVersion<C> {
15    network: Network,
16    chain_tip: C,
17    current_minimum: Version,
18    has_changed: bool,
19}
20
21impl<C> fmt::Debug for MinimumPeerVersion<C> {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        // skip the chain tip to avoid locking issues
24        f.debug_struct(std::any::type_name::<MinimumPeerVersion<C>>())
25            .field("network", &self.network)
26            .field("current_minimum", &self.current_minimum)
27            .field("has_changed", &self.has_changed)
28            .finish()
29    }
30}
31
32impl<C> MinimumPeerVersion<C>
33where
34    C: ChainTip,
35{
36    /// Create a new [`MinimumPeerVersion`] to track the minimum supported peer protocol version
37    /// for the current `chain_tip` on the `network`.
38    pub fn new(chain_tip: C, network: &Network) -> Self {
39        MinimumPeerVersion {
40            network: network.clone(),
41            chain_tip,
42            current_minimum: Version::min_remote_for_height(network, None),
43            has_changed: true,
44        }
45    }
46
47    /// Check if the minimum supported peer version has changed since the last time this was
48    /// called.
49    ///
50    /// The first call returns the current minimum version, and subsequent calls return [`None`]
51    /// until the minimum version changes. When that happens, the next call returns the new minimum
52    /// version, and subsequent calls return [`None`] again until the minimum version changes once
53    /// more.
54    pub fn changed(&mut self) -> Option<Version> {
55        self.update();
56
57        if self.has_changed {
58            self.has_changed = false;
59            Some(self.current_minimum)
60        } else {
61            None
62        }
63    }
64
65    /// Retrieve the current minimum supported peer protocol version.
66    pub fn current(&mut self) -> Version {
67        self.update();
68        self.current_minimum
69    }
70
71    /// Check the current chain tip height to determine the minimum peer version, and detect if it
72    /// has changed.
73    fn update(&mut self) {
74        let height = self.chain_tip.best_tip_height();
75        let new_minimum = Version::min_remote_for_height(&self.network, height);
76
77        if self.current_minimum != new_minimum {
78            self.current_minimum = new_minimum;
79            self.has_changed = true;
80        }
81    }
82
83    /// Return the current chain tip height.
84    ///
85    /// If it is not available return height zero.
86    pub fn chain_tip_height(&self) -> Height {
87        match self.chain_tip.best_tip_height() {
88            Some(height) => height,
89            None => Height(0),
90        }
91    }
92}
93
94/// A custom [`Clone`] implementation to ensure that the first call to
95/// [`MinimumPeerVersion::changed`] after the clone will always return the current version.
96impl<C> Clone for MinimumPeerVersion<C>
97where
98    C: Clone,
99{
100    fn clone(&self) -> Self {
101        MinimumPeerVersion {
102            network: self.network.clone(),
103            chain_tip: self.chain_tip.clone(),
104            current_minimum: self.current_minimum,
105            has_changed: true,
106        }
107    }
108}