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