diff --git a/src/network/constants.rs b/src/network/constants.rs index bd7b523b..f90c1f78 100644 --- a/src/network/constants.rs +++ b/src/network/constants.rs @@ -37,6 +37,10 @@ //! assert_eq!(&bytes[..], &[0xF9, 0xBE, 0xB4, 0xD9]); //! ``` +use std::{io, ops}; + +use consensus::encode::{self, Encodable, Decodable}; + /// Version of the protocol as appearing in network message headers pub const PROTOCOL_VERSION: u32 = 70001; /// Bitfield of services provided by this node @@ -99,9 +103,125 @@ impl Network { } } +/// Flags to indicate which network services a node supports. +#[derive(Debug, Clone, Copy, 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); + + /// 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); + + /// 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 + } + + /// Get the integer representation of this [ServiceFlags]. + pub fn as_u64(&self) -> u64 { + self.0 + } +} + +impl From for ServiceFlags { + fn from(f: u64) -> Self { + ServiceFlags(f) + } +} + +impl Into for ServiceFlags { + fn into(self) -> u64 { + self.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, + mut s: S, + ) -> Result { + self.0.consensus_encode(&mut s) + } +} + +impl Decodable for ServiceFlags { + #[inline] + fn consensus_decode(mut d: D) -> Result { + Ok(ServiceFlags(Decodable::consensus_decode(&mut d)?)) + } +} + #[cfg(test)] mod tests { - use super::Network; + use super::{Network, ServiceFlags}; use consensus::encode::{deserialize, serialize}; #[test] @@ -133,16 +253,43 @@ mod tests { ); } - #[test] - fn string_test() { - assert_eq!(Network::Bitcoin.to_string(), "bitcoin"); - assert_eq!(Network::Testnet.to_string(), "testnet"); - assert_eq!(Network::Regtest.to_string(), "regtest"); + #[test] + fn string_test() { + assert_eq!(Network::Bitcoin.to_string(), "bitcoin"); + assert_eq!(Network::Testnet.to_string(), "testnet"); + assert_eq!(Network::Regtest.to_string(), "regtest"); - assert_eq!("bitcoin".parse::().unwrap(), Network::Bitcoin); - assert_eq!("testnet".parse::().unwrap(), Network::Testnet); - assert_eq!("regtest".parse::().unwrap(), Network::Regtest); - assert!("fakenet".parse::().is_err()); - } + assert_eq!("bitcoin".parse::().unwrap(), Network::Bitcoin); + assert_eq!("testnet".parse::().unwrap(), Network::Testnet); + assert_eq!("regtest".parse::().unwrap(), Network::Regtest); + assert!("fakenet".parse::().is_err()); + } + + #[test] + fn service_flags_test() { + let all = [ + ServiceFlags::NETWORK, + ServiceFlags::GETUTXO, + ServiceFlags::BLOOM, + ServiceFlags::WITNESS, + 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); + } }