What changed, and why it matters
This commit adds optional support for the `arbitrary` crate, a Rust fuzz-testing helper, across several data types in the rust-bitcoin library. It does not change normal runtime behavior; the new code is only compiled when the optional `arbitrary` feature is enabled. There is no direct security vulnerability in the diff, but it slightly expands the library's dependency tree and public API surface for users who opt into the feature.
No immediate security action required. Reviewers may want to confirm that the `arbitrary` feature is documented and that downstream users understand it is intended for fuzz testing only. If supply-chain risk is a concern, verify the `arbitrary` crate version is pinned/expected and consider auditing future feature-gated additions.
Security signals we found
Addition of optional third-party dependency (`arbitrary` 1.4.1)
Expansion of conditional public API surface via new trait implementations
Use of fallible constructors inside `Arbitrary::arbitrary` with error mapping
No validation logic changes for non-arbitrary builds
No unsafe code, no cryptographic operations, no serialization format changes
Evidence from the diff
The patch implements arbitrary::Arbitrary trait instances for PSBT maps (Input, Output, Psbt), BIP32 types (DerivationPath, Fingerprint, ChainCode, ChildNumber, Xpub), public key wrappers, taproot types (TapLeafHash, TapNodeHash, LeafVersion, TapTree, ControlBlock, etc.), hash types in bitcoin_hashes, and NetworkKind. Implementations are guarded by #[cfg(feature = "arbitrary")]. Cargo manifests add arbitrary as an optional dependency and propagate the feature to sub-crates (hashes, network, secp256k1). The implementations mostly delegate to existing Arbitrary impls for primitive fields or use fallible constructors (from_consensus, decode, deserialize, from_collection) and map errors to arbitrary::Error::IncorrectFormat.
Changed components
bitcoin/src/psbt/map/input.rsbitcoin/src/psbt/map/output.rsbitcoin/src/psbt/mod.rsbitcoin/src/bip32.rsbitcoin/src/crypto/key.rsbitcoin/src/taproot/mod.rsbitcoin/src/taproot/merkle_branch/buf.rsbitcoin_hashes (hash types)bitcoin-network-kind (NetworkKind)Cargo feature flags for `arbitrary`Inspect captured patch +280 / −1
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index e2267a74..3c451cac 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -132,6 +132,7 @@ version = "0.0.0"
name = "bitcoin-network-kind"
version = "0.1.0"
dependencies = [
+ "arbitrary",
"bitcoin-internals",
"serde",
"serde_json",
@@ -191,6 +192,7 @@ dependencies = [
name = "bitcoin_hashes"
version = "0.20.0"
dependencies = [
+ "arbitrary",
"bitcoin-consensus-encoding",
"bitcoin-internals",
"cpufeatures",
@@ -371,6 +373,7 @@ version = "0.32.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5fdc7d6e800869d3fd60ff857c479bf0a83ea7bf44b389e64461e844204994"
dependencies = [
+ "arbitrary",
"rand",
"secp256k1-sys",
"serde",
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index 66dd7f12..dacde176 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -131,6 +131,7 @@ version = "0.0.0"
name = "bitcoin-network-kind"
version = "0.1.0"
dependencies = [
+ "arbitrary",
"bitcoin-internals",
"serde",
"serde_json",
@@ -190,6 +191,7 @@ dependencies = [
name = "bitcoin_hashes"
version = "0.20.0"
dependencies = [
+ "arbitrary",
"bitcoin-consensus-encoding",
"bitcoin-internals",
"cpufeatures",
@@ -391,6 +393,7 @@ version = "0.32.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5fdc7d6e800869d3fd60ff857c479bf0a83ea7bf44b389e64461e844204994"
dependencies = [
+ "arbitrary",
"rand",
"secp256k1-sys",
"serde",
diff --git a/api/hashes/all-features.txt b/api/hashes/all-features.txt
index 5ebc399e..0e3dd315 100644
--- a/api/hashes/all-features.txt
+++ b/api/hashes/all-features.txt
@@ -507,6 +507,15 @@ impl std::io::Write for bitcoin_hashes::sha3_256::HashEngine
impl std::io::Write for bitcoin_hashes::sha512::HashEngine
impl std::io::Write for bitcoin_hashes::sha512_256::HashEngine
impl std::io::Write for bitcoin_hashes::siphash24::HashEngine
+impl<'a> arbitrary::Arbitrary<'a> for bitcoin_hashes::hash160::Hash
+impl<'a> arbitrary::Arbitrary<'a> for bitcoin_hashes::ripemd160::Hash
+impl<'a> arbitrary::Arbitrary<'a> for bitcoin_hashes::sha1::Hash
+impl<'a> arbitrary::Arbitrary<'a> for bitcoin_hashes::sha256::Hash
+impl<'a> arbitrary::Arbitrary<'a> for bitcoin_hashes::sha256d::Hash
+impl<'a> arbitrary::Arbitrary<'a> for bitcoin_hashes::sha384::Hash
+impl<'a> arbitrary::Arbitrary<'a> for bitcoin_hashes::sha3_256::Hash
+impl<'a> arbitrary::Arbitrary<'a> for bitcoin_hashes::sha512::Hash
+impl<'a> arbitrary::Arbitrary<'a> for bitcoin_hashes::sha512_256::Hash
impl<'de, T: bitcoin_hashes::Hash + serde::de::Deserialize<'de>> serde::de::Deserialize<'de> for bitcoin_hashes::hmac::Hmac<T>
impl<'de, T: bitcoin_hashes::sha256t::Tag> serde::de::Deserialize<'de> for bitcoin_hashes::sha256t::Hash<T>
impl<'de> serde::de::Deserialize<'de> for bitcoin_hashes::hash160::Hash
@@ -732,6 +741,7 @@ pub fn bitcoin_hashes::HashEngine::n_bytes_hashed(&self) -> u64
pub fn bitcoin_hashes::cmp::fixed_time_eq(a: &[u8], b: &[u8]) -> bool
pub fn bitcoin_hashes::debug_hex<'a>(bytes: impl core::iter::traits::collect::IntoIterator<Item = &'a u8>, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
pub fn bitcoin_hashes::encode_to_engine<T, H>(object: &T, engine: H) -> H where T: bitcoin_consensus_encoding::encode::Encodable + ?core::marker::Sized, H: bitcoin_hashes::HashEngine
+pub fn bitcoin_hashes::hash160::Hash::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_hashes::hash160::Hash::as_byte_array(&self) -> &Self::Bytes
pub fn bitcoin_hashes::hash160::Hash::as_ref(&self) -> &[u8; 20]
pub fn bitcoin_hashes::hash160::Hash::as_ref(&self) -> &[u8]
@@ -820,6 +830,7 @@ pub fn bitcoin_hashes::muhash::Hash::hash<__H: core::hash::Hasher>(&self, state:
pub fn bitcoin_hashes::muhash::Hash::partial_cmp(&self, other: &bitcoin_hashes::muhash::Hash) -> core::option::Option<core::cmp::Ordering>
pub fn bitcoin_hashes::muhash::Hash::serialize<S: serde::ser::Serializer>(&self, s: S) -> core::result::Result<<S as serde::ser::Serializer>::Ok, <S as serde::ser::Serializer>::Error>
pub fn bitcoin_hashes::muhash::Hash::to_byte_array(self) -> Self::Bytes
+pub fn bitcoin_hashes::ripemd160::Hash::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_hashes::ripemd160::Hash::as_byte_array(&self) -> &Self::Bytes
pub fn bitcoin_hashes::ripemd160::Hash::as_ref(&self) -> &[u8; 20]
pub fn bitcoin_hashes::ripemd160::Hash::as_ref(&self) -> &[u8]
@@ -851,6 +862,7 @@ pub fn bitcoin_hashes::ripemd160::HashEngine::n_bytes_hashed(&self) -> u64
pub fn bitcoin_hashes::ripemd160::HashEngine::write(&mut self, buf: &[u8]) -> std::io::error::Result<usize>
pub fn bitcoin_hashes::ripemd160::hash(data: &[u8]) -> bitcoin_hashes::ripemd160::Hash
pub fn bitcoin_hashes::ripemd160::hash_byte_chunks<B, I>(byte_slices: I) -> bitcoin_hashes::ripemd160::Hash where B: core::convert::AsRef<[u8]>, I: core::iter::traits::collect::IntoIterator<Item = B>
+pub fn bitcoin_hashes::sha1::Hash::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_hashes::sha1::Hash::as_byte_array(&self) -> &Self::Bytes
pub fn bitcoin_hashes::sha1::Hash::as_ref(&self) -> &[u8; 20]
pub fn bitcoin_hashes::sha1::Hash::as_ref(&self) -> &[u8]
@@ -882,6 +894,7 @@ pub fn bitcoin_hashes::sha1::HashEngine::n_bytes_hashed(&self) -> u64
pub fn bitcoin_hashes::sha1::HashEngine::write(&mut self, buf: &[u8]) -> std::io::error::Result<usize>
pub fn bitcoin_hashes::sha1::hash(data: &[u8]) -> bitcoin_hashes::sha1::Hash
pub fn bitcoin_hashes::sha1::hash_byte_chunks<B, I>(byte_slices: I) -> bitcoin_hashes::sha1::Hash where B: core::convert::AsRef<[u8]>, I: core::iter::traits::collect::IntoIterator<Item = B>
+pub fn bitcoin_hashes::sha256::Hash::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_hashes::sha256::Hash::as_byte_array(&self) -> &Self::Bytes
pub fn bitcoin_hashes::sha256::Hash::as_ref(&self) -> &[u8; 32]
pub fn bitcoin_hashes::sha256::Hash::as_ref(&self) -> &[u8]
@@ -928,6 +941,7 @@ pub fn bitcoin_hashes::sha256::MidstateError::fmt(&self, f: &mut core::fmt::Form
pub fn bitcoin_hashes::sha256::MidstateError::unprocessed_bytes(&self) -> &[u8]
pub fn bitcoin_hashes::sha256::hash(data: &[u8]) -> bitcoin_hashes::sha256::Hash
pub fn bitcoin_hashes::sha256::hash_byte_chunks<B, I>(byte_slices: I) -> bitcoin_hashes::sha256::Hash where B: core::convert::AsRef<[u8]>, I: core::iter::traits::collect::IntoIterator<Item = B>
+pub fn bitcoin_hashes::sha256d::Hash::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_hashes::sha256d::Hash::as_byte_array(&self) -> &Self::Bytes
pub fn bitcoin_hashes::sha256d::Hash::as_ref(&self) -> &[u8; 32]
pub fn bitcoin_hashes::sha256d::Hash::as_ref(&self) -> &[u8]
@@ -990,6 +1004,7 @@ pub fn bitcoin_hashes::sha256t::HashEngine<T>::n_bytes_hashed(&self) -> u64
pub fn bitcoin_hashes::sha256t::HashEngine<T>::write(&mut self, buf: &[u8]) -> std::io::error::Result<usize>
pub fn bitcoin_hashes::sha256t::hash<T>(data: &[u8]) -> bitcoin_hashes::sha256t::Hash<T> where T: bitcoin_hashes::sha256t::Tag
pub fn bitcoin_hashes::sha256t::hash_byte_chunks<B, I, T>(byte_slices: I) -> bitcoin_hashes::sha256t::Hash<T> where B: core::convert::AsRef<[u8]>, I: core::iter::traits::collect::IntoIterator<Item = B>, T: bitcoin_hashes::sha256t::Tag
+pub fn bitcoin_hashes::sha384::Hash::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_hashes::sha384::Hash::as_byte_array(&self) -> &Self::Bytes
pub fn bitcoin_hashes::sha384::Hash::as_ref(&self) -> &[u8; 48]
pub fn bitcoin_hashes::sha384::Hash::as_ref(&self) -> &[u8]
@@ -1021,6 +1036,7 @@ pub fn bitcoin_hashes::sha384::HashEngine::n_bytes_hashed(&self) -> u64
pub fn bitcoin_hashes::sha384::HashEngine::write(&mut self, buf: &[u8]) -> std::io::error::Result<usize>
pub fn bitcoin_hashes::sha384::hash(data: &[u8]) -> bitcoin_hashes::sha384::Hash
pub fn bitcoin_hashes::sha384::hash_byte_chunks<B, I>(byte_slices: I) -> bitcoin_hashes::sha384::Hash where B: core::convert::AsRef<[u8]>, I: core::iter::traits::collect::IntoIterator<Item = B>
+pub fn bitcoin_hashes::sha3_256::Hash::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_hashes::sha3_256::Hash::as_byte_array(&self) -> &Self::Bytes
pub fn bitcoin_hashes::sha3_256::Hash::as_ref(&self) -> &[u8; 32]
pub fn bitcoin_hashes::sha3_256::Hash::as_ref(&self) -> &[u8]
@@ -1051,6 +1067,7 @@ pub fn bitcoin_hashes::sha3_256::HashEngine::n_bytes_hashed(&self) -> u64
pub fn bitcoin_hashes::sha3_256::HashEngine::write(&mut self, buf: &[u8]) -> std::io::error::Result<usize>
pub fn bitcoin_hashes::sha3_256::hash(data: &[u8]) -> bitcoin_hashes::sha3_256::Hash
pub fn bitcoin_hashes::sha3_256::hash_byte_chunks<B, I>(byte_slices: I) -> bitcoin_hashes::sha3_256::Hash where B: core::convert::AsRef<[u8]>, I: core::iter::traits::collect::IntoIterator<Item = B>
+pub fn bitcoin_hashes::sha512::Hash::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_hashes::sha512::Hash::as_byte_array(&self) -> &Self::Bytes
pub fn bitcoin_hashes::sha512::Hash::as_ref(&self) -> &[u8; 64]
pub fn bitcoin_hashes::sha512::Hash::as_ref(&self) -> &[u8]
@@ -1082,6 +1099,7 @@ pub fn bitcoin_hashes::sha512::HashEngine::n_bytes_hashed(&self) -> u64
pub fn bitcoin_hashes::sha512::HashEngine::write(&mut self, buf: &[u8]) -> std::io::error::Result<usize>
pub fn bitcoin_hashes::sha512::hash(data: &[u8]) -> bitcoin_hashes::sha512::Hash
pub fn bitcoin_hashes::sha512::hash_byte_chunks<B, I>(byte_slices: I) -> bitcoin_hashes::sha512::Hash where B: core::convert::AsRef<[u8]>, I: core::iter::traits::collect::IntoIterator<Item = B>
+pub fn bitcoin_hashes::sha512_256::Hash::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
pub fn bitcoin_hashes::sha512_256::Hash::as_byte_array(&self) -> &Self::Bytes
pub fn bitcoin_hashes::sha512_256::Hash::as_ref(&self) -> &[u8; 32]
pub fn bitcoin_hashes::sha512_256::Hash::as_ref(&self) -> &[u8]
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 7874ede5..9555b1da 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -22,7 +22,7 @@ serde = ["base64", "dep:serde", "hashes/serde", "internals/serde", "network/serd
secp-global-context = ["secp256k1/global-context"]
secp-lowmemory = ["secp256k1/lowmemory"]
secp-recovery = ["secp256k1/recovery"]
-arbitrary = ["dep:arbitrary", "units/arbitrary", "primitives/arbitrary"]
+arbitrary = ["dep:arbitrary", "units/arbitrary", "primitives/arbitrary", "hashes/arbitrary", "secp256k1/arbitrary", "network/arbitrary"]
[dependencies]
base58 = { package = "base58ck", path = "../base58", version = "0.4.0", default-features = false, features = ["alloc"] }
diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs
index f2054ad7..d0b501b3 100644
--- a/bitcoin/src/bip32.rs
+++ b/bitcoin/src/bip32.rs
@@ -5,6 +5,8 @@
//! Implementation of BIP-0032 hierarchical deterministic wallets, as defined
//! at <https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki>.
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use core::convert::Infallible;
use core::ops::Index;
use core::str::FromStr;
@@ -1106,6 +1108,53 @@ impl Common {
}
}
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for DerivationPath {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ let bytes = Vec::<u32>::arbitrary(u)?;
+ Ok(Self::from_u32_slice(bytes.as_slice()))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Fingerprint {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_byte_array(u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for ChainCode {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_byte_array(u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for ChildNumber {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ let index = u.arbitrary()?;
+ match bool::arbitrary(u)? {
+ true => Ok(Self::Hardened { index }),
+ false => Ok(Self::Normal { index })
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Xpub {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self {
+ network: u.arbitrary()?,
+ depth: u.arbitrary()?,
+ parent_fingerprint: u.arbitrary()?,
+ child_number: u.arbitrary()?,
+ public_key: u.arbitrary()?,
+ chain_code: u.arbitrary()?,
+ })
+ }
+}
+
#[cfg(test)]
mod tests {
use alloc::string::ToString;
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index b5b4265f..8642e3ce 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -5,6 +5,8 @@
//! This module provides keys used in Bitcoin that can be roundtrip
//! (de)serialized.
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use core::convert::Infallible;
use core::fmt::{self, Write as _};
use core::ops;
@@ -1723,6 +1725,20 @@ impl fmt::Display for TweakXOnlyPublicKeyError {
#[cfg(feature = "std")]
impl std::error::Error for TweakXOnlyPublicKeyError {}
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for PublicKey {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_secp(secp256k1::PublicKey::arbitrary(u)?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for XOnlyPublicKey {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_secp(secp256k1::XOnlyPublicKey::arbitrary(u)?, u.arbitrary()?))
+ }
+}
+
#[cfg(test)]
mod tests {
use alloc::string::ToString;
diff --git a/bitcoin/src/psbt/map/input.rs b/bitcoin/src/psbt/map/input.rs
index 3d19185e..0b46588a 100644
--- a/bitcoin/src/psbt/map/input.rs
+++ b/bitcoin/src/psbt/map/input.rs
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: CC0-1.0
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use core::fmt;
use core::str::FromStr;
@@ -507,6 +509,43 @@ impl Map for Input {
impl_psbtmap_ser_de_serialize!(Input);
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for PsbtSighashType {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_u32(u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Input {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self {
+ non_witness_utxo: u.arbitrary()?,
+ witness_utxo: u.arbitrary()?,
+ partial_sigs: u.arbitrary()?,
+ sighash_type: u.arbitrary()?,
+ redeem_script: u.arbitrary()?,
+ witness_script: u.arbitrary()?,
+ bip32_derivation: u.arbitrary()?,
+ final_script_sig: u.arbitrary()?,
+ final_script_witness: u.arbitrary()?,
+ ripemd160_preimages: u.arbitrary()?,
+ sha256_preimages: u.arbitrary()?,
+ hash160_preimages: u.arbitrary()?,
+ hash256_preimages: u.arbitrary()?,
+ tap_key_sig: u.arbitrary()?,
+ tap_script_sigs: u.arbitrary()?,
+ tap_scripts: u.arbitrary()?,
+ tap_key_origins: u.arbitrary()?,
+ tap_internal_key: u.arbitrary()?,
+ tap_merkle_root: u.arbitrary()?,
+ musig2_participant_pubkeys: u.arbitrary()?,
+ proprietary: u.arbitrary()?,
+ unknown: u.arbitrary()?,
+ })
+ }
+}
+
#[cfg(test)]
mod test {
use super::*;
diff --git a/bitcoin/src/psbt/map/output.rs b/bitcoin/src/psbt/map/output.rs
index d48fc337..eaed1b83 100644
--- a/bitcoin/src/psbt/map/output.rs
+++ b/bitcoin/src/psbt/map/output.rs
@@ -1,5 +1,8 @@
// SPDX-License-Identifier: CC0-1.0
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
+
use crate::bip32::KeySource;
use crate::crypto::key::XOnlyPublicKey;
use crate::prelude::{btree_map, BTreeMap, Vec};
@@ -170,3 +173,20 @@ impl Map for Output {
}
impl_psbtmap_ser_de_serialize!(Output);
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Output {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self {
+ redeem_script: u.arbitrary()?,
+ witness_script: u.arbitrary()?,
+ bip32_derivation: u.arbitrary()?,
+ tap_internal_key: u.arbitrary()?,
+ tap_tree: u.arbitrary()?,
+ tap_key_origins: u.arbitrary()?,
+ musig2_participant_pubkeys: u.arbitrary()?,
+ proprietary: u.arbitrary()?,
+ unknown: u.arbitrary()?,
+ })
+ }
+}
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index 7465d3e7..89047b86 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -13,6 +13,8 @@ mod map;
pub mod raw;
pub mod serialize;
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use core::convert::Infallible;
use core::{cmp, fmt};
#[cfg(feature = "std")]
@@ -1293,6 +1295,21 @@ mod display_from_str {
#[cfg(feature = "base64")]
pub use self::display_from_str::PsbtParseError;
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for Psbt {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self {
+ unsigned_tx: u.arbitrary()?,
+ version: u.arbitrary()?,
+ xpub: u.arbitrary()?,
+ proprietary: u.arbitrary()?,
+ unknown: u.arbitrary()?,
+ inputs: u.arbitrary()?,
+ outputs: u.arbitrary()?,
+ })
+ }
+}
+
#[cfg(test)]
mod tests {
use alloc::string::ToString;
diff --git a/bitcoin/src/taproot/merkle_branch/buf.rs b/bitcoin/src/taproot/merkle_branch/buf.rs
index 4655b604..d0ba7285 100644
--- a/bitcoin/src/taproot/merkle_branch/buf.rs
+++ b/bitcoin/src/taproot/merkle_branch/buf.rs
@@ -2,6 +2,9 @@
//! Contains `TaprootMerkleBranchBuf` and its associated types.
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
+
use hashes::Hash;
use super::{
@@ -266,3 +269,11 @@ impl DoubleEndedIterator for IntoIter {
impl ExactSizeIterator for IntoIter {}
impl core::iter::FusedIterator for IntoIter {}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for TaprootMerkleBranchBuf {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ let collection = Vec::<TapNodeHash>::arbitrary(u)?;
+ Ok(Self::from_collection(collection).map_err(|_| arbitrary::Error::IncorrectFormat)?)
+ }
+}
diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs
index 1cb61b6b..647910e1 100644
--- a/bitcoin/src/taproot/mod.rs
+++ b/bitcoin/src/taproot/mod.rs
@@ -6,6 +6,8 @@
pub mod merkle_branch;
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use core::cmp::{Ordering, Reverse};
use core::convert::Infallible;
use core::fmt;
@@ -37,6 +39,8 @@ pub use merkle_branch::TaprootMerkleBranch;
#[doc(inline)]
pub use merkle_branch::TaprootMerkleBranchBuf;
+#[cfg(feature = "arbitrary")]
+use crate::psbt::serialize::Deserialize;
#[doc(inline)]
pub use crate::XOnlyPublicKey;
@@ -1651,6 +1655,82 @@ impl fmt::Display for InvalidControlBlockSizeError {
#[cfg(feature = "std")]
impl std::error::Error for InvalidControlBlockSizeError {}
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for TapLeafHash {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_byte_array(u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for TapNodeHash {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_byte_array(u.arbitrary()?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for FutureLeafVersion {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match u8::arbitrary(u)? {
+ TAPROOT_LEAF_TAPSCRIPT => Err(arbitrary::Error::IncorrectFormat),
+ version => Self::from_consensus(version).map_err(|_| arbitrary::Error::IncorrectFormat),
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for LeafVersion {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match bool::arbitrary(u)? {
+ true => Ok(Self::TapScript),
+ false => Ok(Self::Future(u.arbitrary()?))
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for LeafNode {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self{ leaf: u.arbitrary()?, merkle_branch: u.arbitrary()? })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for NodeInfo {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self {
+ hash: u.arbitrary()?,
+ leaves: u.arbitrary()?,
+ has_hidden_nodes: u.arbitrary()?,
+ })
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for TapLeaf {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match bool::arbitrary(u)? {
+ true => Ok(Self::Hidden(u.arbitrary()?)),
+ false => Ok(Self::Script(u.arbitrary()?, u.arbitrary()?))
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for TapTree {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::deserialize(u.arbitrary()?).map_err(|_| arbitrary::Error::IncorrectFormat)?)
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for ControlBlock {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::decode(u.arbitrary()?).map_err(|_| arbitrary::Error::IncorrectFormat)?)
+ }
+}
+
#[cfg(test)]
mod test {
use alloc::string::ToString;
diff --git a/hashes/Cargo.toml b/hashes/Cargo.toml
index 2b34276c..2c78549e 100644
--- a/hashes/Cargo.toml
+++ b/hashes/Cargo.toml
@@ -26,6 +26,7 @@ small-hash = []
internals = { package = "bitcoin-internals", path = "../internals", version = "0.5.0" }
encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encoding", version = "0.1.0", default-features = false }
+arbitrary = { version = "1.4.1", optional = true}
serde = { version = "1.0.195", default-features = false, optional = true }
cpufeatures = { version = "0.2", optional = true }
diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs
index 5d85667d..498eb723 100644
--- a/hashes/src/internal_macros.rs
+++ b/hashes/src/internal_macros.rs
@@ -101,6 +101,13 @@ macro_rules! general_hash_type {
hash_byte_chunks(byte_slices)
}
}
+
+ #[cfg(feature = "arbitrary")]
+ impl<'a> arbitrary::Arbitrary<'a> for Hash {
+ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self(u.arbitrary()?))
+ }
+ }
};
}
pub(crate) use general_hash_type;
diff --git a/hashes/src/ripemd160/mod.rs b/hashes/src/ripemd160/mod.rs
index 018a1297..3083b6b8 100644
--- a/hashes/src/ripemd160/mod.rs
+++ b/hashes/src/ripemd160/mod.rs
@@ -97,3 +97,4 @@ impl crate::HashEngine for HashEngine {
crate::internal_macros::engine_input_impl!();
fn finalize(self) -> Self::Hash { Hash::from_engine(self) }
}
+
diff --git a/hashes/src/sha256/mod.rs b/hashes/src/sha256/mod.rs
index 6d4fd262..ecd453fa 100644
--- a/hashes/src/sha256/mod.rs
+++ b/hashes/src/sha256/mod.rs
@@ -290,3 +290,4 @@ impl fmt::Display for MidstateError {
#[cfg(feature = "std")]
impl std::error::Error for MidstateError {}
+
diff --git a/network/Cargo.toml b/network/Cargo.toml
index a1b9fb3d..74fede67 100644
--- a/network/Cargo.toml
+++ b/network/Cargo.toml
@@ -20,6 +20,7 @@ alloc = ["internals/alloc", "serde?/alloc"]
[dependencies]
internals = { package = "bitcoin-internals", path = "../internals", version = "0.5.0" }
+arbitrary = { version = "1.4.1", optional = true }
serde = { version = "1.0.195", default-features = false, features = [ "derive" ], optional = true }
[dev-dependencies]
diff --git a/network/src/lib.rs b/network/src/lib.rs
index 19a06314..5ed67aab 100644
--- a/network/src/lib.rs
+++ b/network/src/lib.rs
@@ -18,6 +18,8 @@ extern crate std;
#[cfg(feature = "serde")]
extern crate serde;
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
use core::fmt;
use core::str::FromStr;
@@ -274,6 +276,16 @@ impl AsRef<Self> for Network {
fn as_ref(&self) -> &Self { self }
}
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for NetworkKind {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match bool::arbitrary(u)? {
+ true => Ok(Self::Main),
+ false => Ok(Self::Test)
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
#[cfg(feature = "std")]
Why this scored 19/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.