diff --git a/zebra-network/Cargo.toml b/zebra-network/Cargo.toml index f14881b61..12d2d7b8d 100644 --- a/zebra-network/Cargo.toml +++ b/zebra-network/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bitflags = "1.2" bytes = "0.4" rand = "0.7" byteorder = "1.3" diff --git a/zebra-network/src/lib.rs b/zebra-network/src/lib.rs index 46600cc69..f0d4035a1 100644 --- a/zebra-network/src/lib.rs +++ b/zebra-network/src/lib.rs @@ -6,6 +6,8 @@ extern crate failure; #[macro_use] extern crate tracing; +#[macro_use] +extern crate bitflags; mod network; pub use network::Network; diff --git a/zebra-network/src/meta_addr.rs b/zebra-network/src/meta_addr.rs index 3a86e84aa..91eda3b42 100644 --- a/zebra-network/src/meta_addr.rs +++ b/zebra-network/src/meta_addr.rs @@ -31,7 +31,7 @@ pub struct MetaAddr { impl ZcashSerialize for MetaAddr { fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { writer.write_u32::(self.last_seen.timestamp() as u32)?; - writer.write_u64::(self.services.0)?; + writer.write_u64::(self.services.bits())?; writer.write_socket_addr(self.addr)?; Ok(()) } @@ -41,7 +41,8 @@ impl ZcashDeserialize for MetaAddr { fn zcash_deserialize(mut reader: R) -> Result { Ok(MetaAddr { last_seen: Utc.timestamp(reader.read_u32::()? as i64, 0), - services: PeerServices(reader.read_u64::()?), + // Discard unknown service bits. + services: PeerServices::from_bits_truncate(reader.read_u64::()?), addr: reader.read_socket_addr()?, }) } diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 8cff0a49e..c1f47e057 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -174,15 +174,15 @@ impl Codec { ref relay, } => { writer.write_u32::(version.0)?; - writer.write_u64::(services.0)?; + writer.write_u64::(services.bits())?; writer.write_i64::(timestamp.timestamp())?; let (recv_services, recv_addr) = address_recv; - writer.write_u64::(recv_services.0)?; + writer.write_u64::(recv_services.bits())?; writer.write_socket_addr(*recv_addr)?; let (from_services, from_addr) = address_from; - writer.write_u64::(from_services.0)?; + writer.write_u64::(from_services.bits())?; writer.write_socket_addr(*from_addr)?; writer.write_u64::(nonce.0)?; @@ -341,14 +341,15 @@ impl Codec { fn read_version(&self, mut reader: R) -> Result { Ok(Message::Version { version: Version(reader.read_u32::()?), - services: PeerServices(reader.read_u64::()?), + // Use from_bits_truncate to discard unknown service bits. + services: PeerServices::from_bits_truncate(reader.read_u64::()?), timestamp: Utc.timestamp(reader.read_i64::()?, 0), address_recv: ( - PeerServices(reader.read_u64::()?), + PeerServices::from_bits_truncate(reader.read_u64::()?), reader.read_socket_addr()?, ), address_from: ( - PeerServices(reader.read_u64::()?), + PeerServices::from_bits_truncate(reader.read_u64::()?), reader.read_socket_addr()?, ), nonce: Nonce(reader.read_u64::()?), @@ -505,7 +506,7 @@ mod tests { #[test] fn version_message_round_trip() { use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - let services = PeerServices(0x1); + let services = PeerServices::NODE_NETWORK; let timestamp = Utc.timestamp(1568000000, 0); let rt = Runtime::new().unwrap(); diff --git a/zebra-network/src/protocol/types.rs b/zebra-network/src/protocol/types.rs index 1d307ede6..15e3feff4 100644 --- a/zebra-network/src/protocol/types.rs +++ b/zebra-network/src/protocol/types.rs @@ -8,11 +8,21 @@ pub struct Magic(pub [u8; 4]); #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Version(pub u32); -/// Bitfield of features to be enabled for this connection. -// Tower provides utilities for service discovery, so this might go -// away in the future in favor of that. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct PeerServices(pub u64); +bitflags! { + /// A bitflag describing services advertised by a node in the network. + /// + /// Note that bits 24-31 are reserved for temporary experiments; other + /// service bits should be allocated via the ZIP process. + #[derive(Default)] + pub struct PeerServices: u64 { + /// NODE_NETWORK means that the node is a full node capable of serving + /// blocks, as opposed to a light client that makes network requests but + /// does not provide network services. + const NODE_NETWORK = (1 << 0); + /// NODE_BLOOM means that the node supports bloom-filtered connections. + const NODE_BLOOM = (1 << 2); + } +} /// A nonce used in the networking layer to identify messages. #[derive(Copy, Clone, Debug, Eq, PartialEq)] diff --git a/zebrad/src/commands/connect.rs b/zebrad/src/commands/connect.rs index 4c64c2ac0..f6719fe59 100644 --- a/zebrad/src/commands/connect.rs +++ b/zebrad/src/commands/connect.rs @@ -70,13 +70,16 @@ impl ConnectCmd { let version = Message::Version { version: constants::CURRENT_VERSION, - services: PeerServices(1), + services: PeerServices::NODE_NETWORK, timestamp: Utc::now(), - address_recv: (PeerServices(1), self.addr), + address_recv: (PeerServices::NODE_NETWORK, self.addr), // We just make something up because at this stage the `connect` command // doesn't run a server or anything -- will the zcashd respond on the // same tcp connection or try to open one to the bogus address below? - address_from: (PeerServices(1), "127.0.0.1:9000".parse().unwrap()), + address_from: ( + PeerServices::NODE_NETWORK, + "127.0.0.1:9000".parse().unwrap(), + ), nonce: Nonce(1), user_agent: "Zebra Connect".to_owned(), start_height: BlockHeight(0),