Merge rust-bitcoin/rust-bitcoin#4571: Move `p2p` encodings from `encode` and into `p2p`

20779bdbc8 Move `p2p` encodings out of `encode` (rustaceanrob)
94bcff28b1 p2p: Add wrappers for messages that use `Vec` (rustaceanrob)

Pull request description:

  To move `p2p` into a crate:
  1. All `Encodable` implementations for `p2p` must be defined in `p2p`. To accomplish this, and anything else in the future related to encoding, I added a `deser` module with the necessary macro to implement `Encodable` for `Vec<T> where T: Encodable`.
  2. Because the orphan rule would apply for implementing `Encodable`for `Vec` within `p2p`, three types defined within `p2p` are used to wrap the `Vec` and snake around the orphan rule.

ACKs for top commit:
  tcharding:
    ACK 20779bdbc8
  apoelstra:
    ACK 20779bdbc8ebcac2365d122e857c23b3d8e8d1e7; successfully ran local tests

Tree-SHA512: 7abfe5b853e84bb465579309f80a0687c325217f6f342651278bedf540c4d17ae16683ab02dea5e3d98d90790deb12c6bc7d572a92cec8e19ff94e395bc0d29c
This commit is contained in:
merge-script 2025-05-31 21:09:28 +00:00
commit 2113a32276
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
5 changed files with 77 additions and 35 deletions

View File

