rust-bitcoin-unsafe-fast/bitcoin/src/network/constants.rs

352 lines
12 KiB
Rust
Raw Normal View History

// Written in 2014 by Andrew Poelstra <apoelstra@wpsoftware.net>
// SPDX-License-Identifier: CC0-1.0
2014-07-18 13:56:17 +00:00
//! Bitcoin network constants.
2014-07-18 13:56:17 +00:00
//!
//! This module provides various constants relating to the Bitcoin network
//! protocol, such as protocol versioning and magic header bytes.
//!
//! The [`Network`][1] type implements the [`Decodable`][2] and
//! [`Encodable`][3] traits and encodes the magic bytes of the given
//! network.
//!
//! [1]: enum.Network.html
//! [2]: ../../consensus/encode/trait.Decodable.html
//! [3]: ../../consensus/encode/trait.Encodable.html
//!
//! # Example: encoding a network's magic bytes
//!
//! ```rust
//! use bitcoin::network::constants::Network;
//! use bitcoin::consensus::encode::serialize;
//!
//! let network = Network::Bitcoin;
//! let bytes = serialize(&network.magic());
//!
//! assert_eq!(&bytes[..], &[0xF9, 0xBE, 0xB4, 0xD9]);
//! ```
2014-07-18 13:56:17 +00:00
2021-06-09 10:40:41 +00:00
use core::{fmt, ops, convert::From};
2019-11-22 00:42:15 +00:00
use crate::io;
use crate::consensus::encode::{self, Encodable, Decodable};
use crate::internal_macros::user_enum;
2019-11-22 00:42:15 +00:00
/// Version of the protocol as appearing in network message headers
/// This constant is used to signal to other peers which features you support.
/// Increasing it implies that your software also supports every feature prior to this version.
/// Doing so without support may lead to you incorrectly banning other peers or other peers banning you.
/// These are the features required for each version:
/// 70016 - Support receiving `wtxidrelay` message between `version` and `verack` message
/// 70015 - Support receiving invalid compact blocks from a peer without banning them
/// 70014 - Support compact block messages `sendcmpct`, `cmpctblock`, `getblocktxn` and `blocktxn`
/// 70013 - Support `feefilter` message
/// 70012 - Support `sendheaders` message and announce new blocks via headers rather than inv
/// 70011 - Support NODE_BLOOM service flag and don't support bloom filter messages if it is not set
/// 70002 - Support `reject` message
/// 70001 - Support bloom filter messages `filterload`, `filterclear` `filteradd`, `merkleblock` and FILTERED_BLOCK inventory type
/// 60002 - Support `mempool` message
/// 60001 - Support `pong` message and nonce in `ping` message
pub const PROTOCOL_VERSION: u32 = 70001;
user_enum! {
/// The cryptocurrency to act on
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
pub enum Network {
/// Classic Bitcoin
Bitcoin <-> "bitcoin",
/// Bitcoin's testnet
Testnet <-> "testnet",
2019-07-10 04:53:53 +00:00
/// Bitcoin's signet
Signet <-> "signet",
/// Bitcoin's regtest
Regtest <-> "regtest"
}
}
impl Network {
/// Creates a `Network` from the magic bytes.
///
/// # Examples
///
/// ```rust
/// use bitcoin::network::constants::Network;
///
/// assert_eq!(Some(Network::Bitcoin), Network::from_magic(0xD9B4BEF9));
/// assert_eq!(None, Network::from_magic(0xFFFFFFFF));
/// ```
pub fn from_magic(magic: u32) -> Option<Network> {
// Note: any new entries here must be added to `magic` below
match magic {
0xD9B4BEF9 => Some(Network::Bitcoin),
0x0709110B => Some(Network::Testnet),
2019-07-10 04:53:53 +00:00
0x40CF030A => Some(Network::Signet),
0xDAB5BFFA => Some(Network::Regtest),
_ => None
}
}
2014-07-18 13:56:17 +00:00
/// Return the network magic bytes, which should be encoded little-endian
/// at the start of every message
///
/// # Examples
///
/// ```rust
/// use bitcoin::network::constants::Network;
///
/// let network = Network::Bitcoin;
/// assert_eq!(network.magic(), 0xD9B4BEF9);
/// ```
2019-08-05 18:52:34 +00:00
pub fn magic(self) -> u32 {
// Note: any new entries here must be added to `from_magic` above
2019-08-05 18:52:34 +00:00
match self {
Network::Bitcoin => 0xD9B4BEF9,
Network::Testnet => 0x0709110B,
2019-07-10 04:53:53 +00:00
Network::Signet => 0x40CF030A,
Network::Regtest => 0xDAB5BFFA,
}
}
}
2019-11-22 00:42:15 +00:00
/// 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);
/// 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);
2019-11-22 00:42:15 +00:00
/// 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.
2019-11-22 00:42:15 +00:00
/// 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.
2019-08-05 18:52:34 +00:00
pub fn has(self, flags: ServiceFlags) -> bool {
2019-11-22 00:42:15 +00:00
(self.0 | flags.0) == self.0
}
/// Gets the integer representation of this [`ServiceFlags`].
pub fn to_u64(self) -> u64 {
2019-11-22 00:42:15 +00:00
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 {
2019-08-05 18:52:34 +00:00
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, ")")
}
}
2019-11-22 00:42:15 +00:00
impl From<u64> for ServiceFlags {
fn from(f: u64) -> Self {
ServiceFlags(f)
}
}
impl From<ServiceFlags> for u64 {
fn from(flags: ServiceFlags) -> Self {
flags.0
2019-11-22 00:42:15 +00:00
}
}
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<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
Take Writer/Reader by `&mut` in consensus en/decoding Fix #1020 (see more relevant discussion there) This definitely makes the amount of generics compiler has to generate by avoding generating the same functions for `R`, &mut R`, `&mut &mut R` and so on. old: ``` > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266 > strip target/release/deps/bitcoin-07a9dabf1f3e0266 > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266 ``` new: ``` > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266 > strip target/release/deps/bitcoin-07a9dabf1f3e0266 > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266 ``` In the unit-test binary itself, it saves ~100KB of data. I did not expect much performance gains, but turn out I was wrong(*): old: ``` test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871) test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833) test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732) test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519) test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3) test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8) test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0) test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0) ``` new: ``` test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910) test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363) test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695) test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694) test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2) test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0) test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0) test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0) ``` (*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think at least it doesn't make anything slower. While doing all this manual labor that will probably generate conflicts, I took a liberty of changing generic type names and variable names to `r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
self.0.consensus_encode(w)
2019-11-22 00:42:15 +00:00
}
}
impl Decodable for ServiceFlags {
#[inline]
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
Take Writer/Reader by `&mut` in consensus en/decoding Fix #1020 (see more relevant discussion there) This definitely makes the amount of generics compiler has to generate by avoding generating the same functions for `R`, &mut R`, `&mut &mut R` and so on. old: ``` > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266 > strip target/release/deps/bitcoin-07a9dabf1f3e0266 > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266 ``` new: ``` > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266 > strip target/release/deps/bitcoin-07a9dabf1f3e0266 > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266 ``` In the unit-test binary itself, it saves ~100KB of data. I did not expect much performance gains, but turn out I was wrong(*): old: ``` test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871) test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833) test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732) test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519) test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3) test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8) test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0) test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0) ``` new: ``` test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910) test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363) test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695) test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694) test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2) test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0) test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0) test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0) ``` (*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think at least it doesn't make anything slower. While doing all this manual labor that will probably generate conflicts, I took a liberty of changing generic type names and variable names to `r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
Ok(ServiceFlags(Decodable::consensus_decode(r)?))
2019-11-22 00:42:15 +00:00
}
}
#[cfg(test)]
mod tests {
2019-11-22 00:42:15 +00:00
use super::{Network, ServiceFlags};
use crate::consensus::encode::{deserialize, serialize};
#[test]
fn serialize_test() {
assert_eq!(serialize(&Network::Bitcoin.magic()), &[0xf9, 0xbe, 0xb4, 0xd9]);
assert_eq!(serialize(&Network::Testnet.magic()), &[0x0b, 0x11, 0x09, 0x07]);
assert_eq!(serialize(&Network::Signet.magic()), &[0x0a, 0x03, 0xcf, 0x40]);
assert_eq!(serialize(&Network::Regtest.magic()), &[0xfa, 0xbf, 0xb5, 0xda]);
assert_eq!(deserialize(&[0xf9, 0xbe, 0xb4, 0xd9]).ok(), Some(Network::Bitcoin.magic()));
assert_eq!(deserialize(&[0x0b, 0x11, 0x09, 0x07]).ok(), Some(Network::Testnet.magic()));
assert_eq!(deserialize(&[0x0a, 0x03, 0xcf, 0x40]).ok(), Some(Network::Signet.magic()));
assert_eq!(deserialize(&[0xfa, 0xbf, 0xb5, 0xda]).ok(), Some(Network::Regtest.magic()));
}
2019-11-22 00:42:15 +00:00
#[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");
2019-07-10 04:53:53 +00:00
assert_eq!(Network::Signet.to_string(), "signet");
2019-11-22 00:42:15 +00:00
assert_eq!("bitcoin".parse::<Network>().unwrap(), Network::Bitcoin);
assert_eq!("testnet".parse::<Network>().unwrap(), Network::Testnet);
assert_eq!("regtest".parse::<Network>().unwrap(), Network::Regtest);
2019-07-10 04:53:53 +00:00
assert_eq!("signet".parse::<Network>().unwrap(), Network::Signet);
2019-11-22 00:42:15 +00:00
assert!("fakenet".parse::<Network>().is_err());
}
#[test]
fn service_flags_test() {
let all = [
ServiceFlags::NETWORK,
ServiceFlags::GETUTXO,
ServiceFlags::BLOOM,
ServiceFlags::WITNESS,
ServiceFlags::COMPACT_FILTERS,
2019-11-22 00:42:15 +00:00
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);
2019-11-22 00:42:15 +00:00
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());
2019-11-22 00:42:15 +00:00
}
}