Support a minimum protocol version during initial block download (#2395)

* Support a min protocol version during initial block download

But don't actually use the state height yet.

Also rename some functions and constants.

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
This commit is contained in:
teor 2021-06-29 10:49:03 +10:00 committed by GitHub
parent c06cd19239
commit 7586699f86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 56 deletions

View File

@ -100,19 +100,25 @@ pub const USER_AGENT: &str = "/Zebra:1.0.0-alpha.11/";
/// during connection setup. /// during connection setup.
/// ///
/// The current protocol version is checked by our peers. If it is too old, /// The current protocol version is checked by our peers. If it is too old,
/// newer peers will refuse to connect to us. /// newer peers will disconnect from us.
/// ///
/// The current protocol version typically changes before Mainnet and Testnet /// The current protocol version typically changes before Mainnet and Testnet
/// network upgrades. /// network upgrades.
pub const CURRENT_VERSION: Version = Version(170_013); pub const CURRENT_NETWORK_PROTOCOL_VERSION: Version = Version(170_013);
/// The most recent bilateral consensus upgrade implemented by this crate. /// The minimum network protocol version accepted by this crate for each network,
/// represented as a network upgrade.
/// ///
/// The minimum network upgrade is used to check the protocol versions of our /// The minimum protocol version is used to check the protocol versions of our
/// peers. If their versions are too old, we will disconnect from them. /// peers during the initial block download. After the intial block download,
// /// we use the current block height to select the minimum network protocol
// TODO: replace with NetworkUpgrade::current(network, height). (#1334) /// version.
pub const MIN_NETWORK_UPGRADE: NetworkUpgrade = NetworkUpgrade::Canopy; ///
/// If peer versions are too old, we will disconnect from them.
///
/// The minimum network protocol version typically changes after Mainnet network
/// upgrades.
pub const INITIAL_MIN_NETWORK_PROTOCOL_VERSION: NetworkUpgrade = NetworkUpgrade::Canopy;
/// The default RTT estimate for peer responses. /// The default RTT estimate for peer responses.
/// ///

View File

@ -487,7 +487,7 @@ pub async fn negotiate_version(
}; };
let our_version = Message::Version { let our_version = Message::Version {
version: constants::CURRENT_VERSION, version: constants::CURRENT_NETWORK_PROTOCOL_VERSION,
services: our_services, services: our_services,
timestamp, timestamp,
address_recv: (PeerServices::NODE_NETWORK, their_addr), address_recv: (PeerServices::NODE_NETWORK, their_addr),
@ -552,29 +552,17 @@ pub async fn negotiate_version(
Err(HandshakeError::NonceReuse)?; Err(HandshakeError::NonceReuse)?;
} }
// XXX in zcashd remote peer can only send one version message and // TODO: Reject connections with nodes that don't know about the current network upgrade (#1334)
// we would disconnect here if it received a second one. Is it even possible // Use the latest non-finalized block height, rather than the minimum
// for that to happen to us here? if remote_version
< Version::min_remote_for_height(
// TODO: Reject incoming connections from nodes that don't know about the current epoch. config.network,
// zcashd does this: // This code will be replaced in #1334
// const Consensus::Params& consensusParams = chainparams.GetConsensus(); constants::INITIAL_MIN_NETWORK_PROTOCOL_VERSION
// auto currentEpoch = CurrentEpoch(GetHeight(), consensusParams); .activation_height(config.network)
// if (pfrom->nVersion < consensusParams.vUpgrades[currentEpoch].nProtocolVersion) .expect("minimum network protocol network upgrade has an activation height"),
// )
// For approximately 1.5 days before a network upgrade, zcashd also: {
// - avoids old peers, and
// - prefers updated peers.
// We haven't decided if we need this behaviour in Zebra yet (see #706).
//
// At the network upgrade, we also need to disconnect from old peers (see #1334).
//
// TODO: replace min_for_upgrade(network, MIN_NETWORK_UPGRADE) with
// current_min(network, height) where network is the
// configured network, and height is the best tip's block
// height.
if remote_version < Version::min_for_upgrade(config.network, constants::MIN_NETWORK_UPGRADE) {
// Disconnect if peer is using an obsolete version. // Disconnect if peer is using an obsolete version.
Err(HandshakeError::ObsoleteVersion(remote_version))?; Err(HandshakeError::ObsoleteVersion(remote_version))?;
} }
@ -680,7 +668,8 @@ where
} }
// Set the connection's version to the minimum of the received version or our own. // Set the connection's version to the minimum of the received version or our own.
let negotiated_version = std::cmp::min(remote_version, constants::CURRENT_VERSION); let negotiated_version =
std::cmp::min(remote_version, constants::CURRENT_NETWORK_PROTOCOL_VERSION);
// Reconfigure the codec to use the negotiated version. // Reconfigure the codec to use the negotiated version.
// //

View File