@ -26,11 +26,6 @@ use crate::bip152::{PrefilledTransaction, ShortId};
use crate::bip158::{FilterHash, FilterHeader}; use crate::bip158::{FilterHash, FilterHeader};
use crate::block::{self, BlockHash}; use crate::block::{self, BlockHash};
use crate::merkle_tree::TxMerkleNode; use crate::merkle_tree::TxMerkleNode;
#[cfg(feature = "std")]
use crate::p2p::{
address::{AddrV2Message, Address},
message_blockdata::Inventory,
};
use crate::prelude::{rc, sync, Box, Cow, String, Vec}; use crate::prelude::{rc, sync, Box, Cow, String, Vec};
use crate::taproot::TapLeafHash; use crate::taproot::TapLeafHash;
use crate::transaction::{Transaction, TxIn, TxOut}; use crate::transaction::{Transaction, TxIn, TxOut};
@ -552,13 +547,6 @@ impl_vec!(TapLeafHash);
impl_vec!(ShortId); impl_vec!(ShortId);
impl_vec!(PrefilledTransaction); impl_vec!(PrefilledTransaction);
#[cfg(feature = "std")]
impl_vec!(Inventory);
#[cfg(feature = "std")]
impl_vec!((u32, Address));
#[cfg(feature = "std")]
impl_vec!(AddrV2Message);
pub(crate) fn consensus_encode_with_size<W: Write + ?Sized>( pub(crate) fn consensus_encode_with_size<W: Write + ?Sized>(
data: &[u8], data: &[u8],
w: &mut W, w: &mut W,
@ -765,8 +753,6 @@ mod tests {
use core::mem::discriminant; use core::mem::discriminant;
use super::*; use super::*;
#[cfg(feature = "std")]
use crate::p2p::{message_blockdata::Inventory, Address};
#[test] #[test]
fn serialize_int() { fn serialize_int() {
@ -1049,10 +1035,6 @@ mod tests {
test_len_is_max_vec::<TxIn>(); test_len_is_max_vec::<TxIn>();
test_len_is_max_vec::<Vec<u8>>(); test_len_is_max_vec::<Vec<u8>>();
test_len_is_max_vec::<u64>(); test_len_is_max_vec::<u64>();
#[cfg(feature = "std")]
test_len_is_max_vec::<(u32, Address)>();
#[cfg(feature = "std")]
test_len_is_max_vec::<Inventory>();
} }
fn test_len_is_max_vec<T>() fn test_len_is_max_vec<T>()

View File

@ -443,7 +443,7 @@ mod test {
use hex_lit::hex; use hex_lit::hex;
use super::*; use super::*;
use crate::consensus::encode::{deserialize, serialize}; use crate::{consensus::encode::{deserialize, serialize}, p2p::message::AddrV2Payload};
#[test] #[test]
fn serialize_address() { fn serialize_address() {
@ -656,10 +656,10 @@ mod test {
#[test] #[test]
fn addrv2message() { fn addrv2message() {
let raw = hex!("0261bc6649019902abab208d79627683fd4804010409090909208d"); let raw = hex!("0261bc6649019902abab208d79627683fd4804010409090909208d");
let addresses: Vec<AddrV2Message> = deserialize(&raw).unwrap(); let addresses: AddrV2Payload = deserialize(&raw).unwrap();
assert_eq!( assert_eq!(
addresses, addresses.0,
vec![ vec![
AddrV2Message { AddrV2Message {
services: ServiceFlags::NETWORK, services: ServiceFlags::NETWORK,

41
bitcoin/src/p2p/deser.rs Normal file
View File

@ -0,0 +1,41 @@
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

@ -19,6 +19,7 @@ use crate::p2p::{
Magic, Magic,
}; };
use crate::prelude::{Box, Cow, String, ToOwned, Vec}; use crate::prelude::{Box, Cow, String, ToOwned, Vec};
use crate::p2p::deser::impl_vec_wrapper;
use crate::{block, consensus, 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.
@ -163,6 +164,22 @@ pub struct V2NetworkMessage {
payload: NetworkMessage, payload: NetworkMessage,
} }
/// A list of inventory items.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct InventoryPayload(pub Vec<message_blockdata::Inventory>);
/// A list of legacy p2p address messages.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AddrPayload(pub Vec<(u32, Address)>);
/// A list of v2 address messages.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AddrV2Payload(pub Vec<AddrV2Message>);
impl_vec_wrapper!(InventoryPayload, message_blockdata::Inventory);
impl_vec_wrapper!(AddrPayload, (u32, Address));
impl_vec_wrapper!(AddrV2Payload, AddrV2Message);
/// A Network message payload. Proper documentation is available on at /// A Network message payload. Proper documentation is available on at
/// [Bitcoin Wiki: Protocol Specification](https://en.bitcoin.it/wiki/Protocol_specification) /// [Bitcoin Wiki: Protocol Specification](https://en.bitcoin.it/wiki/Protocol_specification)
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
@ -172,13 +189,13 @@ pub enum NetworkMessage {
/// `verack` /// `verack`
Verack, Verack,
/// `addr` /// `addr`
Addr(Vec<(u32, Address)>), Addr(AddrPayload),
/// `inv` /// `inv`
Inv(Vec<message_blockdata::Inventory>), Inv(InventoryPayload),
/// `getdata` /// `getdata`
GetData(Vec<message_blockdata::Inventory>), GetData(InventoryPayload),
/// `notfound` /// `notfound`
NotFound(Vec<message_blockdata::Inventory>), NotFound(InventoryPayload),
/// `getblocks` /// `getblocks`
GetBlocks(message_blockdata::GetBlocksMessage), GetBlocks(message_blockdata::GetBlocksMessage),
/// `getheaders` /// `getheaders`
@ -236,7 +253,7 @@ pub enum NetworkMessage {
/// `wtxidrelay` /// `wtxidrelay`
WtxidRelay, WtxidRelay,
/// `addrv2` /// `addrv2`
AddrV2(Vec<AddrV2Message>), AddrV2(AddrV2Payload),
/// `sendaddrv2` /// `sendaddrv2`
SendAddrV2, SendAddrV2,
@ -734,17 +751,17 @@ mod test {
let msgs = [ let msgs = [
NetworkMessage::Version(version_msg), NetworkMessage::Version(version_msg),
NetworkMessage::Verack, NetworkMessage::Verack,
NetworkMessage::Addr(vec![( NetworkMessage::Addr(AddrPayload(vec![(
45, 45,
Address::new(&([123, 255, 000, 100], 833).into(), ServiceFlags::NETWORK), Address::new(&([123, 255, 000, 100], 833).into(), ServiceFlags::NETWORK),
)]), )])),
NetworkMessage::Inv(vec![Inventory::Block(BlockHash::from_byte_array( NetworkMessage::Inv(InventoryPayload(vec![Inventory::Block(BlockHash::from_byte_array(
hash([8u8; 32]).to_byte_array(), hash([8u8; 32]).to_byte_array(),
))]), ))])),
NetworkMessage::GetData(vec![Inventory::Transaction(Txid::from_byte_array( NetworkMessage::GetData(InventoryPayload(vec![Inventory::Transaction(Txid::from_byte_array(
hash([45u8; 32]).to_byte_array(), hash([45u8; 32]).to_byte_array(),
))]), ))])),
NetworkMessage::NotFound(vec![Inventory::Error([0u8; 32])]), NetworkMessage::NotFound(InventoryPayload(vec![Inventory::Error([0u8; 32])])),
NetworkMessage::GetBlocks(GetBlocksMessage::new( NetworkMessage::GetBlocks(GetBlocksMessage::new(
vec![ vec![
BlockHash::from_byte_array(hash([1u8; 32]).to_byte_array()), BlockHash::from_byte_array(hash([1u8; 32]).to_byte_array()),
@ -826,12 +843,12 @@ mod test {
}), }),
NetworkMessage::FeeFilter(1000), NetworkMessage::FeeFilter(1000),
NetworkMessage::WtxidRelay, NetworkMessage::WtxidRelay,
NetworkMessage::AddrV2(vec![AddrV2Message { NetworkMessage::AddrV2(AddrV2Payload(vec![AddrV2Message {
addr: AddrV2::Ipv4(Ipv4Addr::new(127, 0, 0, 1)), addr: AddrV2::Ipv4(Ipv4Addr::new(127, 0, 0, 1)),
port: 0, port: 0,
services: ServiceFlags::NONE, services: ServiceFlags::NONE,
time: 0, time: 0,
}]), }])),
NetworkMessage::SendAddrV2, NetworkMessage::SendAddrV2,
NetworkMessage::CmpctBlock(cmptblock), NetworkMessage::CmpctBlock(cmptblock),
NetworkMessage::GetBlockTxn(GetBlockTxn { NetworkMessage::GetBlockTxn(GetBlockTxn {

View File

@ -19,6 +19,8 @@ pub mod message_compact_blocks;
pub mod message_filter; pub mod message_filter;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub mod message_network; pub mod message_network;
#[cfg(feature = "std")]
mod deser;
use core::str::FromStr; use core::str::FromStr;
use core::{fmt, ops}; use core::{fmt, ops};