Move `bitcoin/p2p` into `p2p`
Moves all of the content from `bitcoin/p2p` into `p2p`. `TryFrom<Network>` must be implemented for `Network -> Magic` now that `Network` is a foreign, non-exhaustive type. Ser/de test is updated accordingly, as well as the `Magic::from_network` constructor, which I have opted to return an `Option<Self>`. The `TryFrom` implementation uses a new `UnknownNetworkError(Network)` that mirrors the `Magic -> Network` error. The example handshake does not generate a random nonce for the version message, as there is no `rand` dependency in this crate and the program only makes a single, user-defined connection. It appears we can do better than copying and pasting the consensus encoding macros and functions from `bitcoin` without doing weird cross-crate macros that require some special knowledge of the crate to know when they will compile.
This commit is contained in:
parent
9852732311
commit
d9cf7270eb
|
@ -77,6 +77,7 @@ name = "bitcoin-fuzz"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
|
"bitcoin-p2p-messages",
|
||||||
"honggfuzz",
|
"honggfuzz",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -109,6 +110,15 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcoin-p2p-messages"
|
name = "bitcoin-p2p-messages"
|
||||||
version = "0.1.0"
|
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]]
|
[[package]]
|
||||||
name = "bitcoin-primitives"
|
name = "bitcoin-primitives"
|
||||||
|
|
|
@ -76,6 +76,7 @@ name = "bitcoin-fuzz"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
|
"bitcoin-p2p-messages",
|
||||||
"honggfuzz",
|
"honggfuzz",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -108,6 +109,15 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcoin-p2p-messages"
|
name = "bitcoin-p2p-messages"
|
||||||
version = "0.1.0"
|
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]]
|
[[package]]
|
||||||
name = "bitcoin-primitives"
|
name = "bitcoin-primitives"
|
||||||
|
|
|
@ -56,10 +56,6 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "bip32"
|
name = "bip32"
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "handshake"
|
|
||||||
required-features = ["rand-std"]
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "ecdsa-psbt"
|
name = "ecdsa-psbt"
|
||||||
required-features = ["std", "bitcoinconsensus"]
|
required-features = ["std", "bitcoinconsensus"]
|
||||||
|
|
|
@ -94,7 +94,6 @@ extern crate serde;
|
||||||
mod internal_macros;
|
mod internal_macros;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod p2p;
|
|
||||||
pub mod address;
|
pub mod address;
|
||||||
pub mod bip152;
|
pub mod bip152;
|
||||||
pub mod bip158;
|
pub mod bip158;
|
||||||
|
|
|
@ -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;
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,6 +11,7 @@ cargo-fuzz = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
honggfuzz = { version = "0.5.56", default-features = false }
|
honggfuzz = { version = "0.5.56", default-features = false }
|
||||||
bitcoin = { path = "../bitcoin", features = [ "serde" ] }
|
bitcoin = { path = "../bitcoin", features = [ "serde" ] }
|
||||||
|
p2p = { path = "../p2p", package = "bitcoin-p2p-messages" }
|
||||||
|
|
||||||
serde = { version = "1.0.103", features = [ "derive" ] }
|
serde = { version = "1.0.103", features = [ "derive" ] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use honggfuzz::fuzz;
|
use honggfuzz::fuzz;
|
||||||
|
|
||||||
fn do_test(data: &[u8]) {
|
fn do_test(data: &[u8]) {
|
||||||
let _: Result<bitcoin::p2p::message::RawNetworkMessage, _> =
|
let _: Result<p2p::message::RawNetworkMessage, _> =
|
||||||
bitcoin::consensus::encode::deserialize(data);
|
bitcoin::consensus::encode::deserialize(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::convert::TryFrom;
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
use bitcoin::consensus::Decodable;
|
use bitcoin::consensus::Decodable;
|
||||||
use bitcoin::p2p::address::AddrV2;
|
use p2p::address::AddrV2;
|
||||||
use honggfuzz::fuzz;
|
use honggfuzz::fuzz;
|
||||||
|
|
||||||
fn do_test(data: &[u8]) {
|
fn do_test(data: &[u8]) {
|
||||||
|
|
|
@ -13,8 +13,18 @@ rust-version = "1.63.0"
|
||||||
exclude = ["tests", "contrib"]
|
exclude = ["tests", "contrib"]
|
||||||
|
|
||||||
[dependencies]
|
[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]
|
[dev-dependencies]
|
||||||
|
hex_lit = "0.1.1"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "handshake"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
|
@ -4,8 +4,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::p2p::{self, address, message, message_network, Magic};
|
use bitcoin_p2p_messages::{self, address, message, message_network, Magic, ServiceFlags};
|
||||||
use bitcoin::secp256k1::rand::Rng;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// This example establishes a connection to a Bitcoin node, sends the initial
|
// 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);
|
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 = p2p::ServiceFlags::NONE;
|
let services = 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, p2p::ServiceFlags::NONE);
|
let addr_recv = address::Address::new(&address, 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, 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."
|
// "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)"
|
// "User Agent (0x00 if string is 0 bytes long)"
|
||||||
let user_agent = String::from("rust-example");
|
let user_agent = String::from("rust-example");
|
|
@ -10,9 +10,8 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV
|
||||||
|
|
||||||
use io::{BufRead, Read, Write};
|
use io::{BufRead, Read, Write};
|
||||||
|
|
||||||
use crate::consensus;
|
use bitcoin::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt};
|
||||||
use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt};
|
use crate::ServiceFlags;
|
||||||
use crate::p2p::ServiceFlags;
|
|
||||||
|
|
||||||
/// A message which can be sent on the Bitcoin network
|
/// A message which can be sent on the Bitcoin network
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -207,7 +206,7 @@ impl Encodable for AddrV2 {
|
||||||
network: u8,
|
network: u8,
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
) -> Result<usize, io::Error> {
|
) -> 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 {
|
Ok(match *self {
|
||||||
AddrV2::Ipv4(ref addr) => encode_addr(w, 1, &addr.octets())?,
|
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 network_id = u8::consensus_decode(r)?;
|
||||||
let len = r.read_compact_size()?;
|
let len = r.read_compact_size()?;
|
||||||
if len > 512 {
|
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 {
|
Ok(match network_id {
|
||||||
1 => {
|
1 => {
|
||||||
if len != 4 {
|
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)?;
|
let addr: [u8; 4] = Decodable::consensus_decode(r)?;
|
||||||
AddrV2::Ipv4(Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3]))
|
AddrV2::Ipv4(Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3]))
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
if len != 16 {
|
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)?;
|
let addr: [u16; 8] = read_be_address(r)?;
|
||||||
if addr[0..3] == ONION {
|
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",
|
"OnionCat address sent with IPv6 network id",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if addr[0..6] == [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF] {
|
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",
|
"IPV4 wrapped address sent with IPv6 network id",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -257,26 +256,26 @@ impl Decodable for AddrV2 {
|
||||||
|
|
||||||
4 => {
|
4 => {
|
||||||
if len != 32 {
|
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)?;
|
let pubkey = Decodable::consensus_decode(r)?;
|
||||||
AddrV2::TorV3(pubkey)
|
AddrV2::TorV3(pubkey)
|
||||||
}
|
}
|
||||||
5 => {
|
5 => {
|
||||||
if len != 32 {
|
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)?;
|
let hash = Decodable::consensus_decode(r)?;
|
||||||
AddrV2::I2p(hash)
|
AddrV2::I2p(hash)
|
||||||
}
|
}
|
||||||
6 => {
|
6 => {
|
||||||
if len != 16 {
|
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)?;
|
let addr: [u16; 8] = read_be_address(r)?;
|
||||||
// check the first byte for the CJDNS marker
|
// check the first byte for the CJDNS marker
|
||||||
if addr[0] >> 8 != 0xFC {
|
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(
|
AddrV2::Cjdns(Ipv6Addr::new(
|
||||||
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
|
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 {
|
mod test {
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use bitcoin::consensus::encode::{deserialize, serialize};
|
||||||
use hex::FromHex;
|
use hex::FromHex;
|
||||||
use hex_lit::hex;
|
use hex_lit::hex;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
use crate::message::AddrV2Payload;
|
||||||
use crate::p2p::message::AddrV2Payload;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_address() {
|
fn serialize_address() {
|
|
@ -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;
|
492
p2p/src/lib.rs
492
p2p/src/lib.rs
|
@ -14,3 +14,495 @@
|
||||||
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
|
#![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::manual_range_contains)] // More readable than clippy's format.
|
||||||
#![allow(clippy::uninlined_format_args)] // Allow `format!("{}", x)`instead of enforcing `format!("{x}")`
|
#![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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,21 +6,23 @@
|
||||||
//! are used for (de)serializing Bitcoin objects for transmission on the network.
|
//! are used for (de)serializing Bitcoin objects for transmission on the network.
|
||||||
|
|
||||||
use core::{fmt, iter};
|
use core::{fmt, iter};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::boxed::Box;
|
||||||
|
use std::borrow::ToOwned;
|
||||||
|
|
||||||
use hashes::sha256d;
|
use hashes::sha256d;
|
||||||
use internals::ToU64 as _;
|
use internals::ToU64 as _;
|
||||||
use io::{BufRead, Write};
|
use io::{BufRead, Write};
|
||||||
|
|
||||||
use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, ReadExt, WriteExt};
|
use bitcoin::consensus::encode::{self, CheckedData, Decodable, Encodable, ReadExt, WriteExt};
|
||||||
use crate::merkle_tree::MerkleBlock;
|
use bitcoin::merkle_tree::MerkleBlock;
|
||||||
use crate::p2p::address::{AddrV2Message, Address};
|
use crate::address::{AddrV2Message, Address};
|
||||||
use crate::p2p::deser::impl_vec_wrapper;
|
use crate::consensus::impl_vec_wrapper;
|
||||||
use crate::p2p::{
|
use crate::{
|
||||||
message_blockdata, message_bloom, message_compact_blocks, message_filter, message_network,
|
message_blockdata, message_bloom, message_compact_blocks, message_filter, message_network,
|
||||||
Magic,
|
Magic,
|
||||||
};
|
};
|
||||||
use crate::prelude::{Box, Cow, String, ToOwned, Vec};
|
use bitcoin::{block, transaction};
|
||||||
use crate::{block, consensus, transaction};
|
|
||||||
|
|
||||||
/// The maximum number of [super::message_blockdata::Inventory] items in an `inv` message.
|
/// 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 {
|
impl std::error::Error for CommandStringError {
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
|
||||||
}
|
}
|
||||||
|
@ -518,7 +519,7 @@ impl Decodable for HeaderDeserializationWrapper {
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
ret.push(Decodable::consensus_decode(r)?);
|
ret.push(Decodable::consensus_decode(r)?);
|
||||||
if u8::consensus_decode(r)? != 0u8 {
|
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",
|
"Headers message should not contain transactions",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -718,21 +719,21 @@ mod test {
|
||||||
use units::BlockHeight;
|
use units::BlockHeight;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::bip152::BlockTransactionsRequest;
|
use bitcoin::bip152::BlockTransactionsRequest;
|
||||||
use crate::bip158::{FilterHash, FilterHeader};
|
use bitcoin::bip158::{FilterHash, FilterHeader};
|
||||||
use crate::block::{Block, BlockHash};
|
use bitcoin::block::{Block, BlockHash};
|
||||||
use crate::consensus::encode::{deserialize, deserialize_partial, serialize};
|
use bitcoin::consensus::encode::{deserialize, deserialize_partial, serialize};
|
||||||
use crate::p2p::address::AddrV2;
|
use bitcoin::script::ScriptBuf;
|
||||||
use crate::p2p::message_blockdata::{GetBlocksMessage, GetHeadersMessage, Inventory};
|
use bitcoin::transaction::{Transaction, Txid};
|
||||||
use crate::p2p::message_bloom::{BloomFlags, FilterAdd, FilterLoad};
|
use crate::address::AddrV2;
|
||||||
use crate::p2p::message_compact_blocks::{GetBlockTxn, SendCmpct};
|
use crate::message_blockdata::{GetBlocksMessage, GetHeadersMessage, Inventory};
|
||||||
use crate::p2p::message_filter::{
|
use crate::message_bloom::{BloomFlags, FilterAdd, FilterLoad};
|
||||||
|
use crate::message_compact_blocks::{GetBlockTxn, SendCmpct};
|
||||||
|
use crate::message_filter::{
|
||||||
CFCheckpt, CFHeaders, CFilter, GetCFCheckpt, GetCFHeaders, GetCFilters,
|
CFCheckpt, CFHeaders, CFilter, GetCFCheckpt, GetCFHeaders, GetCFilters,
|
||||||
};
|
};
|
||||||
use crate::p2p::message_network::{Reject, RejectReason, VersionMessage};
|
use crate::message_network::{Reject, RejectReason, VersionMessage};
|
||||||
use crate::p2p::ServiceFlags;
|
use crate::ServiceFlags;
|
||||||
use crate::script::ScriptBuf;
|
|
||||||
use crate::transaction::{Transaction, Txid};
|
|
||||||
|
|
||||||
fn hash(array: [u8; 32]) -> sha256d::Hash { sha256d::Hash::from_byte_array(array) }
|
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() {
|
fn full_round_ser_der_raw_network_message() {
|
||||||
let version_msg: VersionMessage = deserialize(&hex!("721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001")).unwrap();
|
let version_msg: VersionMessage = deserialize(&hex!("721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001")).unwrap();
|
||||||
let tx: Transaction = deserialize(&hex!("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000")).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 header: block::Header = deserialize(&hex!("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b")).unwrap();
|
||||||
let script: ScriptBuf =
|
let script: ScriptBuf =
|
||||||
deserialize(&hex!("1976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac")).unwrap();
|
deserialize(&hex!("1976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac")).unwrap();
|
|
@ -7,11 +7,10 @@
|
||||||
|
|
||||||
use io::{BufRead, Write};
|
use io::{BufRead, Write};
|
||||||
|
|
||||||
use crate::block::BlockHash;
|
use bitcoin::block::BlockHash;
|
||||||
use crate::consensus::encode::{self, Decodable, Encodable};
|
use bitcoin::consensus::encode::{self, Decodable, Encodable};
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
use bitcoin::transaction::{Txid, Wtxid};
|
||||||
use crate::p2p;
|
use crate::consensus::impl_consensus_encoding;
|
||||||
use crate::transaction::{Txid, Wtxid};
|
|
||||||
|
|
||||||
/// An inventory item.
|
/// An inventory item.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
|
||||||
|
@ -127,7 +126,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: 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 {
|
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: 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 hex_lit::hex;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
use bitcoin::consensus::encode::{deserialize, serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn getblocks_message() {
|
fn getblocks_message() {
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
use io::{BufRead, Write};
|
use io::{BufRead, Write};
|
||||||
|
|
||||||
use crate::consensus::{self, encode, Decodable, Encodable, ReadExt};
|
use bitcoin::consensus::{encode, Decodable, Encodable, ReadExt};
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
use crate::consensus::impl_consensus_encoding;
|
||||||
|
|
||||||
/// `filterload` message sets the current bloom filter
|
/// `filterload` message sets the current bloom filter
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
@ -52,7 +52,7 @@ impl Decodable for BloomFlags {
|
||||||
0 => BloomFlags::None,
|
0 => BloomFlags::None,
|
||||||
1 => BloomFlags::All,
|
1 => BloomFlags::All,
|
||||||
2 => BloomFlags::PubkeyOnly,
|
2 => BloomFlags::PubkeyOnly,
|
||||||
_ => return Err(consensus::parse_failed_error("unknown bloom flag")),
|
_ => return Err(crate::consensus::parse_failed_error("unknown bloom flag")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,8 +3,8 @@
|
||||||
//!
|
//!
|
||||||
//! BIP152 Compact Blocks network messages
|
//! BIP152 Compact Blocks network messages
|
||||||
|
|
||||||
use crate::bip152;
|
use bitcoin::bip152;
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
use crate::consensus::impl_consensus_encoding;
|
||||||
|
|
||||||
/// sendcmpct message
|
/// sendcmpct message
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Copy, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, Clone, Debug, Copy, PartialOrd, Ord, Hash)]
|
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
use units::BlockHeight;
|
use units::BlockHeight;
|
||||||
|
|
||||||
use crate::bip158::{FilterHash, FilterHeader};
|
use bitcoin::bip158::{FilterHash, FilterHeader};
|
||||||
use crate::block::BlockHash;
|
use bitcoin::block::BlockHash;
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
use crate::consensus::impl_consensus_encoding;
|
||||||
|
|
||||||
/// getcfilters message
|
/// getcfilters message
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
@ -4,16 +4,15 @@
|
||||||
//!
|
//!
|
||||||
//! This module defines network messages which describe peers and their
|
//! This module defines network messages which describe peers and their
|
||||||
//! capabilities.
|
//! capabilities.
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use hashes::sha256d;
|
use hashes::sha256d;
|
||||||
use io::{BufRead, Write};
|
use io::{BufRead, Write};
|
||||||
|
|
||||||
use crate::consensus::{self, encode, Decodable, Encodable, ReadExt};
|
use bitcoin::consensus::{encode, Decodable, Encodable, ReadExt};
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
use crate::address::Address;
|
||||||
use crate::p2p;
|
use crate::consensus::impl_consensus_encoding;
|
||||||
use crate::p2p::address::Address;
|
use crate::ServiceFlags;
|
||||||
use crate::p2p::ServiceFlags;
|
|
||||||
use crate::prelude::{Cow, String};
|
|
||||||
|
|
||||||
// Some simple messages
|
// Some simple messages
|
||||||
|
|
||||||
|
@ -61,7 +60,7 @@ impl VersionMessage {
|
||||||
start_height: i32,
|
start_height: i32,
|
||||||
) -> VersionMessage {
|
) -> VersionMessage {
|
||||||
VersionMessage {
|
VersionMessage {
|
||||||
version: p2p::PROTOCOL_VERSION,
|
version: crate::PROTOCOL_VERSION,
|
||||||
services,
|
services,
|
||||||
timestamp,
|
timestamp,
|
||||||
receiver,
|
receiver,
|
||||||
|
@ -126,7 +125,7 @@ impl Decodable for RejectReason {
|
||||||
0x41 => RejectReason::Dust,
|
0x41 => RejectReason::Dust,
|
||||||
0x42 => RejectReason::Fee,
|
0x42 => RejectReason::Fee,
|
||||||
0x43 => RejectReason::Checkpoint,
|
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 hex_lit::hex;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
use bitcoin::consensus::encode::{deserialize, serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn version_message_test() {
|
fn version_message_test() {
|
Loading…
Reference in New Issue