Implement minimal version handshaking (#295)

Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
Co-authored-by: Henry de Valence <hdevalence@hdevalence.ca>
This commit is contained in:
George Tankersley 2020-04-13 18:33:15 -04:00 committed by GitHub
parent 05ca1c0c8a
commit df79fa75e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 8 deletions

View File

@ -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),
}

View File

@ -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");

View File

@ -29,7 +29,7 @@ impl From<Network> 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! {