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: ACKd4e8f49fc3
Tree-SHA512: 7cee93d36363b8acbece6c92c6afc34bb3abe6d5801373d291db1323c934e7c384f3b5881cefb8b0d102dea53da6ed8da1bf60c324bf60d5dab3af598e0310da
This commit is contained in:
commit
fc562e953e
|
@ -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();
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
|
@ -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() {
|
|
@ -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() }
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
|
@ -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,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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};
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]) {
|
||||||
|
|
Loading…
Reference in New Issue