Merge rust-bitcoin/rust-bitcoin#4589: Move `bitcoin::p2p` into `p2p`

d9cf7270eb Move `bitcoin/p2p` into `p2p` (rustaceanrob)

Pull request description:

  Could use a rebase on #4568

  Somehow lost the branch of my original draft so some of the review context is removed, but all comments were addressed in separate PRs.

  First commit moves everything required to implement encoding within `p2p`, so we have a reference for functions to add to future moves to `internals`. Second commit does the relocation and third commit removes the `bitcoin/p2p` module.

ACKs for top commit:
  apoelstra:
    ACK d9cf7270eb457fd660fa505701895ab4756e394d; successfully ran local tests
  tcharding:
    ACK d9cf7270eb

Tree-SHA512: 31bf960788f45bb60b04dd73793a06828a5540e1e6118376776494bad9330dc8ebcb57749bc84b70a00e4d1d2a5686506e089269833ed99c4f9fa0c3e3b2e5b7
This commit is contained in:
merge-script 2025-06-19 17:00:08 +00:00
commit 5743a81128
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
20 changed files with 687 additions and 613 deletions

View File

@ -77,6 +77,7 @@ name = "bitcoin-fuzz"
version = "0.0.1"
dependencies = [
"bitcoin",
"bitcoin-p2p-messages",
"honggfuzz",
"serde",
"serde_json",
@ -109,6 +110,15 @@ dependencies = [
[[package]]
name = "bitcoin-p2p-messages"
version = "0.1.0"
dependencies = [
"bitcoin",
"bitcoin-internals",
"bitcoin-io 0.2.0",
"bitcoin-units",
"bitcoin_hashes 0.16.0",
"hex-conservative 0.3.0",
"hex_lit",
]
[[package]]
name = "bitcoin-primitives"

View File

@ -76,6 +76,7 @@ name = "bitcoin-fuzz"
version = "0.0.1"
dependencies = [
"bitcoin",
"bitcoin-p2p-messages",
"honggfuzz",
"serde",
"serde_json",
@ -108,6 +109,15 @@ dependencies = [
[[package]]
name = "bitcoin-p2p-messages"
version = "0.1.0"
dependencies = [
"bitcoin",
"bitcoin-internals",
"bitcoin-io 0.2.0",
"bitcoin-units",
"bitcoin_hashes 0.16.0",
"hex-conservative 0.3.0",
"hex_lit",
]
[[package]]
name = "bitcoin-primitives"

View File

@ -56,10 +56,6 @@ rustdoc-args = ["--cfg", "docsrs"]
[[example]]
name = "bip32"
[[example]]
name = "handshake"
required-features = ["rand-std"]
[[example]]
name = "ecdsa-psbt"
required-features = ["std", "bitcoinconsensus"]

View File

@ -94,7 +94,6 @@ extern crate serde;
mod internal_macros;
#[macro_use]
pub mod p2p;
pub mod address;
pub mod bip152;
pub mod bip158;

View File

@ -1,42 +0,0 @@
macro_rules! impl_vec_wrapper {
($wrapper: ident, $type: ty) => {
impl crate::consensus::encode::Encodable for $wrapper {
#[inline]
fn consensus_encode<W: io::Write + ?Sized>(
&self,
w: &mut W,
) -> core::result::Result<usize, io::Error> {
let mut len = 0;
len += w.emit_compact_size(self.0.len())?;
for c in self.0.iter() {
len += c.consensus_encode(w)?;
}
Ok(len)
}
}
impl crate::consensus::encode::Decodable for $wrapper {
#[inline]
fn consensus_decode_from_finite_reader<R: io::BufRead + ?Sized>(
r: &mut R,
) -> core::result::Result<$wrapper, crate::consensus::encode::Error> {
let len = r.read_compact_size()?;
// Do not allocate upfront more items than if the sequence of type
// occupied roughly quarter a block. This should never be the case
// for normal data, but even if that's not true - `push` will just
// reallocate.
// Note: OOM protection relies on reader eventually running out of
// data to feed us.
let max_capacity =
crate::consensus::encode::MAX_VEC_SIZE / 4 / core::mem::size_of::<$type>();
let mut ret = Vec::with_capacity(core::cmp::min(len as usize, max_capacity));
for _ in 0..len {
ret.push(Decodable::consensus_decode_from_finite_reader(r)?);
}
Ok($wrapper(ret))
}
}
};
}
pub(crate) use impl_vec_wrapper;

View File

@ -1,495 +0,0 @@
// SPDX-License-Identifier: CC0-1.0
//! Bitcoin p2p network types.
//!
//! This module defines support for (de)serialization and network transport
//! of Bitcoin data and Bitcoin p2p network messages.
#[cfg(feature = "std")]
pub mod address;
#[cfg(feature = "std")]
mod deser;
#[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;
use core::str::FromStr;
use core::{fmt, ops};
use hex::FromHex;
use internals::{impl_to_hex_from_lower_hex, write_err};
use io::{BufRead, Write};
use crate::consensus::encode::{self, Decodable, Encodable};
use crate::network::{Network, Params, TestnetVersion};
use crate::prelude::{Borrow, BorrowMut, String, ToOwned};
#[rustfmt::skip]
#[doc(inline)]
#[cfg(feature = "std")]
pub use self::address::Address;
/// Version of the protocol as appearing in network message headers.
///
/// This constant is used to signal to other peers which features you support. Increasing it implies
/// that your software also supports every feature prior to this version. Doing so without support
/// may lead to you incorrectly banning other peers or other peers banning you.
///
/// These are the features required for each version:
/// 70016 - Support receiving `wtxidrelay` message between `version` and `verack` message
/// 70015 - Support receiving invalid compact blocks from a peer without banning them
/// 70014 - Support compact block messages `sendcmpct`, `cmpctblock`, `getblocktxn` and `blocktxn`
/// 70013 - Support `feefilter` message
/// 70012 - Support `sendheaders` message and announce new blocks via headers rather than inv
/// 70011 - Support NODE_BLOOM service flag and don't support bloom filter messages if it is not set
/// 70002 - Support `reject` message
/// 70001 - Support bloom filter messages `filterload`, `filterclear` `filteradd`, `merkleblock` and FILTERED_BLOCK inventory type
/// 60002 - Support `mempool` message
/// 60001 - Support `pong` message and nonce in `ping` message
pub const PROTOCOL_VERSION: u32 = 70001;
/// Flags to indicate which network services a node supports.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ServiceFlags(u64);
impl ServiceFlags {
/// NONE means no services supported.
pub const NONE: ServiceFlags = ServiceFlags(0);
/// NETWORK means that the node is capable of serving the complete block chain. It is currently
/// set by all Bitcoin Core non pruned nodes, and is unset by SPV clients or other light
/// clients.
pub const NETWORK: ServiceFlags = ServiceFlags(1 << 0);
/// GETUTXO means the node is capable of responding to the getutxo protocol request. Bitcoin
/// Core does not support this but a patch set called Bitcoin XT does.
/// See BIP 64 for details on how this is implemented.
pub const GETUTXO: ServiceFlags = ServiceFlags(1 << 1);
/// BLOOM means the node is capable and willing to handle bloom-filtered connections. Bitcoin
/// Core nodes used to support this by default, without advertising this bit, but no longer do
/// as of protocol version 70011 (= NO_BLOOM_VERSION)
pub const BLOOM: ServiceFlags = ServiceFlags(1 << 2);
/// WITNESS indicates that a node can be asked for blocks and transactions including witness
/// data.
pub const WITNESS: ServiceFlags = ServiceFlags(1 << 3);
/// COMPACT_FILTERS means the node will service basic block filter requests.
/// See BIP157 and BIP158 for details on how this is implemented.
pub const COMPACT_FILTERS: ServiceFlags = ServiceFlags(1 << 6);
/// NETWORK_LIMITED means the same as NODE_NETWORK with the limitation of only serving the last
/// 288 (2 day) blocks.
/// See BIP159 for details on how this is implemented.
pub const NETWORK_LIMITED: ServiceFlags = ServiceFlags(1 << 10);
/// P2P_V2 indicates that the node supports the P2P v2 encrypted transport protocol.
/// See BIP324 for details on how this is implemented.
pub const P2P_V2: ServiceFlags = ServiceFlags(1 << 11);
// NOTE: When adding new flags, remember to update the Display impl accordingly.
/// Add [ServiceFlags] together.
///
/// Returns itself.
pub fn add(&mut self, other: ServiceFlags) -> ServiceFlags {
self.0 |= other.0;
*self
}
/// Remove [ServiceFlags] from this.
///
/// Returns itself.
pub fn remove(&mut self, other: ServiceFlags) -> ServiceFlags {
self.0 &= !other.0;
*self
}
/// Check whether [ServiceFlags] are included in this one.
pub fn has(self, flags: ServiceFlags) -> bool { (self.0 | flags.0) == self.0 }
/// Gets the integer representation of this [`ServiceFlags`].
pub fn to_u64(self) -> u64 { self.0 }
}
impl fmt::LowerHex for ServiceFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
}
impl_to_hex_from_lower_hex!(ServiceFlags, |service_flags: &ServiceFlags| 16
- service_flags.0.leading_zeros() as usize / 4);
impl fmt::UpperHex for ServiceFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
}
impl fmt::Display for ServiceFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut flags = *self;
if flags == ServiceFlags::NONE {
return write!(f, "ServiceFlags(NONE)");
}
let mut first = true;
macro_rules! write_flag {
($f:ident) => {
if flags.has(ServiceFlags::$f) {
if !first {
write!(f, "|")?;
}
first = false;
write!(f, stringify!($f))?;
flags.remove(ServiceFlags::$f);
}
};
}
write!(f, "ServiceFlags(")?;
write_flag!(NETWORK);
write_flag!(GETUTXO);
write_flag!(BLOOM);
write_flag!(WITNESS);
write_flag!(COMPACT_FILTERS);
write_flag!(NETWORK_LIMITED);
write_flag!(P2P_V2);
// If there are unknown flags left, we append them in hex.
if flags != ServiceFlags::NONE {
if !first {
write!(f, "|")?;
}
write!(f, "0x{:x}", flags)?;
}
write!(f, ")")
}
}
impl From<u64> for ServiceFlags {
fn from(f: u64) -> Self { ServiceFlags(f) }
}
impl From<ServiceFlags> for u64 {
fn from(flags: ServiceFlags) -> Self { flags.0 }
}
impl ops::BitOr for ServiceFlags {
type Output = Self;
fn bitor(mut self, rhs: Self) -> Self { self.add(rhs) }
}
impl ops::BitOrAssign for ServiceFlags {
fn bitor_assign(&mut self, rhs: Self) { self.add(rhs); }
}
impl ops::BitXor for ServiceFlags {
type Output = Self;
fn bitxor(mut self, rhs: Self) -> Self { self.remove(rhs) }
}
impl ops::BitXorAssign for ServiceFlags {
fn bitxor_assign(&mut self, rhs: Self) { self.remove(rhs); }
}
impl Encodable for ServiceFlags {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(w)
}
}
impl Decodable for ServiceFlags {
#[inline]
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
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 testnet3 network magic bytes.
#[deprecated(since = "0.33.0", note = "use `TESTNET3` instead")]
pub const TESTNET: Self = Self([0x0B, 0x11, 0x09, 0x07]);
/// Bitcoin testnet3 network magic bytes.
pub const TESTNET3: Self = Self([0x0B, 0x11, 0x09, 0x07]);
/// Bitcoin testnet4 network magic bytes.
pub const TESTNET4: Self = Self([0x1c, 0x16, 0x3f, 0x28]);
/// 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]);
/// Construct a new network magic from bytes.
pub const fn from_bytes(bytes: [u8; 4]) -> Magic { Magic(bytes) }
/// Get network magic bytes.
pub fn to_bytes(self) -> [u8; 4] { self.0 }
/// Returns the magic bytes for the network defined by `params`.
pub fn from_params(params: impl AsRef<Params>) -> Self { params.as_ref().network.into() }
}
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) -> Self {
match network {
Network::Bitcoin => Magic::BITCOIN,
Network::Testnet(TestnetVersion::V3) => Magic::TESTNET3,
Network::Testnet(TestnetVersion::V4) => Magic::TESTNET4,
Network::Signet => Magic::SIGNET,
Network::Regtest => Magic::REGTEST,
// Remember to add the `TryFrom<Magic>` for new networks
}
}
}
impl TryFrom<Magic> for Network {
type Error = UnknownMagicError;
fn try_from(magic: Magic) -> Result<Self, Self::Error> {
match magic {
Magic::BITCOIN => Ok(Network::Bitcoin),
Magic::TESTNET3 => Ok(Network::Testnet(TestnetVersion::V3)),
Magic::TESTNET4 => Ok(Network::Testnet(TestnetVersion::V4)),
Magic::SIGNET => Ok(Network::Signet),
Magic::REGTEST => Ok(Network::Regtest),
_ => Err(UnknownMagicError(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(())
}
}
impl fmt::Debug for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) }
}
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_to_hex_from_lower_hex!(Magic, |_| 8);
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: Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(writer)
}
}
impl Decodable for Magic {
fn consensus_decode<R: BufRead + ?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 }
}
/// An error in parsing magic bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ParseMagicError {
/// The error that occurred when parsing the string.
error: hex::HexToArrayError,
/// The byte string that failed to parse.
magic: String,
}
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)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseMagicError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.error) }
}
/// Error in creating a Network from Magic bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct UnknownMagicError(Magic);
impl fmt::Display for UnknownMagicError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "unknown network magic {}", self.0)
}
}
#[cfg(feature = "std")]
impl std::error::Error for UnknownMagicError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::consensus::encode::{deserialize, serialize};
#[test]
fn serialize_deserialize() {
assert_eq!(serialize(&Magic::BITCOIN), &[0xf9, 0xbe, 0xb4, 0xd9]);
let magic: Magic = Network::Bitcoin.into();
assert_eq!(serialize(&magic), &[0xf9, 0xbe, 0xb4, 0xd9]);
assert_eq!(serialize(&Magic::TESTNET3), &[0x0b, 0x11, 0x09, 0x07]);
let magic: Magic = Network::Testnet(TestnetVersion::V3).into();
assert_eq!(serialize(&magic), &[0x0b, 0x11, 0x09, 0x07]);
assert_eq!(serialize(&Magic::TESTNET4), &[0x1c, 0x16, 0x3f, 0x28]);
let magic: Magic = Network::Testnet(TestnetVersion::V4).into();
assert_eq!(serialize(&magic), &[0x1c, 0x16, 0x3f, 0x28]);
assert_eq!(serialize(&Magic::SIGNET), &[0x0a, 0x03, 0xcf, 0x40]);
let magic: Magic = Network::Signet.into();
assert_eq!(serialize(&magic), &[0x0a, 0x03, 0xcf, 0x40]);
assert_eq!(serialize(&Magic::REGTEST), &[0xfa, 0xbf, 0xb5, 0xda]);
let magic: Magic = Network::Regtest.into();
assert_eq!(serialize(&magic), &[0xfa, 0xbf, 0xb5, 0xda]);
assert_eq!(
deserialize::<Magic>(&[0xf9, 0xbe, 0xb4, 0xd9]).ok(),
Some(Network::Bitcoin.into())
);
assert_eq!(
deserialize::<Magic>(&[0x0b, 0x11, 0x09, 0x07]).ok(),
Some(Network::Testnet(TestnetVersion::V3).into())
);
assert_eq!(
deserialize::<Magic>(&[0x1c, 0x16, 0x3f, 0x28]).ok(),
Some(Network::Testnet(TestnetVersion::V4).into())
);
assert_eq!(
deserialize::<Magic>(&[0x0a, 0x03, 0xcf, 0x40]).ok(),
Some(Network::Signet.into())
);
assert_eq!(
deserialize::<Magic>(&[0xfa, 0xbf, 0xb5, 0xda]).ok(),
Some(Network::Regtest.into())
);
}
#[test]
fn service_flags_test() {
let all = [
ServiceFlags::NETWORK,
ServiceFlags::GETUTXO,
ServiceFlags::BLOOM,
ServiceFlags::WITNESS,
ServiceFlags::COMPACT_FILTERS,
ServiceFlags::NETWORK_LIMITED,
ServiceFlags::P2P_V2,
];
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());
assert_eq!("ServiceFlags(P2P_V2)", ServiceFlags::P2P_V2.to_string());
let flag = ServiceFlags::WITNESS
| ServiceFlags::BLOOM
| ServiceFlags::NETWORK
| ServiceFlags::P2P_V2;
assert_eq!("ServiceFlags(NETWORK|BLOOM|WITNESS|P2P_V2)", flag.to_string());
let flag = ServiceFlags::WITNESS | 0xf0.into();
assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string());
}
#[test]
fn magic_from_str() {
let known_network_magic_strs = [
("f9beb4d9", Network::Bitcoin),
("0b110907", Network::Testnet(TestnetVersion::V3)),
("1c163f28", Network::Testnet(TestnetVersion::V4)),
("fabfb5da", Network::Regtest),
("0a03cf40", Network::Signet),
];
for (magic_str, network) in &known_network_magic_strs {
let magic: Magic = magic_str.parse::<Magic>().unwrap();
assert_eq!(Network::try_from(magic).unwrap(), *network);
assert_eq!(&magic.to_string(), magic_str);
}
}
}

