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.
///
/// 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
/// 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
/// peers. If their versions are too old, we will disconnect from them.
//
// TODO: replace with NetworkUpgrade::current(network, height). (#1334)
pub const MIN_NETWORK_UPGRADE: NetworkUpgrade = NetworkUpgrade::Canopy;
/// The minimum protocol version is used to check the protocol versions of our
/// peers during the initial block download. After the intial block download,
/// we use the current block height to select the minimum network protocol
/// version.
///
/// 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.
///

View File

@ -487,7 +487,7 @@ pub async fn negotiate_version(
};
let our_version = Message::Version {
version: constants::CURRENT_VERSION,
version: constants::CURRENT_NETWORK_PROTOCOL_VERSION,
services: our_services,
timestamp,
address_recv: (PeerServices::NODE_NETWORK, their_addr),
@ -552,29 +552,17 @@ pub async fn negotiate_version(
Err(HandshakeError::NonceReuse)?;
}
// XXX in zcashd remote peer can only send one version message and
// we would disconnect here if it received a second one. Is it even possible
// for that to happen to us here?
// TODO: Reject incoming connections from nodes that don't know about the current epoch.
// zcashd does this:
// const Consensus::Params& consensusParams = chainparams.GetConsensus();
// auto currentEpoch = CurrentEpoch(GetHeight(), consensusParams);
// if (pfrom->nVersion < consensusParams.vUpgrades[currentEpoch].nProtocolVersion)
//
// 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) {
// TODO: Reject connections with nodes that don't know about the current network upgrade (#1334)
// Use the latest non-finalized block height, rather than the minimum
if remote_version
< Version::min_remote_for_height(
config.network,
// This code will be replaced in #1334
constants::INITIAL_MIN_NETWORK_PROTOCOL_VERSION
.activation_height(config.network)
.expect("minimum network protocol network upgrade has an activation height"),
)
{
// Disconnect if peer is using an obsolete 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.
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.
//

View File

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

View File

@ -1,8 +1,8 @@
#![allow(clippy::unit_arg)]
use crate::constants::magics;
use crate::constants::{self, magics};
use std::fmt;
use std::{cmp::max, fmt};
use zebra_chain::{
block,
@ -42,9 +42,49 @@ impl From<Network> for Magic {
pub struct Version(pub u32);
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`.
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
// sync? zcashd accepts 170_002 or later during its initial sync.
Version(match (network, network_upgrade) {
@ -62,15 +102,6 @@ impl Version {
(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! {
@ -159,21 +190,21 @@ mod test {
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.
fn version_extremes(network: Network) {
zebra_test::init();
assert_eq!(
Version::current_min(network, block::Height(0)),
Version::min_for_upgrade(network, BeforeOverwinter),
Version::min_specified_for_height(network, block::Height(0)),
Version::min_specified_for_upgrade(network, BeforeOverwinter),
);
// We assume that the last version we know about continues forever
// (even if we suspect that won't be true)
assert_ne!(
Version::current_min(network, block::Height::MAX),
Version::min_for_upgrade(network, BeforeOverwinter),
Version::min_specified_for_height(network, block::Height::MAX),
Version::min_specified_for_upgrade(network, BeforeOverwinter),
);
}
@ -187,7 +218,7 @@ mod test {
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`.
fn version_consistent(network: Network) {
zebra_test::init();
@ -208,8 +239,8 @@ mod test {
let height = network_upgrade.activation_height(network);
if let Some(height) = height {
assert_eq!(
Version::min_for_upgrade(network, network_upgrade),
Version::current_min(network, height)
Version::min_specified_for_upgrade(network, network_upgrade),
Version::min_specified_for_height(network, height)
);
}
}