From df79fa75e0bc6197ae3dc3c7579f03bfffe49016 Mon Sep 17 00:00:00 2001 From: George Tankersley Date: Mon, 13 Apr 2020 18:33:15 -0400 Subject: [PATCH] Implement minimal version handshaking (#295) Co-authored-by: Deirdre Connolly Co-authored-by: Henry de Valence --- zebra-network/src/peer/error.rs | 3 ++ zebra-network/src/peer/handshake.rs | 41 ++++++++++++++++---- zebra-network/src/protocol/external/types.rs | 2 +- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/zebra-network/src/peer/error.rs b/zebra-network/src/peer/error.rs index 2236128a..59f27808 100644 --- a/zebra-network/src/peer/error.rs +++ b/zebra-network/src/peer/error.rs @@ -81,4 +81,7 @@ pub enum HandshakeError { /// A serialization error occurred while reading or writing a message. #[error("Serialization error")] Serialization(#[from] SerializationError), + /// The remote peer offered a version older than our minimum version. + #[error("Peer offered obsolete version: {0:?}")] + ObsoleteVersion(crate::protocol::external::types::Version), } diff --git a/zebra-network/src/peer/handshake.rs b/zebra-network/src/peer/handshake.rs index 0b933492..f43f3017 100644 --- a/zebra-network/src/peer/handshake.rs +++ b/zebra-network/src/peer/handshake.rs @@ -128,7 +128,9 @@ where nonce: local_nonce, user_agent, // XXX eventually the `PeerConnector` will need to have a handle - // for a service that gets the current block height. + // for a service that gets the current block height. Among other + // things we need it to reject peers who don't know about the + // current protocol epoch. start_height: BlockHeight(0), relay: false, }; @@ -143,11 +145,14 @@ where // Check that we got a Version and destructure its fields into the local scope. debug!(?remote_msg, "got message from remote peer"); - let (remote_nonce, remote_services) = if let Message::Version { - nonce, services, .. + let (remote_nonce, remote_services, remote_version) = if let Message::Version { + nonce, + services, + version, + .. } = remote_msg { - (nonce, services) + (nonce, services, version) } else { return Err(HandshakeError::UnexpectedMessage(Box::new(remote_msg))); }; @@ -176,9 +181,31 @@ where return Err(HandshakeError::UnexpectedMessage(Box::new(remote_msg))); } - // XXX here is where we would set the version to the minimum of the - // two versions, etc. -- actually is it possible to edit the `Codec` - // after using it to make a framed adapter? + // 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? + + if remote_version < constants::MIN_VERSION { + // Disconnect if peer is using an obsolete version. + return Err(HandshakeError::ObsoleteVersion(remote_version)); + } + + // 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) + + // 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); + + // Reconfigure the codec to use the negotiated version. + // + // XXX The tokio documentation says not to do this while any frames are still being processed. + // Since we don't know that here, another way might be to release the tcp + // stream from the unversioned Framed wrapper and construct a new one with a versioned codec. + let bare_codec = stream.codec_mut(); + bare_codec.reconfigure_version(negotiated_version); debug!("constructing client, spawning server"); diff --git a/zebra-network/src/protocol/external/types.rs b/zebra-network/src/protocol/external/types.rs index b798ea46..3be3f08c 100644 --- a/zebra-network/src/protocol/external/types.rs +++ b/zebra-network/src/protocol/external/types.rs @@ -29,7 +29,7 @@ impl From for Magic { } /// A protocol version number. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct Version(pub u32); bitflags! {