View File

@ -11,6 +11,7 @@ cargo-fuzz = true
[dependencies]
honggfuzz = { version = "0.5.56", default-features = false }
bitcoin = { path = "../bitcoin", features = [ "serde" ] }
p2p = { path = "../p2p", package = "bitcoin-p2p-messages" }
serde = { version = "1.0.103", features = [ "derive" ] }
serde_json = "1.0"

View File

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

View File

@ -2,7 +2,7 @@ use std::convert::TryFrom;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use bitcoin::consensus::Decodable;
use bitcoin::p2p::address::AddrV2;
use p2p::address::AddrV2;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {

View File

@ -13,8 +13,18 @@ rust-version = "1.63.0"
exclude = ["tests", "contrib"]
[dependencies]
bitcoin = { path = "../bitcoin/" }
hashes = { package = "bitcoin_hashes", path = "../hashes", default-features = false, features = ["std"] }
hex = { package = "hex-conservative", version = "0.3.0", default-features = false, features = ["std"] }
internals = { package = "bitcoin-internals", path = "../internals", features = ["std"] }
io = { package = "bitcoin-io", path = "../io", default-features = false, features = ["std"] }
units = { package = "bitcoin-units", path = "../units", default-features = false, features = ["std"] }
[dev-dependencies]
hex_lit = "0.1.1"
[[example]]
name = "handshake"
[package.metadata.docs.rs]
all-features = true

View File

@ -4,8 +4,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use std::{env, process};
use bitcoin::consensus::{encode, Decodable};
use bitcoin::p2p::{self, address, message, message_network, Magic};
use bitcoin::secp256k1::rand::Rng;
use bitcoin_p2p_messages::{self, address, message, message_network, Magic, ServiceFlags};
fn main() {
// This example establishes a connection to a Bitcoin node, sends the initial
@ -71,19 +70,20 @@ fn build_version_message(address: SocketAddr) -> message::NetworkMessage {
let my_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
// "bitfield of features to be enabled for this connection"
let services = p2p::ServiceFlags::NONE;
let services = ServiceFlags::NONE;
// "standard UNIX timestamp in seconds"
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time error").as_secs();
// "The network address of the node receiving this message"
let addr_recv = address::Address::new(&address, p2p::ServiceFlags::NONE);
let addr_recv = address::Address::new(&address, ServiceFlags::NONE);
// "The network address of the node emitting this message"
let addr_from = address::Address::new(&my_address, p2p::ServiceFlags::NONE);
let addr_from = address::Address::new(&my_address, ServiceFlags::NONE);
// "Node random nonce, randomly generated every time a version packet is sent. This nonce is used to detect connections to self."
let nonce: u64 = secp256k1::rand::thread_rng().gen();
// Because this crate does not include the `rand` dependency, this is a fixed value.
let nonce: u64 = 42;
// "User Agent (0x00 if string is 0 bytes long)"
let user_agent = String::from("rust-example");

View File

@ -10,9 +10,8 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV
use io::{BufRead, Read, Write};
use crate::consensus;
use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt};
use crate::p2p::ServiceFlags;
use bitcoin::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt};
use crate::ServiceFlags;
/// A message which can be sent on the Bitcoin network
#[derive(Clone, PartialEq, Eq, Hash)]
@ -207,7 +206,7 @@ impl Encodable for AddrV2 {
network: u8,
bytes: &[u8],
) -> Result<usize, io::Error> {
Ok(network.consensus_encode(w)? + encode::consensus_encode_with_size(bytes, w)?)
Ok(network.consensus_encode(w)? + crate::consensus::consensus_encode_with_size(bytes, w)?)
}
Ok(match *self {
AddrV2::Ipv4(ref addr) => encode_addr(w, 1, &addr.octets())?,
@ -225,28 +224,28 @@ impl Decodable for AddrV2 {
let network_id = u8::consensus_decode(r)?;
let len = r.read_compact_size()?;
if len > 512 {
return Err(consensus::parse_failed_error("IP must be <= 512 bytes"));
return Err(crate::consensus::parse_failed_error("IP must be <= 512 bytes"));
}
Ok(match network_id {
1 => {
if len != 4 {
return Err(consensus::parse_failed_error("invalid IPv4 address"));
return Err(crate::consensus::parse_failed_error("invalid IPv4 address"));
}
let addr: [u8; 4] = Decodable::consensus_decode(r)?;
AddrV2::Ipv4(Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3]))
}
2 => {
if len != 16 {
return Err(consensus::parse_failed_error("invalid IPv6 address"));
return Err(crate::consensus::parse_failed_error("invalid IPv6 address"));
}
let addr: [u16; 8] = read_be_address(r)?;
if addr[0..3] == ONION {
return Err(consensus::parse_failed_error(
return Err(crate::consensus::parse_failed_error(
"OnionCat address sent with IPv6 network id",
));
}
if addr[0..6] == [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF] {
return Err(consensus::parse_failed_error(
return Err(crate::consensus::parse_failed_error(
"IPV4 wrapped address sent with IPv6 network id",
));
}
@ -257,26 +256,26 @@ impl Decodable for AddrV2 {
4 => {
if len != 32 {
return Err(consensus::parse_failed_error("invalid TorV3 address"));
return Err(crate::consensus::parse_failed_error("invalid TorV3 address"));
}
let pubkey = Decodable::consensus_decode(r)?;
AddrV2::TorV3(pubkey)
}
5 => {
if len != 32 {
return Err(consensus::parse_failed_error("invalid I2P address"));
return Err(crate::consensus::parse_failed_error("invalid I2P address"));
}
let hash = Decodable::consensus_decode(r)?;
AddrV2::I2p(hash)
}
6 => {
if len != 16 {
return Err(consensus::parse_failed_error("invalid CJDNS address"));
return Err(crate::consensus::parse_failed_error("invalid CJDNS address"));
}
let addr: [u16; 8] = read_be_address(r)?;
// check the first byte for the CJDNS marker
if addr[0] >> 8 != 0xFC {
return Err(consensus::parse_failed_error("invalid CJDNS address"));
return Err(crate::consensus::parse_failed_error("invalid CJDNS address"));
}
AddrV2::Cjdns(Ipv6Addr::new(
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
@ -438,12 +437,12 @@ impl std::error::Error for AddrV2ToIpv6AddrError {}
mod test {
use std::net::IpAddr;
use bitcoin::consensus::encode::{deserialize, serialize};
use hex::FromHex;
use hex_lit::hex;
use super::*;
use crate::consensus::encode::{deserialize, serialize};
use crate::p2p::message::AddrV2Payload;
use crate::message::AddrV2Payload;
#[test]
fn serialize_address() {

95
p2p/src/consensus.rs Normal file
View File

@ -0,0 +1,95 @@
use bitcoin::consensus::encode::WriteExt;
use io::Write;
pub(crate) fn consensus_encode_with_size<W: Write + ?Sized>(
data: &[u8],
w: &mut W,
) -> Result<usize, io::Error> {
Ok(w.emit_compact_size(data.len())? + w.emit_slice(data)?)
}
pub(crate) fn parse_failed_error(msg: &'static str) -> bitcoin::consensus::encode::Error {
bitcoin::consensus::encode::Error::Parse(bitcoin::consensus::encode::ParseError::ParseFailed(msg))
}
macro_rules! impl_consensus_encoding {
($thing:ident, $($field:ident),+) => (
impl bitcoin::consensus::Encodable for $thing {
#[inline]
fn consensus_encode<W: io::Write + ?Sized>(
&self,
w: &mut W,
) -> core::result::Result<usize, io::Error> {
let mut len = 0;
$(len += self.$field.consensus_encode(w)?;)+
Ok(len)
}
}
impl bitcoin::consensus::Decodable for $thing {
#[inline]
fn consensus_decode_from_finite_reader<R: io::BufRead + ?Sized>(
r: &mut R,
) -> core::result::Result<$thing, bitcoin::consensus::encode::Error> {
Ok($thing {
$($field: bitcoin::consensus::Decodable::consensus_decode_from_finite_reader(r)?),+
})
}
#[inline]
fn consensus_decode<R: io::BufRead + ?Sized>(
r: &mut R,
) -> core::result::Result<$thing, bitcoin::consensus::encode::Error> {
let mut r = r.take(internals::ToU64::to_u64(bitcoin::consensus::encode::MAX_VEC_SIZE));
Ok($thing {
$($field: bitcoin::consensus::Decodable::consensus_decode(&mut r)?),+
})
}
}
);
}
pub(crate) use impl_consensus_encoding;
macro_rules! impl_vec_wrapper {
($wrapper: ident, $type: ty) => {
impl bitcoin::consensus::encode::Encodable for $wrapper {
#[inline]
fn consensus_encode<W: io::Write + ?Sized>(
&self,
w: &mut W,
) -> core::result::Result<usize, io::Error> {
let mut len = 0;
len += w.emit_compact_size(self.0.len())?;
for c in self.0.iter() {
len += c.consensus_encode(w)?;
}
Ok(len)
}
}
impl bitcoin::consensus::encode::Decodable for $wrapper {
#[inline]
fn consensus_decode_from_finite_reader<R: io::BufRead + ?Sized>(
r: &mut R,
) -> core::result::Result<$wrapper, bitcoin::consensus::encode::Error> {
let len = r.read_compact_size()?;
// Do not allocate upfront more items than if the sequence of type
// occupied roughly quarter a block. This should never be the case
// for normal data, but even if that's not true - `push` will just
// reallocate.
// Note: OOM protection relies on reader eventually running out of
// data to feed us.
let max_capacity =
bitcoin::consensus::encode::MAX_VEC_SIZE / 4 / core::mem::size_of::<$type>();
let mut ret = Vec::with_capacity(core::cmp::min(len as usize, max_capacity));
for _ in 0..len {
ret.push(Decodable::consensus_decode_from_finite_reader(r)?);
}
Ok($wrapper(ret))
}
}
};
}
pub(crate) use impl_vec_wrapper;

View File

@ -14,3 +14,495 @@
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
#![allow(clippy::uninlined_format_args)] // Allow `format!("{}", x)`instead of enforcing `format!("{x}")`
pub mod address;
pub mod message;
pub mod message_blockdata;
pub mod message_bloom;
pub mod message_compact_blocks;
pub mod message_filter;
pub mod message_network;
mod consensus;
extern crate alloc;
use core::str::FromStr;
use core::{fmt, ops};
use std::borrow::{Borrow, BorrowMut, ToOwned};
use hex::FromHex;
use internals::impl_to_hex_from_lower_hex;
use io::{BufRead, Write};
use bitcoin::consensus::encode::{self, Decodable, Encodable};
use bitcoin::network::{Network, Params, TestnetVersion};
#[rustfmt::skip]
#[doc(inline)]
pub use self::address::Address;
/// Version of the protocol as appearing in network message headers.
///
/// This constant is used to signal to other peers which features you support. Increasing it implies
/// that your software also supports every feature prior to this version. Doing so without support
/// may lead to you incorrectly banning other peers or other peers banning you.
///
/// These are the features required for each version:
/// 70016 - Support receiving `wtxidrelay` message between `version` and `verack` message
/// 70015 - Support receiving invalid compact blocks from a peer without banning them
/// 70014 - Support compact block messages `sendcmpct`, `cmpctblock`, `getblocktxn` and `blocktxn`
/// 70013 - Support `feefilter` message
/// 70012 - Support `sendheaders` message and announce new blocks via headers rather than inv
/// 70011 - Support NODE_BLOOM service flag and don't support bloom filter messages if it is not set
/// 70002 - Support `reject` message
/// 70001 - Support bloom filter messages `filterload`, `filterclear` `filteradd`, `merkleblock` and FILTERED_BLOCK inventory type
/// 60002 - Support `mempool` message
/// 60001 - Support `pong` message and nonce in `ping` message
pub const PROTOCOL_VERSION: u32 = 70001;
/// Flags to indicate which network services a node supports.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ServiceFlags(u64);
impl ServiceFlags {
/// NONE means no services supported.
pub const NONE: ServiceFlags = ServiceFlags(0);
/// NETWORK means that the node is capable of serving the complete block chain. It is currently
/// set by all Bitcoin Core non pruned nodes, and is unset by SPV clients or other light
/// clients.
pub const NETWORK: ServiceFlags = ServiceFlags(1 << 0);
/// GETUTXO means the node is capable of responding to the getutxo protocol request. Bitcoin
/// Core does not support this but a patch set called Bitcoin XT does.
/// See BIP 64 for details on how this is implemented.
pub const GETUTXO: ServiceFlags = ServiceFlags(1 << 1);
/// BLOOM means the node is capable and willing to handle bloom-filtered connections. Bitcoin
/// Core nodes used to support this by default, without advertising this bit, but no longer do
/// as of protocol version 70011 (= NO_BLOOM_VERSION)
pub const BLOOM: ServiceFlags = ServiceFlags(1 << 2);
/// WITNESS indicates that a node can be asked for blocks and transactions including witness
/// data.
pub const WITNESS: ServiceFlags = ServiceFlags(1 << 3);
/// COMPACT_FILTERS means the node will service basic block filter requests.
/// See BIP157 and BIP158 for details on how this is implemented.
pub const COMPACT_FILTERS: ServiceFlags = ServiceFlags(1 << 6);
/// NETWORK_LIMITED means the same as NODE_NETWORK with the limitation of only serving the last
/// 288 (2 day) blocks.
/// See BIP159 for details on how this is implemented.
pub const NETWORK_LIMITED: ServiceFlags = ServiceFlags(1 << 10);
/// P2P_V2 indicates that the node supports the P2P v2 encrypted transport protocol.
/// See BIP324 for details on how this is implemented.
pub const P2P_V2: ServiceFlags = ServiceFlags(1 << 11);
// NOTE: When adding new flags, remember to update the Display impl accordingly.
/// Add [ServiceFlags] together.
///
/// Returns itself.
#[must_use]
pub fn add(&mut self, other: ServiceFlags) -> ServiceFlags {
self.0 |= other.0;
*self
}
/// Remove [ServiceFlags] from this.
///
/// Returns itself.
#[must_use]
pub fn remove(&mut self, other: ServiceFlags) -> ServiceFlags {
self.0 &= !other.0;
*self
}
/// Check whether [ServiceFlags] are included in this one.
pub fn has(self, flags: ServiceFlags) -> bool { (self.0 | flags.0) == self.0 }
/// Gets the integer representation of this [`ServiceFlags`].
pub fn to_u64(self) -> u64 { self.0 }
}
impl fmt::LowerHex for ServiceFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
}
impl_to_hex_from_lower_hex!(ServiceFlags, |service_flags: &ServiceFlags| 16
- service_flags.0.leading_zeros() as usize / 4);
impl fmt::UpperHex for ServiceFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
}
impl fmt::Display for ServiceFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut flags = *self;
if flags == ServiceFlags::NONE {
return write!(f, "ServiceFlags(NONE)");
}
let mut first = true;
macro_rules! write_flag {
($f:ident) => {
if flags.has(ServiceFlags::$f) {
if !first {
write!(f, "|")?;
}
first = false;
write!(f, stringify!($f))?;
let _ = flags.remove(ServiceFlags::$f);
}
};
}
write!(f, "ServiceFlags(")?;
write_flag!(NETWORK);
write_flag!(GETUTXO);
write_flag!(BLOOM);
write_flag!(WITNESS);
write_flag!(COMPACT_FILTERS);
write_flag!(NETWORK_LIMITED);
write_flag!(P2P_V2);
// If there are unknown flags left, we append them in hex.
if flags != ServiceFlags::NONE {
if !first {
write!(f, "|")?;
}
write!(f, "0x{:x}", flags)?;
}
write!(f, ")")
}
}
impl From<u64> for ServiceFlags {
fn from(f: u64) -> Self { ServiceFlags(f) }
}
impl From<ServiceFlags> for u64 {
fn from(flags: ServiceFlags) -> Self { flags.0 }
}
impl ops::BitOr for ServiceFlags {
type Output = Self;
fn bitor(mut self, rhs: Self) -> Self { self.add(rhs) }
}
impl ops::BitOrAssign for ServiceFlags {
fn bitor_assign(&mut self, rhs: Self) { let _ = self.add(rhs); }
}
impl ops::BitXor for ServiceFlags {
type Output = Self;
fn bitxor(mut self, rhs: Self) -> Self { self.remove(rhs) }
}
impl ops::BitXorAssign for ServiceFlags {
fn bitxor_assign(&mut self, rhs: Self) { let _ = self.remove(rhs); }
}
impl Encodable for ServiceFlags {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(w)
}
}
impl Decodable for ServiceFlags {
#[inline]
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
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 testnet3 network magic bytes.
#[deprecated(since = "0.33.0", note = "use `TESTNET3` instead")]
pub const TESTNET: Self = Self([0x0B, 0x11, 0x09, 0x07]);
/// Bitcoin testnet3 network magic bytes.
pub const TESTNET3: Self = Self([0x0B, 0x11, 0x09, 0x07]);
/// Bitcoin testnet4 network magic bytes.
pub const TESTNET4: Self = Self([0x1c, 0x16, 0x3f, 0x28]);
/// 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]);
/// Construct a new network magic from bytes.
pub const fn from_bytes(bytes: [u8; 4]) -> Magic { Magic(bytes) }
/// Get network magic bytes.
pub fn to_bytes(self) -> [u8; 4] { self.0 }
/// Returns the magic bytes for the network defined by `params`.
pub fn from_params(params: impl AsRef<Params>) -> Option<Self> { params.as_ref().network.try_into().ok() }
}
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 TryFrom<Network> for Magic {
type Error = UnknownNetworkError;
fn try_from(network: Network) -> Result<Self, Self::Error> {
match network {
Network::Bitcoin => Ok(Magic::BITCOIN),
Network::Testnet(TestnetVersion::V3) => Ok(Magic::TESTNET3),
Network::Testnet(TestnetVersion::V4) => Ok(Magic::TESTNET4),
Network::Signet => Ok(Magic::SIGNET),
Network::Regtest => Ok(Magic::REGTEST),
_ => Err(UnknownNetworkError(network)),
}
}
}
impl TryFrom<Magic> for Network {
type Error = UnknownMagicError;
fn try_from(magic: Magic) -> Result<Self, Self::Error> {
match magic {
Magic::BITCOIN => Ok(Network::Bitcoin),
Magic::TESTNET3 => Ok(Network::Testnet(TestnetVersion::V3)),
Magic::TESTNET4 => Ok(Network::Testnet(TestnetVersion::V4)),
Magic::SIGNET => Ok(Network::Signet),
Magic::REGTEST => Ok(Network::Regtest),
_ => Err(UnknownMagicError(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(())
}
}
impl fmt::Debug for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) }
}
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_to_hex_from_lower_hex!(Magic, |_| 8);
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: Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(writer)
}
}
impl Decodable for Magic {
fn consensus_decode<R: BufRead + ?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 }
}
/// An error in parsing magic bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ParseMagicError {
/// The error that occurred when parsing the string.
error: hex::HexToArrayError,
/// The byte string that failed to parse.
magic: String,
}
impl fmt::Display for ParseMagicError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "failed to parse {} as network magic", self.magic)
}
}
impl std::error::Error for ParseMagicError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.error) }
}
/// Error in creating a Network from Magic bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct UnknownMagicError(Magic);
impl fmt::Display for UnknownMagicError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "unknown network magic {}", self.0)
}
}
impl std::error::Error for UnknownMagicError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}
/// Error in creating a Magic from a Network.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct UnknownNetworkError(Network);
impl fmt::Display for UnknownNetworkError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "unknown network {}", self.0)
}
}
impl std::error::Error for UnknownNetworkError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}
#[cfg(test)]
mod tests {
use super::*;
use bitcoin::consensus::encode::{deserialize, serialize};
#[test]
fn serialize_deserialize() {
assert_eq!(serialize(&Magic::BITCOIN), &[0xf9, 0xbe, 0xb4, 0xd9]);
let magic: Magic = Network::Bitcoin.try_into().unwrap();
assert_eq!(serialize(&magic), &[0xf9, 0xbe, 0xb4, 0xd9]);
assert_eq!(serialize(&Magic::TESTNET3), &[0x0b, 0x11, 0x09, 0x07]);
let magic: Magic = Network::Testnet(TestnetVersion::V3).try_into().unwrap();
assert_eq!(serialize(&magic), &[0x0b, 0x11, 0x09, 0x07]);
assert_eq!(serialize(&Magic::TESTNET4), &[0x1c, 0x16, 0x3f, 0x28]);
let magic: Magic = Network::Testnet(TestnetVersion::V4).try_into().unwrap();
assert_eq!(serialize(&magic), &[0x1c, 0x16, 0x3f, 0x28]);
assert_eq!(serialize(&Magic::SIGNET), &[0x0a, 0x03, 0xcf, 0x40]);
let magic: Magic = Network::Signet.try_into().unwrap();
assert_eq!(serialize(&magic), &[0x0a, 0x03, 0xcf, 0x40]);
assert_eq!(serialize(&Magic::REGTEST), &[0xfa, 0xbf, 0xb5, 0xda]);
let magic: Magic = Network::Regtest.try_into().unwrap();
assert_eq!(serialize(&magic), &[0xfa, 0xbf, 0xb5, 0xda]);
assert_eq!(deserialize::<Magic>(&[0xf9, 0xbe, 0xb4, 0xd9]).ok(), Network::Bitcoin.try_into().ok());
assert_eq!(
deserialize::<Magic>(&[0x0b, 0x11, 0x09, 0x07]).ok(),
Network::Testnet(TestnetVersion::V3).try_into().ok()
);
assert_eq!(
deserialize::<Magic>(&[0x1c, 0x16, 0x3f, 0x28]).ok(),
Network::Testnet(TestnetVersion::V4).try_into().ok()
);
assert_eq!(deserialize::<Magic>(&[0x0a, 0x03, 0xcf, 0x40]).ok(), Network::Signet.try_into().ok());
assert_eq!(deserialize::<Magic>(&[0xfa, 0xbf, 0xb5, 0xda]).ok(), Network::Regtest.try_into().ok());
}
#[test]
fn service_flags_test() {
let all = [
ServiceFlags::NETWORK,
ServiceFlags::GETUTXO,
ServiceFlags::BLOOM,
ServiceFlags::WITNESS,
ServiceFlags::COMPACT_FILTERS,
ServiceFlags::NETWORK_LIMITED,
ServiceFlags::P2P_V2,
];
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());
assert_eq!("ServiceFlags(P2P_V2)", ServiceFlags::P2P_V2.to_string());
let flag = ServiceFlags::WITNESS
| ServiceFlags::BLOOM
| ServiceFlags::NETWORK
| ServiceFlags::P2P_V2;
assert_eq!("ServiceFlags(NETWORK|BLOOM|WITNESS|P2P_V2)", flag.to_string());
let flag = ServiceFlags::WITNESS | 0xf0.into();
assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string());
}
#[test]
fn magic_from_str() {
let known_network_magic_strs = [
("f9beb4d9", Network::Bitcoin),
("0b110907", Network::Testnet(TestnetVersion::V3)),
("1c163f28", Network::Testnet(TestnetVersion::V4)),
("fabfb5da", Network::Regtest),
("0a03cf40", Network::Signet),
];
for (magic_str, network) in &known_network_magic_strs {
let magic: Magic = magic_str.parse::<Magic>().unwrap();
assert_eq!(Network::try_from(magic).unwrap(), *network);
assert_eq!(&magic.to_string(), magic_str);
}
}
}

