1//! Watches for chain tip height updates to determine the minimum supported peer protocol version.
23use std::fmt;
45use zebra_chain::{block::Height, chain_tip::ChainTip, parameters::Network};
67use crate::protocol::external::types::Version;
89#[cfg(any(test, feature = "proptest-impl"))]
10mod tests;
1112/// 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}
2021impl<C> fmt::Debug for MinimumPeerVersion<C> {
22fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23// skip the chain tip to avoid locking issues
24f.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}
3132impl<C> MinimumPeerVersion<C>
33where
34C: ChainTip,
35{
36/// Create a new [`MinimumPeerVersion`] to track the minimum supported peer protocol version
37 /// for the current `chain_tip` on the `network`.
38pub 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 }
4647/// 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.
54pub fn changed(&mut self) -> Option<Version> {
55self.update();
5657if self.has_changed {
58self.has_changed = false;
59Some(self.current_minimum)
60 } else {
61None
62}
63 }
6465/// Retrieve the current minimum supported peer protocol version.
66pub fn current(&mut self) -> Version {
67self.update();
68self.current_minimum
69 }
7071/// Check the current chain tip height to determine the minimum peer version, and detect if it
72 /// has changed.
73fn update(&mut self) {
74let height = self.chain_tip.best_tip_height();
75let new_minimum = Version::min_remote_for_height(&self.network, height);
7677if self.current_minimum != new_minimum {
78self.current_minimum = new_minimum;
79self.has_changed = true;
80 }
81 }
8283/// Return the current chain tip height.
84 ///
85 /// If it is not available return height zero.
86pub fn chain_tip_height(&self) -> Height {
87match self.chain_tip.best_tip_height() {
88Some(height) => height,
89None => Height(0),
90 }
91 }
92}
9394/// 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
98C: Clone,
99{
100fn 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}