@ -54,7 +54,7 @@ impl Codec {
pub fn builder() -> Builder { pub fn builder() -> Builder {
Builder { Builder {
network: Network::Mainnet, network: Network::Mainnet,
version: constants::CURRENT_VERSION, version: constants::CURRENT_NETWORK_PROTOCOL_VERSION,
max_len: MAX_PROTOCOL_MESSAGE_LEN, max_len: MAX_PROTOCOL_MESSAGE_LEN,
metrics_addr_label: None, metrics_addr_label: None,
} }
@ -650,7 +650,7 @@ mod tests {
let services = PeerServices::NODE_NETWORK; let services = PeerServices::NODE_NETWORK;
let timestamp = Utc.timestamp(1_568_000_000, 0); let timestamp = Utc.timestamp(1_568_000_000, 0);
Message::Version { Message::Version {
version: crate::constants::CURRENT_VERSION, version: crate::constants::CURRENT_NETWORK_PROTOCOL_VERSION,
services, services,
timestamp, timestamp,
address_recv: ( address_recv: (

View File

@ -1,8 +1,8 @@
#![allow(clippy::unit_arg)] #![allow(clippy::unit_arg)]
use crate::constants::magics; use crate::constants::{self, magics};
use std::fmt; use std::{cmp::max, fmt};
use zebra_chain::{ use zebra_chain::{
block, block,
@ -42,9 +42,49 @@ impl From<Network> for Magic {
pub struct Version(pub u32); pub struct Version(pub u32);
impl Version { impl Version {
/// Returns the minimum network protocol version for `network` and /// Returns the minimum remote node network protocol version for `network` and
/// `height`. Zebra disconnects from peers with lower versions.
///
/// # Panics
///
/// If we are incompatible with our own minimum remote protocol version.
pub fn min_remote_for_height(network: Network, height: block::Height) -> Version {
let min_spec = Version::min_specified_for_height(network, height);
// shut down if our own version is too old
assert!(
constants::CURRENT_NETWORK_PROTOCOL_VERSION >= min_spec,
"Zebra does not implement the minimum specified {:?} protocol version for {:?} at {:?}",
NetworkUpgrade::current(network, height),
network,
height,
);
max(min_spec, Version::initial_min_for_network(network))
}
/// Returns the minimum supported network protocol version for `network`.
///
/// This is the minimum peer version when Zebra is significantly behind current tip:
/// - during the initial block download,
/// - after Zebra restarts, and
/// - after Zebra's local network is slow or shut down.
fn initial_min_for_network(network: Network) -> Version {
Version::min_specified_for_upgrade(network, constants::INITIAL_MIN_NETWORK_PROTOCOL_VERSION)
}
/// Returns the minimum specified network protocol version for `network` and
/// `height`.
///
/// This is the minimum peer version when Zebra is close to the current tip.
fn min_specified_for_height(network: Network, height: block::Height) -> Version {
let network_upgrade = NetworkUpgrade::current(network, height);
Version::min_specified_for_upgrade(network, network_upgrade)
}
/// Returns the minimum specified network protocol version for `network` and
/// `network_upgrade`. /// `network_upgrade`.
pub fn min_for_upgrade(network: Network, network_upgrade: NetworkUpgrade) -> Self { fn min_specified_for_upgrade(network: Network, network_upgrade: NetworkUpgrade) -> Version {
// TODO: Should we reject earlier protocol versions during our initial // TODO: Should we reject earlier protocol versions during our initial
// sync? zcashd accepts 170_002 or later during its initial sync. // sync? zcashd accepts 170_002 or later during its initial sync.
Version(match (network, network_upgrade) { Version(match (network, network_upgrade) {
@ -62,15 +102,6 @@ impl Version {
(Mainnet, Nu5) => 170_015, (Mainnet, Nu5) => 170_015,
}) })
} }
/// Returns the current minimum protocol version for `network` and `height`.
///
/// Returns None if the network has no branch id at this height.
#[allow(dead_code)]
pub fn current_min(network: Network, height: block::Height) -> Version {
let network_upgrade = NetworkUpgrade::current(network, height);
Version::min_for_upgrade(network, network_upgrade)
}
} }
bitflags! { bitflags! {
@ -159,21 +190,21 @@ mod test {
version_extremes(Testnet) version_extremes(Testnet)
} }
/// Test the min_for_upgrade and current_min functions for `network` with /// Test the min_specified_for_upgrade and min_specified_for_height functions for `network` with
/// extreme values. /// extreme values.
fn version_extremes(network: Network) { fn version_extremes(network: Network) {
zebra_test::init(); zebra_test::init();
assert_eq!( assert_eq!(
Version::current_min(network, block::Height(0)), Version::min_specified_for_height(network, block::Height(0)),
Version::min_for_upgrade(network, BeforeOverwinter), Version::min_specified_for_upgrade(network, BeforeOverwinter),
); );
// We assume that the last version we know about continues forever // We assume that the last version we know about continues forever
// (even if we suspect that won't be true) // (even if we suspect that won't be true)
assert_ne!( assert_ne!(
Version::current_min(network, block::Height::MAX), Version::min_specified_for_height(network, block::Height::MAX),
Version::min_for_upgrade(network, BeforeOverwinter), Version::min_specified_for_upgrade(network, BeforeOverwinter),
); );
} }
@ -187,7 +218,7 @@ mod test {
version_consistent(Testnet) version_consistent(Testnet)
} }
/// Check that the min_for_upgrade and current_min functions /// Check that the min_specified_for_upgrade and min_specified_for_height functions
/// are consistent for `network`. /// are consistent for `network`.
fn version_consistent(network: Network) { fn version_consistent(network: Network) {
zebra_test::init(); zebra_test::init();
@ -208,8 +239,8 @@ mod test {
let height = network_upgrade.activation_height(network); let height = network_upgrade.activation_height(network);
if let Some(height) = height { if let Some(height) = height {
assert_eq!( assert_eq!(
Version::min_for_upgrade(network, network_upgrade), Version::min_specified_for_upgrade(network, network_upgrade),
Version::current_min(network, height) Version::min_specified_for_height(network, height)
); );
} }
} }