Update p2p fuzz targets with `Arbitrary` impls
What changed, and why it matters
This commit is a routine test and fuzzing infrastructure update. It adds new ways to automatically generate random P2P message objects for fuzz testing, moves existing fuzz targets into a dedicated 'p2p' directory, and renames them. There is no change to production behavior, no bug fix, and no security patch in the code that runs in real Bitcoin applications.
No security action required. This is a testing-only refactoring. Reviewers may optionally verify that the new `Arbitrary` impls are feature-gated and do not affect normal builds.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit adds Arbitrary trait implementations (gated behind the arbitrary feature) to P2P types in p2p/src/address.rs, lib.rs, message.rs, message_blockdata.rs, message_bloom.rs, message_compact_blocks.rs, message_filter.rs, and message_network.rs. It also reorganizes fuzz targets: deletes bitcoin_deser_net_msg and bitcoin_p2p_address_roundtrip, and adds p2p_arbitrary_addrv2, p2p_deserialize_addrv2, and p2p_deserialize_raw_net_msg. The only non-test code change is the addition of Arbitrary impls; no consensus serialization, deserialization, or runtime logic is modified.
Changed components
fuzz/Cargo.toml.github/workflows/cron-daily-fuzz.ymlfuzz/fuzz_targets/p2p/*p2p/src/address.rsp2p/src/lib.rsp2p/src/message.rsp2p/src/message_blockdata.rsp2p/src/message_bloom.rsp2p/src/message_compact_blocks.rsp2p/src/message_filter.rsp2p/src/message_network.rsInspect captured patch +557 / −121
diff --git a/.github/workflows/cron-daily-fuzz.yml b/.github/workflows/cron-daily-fuzz.yml
index 3e9d6e6f..06795fa5 100644
--- a/.github/workflows/cron-daily-fuzz.yml
+++ b/.github/workflows/cron-daily-fuzz.yml
@@ -18,7 +18,6 @@ jobs:
# We only get 20 jobs at a time, we probably don't want to go
# over that limit with fuzzing because of the hour run time.
fuzz_target: [
- bitcoin_deser_net_msg,
bitcoin_deserialize_address,
bitcoin_deserialize_block,
bitcoin_deserialize_prefilled_transaction,
@@ -27,7 +26,6 @@ jobs:
bitcoin_deserialize_transaction,
bitcoin_deserialize_witness,
bitcoin_outpoint_string,
- bitcoin_p2p_address_roundtrip,
bitcoin_script_bytes_to_asm_fmt,
hashes_json,
hashes_ripemd160,
@@ -35,6 +33,9 @@ jobs:
hashes_sha256,
hashes_sha512,
hashes_sha512_256,
+ p2p_arbitrary_addrv2,
+ p2p_deserialize_addrv2,
+ p2p_deserialize_raw_net_msg,
units_arbitrary_weight,
units_parse_amount,
]
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index 2419bbfb..e58c756b 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -21,10 +21,6 @@ serde_json = "1.0.68"
[lints.rust]
unexpected_cfgs = { level = "deny", check-cfg = ['cfg(fuzzing)'] }
-[[bin]]
-name = "bitcoin_deser_net_msg"
-path = "fuzz_targets/bitcoin/deser_net_msg.rs"
-
[[bin]]
name = "bitcoin_deserialize_address"
path = "fuzz_targets/bitcoin/deserialize_address.rs"
@@ -57,10 +53,6 @@ path = "fuzz_targets/bitcoin/deserialize_witness.rs"
name = "bitcoin_outpoint_string"
path = "fuzz_targets/bitcoin/outpoint_string.rs"
-[[bin]]
-name = "bitcoin_p2p_address_roundtrip"
-path = "fuzz_targets/bitcoin/p2p_address_roundtrip.rs"
-
[[bin]]
name = "bitcoin_script_bytes_to_asm_fmt"
path = "fuzz_targets/bitcoin/script_bytes_to_asm_fmt.rs"
@@ -89,6 +81,18 @@ path = "fuzz_targets/hashes/sha512.rs"
name = "hashes_sha512_256"
path = "fuzz_targets/hashes/sha512_256.rs"
+[[bin]]
+name = "p2p_arbitrary_addrv2"
+path = "fuzz_targets/p2p/arbitrary_addrv2.rs"
+
+[[bin]]
+name = "p2p_deserialize_addrv2"
+path = "fuzz_targets/p2p/deserialize_addrv2.rs"
+
+[[bin]]
+name = "p2p_deserialize_raw_net_msg"
+path = "fuzz_targets/p2p/deserialize_raw_net_msg.rs"
+
[[bin]]
name = "units_arbitrary_weight"
path = "fuzz_targets/units/arbitrary_weight.rs"
diff --git a/fuzz/fuzz_targets/bitcoin/deser_net_msg.rs b/fuzz/fuzz_targets/bitcoin/deser_net_msg.rs
deleted file mode 100644
index 635a4a7a..00000000
--- a/fuzz/fuzz_targets/bitcoin/deser_net_msg.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use honggfuzz::fuzz;
-
-fn do_test(data: &[u8]) {
- let _: Result<p2p::message::RawNetworkMessage, _> =
- bitcoin::consensus::encode::deserialize(data);
-}
-
-fn main() {
- loop {
- fuzz!(|data| {
- do_test(data);
- });
- }
-}
-
-#[cfg(all(test, fuzzing))]
-mod tests {
- fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
- let mut b = 0;
- for (idx, c) in hex.as_bytes().iter().enumerate() {
- b <<= 4;
- match *c {
- b'A'..=b'F' => b |= c - b'A' + 10,
- b'a'..=b'f' => b |= c - b'a' + 10,
- b'0'..=b'9' => b |= c - b'0',
- _ => panic!("Bad hex"),
- }
- if (idx & 1) == 1 {
- out.push(b);
- b = 0;
- }
- }
- }
-
- #[test]
- fn duplicate_crash() {
- let mut a = Vec::new();
- extend_vec_from_hex("00", &mut a);
- super::do_test(&a);
- }
-}
diff --git a/fuzz/fuzz_targets/bitcoin/p2p_address_roundtrip.rs b/fuzz/fuzz_targets/bitcoin/p2p_address_roundtrip.rs
deleted file mode 100644
index 91d2ba02..00000000
--- a/fuzz/fuzz_targets/bitcoin/p2p_address_roundtrip.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use std::convert::TryFrom;
-use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
-
-use bitcoin::consensus::Decodable;
-use honggfuzz::fuzz;
-use p2p::address::AddrV2;
-
-fn do_test(data: &[u8]) {
- if data.len() < 2 {
- return;
- }
-
- let mut cursor = std::io::Cursor::new(data);
- let addr_v2 = if let Ok(addr) = AddrV2::consensus_decode(&mut cursor) {
- addr
- } else {
- return;
- };
-
- if let Ok(ip_addr) = IpAddr::try_from(addr_v2.clone()) {
- let round_trip: AddrV2 = AddrV2::from(ip_addr);
- assert_eq!(addr_v2, round_trip, "AddrV2 -> IpAddr -> AddrV2 should round-trip correctly");
- }
-
- if let Ok(ip_addr) = Ipv4Addr::try_from(addr_v2.clone()) {
- let round_trip: AddrV2 = AddrV2::from(ip_addr);
- assert_eq!(addr_v2, round_trip, "AddrV2 -> Ipv4Addr -> AddrV2 should round-trip correctly");
- }
-
- if let Ok(ip_addr) = Ipv6Addr::try_from(addr_v2.clone()) {
- let round_trip: AddrV2 = AddrV2::from(ip_addr);
- assert_eq!(addr_v2, round_trip, "AddrV2 -> Ipv6Addr -> AddrV2 should round-trip correctly");
- }
-}
-
-fn main() {
- loop {
- fuzz!(|data| {
- do_test(data);
- });
- }
-}
-
-#[cfg(all(test, fuzzing))]
-mod tests {
- fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
- let mut b = 0;
- for (idx, c) in hex.as_bytes().iter().enumerate() {
- b <<= 4;
- match *c {
- b'A'..=b'F' => b |= c - b'A' + 10,
- b'a'..=b'f' => b |= c - b'a' + 10,
- b'0'..=b'9' => b |= c - b'0',
- _ => panic!("Bad hex"),
- }
- if (idx & 1) == 1 {
- out.push(b);
- b = 0;
- }
- }
- }
-
- #[test]
- fn duplicate_crash() {
- let mut a = Vec::new();
- extend_vec_from_hex("00", &mut a);
- super::do_test(&a);
- }
-}
diff --git a/fuzz/fuzz_targets/p2p/arbitrary_addrv2.rs b/fuzz/fuzz_targets/p2p/arbitrary_addrv2.rs
new file mode 100644
index 00000000..3bde4f2c
--- /dev/null
+++ b/fuzz/fuzz_targets/p2p/arbitrary_addrv2.rs
@@ -0,0 +1,62 @@
+use std::convert::TryFrom;
+use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+use arbitrary::{Arbitrary, Unstructured};
+use honggfuzz::fuzz;
+use p2p::address::AddrV2;
+
+fn do_test(data: &[u8]) {
+ let mut u = Unstructured::new(data);
+ let a = AddrV2::arbitrary(&mut u);
+
+ if let Ok(addr_v2) = a {
+ if let Ok(ip_addr) = IpAddr::try_from(addr_v2.clone()) {
+ let round_trip: AddrV2 = AddrV2::from(ip_addr);
+ assert_eq!(addr_v2, round_trip, "AddrV2 -> IpAddr -> AddrV2 should round-trip correctly");
+ }
+
+ if let Ok(ip_addr) = Ipv4Addr::try_from(addr_v2.clone()) {
+ let round_trip: AddrV2 = AddrV2::from(ip_addr);
+ assert_eq!(addr_v2, round_trip, "AddrV2 -> Ipv4Addr -> AddrV2 should round-trip correctly");
+ }
+
+ if let Ok(ip_addr) = Ipv6Addr::try_from(addr_v2.clone()) {
+ let round_trip: AddrV2 = AddrV2::from(ip_addr);
+ assert_eq!(addr_v2, round_trip, "AddrV2 -> Ipv6Addr -> AddrV2 should round-trip correctly");
+ }
+ }
+}
+
+fn main() {
+ loop {
+ fuzz!(|data| {
+ do_test(data);
+ });
+ }
+}
+
+#[cfg(all(test, fuzzing))]
+mod tests {
+ fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
+ let mut b = 0;
+ for (idx, c) in hex.as_bytes().iter().enumerate() {
+ b <<= 4;
+ match *c {
+ b'A'..=b'F' => b |= c - b'A' + 10,
+ b'a'..=b'f' => b |= c - b'a' + 10,
+ b'0'..=b'9' => b |= c - b'0',
+ _ => panic!("Bad hex"),
+ }
+ if (idx & 1) == 1 {
+ out.push(b);
+ b = 0;
+ }
+ }
+ }
+
+ #[test]
+ fn duplicate_crash() {
+ let mut a = Vec::new();
+ extend_vec_from_hex("00", &mut a);
+ super::do_test(&a);
+ }
+}
diff --git a/fuzz/fuzz_targets/p2p/deserialize_addrv2.rs b/fuzz/fuzz_targets/p2p/deserialize_addrv2.rs
new file mode 100644
index 00000000..712aa096
--- /dev/null
+++ b/fuzz/fuzz_targets/p2p/deserialize_addrv2.rs
@@ -0,0 +1,41 @@
+use honggfuzz::fuzz;
+
+fn do_test(data: &[u8]) {
+ let _: Result<p2p::address::AddrV2, _> =
+ bitcoin::consensus::encode::deserialize(data);
+}
+
+fn main() {
+ loop {
+ fuzz!(|data| {
+ do_test(data);
+ });
+ }
+}
+
+#[cfg(all(test, fuzzing))]
+mod tests {
+ fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
+ let mut b = 0;
+ for (idx, c) in hex.as_bytes().iter().enumerate() {
+ b <<= 4;
+ match *c {
+ b'A'..=b'F' => b |= c - b'A' + 10,
+ b'a'..=b'f' => b |= c - b'a' + 10,
+ b'0'..=b'9' => b |= c - b'0',
+ _ => panic!("Bad hex"),
+ }
+ if (idx & 1) == 1 {
+ out.push(b);
+ b = 0;
+ }
+ }
+ }
+
+ #[test]
+ fn duplicate_crash() {
+ let mut a = Vec::new();
+ extend_vec_from_hex("00", &mut a);
+ super::do_test(&a);
+ }
+}
diff --git a/fuzz/fuzz_targets/p2p/deserialize_raw_net_msg.rs b/fuzz/fuzz_targets/p2p/deserialize_raw_net_msg.rs
new file mode 100644
index 00000000..635a4a7a
--- /dev/null
+++ b/fuzz/fuzz_targets/p2p/deserialize_raw_net_msg.rs
@@ -0,0 +1,41 @@
+use honggfuzz::fuzz;
+
+fn do_test(data: &[u8]) {
+ let _: Result<p2p::message::RawNetworkMessage, _> =
+ bitcoin::consensus::encode::deserialize(data);
+}
+
+fn main() {
+ loop {
+ fuzz!(|data| {
+ do_test(data);
+ });
+ }
+}
+
+#[cfg(all(test, fuzzing))]
+mod tests {
+ fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
+ let mut b = 0;
+ for (idx, c) in hex.as_bytes().iter().enumerate() {
+ b <<= 4;
+ match *c {
+ b'A'..=b'F' => b |= c - b'A' + 10,
+ b'a'..=b'f' => b |= c - b'a' + 10,
+ b'0'..=b'9' => b |= c - b'0',
+ _ => panic!("Bad hex"),
+ }
+ if (idx & 1) == 1 {
+ out.push(b);
+ b = 0;
+ }
+ }
+ }
+
+ #[test]
+ fn duplicate_crash() {
+ let mut a = Vec::new();
+ extend_vec_from_hex("00", &mut a);
+ super::do_test(&a);
+ }
+}
diff --git a/p2p/src/address.rs b/p2p/src/address.rs
index 4047814a..8274eca6 100644
--- a/p2p/src/address.rs
+++ b/p2p/src/address.rs
@@ -9,6 +9,8 @@ use alloc::vec;
use alloc::vec::Vec;
use core::{fmt, iter};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use bitcoin::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt};
use io::{BufRead, Read, Write};
@@ -442,6 +444,43 @@ impl fmt::Display for AddrV2ToIpv6AddrError {
impl std::error::Error for AddrV2ToIpv6AddrError {}
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Address {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ let socket_addr = match bool::arbitrary(u)? {
+ true => SocketAddr::new(IpAddr::V4(Ipv4Addr::new(u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?)), u.arbitrary()?),
+ false => SocketAddr::new(IpAddr::V6(Ipv6Addr::new(u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?)), u.arbitrary()?)
+ };
+
+ Ok(Address::new(&socket_addr, u.arbitrary()?))
+ }
+}
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for AddrV2 {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match u.int_in_range(0..=5)? {
+ 0 => Ok(AddrV2::Ipv4(Ipv4Addr::new(u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?))),
+ 1 => Ok(AddrV2::Ipv6(Ipv6Addr::new(u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?))),
+ 2 => Ok(AddrV2::TorV3(u.arbitrary()?)),
+ 3 => Ok(AddrV2::I2p(u.arbitrary()?)),
+ 4 => Ok(AddrV2::Cjdns(Ipv6Addr::new(u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?))),
+ _ => Ok(AddrV2::Unknown(u.arbitrary()?, Vec::<u8>::arbitrary(u)?))
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for AddrV2Message {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(AddrV2Message{
+ time: u.arbitrary()?,
+ services: u.arbitrary()?,
+ addr: u.arbitrary()?,
+ port: u.arbitrary()?
+ })
+ }
+}
+
#[cfg(test)]
mod test {
use alloc::{format, vec};
diff --git a/p2p/src/lib.rs b/p2p/src/lib.rs
index 666c0b8b..d941e456 100644
--- a/p2p/src/lib.rs
+++ b/p2p/src/lib.rs
@@ -39,6 +39,8 @@ use alloc::string::String;
use core::borrow::{Borrow, BorrowMut};
use core::str::FromStr;
use core::{fmt, ops};
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use bitcoin::consensus::encode::{self, Decodable, Encodable};
use bitcoin::network::{Network, Params, TestnetVersion};
@@ -463,6 +465,27 @@ impl std::error::Error for UnknownNetworkError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for ProtocolVersion {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(ProtocolVersion(u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for ServiceFlags {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(ServiceFlags(u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Magic {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Magic(u.arbitrary()?))
+ }
+}
+
#[cfg(test)]
mod tests {
use alloc::string::ToString;
diff --git a/p2p/src/message.rs b/p2p/src/message.rs
index 4867e96a..7eb5d740 100644
--- a/p2p/src/message.rs
+++ b/p2p/src/message.rs
@@ -10,6 +10,9 @@ use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
+
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use core::{cmp, fmt};
use bitcoin::block::HeaderExt;
@@ -814,7 +817,7 @@ impl Decodable for CheckedData {
expected: expected_checksum,
actual: checksum,
}
- .into())
+ .into())
} else {
Ok(CheckedData { data, checksum })
}
@@ -858,6 +861,96 @@ fn sha2_checksum(data: &[u8]) -> [u8; 4] {
[checksum[0], checksum[1], checksum[2], checksum[3]]
}
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for AddrPayload {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(AddrPayload(Vec::<(u32, Address)>::arbitrary(u)?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for AddrV2Payload {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(AddrV2Payload(Vec::<AddrV2Message>::arbitrary(u)?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for InventoryPayload {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(InventoryPayload(Vec::<message_blockdata::Inventory>::arbitrary(u)?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for CommandString {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(CommandString(u.arbitrary::<String>()?.into()))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for HeadersMessage {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(HeadersMessage(u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for NetworkMessage {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match u.int_in_range(0..=36)? {
+ 0 => Ok(NetworkMessage::Version(u.arbitrary()?)),
+ 1 => Ok(NetworkMessage::Verack),
+ 2 => Ok(NetworkMessage::Addr(u.arbitrary()?)),
+ 3 => Ok(NetworkMessage::Inv(u.arbitrary()?)),
+ 4 => Ok(NetworkMessage::GetData(u.arbitrary()?)),
+ 5 => Ok(NetworkMessage::NotFound(u.arbitrary()?)),
+ 6 => Ok(NetworkMessage::GetBlocks(u.arbitrary()?)),
+ 7 => Ok(NetworkMessage::GetHeaders(u.arbitrary()?)),
+ 8 => Ok(NetworkMessage::MemPool),
+ 9 => Ok(NetworkMessage::Tx(u.arbitrary()?)),
+ 10 => Ok(NetworkMessage::Block(u.arbitrary()?)),
+ 11 => Ok(NetworkMessage::Headers(u.arbitrary()?)),
+ 12 => Ok(NetworkMessage::SendHeaders),
+ 13 => Ok(NetworkMessage::GetAddr),
+ 14 => Ok(NetworkMessage::Ping(u.arbitrary()?)),
+ 15 => Ok(NetworkMessage::Pong(u.arbitrary()?)),
+ 16 => Ok(NetworkMessage::MerkleBlock(u.arbitrary()?)),
+ 17 => Ok(NetworkMessage::FilterLoad(u.arbitrary()?)),
+ 18 => Ok(NetworkMessage::FilterAdd(u.arbitrary()?)),
+ 19 => Ok(NetworkMessage::FilterClear),
+ 20 => Ok(NetworkMessage::GetCFilters(u.arbitrary()?)),
+ 21 => Ok(NetworkMessage::CFilter(u.arbitrary()?)),
+ 22 => Ok(NetworkMessage::GetCFHeaders(u.arbitrary()?)),
+ 23 => Ok(NetworkMessage::CFHeaders(u.arbitrary()?)),
+ 24 => Ok(NetworkMessage::GetCFCheckpt(u.arbitrary()?)),
+ 25 => Ok(NetworkMessage::CFCheckpt(u.arbitrary()?)),
+ 26 => Ok(NetworkMessage::SendCmpct(u.arbitrary()?)),
+ 27 => Ok(NetworkMessage::CmpctBlock(u.arbitrary()?)),
+ 28 => Ok(NetworkMessage::GetBlockTxn(u.arbitrary()?)),
+ 29 => Ok(NetworkMessage::BlockTxn(u.arbitrary()?)),
+ 30 => Ok(NetworkMessage::Alert(u.arbitrary()?)),
+ 31 => Ok(NetworkMessage::Reject(u.arbitrary()?)),
+ 32 => Ok(NetworkMessage::FeeFilter(u.arbitrary()?)),
+ 33 => Ok(NetworkMessage::WtxidRelay),
+ 34 => Ok(NetworkMessage::AddrV2(u.arbitrary()?)),
+ 35 => Ok(NetworkMessage::SendAddrV2),
+ _ => Ok(NetworkMessage::Unknown {
+ command: u.arbitrary()?,
+ payload: Vec::<u8>::arbitrary(u)?,
+ }),
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for RawNetworkMessage {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(RawNetworkMessage::new(u.arbitrary()?, u.arbitrary()?))
+ }
+}
+
#[cfg(test)]
mod test {
use alloc::string::ToString;
diff --git a/p2p/src/message_blockdata.rs b/p2p/src/message_blockdata.rs
index 5e4ca85a..00760a27 100644
--- a/p2p/src/message_blockdata.rs
+++ b/p2p/src/message_blockdata.rs
@@ -6,6 +6,8 @@
//! Bitcoin data (blocks and transactions) around.
use alloc::vec::Vec;
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use bitcoin::block::BlockHash;
use bitcoin::consensus::encode::{self, Decodable, Encodable};
@@ -130,6 +132,39 @@ impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for GetHeadersMessage {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(GetHeadersMessage{version: u.arbitrary()?, locator_hashes: Vec::<BlockHash>::arbitrary(u)?, stop_hash: u.arbitrary()?})
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for GetBlocksMessage {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(GetBlocksMessage{version: u.arbitrary()?, locator_hashes: Vec::<BlockHash>::arbitrary(u)?, stop_hash: u.arbitrary()?})
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Inventory {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match u.int_in_range(0..=7)? {
+ 0 => Ok(Inventory::Error(u.arbitrary()?)),
+ 1 => Ok(Inventory::Transaction(u.arbitrary()?)),
+ 2 => Ok(Inventory::Block(u.arbitrary()?)),
+ 3 => Ok(Inventory::CompactBlock(u.arbitrary()?)),
+ 4 => Ok(Inventory::WTx(u.arbitrary()?)),
+ 5 => Ok(Inventory::WitnessTransaction(u.arbitrary()?)),
+ 6 => Ok(Inventory::WitnessBlock(u.arbitrary()?)),
+ _ => Ok(Inventory::Unknown {
+ inv_type: u.arbitrary()?,
+ hash: u.arbitrary()?
+ })
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use bitcoin::consensus::encode::{deserialize, serialize};
diff --git a/p2p/src/message_bloom.rs b/p2p/src/message_bloom.rs
index 09051b9a..d6daadee 100644
--- a/p2p/src/message_bloom.rs
+++ b/p2p/src/message_bloom.rs
@@ -6,6 +6,9 @@
use alloc::vec::Vec;
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
+
use bitcoin::consensus::{encode, Decodable, Encodable, ReadExt};
use io::{BufRead, Write};
@@ -67,3 +70,33 @@ pub struct FilterAdd {
}
impl_consensus_encoding!(FilterAdd, data);
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for BloomFlags {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match u.int_in_range(0..=2)? {
+ 0 => Ok(BloomFlags::None),
+ 1 => Ok(BloomFlags::All),
+ _ => Ok(BloomFlags::PubkeyOnly)
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for FilterAdd {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(FilterAdd{ data: Vec::<u8>::arbitrary(u)? })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for FilterLoad {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(FilterLoad{
+ filter: Vec::<u8>::arbitrary(u)?,
+ hash_funcs: u.arbitrary()?,
+ tweak: u.arbitrary()?,
+ flags: u.arbitrary()?,
+ })
+ }
+}
diff --git a/p2p/src/message_compact_blocks.rs b/p2p/src/message_compact_blocks.rs
index f8a0d0e7..cee25810 100644
--- a/p2p/src/message_compact_blocks.rs
+++ b/p2p/src/message_compact_blocks.rs
@@ -3,6 +3,9 @@
//!
//! BIP-0152 Compact Blocks network messages
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
+
use bitcoin::bip152;
use crate::consensus::impl_consensus_encoding;
@@ -45,3 +48,31 @@ pub struct BlockTxn {
pub transactions: bip152::BlockTransactions,
}
impl_consensus_encoding!(BlockTxn, transactions);
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for SendCmpct {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(SendCmpct{ send_compact: u.arbitrary()?, version: u.arbitrary()? })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for CmpctBlock {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(CmpctBlock{ compact_block: u.arbitrary()? })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for GetBlockTxn {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(GetBlockTxn{ txs_request: u.arbitrary()? })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for BlockTxn {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(BlockTxn{ transactions: u.arbitrary()? })
+ }
+}
diff --git a/p2p/src/message_filter.rs b/p2p/src/message_filter.rs
index 8b6395dd..65dba191 100644
--- a/p2p/src/message_filter.rs
+++ b/p2p/src/message_filter.rs
@@ -6,6 +6,9 @@
use alloc::vec::Vec;
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
+
use bitcoin::bip158::{FilterHash, FilterHeader};
use bitcoin::block::BlockHash;
use units::BlockHeight;
@@ -83,3 +86,67 @@ pub struct CFCheckpt {
pub filter_headers: Vec<FilterHeader>,
}
impl_consensus_encoding!(CFCheckpt, filter_type, stop_hash, filter_headers);
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for GetCFilters {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(GetCFilters{
+ filter_type: u.arbitrary()?,
+ start_height: u.arbitrary()?,
+ stop_hash: u.arbitrary()?,
+ })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for CFilter {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(CFilter{
+ filter_type: u.arbitrary()?,
+ block_hash: u.arbitrary()?,
+ filter: Vec::<u8>::arbitrary(u)?,
+ })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for GetCFHeaders {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(GetCFHeaders{
+ filter_type: u.arbitrary()?,
+ start_height: u.arbitrary()?,
+ stop_hash: u.arbitrary()?,
+ })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for CFHeaders {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(CFHeaders{
+ filter_type: u.arbitrary()?,
+ stop_hash: u.arbitrary()?,
+ previous_filter_header: u.arbitrary()?,
+ filter_hashes: u.arbitrary()?,
+ })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for GetCFCheckpt {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(GetCFCheckpt{ filter_type: u.arbitrary()?, stop_hash: u.arbitrary()? })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for CFCheckpt {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(CFCheckpt{
+ filter_type: u.arbitrary()?,
+ stop_hash: u.arbitrary()?,
+ filter_headers: Vec::<FilterHeader>::arbitrary(u)?,
+ })
+ }
+}
+
diff --git a/p2p/src/message_network.rs b/p2p/src/message_network.rs
index 62d4427f..270bc955 100644
--- a/p2p/src/message_network.rs
+++ b/p2p/src/message_network.rs
@@ -7,6 +7,8 @@
use alloc::borrow::Cow;
use alloc::format;
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use alloc::string::{String, ToString};
use alloc::vec::Vec;
@@ -328,6 +330,80 @@ impl Alert {
impl_vec_wrapper!(Alert, Vec<u8>);
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for ClientSoftwareVersion {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match bool::arbitrary(u)? {
+ true => Ok(ClientSoftwareVersion::Date {
+ yyyy: u.arbitrary()?,
+ mm: u.arbitrary()?,
+ dd: u.arbitrary()?,
+ }),
+ false => Ok(ClientSoftwareVersion::SemVer {
+ major: u.arbitrary()?,
+ minor: u.arbitrary()?,
+ revision: u.arbitrary()?,
+ })
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for UserAgentVersion {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(UserAgentVersion::new(u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for UserAgent {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(UserAgent::new(u.arbitrary::<String>()?, u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for VersionMessage {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(VersionMessage::new(u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?, u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for RejectReason {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match u.int_in_range(0..=7)? {
+ 0 => Ok(RejectReason::Malformed),
+ 1 => Ok(RejectReason::Invalid),
+ 2 => Ok(RejectReason::Obsolete),
+ 3 => Ok(RejectReason::Duplicate),
+ 4 => Ok(RejectReason::NonStandard),
+ 5 => Ok(RejectReason::Dust),
+ 6 => Ok(RejectReason::Fee),
+ _ => Ok(RejectReason::Checkpoint)
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Reject {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Reject{
+ message: u.arbitrary::<String>()?.into(),
+ ccode: u.arbitrary()?,
+ reason: u.arbitrary::<String>()?.into(),
+ hash: sha256d::Hash::from_byte_array(u.arbitrary()?),
+ })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Alert {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Alert(Vec::<u8>::arbitrary(u)?))
+ }
+}
+
#[cfg(test)]
mod tests {
use alloc::string::ToString;
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.