View File

@ -6,21 +6,23 @@
//! are used for (de)serializing Bitcoin objects for transmission on the network.
use core::{fmt, iter};
use std::borrow::Cow;
use std::boxed::Box;
use std::borrow::ToOwned;
use hashes::sha256d;
use internals::ToU64 as _;
use io::{BufRead, Write};
use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, ReadExt, WriteExt};
use crate::merkle_tree::MerkleBlock;
use crate::p2p::address::{AddrV2Message, Address};
use crate::p2p::deser::impl_vec_wrapper;
use crate::p2p::{
use bitcoin::consensus::encode::{self, CheckedData, Decodable, Encodable, ReadExt, WriteExt};
use bitcoin::merkle_tree::MerkleBlock;
use crate::address::{AddrV2Message, Address};
use crate::consensus::impl_vec_wrapper;
use crate::{
message_blockdata, message_bloom, message_compact_blocks, message_filter, message_network,
Magic,
};
use crate::prelude::{Box, Cow, String, ToOwned, Vec};
use crate::{block, consensus, transaction};
use bitcoin::{block, transaction};
/// The maximum number of [super::message_blockdata::Inventory] items in an `inv` message.
///
@ -144,7 +146,6 @@ impl fmt::Display for CommandStringError {
}
}
#[cfg(feature = "std")]
impl std::error::Error for CommandStringError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}
@ -518,7 +519,7 @@ impl Decodable for HeaderDeserializationWrapper {
for _ in 0..len {
ret.push(Decodable::consensus_decode(r)?);
if u8::consensus_decode(r)? != 0u8 {
return Err(consensus::parse_failed_error(
return Err(crate::consensus::parse_failed_error(
"Headers message should not contain transactions",
));
}
@ -718,21 +719,21 @@ mod test {
use units::BlockHeight;
use super::*;
use crate::bip152::BlockTransactionsRequest;
use crate::bip158::{FilterHash, FilterHeader};
use crate::block::{Block, BlockHash};
use crate::consensus::encode::{deserialize, deserialize_partial, serialize};
use crate::p2p::address::AddrV2;
use crate::p2p::message_blockdata::{GetBlocksMessage, GetHeadersMessage, Inventory};
use crate::p2p::message_bloom::{BloomFlags, FilterAdd, FilterLoad};
use crate::p2p::message_compact_blocks::{GetBlockTxn, SendCmpct};
use crate::p2p::message_filter::{
use bitcoin::bip152::BlockTransactionsRequest;
use bitcoin::bip158::{FilterHash, FilterHeader};
use bitcoin::block::{Block, BlockHash};
use bitcoin::consensus::encode::{deserialize, deserialize_partial, serialize};
use bitcoin::script::ScriptBuf;
use bitcoin::transaction::{Transaction, Txid};
use crate::address::AddrV2;
use crate::message_blockdata::{GetBlocksMessage, GetHeadersMessage, Inventory};
use crate::message_bloom::{BloomFlags, FilterAdd, FilterLoad};
use crate::message_compact_blocks::{GetBlockTxn, SendCmpct};
use crate::message_filter::{
CFCheckpt, CFHeaders, CFilter, GetCFCheckpt, GetCFHeaders, GetCFilters,
};
use crate::p2p::message_network::{Reject, RejectReason, VersionMessage};
use crate::p2p::ServiceFlags;
use crate::script::ScriptBuf;
use crate::transaction::{Transaction, Txid};
use crate::message_network::{Reject, RejectReason, VersionMessage};
use crate::ServiceFlags;
fn hash(array: [u8; 32]) -> sha256d::Hash { sha256d::Hash::from_byte_array(array) }
@ -740,7 +741,7 @@ mod test {
fn full_round_ser_der_raw_network_message() {
let version_msg: VersionMessage = deserialize(&hex!("721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001")).unwrap();
let tx: Transaction = deserialize(&hex!("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000")).unwrap();
let block: Block = deserialize(&include_bytes!("../../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw")[..]).unwrap();
let block: Block = deserialize(&hex!("00608e2e094d41aecfbcbf8fe70cb60be57516b07db1bafee4c4de5dad760000000000004aec16eab3be95abe9c54e01cf850c14b8c5cad1bc6b2e73e811db5d5998ada404503e66fcff031b4ebd99d701010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff3402983a000404503e6604f1f617271083bc3d6600000000000000000007bb1b0a636b706f6f6c0d506f72746c616e642e484f444cffffffff0200f2052a010000001976a9142ce72b25fe97b52638c199acfaa5e3891ddfed5b88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000")).unwrap();
let header: block::Header = deserialize(&hex!("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b")).unwrap();
let script: ScriptBuf =
deserialize(&hex!("1976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac")).unwrap();

View File

@ -7,11 +7,10 @@
use io::{BufRead, Write};
use crate::block::BlockHash;
use crate::consensus::encode::{self, Decodable, Encodable};
use crate::internal_macros::impl_consensus_encoding;
use crate::p2p;
use crate::transaction::{Txid, Wtxid};
use bitcoin::block::BlockHash;
use bitcoin::consensus::encode::{self, Decodable, Encodable};
use bitcoin::transaction::{Txid, Wtxid};
use crate::consensus::impl_consensus_encoding;
/// An inventory item.
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
@ -127,7 +126,7 @@ pub struct GetHeadersMessage {
impl GetBlocksMessage {
/// Construct a new `getblocks` message
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage {
GetBlocksMessage { version: p2p::PROTOCOL_VERSION, locator_hashes, stop_hash }
GetBlocksMessage { version: crate::PROTOCOL_VERSION, locator_hashes, stop_hash }
}
}
@ -136,7 +135,7 @@ impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
impl GetHeadersMessage {
/// Construct a new `getheaders` message
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage {
GetHeadersMessage { version: p2p::PROTOCOL_VERSION, locator_hashes, stop_hash }
GetHeadersMessage { version: crate::PROTOCOL_VERSION, locator_hashes, stop_hash }
}
}
@ -147,7 +146,7 @@ mod tests {
use hex_lit::hex;
use super::*;
use crate::consensus::encode::{deserialize, serialize};
use bitcoin::consensus::encode::{deserialize, serialize};
#[test]
fn getblocks_message() {

View File

@ -6,8 +6,8 @@
use io::{BufRead, Write};
use crate::consensus::{self, encode, Decodable, Encodable, ReadExt};
use crate::internal_macros::impl_consensus_encoding;
use bitcoin::consensus::{encode, Decodable, Encodable, ReadExt};
use crate::consensus::impl_consensus_encoding;
/// `filterload` message sets the current bloom filter
#[derive(Clone, PartialEq, Eq, Debug)]
@ -52,7 +52,7 @@ impl Decodable for BloomFlags {
0 => BloomFlags::None,
1 => BloomFlags::All,
2 => BloomFlags::PubkeyOnly,
_ => return Err(consensus::parse_failed_error("unknown bloom flag")),
_ => return Err(crate::consensus::parse_failed_error("unknown bloom flag")),
})
}
}

View File

@ -3,8 +3,8 @@
//!
//! BIP152 Compact Blocks network messages
use crate::bip152;
use crate::internal_macros::impl_consensus_encoding;
use bitcoin::bip152;
use crate::consensus::impl_consensus_encoding;
/// sendcmpct message
#[derive(PartialEq, Eq, Clone, Debug, Copy, PartialOrd, Ord, Hash)]

View File

@ -6,9 +6,9 @@
use units::BlockHeight;
use crate::bip158::{FilterHash, FilterHeader};
use crate::block::BlockHash;
use crate::internal_macros::impl_consensus_encoding;
use bitcoin::bip158::{FilterHash, FilterHeader};
use bitcoin::block::BlockHash;
use crate::consensus::impl_consensus_encoding;
/// getcfilters message
#[derive(PartialEq, Eq, Clone, Debug)]

View File

@ -4,16 +4,15 @@
//!
//! This module defines network messages which describe peers and their
//! capabilities.
use std::borrow::Cow;
use hashes::sha256d;
use io::{BufRead, Write};
use crate::consensus::{self, encode, Decodable, Encodable, ReadExt};
use crate::internal_macros::impl_consensus_encoding;
use crate::p2p;
use crate::p2p::address::Address;
use crate::p2p::ServiceFlags;
use crate::prelude::{Cow, String};
use bitcoin::consensus::{encode, Decodable, Encodable, ReadExt};
use crate::address::Address;
use crate::consensus::impl_consensus_encoding;
use crate::ServiceFlags;
// Some simple messages
@ -61,7 +60,7 @@ impl VersionMessage {
start_height: i32,
) -> VersionMessage {
VersionMessage {
version: p2p::PROTOCOL_VERSION,
version: crate::PROTOCOL_VERSION,
services,
timestamp,
receiver,
@ -126,7 +125,7 @@ impl Decodable for RejectReason {
0x41 => RejectReason::Dust,
0x42 => RejectReason::Fee,
0x43 => RejectReason::Checkpoint,
_ => return Err(consensus::parse_failed_error("unknown reject code")),
_ => return Err(crate::consensus::parse_failed_error("unknown reject code")),
})
}
}
@ -151,7 +150,7 @@ mod tests {
use hex_lit::hex;
use super::*;
use crate::consensus::encode::{deserialize, serialize};
use bitcoin::consensus::encode::{deserialize, serialize};
#[test]
fn version_message_test() {