Merge rust-bitcoin/rust-bitcoin#1854: Rename `network` module and move/clean up types

d4e8f49fc3 Move p2p::constants::Network to crate root (Tobin C. Harding)
0f78943ef0 Move p2p::constants::Magic to p2p module (Tobin C. Harding)
d9d5a4ed4f Move p2p::constants::ServiceFlags to p2p module (Tobin C. Harding)
99d8ae1173 Improve rustdocs on PROTOCOL_VERSION (Tobin C. Harding)
4330722d62 Move p2p::constants::PROTOCOL_VERSION to p2p module (Tobin C. Harding)
1bac1fd518 Rename the network module to p2p (Tobin C. Harding)
dac97072a5 Add BorrowMut to prelude (Tobin C. Harding)

Pull request description:

  This PR has grown.

  - Rename the `network` module to `p2p` (fix: #1855)
  - add `BorrowMut` to prelude
  - Move the `Network`, `ChainHash`, and `Magic` structs to a newly create `p2p::network` module
  - Do a few cleanups

ACKs for top commit:
  apoelstra:
    ACK d4e8f49fc3

Tree-SHA512: 7cee93d36363b8acbece6c92c6afc34bb3abe6d5801373d291db1323c934e7c384f3b5881cefb8b0d102dea53da6ed8da1bf60c324bf60d5dab3af598e0310da
This commit is contained in:
Andrew Poelstra 2023-08-01 18:52:25 +00:00
commit fc562e953e
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
23 changed files with 625 additions and 581 deletions

View File

@ -6,7 +6,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use std::{env, process}; use std::{env, process};
use bitcoin::consensus::{encode, Decodable}; use bitcoin::consensus::{encode, Decodable};
use bitcoin::network::{address, constants, message, message_network}; use bitcoin::p2p::{self, address, message, message_network};
use bitcoin::secp256k1; use bitcoin::secp256k1;
use bitcoin::secp256k1::rand::Rng; use bitcoin::secp256k1::rand::Rng;
@ -29,7 +29,7 @@ fn main() {
let version_message = build_version_message(address); let version_message = build_version_message(address);
let first_message = let first_message =
message::RawNetworkMessage::new(constants::Network::Bitcoin.magic(), version_message); message::RawNetworkMessage::new(bitcoin::Network::Bitcoin.magic(), version_message);
if let Ok(mut stream) = TcpStream::connect(address) { if let Ok(mut stream) = TcpStream::connect(address) {
// Send the message // Send the message
@ -47,7 +47,7 @@ fn main() {
println!("Received version message: {:?}", reply.payload()); println!("Received version message: {:?}", reply.payload());
let second_message = message::RawNetworkMessage::new( let second_message = message::RawNetworkMessage::new(
constants::Network::Bitcoin.magic(), bitcoin::Network::Bitcoin.magic(),
message::NetworkMessage::Verack, message::NetworkMessage::Verack,
); );
@ -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); let my_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
// "bitfield of features to be enabled for this connection" // "bitfield of features to be enabled for this connection"
let services = constants::ServiceFlags::NONE; let services = p2p::ServiceFlags::NONE;
// "standard UNIX timestamp in seconds" // "standard UNIX timestamp in seconds"
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time error").as_secs(); let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time error").as_secs();
// "The network address of the node receiving this message" // "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" // "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." // "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(); let nonce: u64 = secp256k1::rand::thread_rng().gen();

View File

@ -46,7 +46,7 @@ use crate::blockdata::script::witness_version::{self, WitnessVersion};
use crate::blockdata::script::{self, Script, ScriptBuf}; use crate::blockdata::script::{self, Script, ScriptBuf};
use crate::crypto::key::{PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey}; use crate::crypto::key::{PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey};
use crate::hash_types::{PubkeyHash, ScriptHash}; use crate::hash_types::{PubkeyHash, ScriptHash};
use crate::network::constants::Network; use crate::network::Network;
use crate::prelude::*; use crate::prelude::*;
use crate::taproot::TapNodeHash; use crate::taproot::TapNodeHash;
@ -996,7 +996,7 @@ mod tests {
use super::*; use super::*;
use crate::crypto::key::PublicKey; use crate::crypto::key::PublicKey;
use crate::network::constants::Network::{Bitcoin, Testnet}; use crate::network::Network::{Bitcoin, Testnet};
fn roundtrips(addr: &Address) { fn roundtrips(addr: &Address) {
assert_eq!( assert_eq!(

View File

@ -23,7 +23,7 @@ use crate::crypto::key::{self, KeyPair, PrivateKey, PublicKey};
use crate::hash_types::XpubIdentifier; use crate::hash_types::XpubIdentifier;
use crate::internal_macros::impl_bytes_newtype; use crate::internal_macros::impl_bytes_newtype;
use crate::io::Write; use crate::io::Write;
use crate::network::constants::Network; use crate::network::Network;
use crate::prelude::*; use crate::prelude::*;
/// A chain code /// A chain code
@ -866,7 +866,7 @@ mod tests {
use super::ChildNumber::{Hardened, Normal}; use super::ChildNumber::{Hardened, Normal};
use super::*; use super::*;
use crate::internal_macros::hex; use crate::internal_macros::hex;
use crate::network::constants::Network::{self, Bitcoin}; use crate::network::Network::{self, Bitcoin};
#[test] #[test]
fn test_parse_derivation_path() { fn test_parse_derivation_path() {

View File

@ -20,7 +20,7 @@ use crate::blockdata::script;
use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut}; use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut};
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::internal_macros::impl_bytes_newtype; use crate::internal_macros::impl_bytes_newtype;
use crate::network::constants::Network; use crate::network::Network;
use crate::pow::CompactTarget; use crate::pow::CompactTarget;
use crate::Amount; use crate::Amount;
@ -198,7 +198,7 @@ mod test {
use crate::blockdata::locktime::absolute; use crate::blockdata::locktime::absolute;
use crate::consensus::encode::serialize; use crate::consensus::encode::serialize;
use crate::internal_macros::hex; use crate::internal_macros::hex;
use crate::network::constants::Network; use crate::network::Network;
#[test] #[test]
fn bitcoin_genesis_first_transaction() { fn bitcoin_genesis_first_transaction() {

View File

@ -68,7 +68,7 @@ impl OutPoint {
/// ///
/// ```rust /// ```rust
/// use bitcoin::constants::genesis_block; /// use bitcoin::constants::genesis_block;
/// use bitcoin::network::constants::Network; /// use bitcoin::Network;
/// ///
/// let block = genesis_block(Network::Bitcoin); /// let block = genesis_block(Network::Bitcoin);
/// let tx = &block.txdata[0]; /// let tx = &block.txdata[0];
@ -1508,7 +1508,7 @@ mod tests {
#[test] #[test]
fn test_is_coinbase() { fn test_is_coinbase() {
use crate::blockdata::constants; use crate::blockdata::constants;
use crate::network::constants::Network; use crate::network::Network;
let genesis = constants::genesis_block(Network::Bitcoin); let genesis = constants::genesis_block(Network::Bitcoin);
assert!(genesis.txdata[0].is_coinbase()); assert!(genesis.txdata[0].is_coinbase());

View File

@ -26,7 +26,7 @@ use crate::blockdata::transaction::{Transaction, TxIn, TxOut};
use crate::hash_types::{BlockHash, FilterHash, FilterHeader, TxMerkleNode}; use crate::hash_types::{BlockHash, FilterHash, FilterHeader, TxMerkleNode};
use crate::io::{self, Cursor, Read}; use crate::io::{self, Cursor, Read};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use crate::network::{ use crate::p2p::{
address::{AddrV2Message, Address}, address::{AddrV2Message, Address},
message_blockdata::Inventory, message_blockdata::Inventory,
}; };
@ -836,7 +836,7 @@ mod tests {
use super::*; use super::*;
use crate::consensus::{deserialize_partial, Decodable, Encodable}; use crate::consensus::{deserialize_partial, Decodable, Encodable};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use crate::network::{message_blockdata::Inventory, Address}; use crate::p2p::{message_blockdata::Inventory, Address};
#[test] #[test]
fn serialize_int_test() { fn serialize_int_test() {

View File

@ -6,7 +6,7 @@
//! chains (such as mainnet, testnet). //! chains (such as mainnet, testnet).
//! //!
use crate::network::constants::Network; use crate::network::Network;
use crate::pow::Work; use crate::pow::Work;
/// Parameters that influence chain consensus. /// Parameters that influence chain consensus.

View File

@ -18,7 +18,7 @@ pub use secp256k1::{self, constants, KeyPair, Parity, Secp256k1, Verification, X
use crate::crypto::ecdsa; use crate::crypto::ecdsa;
use crate::hash_types::{PubkeyHash, WPubkeyHash}; use crate::hash_types::{PubkeyHash, WPubkeyHash};
use crate::network::constants::Network; use crate::network::Network;
use crate::prelude::*; use crate::prelude::*;
use crate::taproot::{TapNodeHash, TapTweakHash}; use crate::taproot::{TapNodeHash, TapTweakHash};
use crate::{base58, io}; use crate::{base58, io};
@ -741,7 +741,7 @@ mod tests {
use super::*; use super::*;
use crate::address::Address; use crate::address::Address;
use crate::io; use crate::io;
use crate::network::constants::Network::{Bitcoin, Testnet}; use crate::network::Network::{Bitcoin, Testnet};
#[test] #[test]
fn test_key_derivation() { fn test_key_derivation() {

View File

@ -1120,7 +1120,7 @@ mod tests {
use crate::crypto::key::PublicKey; use crate::crypto::key::PublicKey;
use crate::crypto::sighash::{LegacySighash, TapSighash}; use crate::crypto::sighash::{LegacySighash, TapSighash};
use crate::internal_macros::hex; use crate::internal_macros::hex;
use crate::network::constants::Network; use crate::network::Network;
use crate::taproot::TapLeafHash; use crate::taproot::TapLeafHash;
extern crate serde_json; extern crate serde_json;

View File

@ -92,7 +92,7 @@ mod parse;
mod serde_utils; mod serde_utils;
#[macro_use] #[macro_use]
pub mod network; pub mod p2p;
pub mod address; pub mod address;
pub mod amount; pub mod amount;
pub mod base58; pub mod base58;
@ -106,6 +106,7 @@ pub(crate) mod crypto;
pub mod error; pub mod error;
pub mod hash_types; pub mod hash_types;
pub mod merkle_tree; pub mod merkle_tree;
pub mod network;
pub mod policy; pub mod policy;
pub mod pow; pub mod pow;
pub mod psbt; pub mod psbt;
@ -146,7 +147,7 @@ pub use crate::hash_types::{
BlockHash, PubkeyHash, ScriptHash, Txid, WPubkeyHash, WScriptHash, Wtxid, BlockHash, PubkeyHash, ScriptHash, Txid, WPubkeyHash, WScriptHash, Wtxid,
}; };
pub use crate::merkle_tree::MerkleBlock; pub use crate::merkle_tree::MerkleBlock;
pub use crate::network::constants::Network; pub use crate::network::Network;
pub use crate::pow::{CompactTarget, Target, Work}; pub use crate::pow::{CompactTarget, Target, Work};
pub use crate::psbt::Psbt; pub use crate::psbt::Psbt;
@ -172,13 +173,13 @@ mod io_extras {
#[rustfmt::skip] #[rustfmt::skip]
mod prelude { mod prelude {
#[cfg(all(not(feature = "std"), not(test)))] #[cfg(all(not(feature = "std"), not(test)))]
pub use alloc::{string::{String, ToString}, vec::Vec, boxed::Box, borrow::{Borrow, Cow, ToOwned}, slice, rc}; pub use alloc::{string::{String, ToString}, vec::Vec, boxed::Box, borrow::{Borrow, BorrowMut, Cow, ToOwned}, slice, rc};
#[cfg(all(not(feature = "std"), not(test), any(not(rust_v_1_60), target_has_atomic = "ptr")))] #[cfg(all(not(feature = "std"), not(test), any(not(rust_v_1_60), target_has_atomic = "ptr")))]
pub use alloc::sync; pub use alloc::sync;
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
pub use std::{string::{String, ToString}, vec::Vec, boxed::Box, borrow::{Borrow, Cow, ToOwned}, slice, rc, sync}; pub use std::{string::{String, ToString}, vec::Vec, boxed::Box, borrow::{Borrow, BorrowMut, Cow, ToOwned}, slice, rc, sync};
#[cfg(all(not(feature = "std"), not(test)))] #[cfg(all(not(feature = "std"), not(test)))]
pub use alloc::collections::{BTreeMap, BTreeSet, btree_map, BinaryHeap}; pub use alloc::collections::{BTreeMap, BTreeSet, btree_map, BinaryHeap};

397
bitcoin/src/network.rs Normal file
View File

@ -0,0 +1,397 @@
// SPDX-License-Identifier: CC0-1.0
//! Bitcoin network.
//!
//! The term "network" is overloaded, here [`Network`] refers to the specific
//! Bitcoin network we are operating on e.g., signet, regtest. The terms
//! "network" and "chain" are often used interchangeably for this concept.
//!
//! # Example: encoding a network's magic bytes
//!
//! ```rust
//! use bitcoin::Network;
//! use bitcoin::consensus::encode::serialize;
//!
//! let network = Network::Bitcoin;
//! let bytes = serialize(&network.magic());
//!
//! assert_eq!(&bytes[..], &[0xF9, 0xBE, 0xB4, 0xD9]);
//! ```
use core::convert::TryFrom;
use core::fmt;
use core::fmt::Display;
use core::str::FromStr;
use internals::write_err;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::constants::ChainHash;
use crate::error::impl_std_error;
use crate::p2p::Magic;
use crate::prelude::{String, ToOwned};
/// The cryptocurrency network to act on.
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[non_exhaustive]
pub enum Network {
/// Mainnet Bitcoin.
Bitcoin,
/// Bitcoin's testnet network.
Testnet,
/// Bitcoin's signet network.
Signet,
/// Bitcoin's regtest network.
Regtest,
}
impl Network {
/// Creates a `Network` from the magic bytes.
///
/// # Examples
///
/// ```rust
/// use bitcoin::p2p::Magic;
/// use bitcoin::Network;
/// use std::convert::TryFrom;
///
/// assert_eq!(Ok(Network::Bitcoin), Network::try_from(Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9])));
/// assert_eq!(None, Network::from_magic(Magic::from_bytes([0xFF, 0xFF, 0xFF, 0xFF])));
/// ```
pub fn from_magic(magic: Magic) -> Option<Network> { Network::try_from(magic).ok() }
/// Return the network magic bytes, which should be encoded little-endian
/// at the start of every message
///
/// # Examples
///
/// ```rust
/// use bitcoin::p2p::Magic;
/// use bitcoin::Network;
///
/// let network = Network::Bitcoin;
/// assert_eq!(network.magic(), Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9]));
/// ```
pub fn magic(self) -> Magic { Magic::from(self) }
/// Converts a `Network` to its equivalent `bitcoind -chain` argument name.
///
/// ```bash
/// $ bitcoin-23.0/bin/bitcoind --help | grep -C 3 '\-chain=<chain>'
/// Chain selection options:
///
/// -chain=<chain>
/// Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest
/// ```
pub fn to_core_arg(self) -> &'static str {
match self {
Network::Bitcoin => "main",
Network::Testnet => "test",
Network::Signet => "signet",
Network::Regtest => "regtest",
}
}
/// Converts a `bitcoind -chain` argument name to its equivalent `Network`.
///
/// ```bash
/// $ bitcoin-23.0/bin/bitcoind --help | grep -C 3 '\-chain=<chain>'
/// Chain selection options:
///
/// -chain=<chain>
/// Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest
/// ```
pub fn from_core_arg(core_arg: &str) -> Result<Self, ParseNetworkError> {
use Network::*;
let network = match core_arg {
"main" => Bitcoin,
"test" => Testnet,
"signet" => Signet,
"regtest" => Regtest,
_ => return Err(ParseNetworkError(core_arg.to_owned())),
};
Ok(network)
}
/// Return the network's chain hash (genesis block hash).
///
/// # Examples
///
/// ```rust
/// use bitcoin::Network;
/// use bitcoin::blockdata::constants::ChainHash;
///
/// let network = Network::Bitcoin;
/// assert_eq!(network.chain_hash(), ChainHash::BITCOIN);
/// ```
pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block(self) }
/// Creates a `Network` from the chain hash (genesis block hash).
///
/// # Examples
///
/// ```rust
/// use bitcoin::Network;
/// use bitcoin::blockdata::constants::ChainHash;
/// use std::convert::TryFrom;
///
/// assert_eq!(Ok(Network::Bitcoin), Network::try_from(ChainHash::BITCOIN));
/// ```
pub fn from_chain_hash(chain_hash: ChainHash) -> Option<Network> {
Network::try_from(chain_hash).ok()
}
}
#[cfg(feature = "serde")]
pub mod as_core_arg {
//! Module for serialization/deserialization of network variants into/from Bitcoin Core values
#![allow(missing_docs)]
use serde;
use crate::Network;
pub fn serialize<S>(network: &Network, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(network.to_core_arg())
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Network, D::Error>
where
D: serde::Deserializer<'de>,
{
struct NetworkVisitor;
impl<'de> serde::de::Visitor<'de> for NetworkVisitor {
type Value = Network;
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
Network::from_core_arg(s).map_err(|_| {
E::invalid_value(
serde::de::Unexpected::Str(s),
&"bitcoin network encoded as a string (either main, test, signet or regtest)",
)
})
}
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
formatter,
"bitcoin network encoded as a string (either main, test, signet or regtest)"
)
}
}
deserializer.deserialize_str(NetworkVisitor)
}
}
/// An error in parsing network string.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseNetworkError(String);
impl fmt::Display for ParseNetworkError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write_err!(f, "failed to parse {} as network", self.0; self)
}
}
impl_std_error!(ParseNetworkError);
impl FromStr for Network {
type Err = ParseNetworkError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
use Network::*;
let network = match s {
"bitcoin" => Bitcoin,
"testnet" => Testnet,
"signet" => Signet,
"regtest" => Regtest,
_ => return Err(ParseNetworkError(s.to_owned())),
};
Ok(network)
}
}
impl fmt::Display for Network {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use Network::*;
let s = match *self {
Bitcoin => "bitcoin",
Testnet => "testnet",
Signet => "signet",
Regtest => "regtest",
};
write!(f, "{}", s)
}
}
/// Error in parsing network from chain hash.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnknownChainHash(ChainHash);
impl Display for UnknownChainHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unknown chain hash: {}", self.0)
}
}
impl_std_error!(UnknownChainHash);
impl TryFrom<ChainHash> for Network {
type Error = UnknownChainHash;
fn try_from(chain_hash: ChainHash) -> Result<Self, Self::Error> {
match chain_hash {
// Note: any new network entries must be matched against here.
ChainHash::BITCOIN => Ok(Network::Bitcoin),
ChainHash::TESTNET => Ok(Network::Testnet),
ChainHash::SIGNET => Ok(Network::Signet),
ChainHash::REGTEST => Ok(Network::Regtest),
_ => Err(UnknownChainHash(chain_hash)),
}
}
}
#[cfg(test)]
mod tests {
use super::Network;
use crate::consensus::encode::{deserialize, serialize};
use crate::p2p::ServiceFlags;
#[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()));
}
#[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!(Network::Signet.to_string(), "signet");
assert_eq!("bitcoin".parse::<Network>().unwrap(), Network::Bitcoin);
assert_eq!("testnet".parse::<Network>().unwrap(), Network::Testnet);
assert_eq!("regtest".parse::<Network>().unwrap(), Network::Regtest);
assert_eq!("signet".parse::<Network>().unwrap(), Network::Signet);
assert!("fakenet".parse::<Network>().is_err());
}
#[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());
}
#[test]
#[cfg(feature = "serde")]
fn serde_roundtrip() {
use Network::*;
let tests = vec![
(Bitcoin, "bitcoin"),
(Testnet, "testnet"),
(Signet, "signet"),
(Regtest, "regtest"),
];
for tc in tests {
let network = tc.0;
let want = format!("\"{}\"", tc.1);
let got = serde_json::to_string(&tc.0).expect("failed to serialize network");
assert_eq!(got, want);
let back: Network = serde_json::from_str(&got).expect("failed to deserialize network");
assert_eq!(back, network);
}
}
#[test]
fn from_to_core_arg() {
let expected_pairs = [
(Network::Bitcoin, "main"),
(Network::Testnet, "test"),
(Network::Regtest, "regtest"),
(Network::Signet, "signet"),
];
for (net, core_arg) in &expected_pairs {
assert_eq!(Network::from_core_arg(core_arg), Ok(*net));
assert_eq!(net.to_core_arg(), *core_arg);
}
}
#[cfg(feature = "serde")]
#[test]
fn serde_as_core_arg() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(crate = "actual_serde")]
struct T {
#[serde(with = "crate::network::as_core_arg")]
pub network: Network,
}
serde_test::assert_tokens(
&T { network: Network::Bitcoin },
&[
serde_test::Token::Struct { name: "T", len: 1 },
serde_test::Token::Str("network"),
serde_test::Token::Str("main"),
serde_test::Token::StructEnd,
],
);
}
}

View File

@ -1,28 +0,0 @@
// SPDX-License-Identifier: CC0-1.0
//! Bitcoin network support.
//!
//! This module defines support for (de)serialization and network transport
//! of Bitcoin data and network messages.
//!
pub mod constants;
#[cfg(feature = "std")]
pub mod address;
#[cfg(feature = "std")]
pub use self::address::Address;
#[cfg(feature = "std")]
pub mod message;
#[cfg(feature = "std")]
pub mod message_blockdata;
#[cfg(feature = "std")]
pub mod message_bloom;
#[cfg(feature = "std")]
pub mod message_compact_blocks;
#[cfg(feature = "std")]
pub mod message_filter;
#[cfg(feature = "std")]
pub mod message_network;
pub use self::constants::Magic;

View File

@ -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::consensus::encode::{self, Decodable, Encodable, ReadExt, VarInt, WriteExt};
use crate::io; use crate::io;
use crate::network::constants::ServiceFlags; use crate::p2p::ServiceFlags;
use crate::prelude::*; use crate::prelude::*;
/// A message which can be sent on the Bitcoin network /// A message which can be sent on the Bitcoin network
@ -311,7 +311,7 @@ mod test {
use super::{AddrV2, AddrV2Message, Address}; use super::{AddrV2, AddrV2Message, Address};
use crate::consensus::encode::{deserialize, serialize}; use crate::consensus::encode::{deserialize, serialize};
use crate::internal_macros::hex; use crate::internal_macros::hex;
use crate::network::constants::ServiceFlags; use crate::p2p::ServiceFlags;
#[test] #[test]
fn serialize_address_test() { fn serialize_address_test() {

View File

@ -16,10 +16,10 @@ use crate::blockdata::{block, transaction};
use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, VarInt}; use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, VarInt};
use crate::io; use crate::io;
use crate::merkle_tree::MerkleBlock; use crate::merkle_tree::MerkleBlock;
use crate::network::address::{AddrV2Message, Address}; use crate::p2p::address::{AddrV2Message, Address};
use crate::network::constants::Magic; use crate::p2p::{
use crate::network::{
message_blockdata, message_bloom, message_compact_blocks, message_filter, message_network, message_blockdata, message_bloom, message_compact_blocks, message_filter, message_network,
Magic,
}; };
use crate::prelude::*; use crate::prelude::*;
@ -549,14 +549,15 @@ mod test {
use crate::blockdata::transaction::Transaction; use crate::blockdata::transaction::Transaction;
use crate::consensus::encode::{deserialize, deserialize_partial, serialize}; use crate::consensus::encode::{deserialize, deserialize_partial, serialize};
use crate::internal_macros::hex; use crate::internal_macros::hex;
use crate::network::address::{AddrV2, AddrV2Message, Address}; use crate::network::Network;
use crate::network::constants::{Magic, Network, ServiceFlags}; use crate::p2p::address::{AddrV2, AddrV2Message, Address};
use crate::network::message_blockdata::{GetBlocksMessage, GetHeadersMessage, Inventory}; use crate::p2p::message_blockdata::{GetBlocksMessage, GetHeadersMessage, Inventory};
use crate::network::message_bloom::{BloomFlags, FilterAdd, FilterLoad}; use crate::p2p::message_bloom::{BloomFlags, FilterAdd, FilterLoad};
use crate::network::message_compact_blocks::{GetBlockTxn, SendCmpct}; use crate::p2p::message_compact_blocks::{GetBlockTxn, SendCmpct};
use crate::network::message_filter::{ use crate::p2p::message_filter::{
CFCheckpt, CFHeaders, CFilter, GetCFCheckpt, GetCFHeaders, GetCFilters, CFCheckpt, CFHeaders, CFilter, GetCFCheckpt, GetCFHeaders, GetCFilters,
}; };
use crate::p2p::{Magic, ServiceFlags};
fn hash(slice: [u8; 32]) -> Hash { Hash::from_slice(&slice).unwrap() } fn hash(slice: [u8; 32]) -> Hash { Hash::from_slice(&slice).unwrap() }

View File

@ -11,9 +11,8 @@ use hashes::{sha256d, Hash as _};
use crate::consensus::encode::{self, Decodable, Encodable}; use crate::consensus::encode::{self, Decodable, Encodable};
use crate::hash_types::{BlockHash, Txid, Wtxid}; use crate::hash_types::{BlockHash, Txid, Wtxid};
use crate::internal_macros::impl_consensus_encoding; use crate::internal_macros::impl_consensus_encoding;
use crate::io;
use crate::network::constants;
use crate::prelude::*; use crate::prelude::*;
use crate::{io, p2p};
/// An inventory item. /// An inventory item.
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)] #[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
@ -128,7 +127,7 @@ pub struct GetHeadersMessage {
impl GetBlocksMessage { impl GetBlocksMessage {
/// Construct a new `getblocks` message /// Construct a new `getblocks` message
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage { pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage {
GetBlocksMessage { version: constants::PROTOCOL_VERSION, locator_hashes, stop_hash } GetBlocksMessage { version: p2p::PROTOCOL_VERSION, locator_hashes, stop_hash }
} }
} }
@ -137,7 +136,7 @@ impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
impl GetHeadersMessage { impl GetHeadersMessage {
/// Construct a new `getheaders` message /// Construct a new `getheaders` message
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage { pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage {
GetHeadersMessage { version: constants::PROTOCOL_VERSION, locator_hashes, stop_hash } GetHeadersMessage { version: p2p::PROTOCOL_VERSION, locator_hashes, stop_hash }
} }
} }

View File

@ -10,10 +10,10 @@ use hashes::sha256d;
use crate::consensus::{encode, Decodable, Encodable, ReadExt}; use crate::consensus::{encode, Decodable, Encodable, ReadExt};
use crate::internal_macros::impl_consensus_encoding; use crate::internal_macros::impl_consensus_encoding;
use crate::io; use crate::p2p::address::Address;
use crate::network::address::Address; use crate::p2p::ServiceFlags;
use crate::network::constants::{self, ServiceFlags};
use crate::prelude::*; use crate::prelude::*;
use crate::{io, p2p};
/// Some simple messages /// Some simple messages
@ -54,7 +54,7 @@ impl VersionMessage {
start_height: i32, start_height: i32,
) -> VersionMessage { ) -> VersionMessage {
VersionMessage { VersionMessage {
version: constants::PROTOCOL_VERSION, version: p2p::PROTOCOL_VERSION,
services, services,
timestamp, timestamp,
receiver, receiver,
@ -146,7 +146,7 @@ mod tests {
use super::{Reject, RejectReason, VersionMessage}; use super::{Reject, RejectReason, VersionMessage};
use crate::consensus::encode::{deserialize, serialize}; use crate::consensus::encode::{deserialize, serialize};
use crate::internal_macros::hex; use crate::internal_macros::hex;
use crate::network::constants::ServiceFlags; use crate::p2p::ServiceFlags;
#[test] #[test]
fn version_message_test() { fn version_message_test() {

View File

@ -1,51 +1,45 @@
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
//! Bitcoin network constants. //! Bitcoin p2p network types.
//! //!
//! This module provides various constants relating to the Bitcoin network //! This module defines support for (de)serialization and network transport
//! protocol, such as protocol versioning and magic header bytes. //! of Bitcoin data and Bitcoin p2p network messages.
//!
//! The [`Network`][1] type implements the [`Decodable`][2] and #[cfg(feature = "std")]
//! [`Encodable`][3] traits and encodes the magic bytes of the given pub mod address;
//! network. #[cfg(feature = "std")]
//! pub use self::address::Address;
//! [1]: enum.Network.html #[cfg(feature = "std")]
//! [2]: ../../consensus/encode/trait.Decodable.html pub mod message;
//! [3]: ../../consensus/encode/trait.Encodable.html #[cfg(feature = "std")]
//! pub mod message_blockdata;
//! # Example: encoding a network's magic bytes #[cfg(feature = "std")]
//! pub mod message_bloom;
//! ```rust #[cfg(feature = "std")]
//! use bitcoin::network::constants::Network; pub mod message_compact_blocks;
//! use bitcoin::consensus::encode::serialize; #[cfg(feature = "std")]
//! pub mod message_filter;
//! let network = Network::Bitcoin; #[cfg(feature = "std")]
//! let bytes = serialize(&network.magic()); pub mod message_network;
//!
//! assert_eq!(&bytes[..], &[0xF9, 0xBE, 0xB4, 0xD9]);
//! ```
use core::borrow::{Borrow, BorrowMut};
use core::convert::TryFrom; use core::convert::TryFrom;
use core::fmt::Display;
use core::str::FromStr; use core::str::FromStr;
use core::{fmt, ops}; use core::{fmt, ops};
use hex::FromHex; use hex::FromHex;
use internals::{debug_from_display, write_err}; use internals::{debug_from_display, write_err};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::consensus::encode::{self, Decodable, Encodable}; use crate::consensus::encode::{self, Decodable, Encodable};
use crate::constants::ChainHash;
use crate::error::impl_std_error; use crate::error::impl_std_error;
use crate::io; use crate::prelude::{Borrow, BorrowMut, String, ToOwned};
use crate::prelude::{String, ToOwned}; use crate::{io, Network};
/// Version of the protocol as appearing in network message headers /// 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. /// This constant is used to signal to other peers which features you support. Increasing it implies
/// Doing so without support may lead to you incorrectly banning other peers or other peers banning you. /// 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: /// These are the features required for each version:
/// 70016 - Support receiving `wtxidrelay` message between `version` and `verack` message /// 70016 - Support receiving `wtxidrelay` message between `version` and `verack` message
/// 70015 - Support receiving invalid compact blocks from a peer without banning them /// 70015 - Support receiving invalid compact blocks from a peer without banning them
@ -59,387 +53,6 @@ use crate::prelude::{String, ToOwned};
/// 60001 - Support `pong` message and nonce in `ping` message /// 60001 - Support `pong` message and nonce in `ping` message
pub const PROTOCOL_VERSION: u32 = 70001; pub const PROTOCOL_VERSION: u32 = 70001;
/// The cryptocurrency network to act on.
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[non_exhaustive]
pub enum Network {
/// Mainnet Bitcoin.
Bitcoin,
/// Bitcoin's testnet network.
Testnet,
/// Bitcoin's signet network.
Signet,
/// Bitcoin's regtest network.
Regtest,
}
impl Network {
/// Creates a `Network` from the magic bytes.
///
/// # Examples
///
/// ```rust
/// use bitcoin::network::constants::{Network, Magic};
/// use std::convert::TryFrom;
///
/// assert_eq!(Ok(Network::Bitcoin), Network::try_from(Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9])));
/// assert_eq!(None, Network::from_magic(Magic::from_bytes([0xFF, 0xFF, 0xFF, 0xFF])));
/// ```
pub fn from_magic(magic: Magic) -> Option<Network> { Network::try_from(magic).ok() }
/// Return the network magic bytes, which should be encoded little-endian
/// at the start of every message
///
/// # Examples
///
/// ```rust
/// use bitcoin::network::constants::{Network, Magic};
///
/// let network = Network::Bitcoin;
/// assert_eq!(network.magic(), Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9]));
/// ```
pub fn magic(self) -> Magic { Magic::from(self) }
/// Converts a `Network` to its equivalent `bitcoind -chain` argument name.
///
/// ```bash
/// $ bitcoin-23.0/bin/bitcoind --help | grep -C 3 '\-chain=<chain>'
/// Chain selection options:
///
/// -chain=<chain>
/// Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest
/// ```
pub fn to_core_arg(self) -> &'static str {
match self {
Network::Bitcoin => "main",
Network::Testnet => "test",
Network::Signet => "signet",
Network::Regtest => "regtest",
}
}
/// Converts a `bitcoind -chain` argument name to its equivalent `Network`.
///
/// ```bash
/// $ bitcoin-23.0/bin/bitcoind --help | grep -C 3 '\-chain=<chain>'
/// Chain selection options:
///
/// -chain=<chain>
/// Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest
/// ```
pub fn from_core_arg(core_arg: &str) -> Result<Self, ParseNetworkError> {
use Network::*;
let network = match core_arg {
"main" => Bitcoin,
"test" => Testnet,
"signet" => Signet,
"regtest" => Regtest,
_ => return Err(ParseNetworkError(core_arg.to_owned())),
};
Ok(network)
}
/// Return the network's chain hash (genesis block hash).
///
/// # Examples
///
/// ```rust
/// use bitcoin::network::constants::Network;
/// use bitcoin::blockdata::constants::ChainHash;
///
/// let network = Network::Bitcoin;
/// assert_eq!(network.chain_hash(), ChainHash::BITCOIN);
/// ```
pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block(self) }
/// Creates a `Network` from the chain hash (genesis block hash).
///
/// # Examples
///
/// ```rust
/// use bitcoin::network::constants::Network;
/// use bitcoin::blockdata::constants::ChainHash;
/// use std::convert::TryFrom;
///
/// assert_eq!(Ok(Network::Bitcoin), Network::try_from(ChainHash::BITCOIN));
/// ```
pub fn from_chain_hash(chain_hash: ChainHash) -> Option<Network> {
Network::try_from(chain_hash).ok()
}
}
#[cfg(feature = "serde")]
pub mod as_core_arg {
//! Module for serialization/deserialization of network variants into/from Bitcoin Core values
#![allow(missing_docs)]
use serde;
use crate::Network;
pub fn serialize<S>(network: &Network, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(network.to_core_arg())
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Network, D::Error>
where
D: serde::Deserializer<'de>,
{
struct NetworkVisitor;
impl<'de> serde::de::Visitor<'de> for NetworkVisitor {
type Value = Network;
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
Network::from_core_arg(s).map_err(|_| {
E::invalid_value(
serde::de::Unexpected::Str(s),
&"bitcoin network encoded as a string (either main, test, signet or regtest)",
)
})
}
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
formatter,
"bitcoin network encoded as a string (either main, test, signet or regtest)"
)
}
}
deserializer.deserialize_str(NetworkVisitor)
}
}
/// An error in parsing network string.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseNetworkError(String);
impl fmt::Display for ParseNetworkError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write_err!(f, "failed to parse {} as network", self.0; self)
}
}
impl_std_error!(ParseNetworkError);
impl FromStr for Network {
type Err = ParseNetworkError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
use Network::*;
let network = match s {
"bitcoin" => Bitcoin,
"testnet" => Testnet,
"signet" => Signet,
"regtest" => Regtest,
_ => return Err(ParseNetworkError(s.to_owned())),
};
Ok(network)
}
}
impl fmt::Display for Network {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use Network::*;
let s = match *self {
Bitcoin => "bitcoin",
Testnet => "testnet",
Signet => "signet",
Regtest => "regtest",
};
write!(f, "{}", s)
}
}
/// Error in parsing network from chain hash.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnknownChainHash(ChainHash);
impl Display for UnknownChainHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unknown chain hash: {}", self.0)
}
}
impl_std_error!(UnknownChainHash);
impl TryFrom<ChainHash> for Network {
type Error = UnknownChainHash;
fn try_from(chain_hash: ChainHash) -> Result<Self, Self::Error> {
match chain_hash {
// Note: any new network entries must be matched against here.
ChainHash::BITCOIN => Ok(Network::Bitcoin),
ChainHash::TESTNET => Ok(Network::Testnet),
ChainHash::SIGNET => Ok(Network::Signet),
ChainHash::REGTEST => Ok(Network::Regtest),
_ => Err(UnknownChainHash(chain_hash)),
}
}
}
/// Network magic bytes to identify the cryptocurrency network the message was intended for.
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Magic([u8; 4]);
impl Magic {
/// Bitcoin mainnet network magic bytes.
pub const BITCOIN: Self = Self([0xF9, 0xBE, 0xB4, 0xD9]);
/// Bitcoin testnet network magic bytes.
pub const TESTNET: Self = Self([0x0B, 0x11, 0x09, 0x07]);
/// Bitcoin signet network magic bytes.
pub const SIGNET: Self = Self([0x0A, 0x03, 0xCF, 0x40]);
/// Bitcoin regtest network magic bytes.
pub const REGTEST: Self = Self([0xFA, 0xBF, 0xB5, 0xDA]);
/// Create network magic from bytes.
pub fn from_bytes(bytes: [u8; 4]) -> Magic { Magic(bytes) }
/// Get network magic bytes.
pub fn to_bytes(self) -> [u8; 4] { self.0 }
}
/// An error in parsing magic bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseMagicError {
/// The error that occurred when parsing the string.
error: hex::HexToArrayError,
/// The byte string that failed to parse.
magic: String,
}
impl FromStr for Magic {
type Err = ParseMagicError;
fn from_str(s: &str) -> Result<Magic, Self::Err> {
match <[u8; 4]>::from_hex(s) {
Ok(magic) => Ok(Magic::from_bytes(magic)),
Err(e) => Err(ParseMagicError { error: e, magic: s.to_owned() }),
}
}
}
impl From<Network> for Magic {
fn from(network: Network) -> Magic {
match network {
// Note: new network entries must explicitly be matched in `try_from` below.
Network::Bitcoin => Magic::BITCOIN,
Network::Testnet => Magic::TESTNET,
Network::Signet => Magic::SIGNET,
Network::Regtest => Magic::REGTEST,
}
}
}
/// Error in creating a Network from Magic bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnknownMagic(Magic);
impl TryFrom<Magic> for Network {
type Error = UnknownMagic;
fn try_from(magic: Magic) -> Result<Self, Self::Error> {
match magic {
// Note: any new network entries must be matched against here.
Magic::BITCOIN => Ok(Network::Bitcoin),
Magic::TESTNET => Ok(Network::Testnet),
Magic::SIGNET => Ok(Network::Signet),
Magic::REGTEST => Ok(Network::Regtest),
_ => Err(UnknownMagic(magic)),
}
}
}
impl fmt::Display for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
hex::fmt_hex_exact!(f, 4, &self.0, hex::Case::Lower)?;
Ok(())
}
}
debug_from_display!(Magic);
impl fmt::LowerHex for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
hex::fmt_hex_exact!(f, 4, &self.0, hex::Case::Lower)?;
Ok(())
}
}
impl fmt::UpperHex for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
hex::fmt_hex_exact!(f, 4, &self.0, hex::Case::Upper)?;
Ok(())
}
}
impl Encodable for Magic {
fn consensus_encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(writer)
}
}
impl Decodable for Magic {
fn consensus_decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, encode::Error> {
Ok(Magic(Decodable::consensus_decode(reader)?))
}
}
impl AsRef<[u8]> for Magic {
fn as_ref(&self) -> &[u8] { &self.0 }
}
impl AsRef<[u8; 4]> for Magic {
fn as_ref(&self) -> &[u8; 4] { &self.0 }
}
impl AsMut<[u8]> for Magic {
fn as_mut(&mut self) -> &mut [u8] { &mut self.0 }
}
impl AsMut<[u8; 4]> for Magic {
fn as_mut(&mut self) -> &mut [u8; 4] { &mut self.0 }
}
impl Borrow<[u8]> for Magic {
fn borrow(&self) -> &[u8] { &self.0 }
}
impl Borrow<[u8; 4]> for Magic {
fn borrow(&self) -> &[u8; 4] { &self.0 }
}
impl BorrowMut<[u8]> for Magic {
fn borrow_mut(&mut self) -> &mut [u8] { &mut self.0 }
}
impl BorrowMut<[u8; 4]> for Magic {
fn borrow_mut(&mut self) -> &mut [u8; 4] { &mut self.0 }
}
impl fmt::Display for ParseMagicError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write_err!(f, "failed to parse {} as network magic", self.magic; self.error)
}
}
impl_std_error!(ParseMagicError, error);
impl fmt::Display for UnknownMagic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "unknown network magic {}", self.0)
}
}
impl_std_error!(UnknownMagic);
/// Flags to indicate which network services a node supports. /// Flags to indicate which network services a node supports.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ServiceFlags(u64); pub struct ServiceFlags(u64);
@ -587,41 +200,161 @@ impl Decodable for ServiceFlags {
Ok(ServiceFlags(Decodable::consensus_decode(r)?)) Ok(ServiceFlags(Decodable::consensus_decode(r)?))
} }
} }
/// Network magic bytes to identify the cryptocurrency network the message was intended for.
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Magic([u8; 4]);
impl Magic {
/// Bitcoin mainnet network magic bytes.
pub const BITCOIN: Self = Self([0xF9, 0xBE, 0xB4, 0xD9]);
/// Bitcoin testnet network magic bytes.
pub const TESTNET: Self = Self([0x0B, 0x11, 0x09, 0x07]);
/// Bitcoin signet network magic bytes.
pub const SIGNET: Self = Self([0x0A, 0x03, 0xCF, 0x40]);
/// Bitcoin regtest network magic bytes.
pub const REGTEST: Self = Self([0xFA, 0xBF, 0xB5, 0xDA]);
/// Create network magic from bytes.
pub fn from_bytes(bytes: [u8; 4]) -> Magic { Magic(bytes) }
/// Get network magic bytes.
pub fn to_bytes(self) -> [u8; 4] { self.0 }
}
/// An error in parsing magic bytes.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ParseMagicError {
/// The error that occurred when parsing the string.
error: hex::HexToArrayError,
/// The byte string that failed to parse.
magic: String,
}
impl FromStr for Magic {
type Err = ParseMagicError;
fn from_str(s: &str) -> Result<Magic, Self::Err> {
match <[u8; 4]>::from_hex(s) {
Ok(magic) => Ok(Magic::from_bytes(magic)),
Err(e) => Err(ParseMagicError { error: e, magic: s.to_owned() }),
}
}
}
impl From<Network> for Magic {
fn from(network: Network) -> Magic {
match network {
// Note: new network entries must explicitly be matched in `try_from` below.
Network::Bitcoin => Magic::BITCOIN,
Network::Testnet => Magic::TESTNET,
Network::Signet => Magic::SIGNET,
Network::Regtest => Magic::REGTEST,
}
}
}
/// Error in creating a Network from Magic bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnknownMagic(Magic);
impl TryFrom<Magic> for Network {
type Error = UnknownMagic;
fn try_from(magic: Magic) -> Result<Self, Self::Error> {
match magic {
// Note: any new network entries must be matched against here.
Magic::BITCOIN => Ok(Network::Bitcoin),
Magic::TESTNET => Ok(Network::Testnet),
Magic::SIGNET => Ok(Network::Signet),
Magic::REGTEST => Ok(Network::Regtest),
_ => Err(UnknownMagic(magic)),
}
}
}
impl fmt::Display for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
hex::fmt_hex_exact!(f, 4, &self.0, hex::Case::Lower)?;
Ok(())
}
}
debug_from_display!(Magic);
impl fmt::LowerHex for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
hex::fmt_hex_exact!(f, 4, &self.0, hex::Case::Lower)?;
Ok(())
}
}
impl fmt::UpperHex for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
hex::fmt_hex_exact!(f, 4, &self.0, hex::Case::Upper)?;
Ok(())
}
}
impl Encodable for Magic {
fn consensus_encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(writer)
}
}
impl Decodable for Magic {
fn consensus_decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, encode::Error> {
Ok(Magic(Decodable::consensus_decode(reader)?))
}
}
impl AsRef<[u8]> for Magic {
fn as_ref(&self) -> &[u8] { &self.0 }
}
impl AsRef<[u8; 4]> for Magic {
fn as_ref(&self) -> &[u8; 4] { &self.0 }
}
impl AsMut<[u8]> for Magic {
fn as_mut(&mut self) -> &mut [u8] { &mut self.0 }
}
impl AsMut<[u8; 4]> for Magic {
fn as_mut(&mut self) -> &mut [u8; 4] { &mut self.0 }
}
impl Borrow<[u8]> for Magic {
fn borrow(&self) -> &[u8] { &self.0 }
}
impl Borrow<[u8; 4]> for Magic {
fn borrow(&self) -> &[u8; 4] { &self.0 }
}
impl BorrowMut<[u8]> for Magic {
fn borrow_mut(&mut self) -> &mut [u8] { &mut self.0 }
}
impl BorrowMut<[u8; 4]> for Magic {
fn borrow_mut(&mut self) -> &mut [u8; 4] { &mut self.0 }
}
impl fmt::Display for ParseMagicError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write_err!(f, "failed to parse {} as network magic", self.magic; self.error)
}
}
impl_std_error!(ParseMagicError, error);
impl fmt::Display for UnknownMagic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "unknown network magic {}", self.0)
}
}
impl_std_error!(UnknownMagic);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::convert::TryFrom; use super::*;
use std::str::FromStr;
use super::{Magic, 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()));
}
#[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!(Network::Signet.to_string(), "signet");
assert_eq!("bitcoin".parse::<Network>().unwrap(), Network::Bitcoin);
assert_eq!("testnet".parse::<Network>().unwrap(), Network::Testnet);
assert_eq!("regtest".parse::<Network>().unwrap(), Network::Regtest);
assert_eq!("signet".parse::<Network>().unwrap(), Network::Signet);
assert!("fakenet".parse::<Network>().is_err());
}
#[test] #[test]
fn service_flags_test() { fn service_flags_test() {
@ -663,29 +396,6 @@ mod tests {
assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string()); assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string());
} }
#[test]
#[cfg(feature = "serde")]
fn serde_roundtrip() {
use Network::*;
let tests = vec![
(Bitcoin, "bitcoin"),
(Testnet, "testnet"),
(Signet, "signet"),
(Regtest, "regtest"),
];
for tc in tests {
let network = tc.0;
let want = format!("\"{}\"", tc.1);
let got = serde_json::to_string(&tc.0).expect("failed to serialize network");
assert_eq!(got, want);
let back: Network = serde_json::from_str(&got).expect("failed to deserialize network");
assert_eq!(back, network);
}
}
#[test] #[test]
fn magic_from_str() { fn magic_from_str() {
let known_network_magic_strs = [ let known_network_magic_strs = [
@ -701,40 +411,4 @@ mod tests {
assert_eq!(&magic.to_string(), magic_str); assert_eq!(&magic.to_string(), magic_str);
} }
} }
#[test]
fn from_to_core_arg() {
let expected_pairs = [
(Network::Bitcoin, "main"),
(Network::Testnet, "test"),
(Network::Regtest, "regtest"),
(Network::Signet, "signet"),
];
for (net, core_arg) in &expected_pairs {
assert_eq!(Network::from_core_arg(core_arg), Ok(*net));
assert_eq!(net.to_core_arg(), *core_arg);
}
}
#[cfg(feature = "serde")]
#[test]
fn serde_as_core_arg() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(crate = "actual_serde")]
struct T {
#[serde(with = "crate::network::constants::as_core_arg")]
pub network: Network,
}
serde_test::assert_tokens(
&T { network: Network::Bitcoin },
&[
serde_test::Token::Struct { name: "T", len: 1 },
serde_test::Token::Str("network"),
serde_test::Token::Str("main"),
serde_test::Token::StructEnd,
],
);
}
} }

