Implement Arbitrary for Transaction

This commit is contained in:
Shing Him Ng 2024-09-15 15:07:56 -05:00
parent fe2985baf7
commit 5b4e81b379
10 changed files with 92 additions and 3 deletions

View File

@ -110,6 +110,7 @@ dependencies = [
name = "bitcoin-primitives"
version = "0.100.0"
dependencies = [
"arbitrary",
"bitcoin-internals",
"bitcoin-io",
"bitcoin-units",

View File

@ -109,6 +109,7 @@ dependencies = [
name = "bitcoin-primitives"
version = "0.100.0"
dependencies = [
"arbitrary",
"bitcoin-internals",
"bitcoin-io",
"bitcoin-units",

View File

@ -22,7 +22,7 @@ rand = ["secp256k1/rand"]
serde = ["dep:serde", "hashes/serde", "internals/serde", "primitives/serde", "secp256k1/serde", "units/serde"]
secp-lowmemory = ["secp256k1/lowmemory"]
secp-recovery = ["secp256k1/recovery"]
arbitrary = ["dep:arbitrary", "units/arbitrary"]
arbitrary = ["dep:arbitrary", "units/arbitrary", "primitives/arbitrary"]
[dependencies]
base58 = { package = "base58ck", version = "0.1.0", default-features = false, features = ["alloc"] }

View File

@ -1409,6 +1409,42 @@ impl InputWeightPrediction {
}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for OutPoint {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(OutPoint{
txid: Txid::arbitrary(u)?,
vout: u32::arbitrary(u)?
})
}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for TxIn {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(TxIn{
previous_output: OutPoint::arbitrary(u)?,
script_sig: ScriptBuf::arbitrary(u)?,
sequence: Sequence::arbitrary(u)?,
witness: Witness::arbitrary(u)?
})
}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Transaction {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
use primitives::absolute::LockTime;
Ok(Transaction {
version: Version::arbitrary(u)?,
lock_time: LockTime::arbitrary(u)?,
input: Vec::<TxIn>::arbitrary(u)?,
output: Vec::<TxOut>::arbitrary(u)?
})
}
}
#[cfg(test)]
mod tests {
use hex::{test_hex_unwrap as hex, FromHex};

View File

@ -18,6 +18,8 @@ use crate::prelude::Vec;
use crate::script::ScriptExt as _;
use crate::taproot::{self, TAPROOT_ANNEX_PREFIX};
use crate::{Script, VarInt};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
/// The Witness is the data used to unlock bitcoin since the [segwit upgrade].
///
@ -618,6 +620,14 @@ impl Default for Witness {
fn default() -> Self { Self::new() }
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Witness {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let arbitrary_bytes = Vec::<Vec::<u8>>::arbitrary(u)?;
Ok(Witness::from_slice(&arbitrary_bytes))
}
}
#[cfg(test)]
mod test {
use hex::test_hex_unwrap as hex;

View File

@ -19,8 +19,10 @@ default = ["std"]
std = ["alloc", "hashes/std", "internals/std", "io/std", "units/std"]
alloc = ["hashes/alloc", "internals/alloc", "io/alloc", "units/alloc"]
serde = ["dep:serde", "hashes/serde", "internals/serde", "units/serde", "alloc"]
arbitrary = ["dep:arbitrary", "units/arbitrary"]
[dependencies]
arbitrary = { version = "1", optional = true }
hashes = { package = "bitcoin_hashes", version = "0.14.0", default-features = false, features = ["bitcoin-io"] }
internals = { package = "bitcoin-internals", version = "0.3.0" }
io = { package = "bitcoin-io", version = "0.1.1", default-features = false }

View File

@ -5,10 +5,10 @@
# shellcheck disable=SC2034
# Test these features with "std" enabled.
FEATURES_WITH_STD="ordered serde"
FEATURES_WITH_STD="ordered serde arbitrary"
# Test these features without "std" enabled.
FEATURES_WITHOUT_STD="alloc ordered serde"
FEATURES_WITHOUT_STD="alloc ordered serde arbitrary"
# Run these examples.
EXAMPLES=""

View File

@ -8,6 +8,8 @@
use core::cmp::Ordering;
use core::fmt;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
#[cfg(all(test, mutate))]
use mutagen::mutate;
use units::parse::{self, PrefixedHexError, UnprefixedHexError};
@ -395,6 +397,14 @@ impl ordered::ArbitraryOrd for LockTime {
}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for LockTime {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let l = u32::arbitrary(u)?;
Ok(LockTime::from_consensus(l))
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -24,6 +24,8 @@ use serde::{Deserialize, Serialize};
use units::locktime::relative::TimeOverflowError;
#[cfg(feature = "alloc")]
use units::parse::{self, PrefixedHexError, UnprefixedHexError};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
#[cfg(feature = "alloc")]
use crate::locktime::relative;
@ -238,3 +240,11 @@ impl fmt::Debug for Sequence {
#[cfg(feature = "alloc")]
units::impl_parse_str_from_int_infallible!(Sequence, u32, from_consensus);
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Sequence {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let s = u32::arbitrary(u)?;
Ok(Sequence(s))
}
}

View File

@ -13,6 +13,8 @@
use core::fmt;
use hashes::sha256d;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
hashes::hash_newtype! {
/// A bitcoin transaction hash/transaction ID.
@ -70,3 +72,20 @@ impl Version {
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Version {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let v = i32::arbitrary(u)?;
Ok(Version(v))
}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Txid {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let arbitrary_bytes = u.arbitrary()?;
let t = sha256d::Hash::from_byte_array(arbitrary_bytes);
Ok(Txid(t))
}
}