diff --git a/bitcoin/examples/handshake.rs b/bitcoin/examples/handshake.rs index 12da7e59..d62d1699 100644 --- a/bitcoin/examples/handshake.rs +++ b/bitcoin/examples/handshake.rs @@ -6,7 +6,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use std::{env, process}; use bitcoin::consensus::{encode, Decodable}; -use bitcoin::p2p::{address, constants, message, message_network}; +use bitcoin::p2p::{self, address, constants, message, message_network}; use bitcoin::secp256k1; use bitcoin::secp256k1::rand::Rng; @@ -75,16 +75,16 @@ fn build_version_message(address: SocketAddr) -> message::NetworkMessage { let my_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); // "bitfield of features to be enabled for this connection" - let services = constants::ServiceFlags::NONE; + let services = p2p::ServiceFlags::NONE; // "standard UNIX timestamp in seconds" let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time error").as_secs(); // "The network address of the node receiving this message" - let addr_recv = address::Address::new(&address, constants::ServiceFlags::NONE); + let addr_recv = address::Address::new(&address, p2p::ServiceFlags::NONE); // "The network address of the node emitting this message" - let addr_from = address::Address::new(&my_address, constants::ServiceFlags::NONE); + let addr_from = address::Address::new(&my_address, p2p::ServiceFlags::NONE); // "Node random nonce, randomly generated every time a version packet is sent. This nonce is used to detect connections to self." let nonce: u64 = secp256k1::rand::thread_rng().gen(); diff --git a/bitcoin/src/p2p/address.rs b/bitcoin/src/p2p/address.rs index 5696026b..58678bc9 100644 --- a/bitcoin/src/p2p/address.rs +++ b/bitcoin/src/p2p/address.rs @@ -11,7 +11,7 @@ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSoc use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, VarInt, WriteExt}; use crate::io; -use crate::p2p::constants::ServiceFlags; +use crate::p2p::ServiceFlags; use crate::prelude::*; /// A message which can be sent on the Bitcoin network @@ -311,7 +311,7 @@ mod test { use super::{AddrV2, AddrV2Message, Address}; use crate::consensus::encode::{deserialize, serialize}; use crate::internal_macros::hex; - use crate::p2p::constants::ServiceFlags; + use crate::p2p::ServiceFlags; #[test] fn serialize_address_test() { diff --git a/bitcoin/src/p2p/constants.rs b/bitcoin/src/p2p/constants.rs index 90c0e591..2dce6961 100644 --- a/bitcoin/src/p2p/constants.rs +++ b/bitcoin/src/p2p/constants.rs @@ -27,9 +27,9 @@ use core::borrow::{Borrow, BorrowMut}; use core::convert::TryFrom; +use core::fmt; use core::fmt::Display; use core::str::FromStr; -use core::{fmt, ops}; use hex::FromHex; use internals::{debug_from_display, write_err}; @@ -423,161 +423,14 @@ impl fmt::Display for UnknownMagic { } impl_std_error!(UnknownMagic); -/// Flags to indicate which network services a node supports. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ServiceFlags(u64); - -impl ServiceFlags { - /// NONE means no services supported. - pub const NONE: ServiceFlags = ServiceFlags(0); - - /// NETWORK means that the node is capable of serving the complete block chain. It is currently - /// set by all Bitcoin Core non pruned nodes, and is unset by SPV clients or other light - /// clients. - pub const NETWORK: ServiceFlags = ServiceFlags(1 << 0); - - /// GETUTXO means the node is capable of responding to the getutxo protocol request. Bitcoin - /// Core does not support this but a patch set called Bitcoin XT does. - /// See BIP 64 for details on how this is implemented. - pub const GETUTXO: ServiceFlags = ServiceFlags(1 << 1); - - /// BLOOM means the node is capable and willing to handle bloom-filtered connections. Bitcoin - /// Core nodes used to support this by default, without advertising this bit, but no longer do - /// as of protocol version 70011 (= NO_BLOOM_VERSION) - pub const BLOOM: ServiceFlags = ServiceFlags(1 << 2); - - /// WITNESS indicates that a node can be asked for blocks and transactions including witness - /// data. - pub const WITNESS: ServiceFlags = ServiceFlags(1 << 3); - - /// COMPACT_FILTERS means the node will service basic block filter requests. - /// See BIP157 and BIP158 for details on how this is implemented. - pub const COMPACT_FILTERS: ServiceFlags = ServiceFlags(1 << 6); - - /// NETWORK_LIMITED means the same as NODE_NETWORK with the limitation of only serving the last - /// 288 (2 day) blocks. - /// See BIP159 for details on how this is implemented. - pub const NETWORK_LIMITED: ServiceFlags = ServiceFlags(1 << 10); - - // NOTE: When adding new flags, remember to update the Display impl accordingly. - - /// Add [ServiceFlags] together. - /// - /// Returns itself. - pub fn add(&mut self, other: ServiceFlags) -> ServiceFlags { - self.0 |= other.0; - *self - } - - /// Remove [ServiceFlags] from this. - /// - /// Returns itself. - pub fn remove(&mut self, other: ServiceFlags) -> ServiceFlags { - self.0 ^= other.0; - *self - } - - /// Check whether [ServiceFlags] are included in this one. - pub fn has(self, flags: ServiceFlags) -> bool { (self.0 | flags.0) == self.0 } - - /// Gets the integer representation of this [`ServiceFlags`]. - pub fn to_u64(self) -> u64 { self.0 } -} - -impl fmt::LowerHex for ServiceFlags { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) } -} - -impl fmt::UpperHex for ServiceFlags { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) } -} - -impl fmt::Display for ServiceFlags { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut flags = *self; - if flags == ServiceFlags::NONE { - return write!(f, "ServiceFlags(NONE)"); - } - let mut first = true; - macro_rules! write_flag { - ($f:ident) => { - if flags.has(ServiceFlags::$f) { - if !first { - write!(f, "|")?; - } - first = false; - write!(f, stringify!($f))?; - flags.remove(ServiceFlags::$f); - } - }; - } - write!(f, "ServiceFlags(")?; - write_flag!(NETWORK); - write_flag!(GETUTXO); - write_flag!(BLOOM); - write_flag!(WITNESS); - write_flag!(COMPACT_FILTERS); - write_flag!(NETWORK_LIMITED); - // If there are unknown flags left, we append them in hex. - if flags != ServiceFlags::NONE { - if !first { - write!(f, "|")?; - } - write!(f, "0x{:x}", flags)?; - } - write!(f, ")") - } -} - -impl From for ServiceFlags { - fn from(f: u64) -> Self { ServiceFlags(f) } -} - -impl From for u64 { - fn from(flags: ServiceFlags) -> Self { flags.0 } -} - -impl ops::BitOr for ServiceFlags { - type Output = Self; - - fn bitor(mut self, rhs: Self) -> Self { self.add(rhs) } -} - -impl ops::BitOrAssign for ServiceFlags { - fn bitor_assign(&mut self, rhs: Self) { self.add(rhs); } -} - -impl ops::BitXor for ServiceFlags { - type Output = Self; - - fn bitxor(mut self, rhs: Self) -> Self { self.remove(rhs) } -} - -impl ops::BitXorAssign for ServiceFlags { - fn bitxor_assign(&mut self, rhs: Self) { self.remove(rhs); } -} - -impl Encodable for ServiceFlags { - #[inline] - fn consensus_encode(&self, w: &mut W) -> Result { - self.0.consensus_encode(w) - } -} - -impl Decodable for ServiceFlags { - #[inline] - fn consensus_decode(r: &mut R) -> Result { - Ok(ServiceFlags(Decodable::consensus_decode(r)?)) - } -} - #[cfg(test)] mod tests { use std::convert::TryFrom; use std::str::FromStr; - use super::{Magic, Network, ServiceFlags}; + use super::{Magic, Network}; use crate::consensus::encode::{deserialize, serialize}; + use crate::p2p::ServiceFlags; #[test] fn serialize_test() { diff --git a/bitcoin/src/p2p/message.rs b/bitcoin/src/p2p/message.rs index 71bec7c4..098c5e73 100644 --- a/bitcoin/src/p2p/message.rs +++ b/bitcoin/src/p2p/message.rs @@ -550,13 +550,14 @@ mod test { use crate::consensus::encode::{deserialize, deserialize_partial, serialize}; use crate::internal_macros::hex; use crate::p2p::address::{AddrV2, AddrV2Message, Address}; - use crate::p2p::constants::{Magic, Network, ServiceFlags}; + use crate::p2p::constants::{Magic, Network}; use crate::p2p::message_blockdata::{GetBlocksMessage, GetHeadersMessage, Inventory}; use crate::p2p::message_bloom::{BloomFlags, FilterAdd, FilterLoad}; use crate::p2p::message_compact_blocks::{GetBlockTxn, SendCmpct}; use crate::p2p::message_filter::{ CFCheckpt, CFHeaders, CFilter, GetCFCheckpt, GetCFHeaders, GetCFilters, }; + use crate::p2p::ServiceFlags; fn hash(slice: [u8; 32]) -> Hash { Hash::from_slice(&slice).unwrap() } diff --git a/bitcoin/src/p2p/message_network.rs b/bitcoin/src/p2p/message_network.rs index a0ab6cb4..fad323f6 100644 --- a/bitcoin/src/p2p/message_network.rs +++ b/bitcoin/src/p2p/message_network.rs @@ -11,7 +11,7 @@ use hashes::sha256d; use crate::consensus::{encode, Decodable, Encodable, ReadExt}; use crate::internal_macros::impl_consensus_encoding; use crate::p2p::address::Address; -use crate::p2p::constants::ServiceFlags; +use crate::p2p::ServiceFlags; use crate::prelude::*; use crate::{io, p2p}; @@ -146,7 +146,7 @@ mod tests { use super::{Reject, RejectReason, VersionMessage}; use crate::consensus::encode::{deserialize, serialize}; use crate::internal_macros::hex; - use crate::p2p::constants::ServiceFlags; + use crate::p2p::ServiceFlags; #[test] fn version_message_test() { diff --git a/bitcoin/src/p2p/mod.rs b/bitcoin/src/p2p/mod.rs index 95184ccf..f46733f7 100644 --- a/bitcoin/src/p2p/mod.rs +++ b/bitcoin/src/p2p/mod.rs @@ -24,7 +24,11 @@ pub mod message_filter; #[cfg(feature = "std")] pub mod message_network; +use core::{fmt, ops}; + pub use self::constants::Magic; +use crate::consensus::encode::{self, Decodable, Encodable}; +use crate::io; /// Version of the protocol as appearing in network message headers. /// @@ -44,3 +48,196 @@ pub use self::constants::Magic; /// 60002 - Support `mempool` message /// 60001 - Support `pong` message and nonce in `ping` message pub const PROTOCOL_VERSION: u32 = 70001; + +/// Flags to indicate which network services a node supports. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ServiceFlags(u64); + +impl ServiceFlags { + /// NONE means no services supported. + pub const NONE: ServiceFlags = ServiceFlags(0); + + /// NETWORK means that the node is capable of serving the complete block chain. It is currently + /// set by all Bitcoin Core non pruned nodes, and is unset by SPV clients or other light + /// clients. + pub const NETWORK: ServiceFlags = ServiceFlags(1 << 0); + + /// GETUTXO means the node is capable of responding to the getutxo protocol request. Bitcoin + /// Core does not support this but a patch set called Bitcoin XT does. + /// See BIP 64 for details on how this is implemented. + pub const GETUTXO: ServiceFlags = ServiceFlags(1 << 1); + + /// BLOOM means the node is capable and willing to handle bloom-filtered connections. Bitcoin + /// Core nodes used to support this by default, without advertising this bit, but no longer do + /// as of protocol version 70011 (= NO_BLOOM_VERSION) + pub const BLOOM: ServiceFlags = ServiceFlags(1 << 2); + + /// WITNESS indicates that a node can be asked for blocks and transactions including witness + /// data. + pub const WITNESS: ServiceFlags = ServiceFlags(1 << 3); + + /// COMPACT_FILTERS means the node will service basic block filter requests. + /// See BIP157 and BIP158 for details on how this is implemented. + pub const COMPACT_FILTERS: ServiceFlags = ServiceFlags(1 << 6); + + /// NETWORK_LIMITED means the same as NODE_NETWORK with the limitation of only serving the last + /// 288 (2 day) blocks. + /// See BIP159 for details on how this is implemented. + pub const NETWORK_LIMITED: ServiceFlags = ServiceFlags(1 << 10); + + // NOTE: When adding new flags, remember to update the Display impl accordingly. + + /// Add [ServiceFlags] together. + /// + /// Returns itself. + pub fn add(&mut self, other: ServiceFlags) -> ServiceFlags { + self.0 |= other.0; + *self + } + + /// Remove [ServiceFlags] from this. + /// + /// Returns itself. + pub fn remove(&mut self, other: ServiceFlags) -> ServiceFlags { + self.0 ^= other.0; + *self + } + + /// Check whether [ServiceFlags] are included in this one. + pub fn has(self, flags: ServiceFlags) -> bool { (self.0 | flags.0) == self.0 } + + /// Gets the integer representation of this [`ServiceFlags`]. + pub fn to_u64(self) -> u64 { self.0 } +} + +impl fmt::LowerHex for ServiceFlags { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) } +} + +impl fmt::UpperHex for ServiceFlags { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) } +} + +impl fmt::Display for ServiceFlags { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut flags = *self; + if flags == ServiceFlags::NONE { + return write!(f, "ServiceFlags(NONE)"); + } + let mut first = true; + macro_rules! write_flag { + ($f:ident) => { + if flags.has(ServiceFlags::$f) { + if !first { + write!(f, "|")?; + } + first = false; + write!(f, stringify!($f))?; + flags.remove(ServiceFlags::$f); + } + }; + } + write!(f, "ServiceFlags(")?; + write_flag!(NETWORK); + write_flag!(GETUTXO); + write_flag!(BLOOM); + write_flag!(WITNESS); + write_flag!(COMPACT_FILTERS); + write_flag!(NETWORK_LIMITED); + // If there are unknown flags left, we append them in hex. + if flags != ServiceFlags::NONE { + if !first { + write!(f, "|")?; + } + write!(f, "0x{:x}", flags)?; + } + write!(f, ")") + } +} + +impl From for ServiceFlags { + fn from(f: u64) -> Self { ServiceFlags(f) } +} + +impl From for u64 { + fn from(flags: ServiceFlags) -> Self { flags.0 } +} + +impl ops::BitOr for ServiceFlags { + type Output = Self; + + fn bitor(mut self, rhs: Self) -> Self { self.add(rhs) } +} + +impl ops::BitOrAssign for ServiceFlags { + fn bitor_assign(&mut self, rhs: Self) { self.add(rhs); } +} + +impl ops::BitXor for ServiceFlags { + type Output = Self; + + fn bitxor(mut self, rhs: Self) -> Self { self.remove(rhs) } +} + +impl ops::BitXorAssign for ServiceFlags { + fn bitxor_assign(&mut self, rhs: Self) { self.remove(rhs); } +} + +impl Encodable for ServiceFlags { + #[inline] + fn consensus_encode(&self, w: &mut W) -> Result { + self.0.consensus_encode(w) + } +} + +impl Decodable for ServiceFlags { + #[inline] + fn consensus_decode(r: &mut R) -> Result { + Ok(ServiceFlags(Decodable::consensus_decode(r)?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn service_flags_test() { + let all = [ + ServiceFlags::NETWORK, + ServiceFlags::GETUTXO, + ServiceFlags::BLOOM, + ServiceFlags::WITNESS, + ServiceFlags::COMPACT_FILTERS, + ServiceFlags::NETWORK_LIMITED, + ]; + + let mut flags = ServiceFlags::NONE; + for f in all.iter() { + assert!(!flags.has(*f)); + } + + flags |= ServiceFlags::WITNESS; + assert_eq!(flags, ServiceFlags::WITNESS); + + let mut flags2 = flags | ServiceFlags::GETUTXO; + for f in all.iter() { + assert_eq!(flags2.has(*f), *f == ServiceFlags::WITNESS || *f == ServiceFlags::GETUTXO); + } + + flags2 ^= ServiceFlags::WITNESS; + assert_eq!(flags2, ServiceFlags::GETUTXO); + + flags2 |= ServiceFlags::COMPACT_FILTERS; + flags2 ^= ServiceFlags::GETUTXO; + assert_eq!(flags2, ServiceFlags::COMPACT_FILTERS); + + // Test formatting. + assert_eq!("ServiceFlags(NONE)", ServiceFlags::NONE.to_string()); + assert_eq!("ServiceFlags(WITNESS)", ServiceFlags::WITNESS.to_string()); + let flag = ServiceFlags::WITNESS | ServiceFlags::BLOOM | ServiceFlags::NETWORK; + assert_eq!("ServiceFlags(NETWORK|BLOOM|WITNESS)", flag.to_string()); + let flag = ServiceFlags::WITNESS | 0xf0.into(); + assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string()); + } +}