View File

@ -823,7 +823,7 @@ mod tests {
use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut}; use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut};
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::internal_macros::hex; use crate::internal_macros::hex;
use crate::network::constants::Network::Bitcoin; use crate::network::Network::Bitcoin;
use crate::psbt::map::{Input, Output}; use crate::psbt::map::{Input, Output};
use crate::psbt::raw; use crate::psbt::raw;
use crate::psbt::serialize::{Deserialize, Serialize}; use crate::psbt::serialize::{Deserialize, Serialize};

View File

@ -1,7 +1,7 @@
use honggfuzz::fuzz; use honggfuzz::fuzz;
fn do_test(data: &[u8]) { fn do_test(data: &[u8]) {
let _: Result<bitcoin::network::message::RawNetworkMessage, _> = let _: Result<bitcoin::p2p::message::RawNetworkMessage, _> =
bitcoin::consensus::encode::deserialize(data); bitcoin::consensus::encode::deserialize(data);
} }

View File

@ -1,7 +1,7 @@
use bitcoin::address::Address; use bitcoin::address::Address;
use bitcoin::blockdata::script; use bitcoin::blockdata::script;
use bitcoin::consensus::encode; use bitcoin::consensus::encode;
use bitcoin::network::constants::Network; use bitcoin::Network;
use honggfuzz::fuzz; use honggfuzz::fuzz;
fn do_test(data: &[u8]) { fn do_test(data: &[u8]) {