Merge rust-bitcoin/rust-bitcoin#3015: Add Arbitrary
3e034d5ede
Add Arbitrary dependency (yancy) Pull request description: Adds an example draft showing what is needed to use Arbitrary for coin selection. Shot out to how nice Arbitrary is for fuzzing a target by taking unstructured randomness and creating structured rust-bitcoin types for fuzzing. Is there a way we could add this to rust-bitcoin for structuring the fuzz data needed? This is then the example to fuzz test a SRD algo (after applying this PR to rust-bitcoin) using rust-bitcoin types :) ``` #![no_main] use arbitrary::Arbitrary; use bitcoin::{Amount, FeeRate}; use bitcoin_coin_selection::{select_coins_srd, WeightedUtxo}; use libfuzzer_sys::fuzz_target; use rand::thread_rng; #[derive(Arbitrary, Debug)] pub struct Params { target: Amount, fee_rate: FeeRate, weighted_utxos: Vec<WeightedUtxo>, } fuzz_target!(|params: Params| { let Params { target: t, fee_rate: f, weighted_utxos: wu } = params; select_coins_srd(t, f, &wu, &mut thread_rng()); }); ``` ACKs for top commit: tcharding: ACK3e034d5ede
Kixunil: ACK3e034d5ede
apoelstra: ACK3e034d5ede
successfully ran local tests Tree-SHA512: accd565815de3b37730d2ff12a24fcfc84e52ad357e5c940b1500a1e0bb17f4ff5fd6e52d31e8e96bb5290ee4fa050cfd2a9bbd6bbae13fc378f43093b64177f
This commit is contained in:
commit
51af258eaa
|
@ -8,6 +8,12 @@ version = "1.0.57"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
|
@ -49,6 +55,7 @@ dependencies = [
|
||||||
name = "bitcoin"
|
name = "bitcoin"
|
||||||
version = "0.32.0-rc1"
|
version = "0.32.0-rc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
"base58ck",
|
"base58ck",
|
||||||
"base64",
|
"base64",
|
||||||
"bech32",
|
"bech32",
|
||||||
|
@ -115,6 +122,7 @@ dependencies = [
|
||||||
name = "bitcoin-units"
|
name = "bitcoin-units"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
"bitcoin-internals",
|
"bitcoin-internals",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -8,6 +8,12 @@ version = "1.0.71"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
|
@ -48,6 +54,7 @@ dependencies = [
|
||||||
name = "bitcoin"
|
name = "bitcoin"
|
||||||
version = "0.32.0-rc1"
|
version = "0.32.0-rc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
"base58ck",
|
"base58ck",
|
||||||
"base64",
|
"base64",
|
||||||
"bech32",
|
"bech32",
|
||||||
|
@ -114,6 +121,7 @@ dependencies = [
|
||||||
name = "bitcoin-units"
|
name = "bitcoin-units"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
"bitcoin-internals",
|
"bitcoin-internals",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -22,6 +22,7 @@ rand = ["secp256k1/rand"]
|
||||||
serde = ["dep:serde", "hashes/serde", "internals/serde", "primitives/serde", "secp256k1/serde", "units/serde"]
|
serde = ["dep:serde", "hashes/serde", "internals/serde", "primitives/serde", "secp256k1/serde", "units/serde"]
|
||||||
secp-lowmemory = ["secp256k1/lowmemory"]
|
secp-lowmemory = ["secp256k1/lowmemory"]
|
||||||
secp-recovery = ["secp256k1/recovery"]
|
secp-recovery = ["secp256k1/recovery"]
|
||||||
|
arbitrary = ["dep:arbitrary", "units/arbitrary"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base58 = { package = "base58ck", version = "0.1.0", default-features = false, features = ["alloc"] }
|
base58 = { package = "base58ck", version = "0.1.0", default-features = false, features = ["alloc"] }
|
||||||
|
@ -40,6 +41,7 @@ ordered = { version = "0.2.0", optional = true }
|
||||||
bitcoinconsensus = { version = "0.106.0", default-features = false, optional = true }
|
bitcoinconsensus = { version = "0.106.0", default-features = false, optional = true }
|
||||||
|
|
||||||
serde = { version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true }
|
serde = { version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true }
|
||||||
|
arbitrary = { version = "1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
internals = { package = "bitcoin-internals", version = "0.3.0", features = ["test-serde"] }
|
internals = { package = "bitcoin-internals", version = "0.3.0", features = ["test-serde"] }
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
|
|
||||||
# Test all these features with "std" enabled.
|
# Test all these features with "std" enabled.
|
||||||
FEATURES_WITH_STD="rand-std serde secp-recovery bitcoinconsensus base64 ordered"
|
FEATURES_WITH_STD="rand-std serde secp-recovery bitcoinconsensus base64 ordered arbitrary"
|
||||||
|
|
||||||
# Test all these features without "std" or "alloc" enabled.
|
# Test all these features without "std" or "alloc" enabled.
|
||||||
FEATURES_WITHOUT_STD="rand serde secp-recovery bitcoinconsensus base64 ordered"
|
FEATURES_WITHOUT_STD="rand serde secp-recovery bitcoinconsensus base64 ordered arbitrary"
|
||||||
|
|
||||||
# Run these examples.
|
# Run these examples.
|
||||||
EXAMPLES="ecdsa-psbt:std,bitcoinconsensus sign-tx-segwit-v0:rand-std sign-tx-taproot:rand-std taproot-psbt:bitcoinconsensus,rand-std sighash:std"
|
EXAMPLES="ecdsa-psbt:std,bitcoinconsensus sign-tx-segwit-v0:rand-std sign-tx-taproot:rand-std taproot-psbt:bitcoinconsensus,rand-std sighash:std"
|
||||||
|
|
|
@ -11,6 +11,9 @@ use crate::opcodes::all::*;
|
||||||
use crate::opcodes::{self, Opcode};
|
use crate::opcodes::{self, Opcode};
|
||||||
use crate::prelude::{Box, Vec};
|
use crate::prelude::{Box, Vec};
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
|
||||||
/// An owned, growable script.
|
/// An owned, growable script.
|
||||||
///
|
///
|
||||||
/// `ScriptBuf` is the most common script type that has the ownership over the contents of the
|
/// `ScriptBuf` is the most common script type that has the ownership over the contents of the
|
||||||
|
@ -155,6 +158,14 @@ crate::internal_macros::define_extension_trait! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
impl<'a> Arbitrary<'a> for ScriptBuf {
|
||||||
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
|
let v = Vec::<u8>::arbitrary(u)?;
|
||||||
|
Ok(ScriptBuf(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
crate::internal_macros::define_extension_trait! {
|
crate::internal_macros::define_extension_trait! {
|
||||||
pub(crate) trait ScriptBufExtPriv impl for ScriptBuf {
|
pub(crate) trait ScriptBufExtPriv impl for ScriptBuf {
|
||||||
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.
|
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.
|
||||||
|
|
|
@ -29,6 +29,9 @@ use crate::sighash::{EcdsaSighashType, TapSighashType};
|
||||||
use crate::witness::Witness;
|
use crate::witness::Witness;
|
||||||
use crate::{Amount, FeeRate, SignedAmount, VarInt};
|
use crate::{Amount, FeeRate, SignedAmount, VarInt};
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
|
||||||
hashes::hash_newtype! {
|
hashes::hash_newtype! {
|
||||||
/// A bitcoin transaction hash/transaction ID.
|
/// A bitcoin transaction hash/transaction ID.
|
||||||
///
|
///
|
||||||
|
@ -399,6 +402,16 @@ impl TxOut {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
impl<'a> Arbitrary<'a> for TxOut {
|
||||||
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
|
Ok(TxOut {
|
||||||
|
value: Amount::arbitrary(u)?,
|
||||||
|
script_pubkey: ScriptBuf::arbitrary(u)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the total number of bytes that this script pubkey would contribute to a transaction.
|
/// Returns the total number of bytes that this script pubkey would contribute to a transaction.
|
||||||
fn size_from_script_pubkey(script_pubkey: &Script) -> usize {
|
fn size_from_script_pubkey(script_pubkey: &Script) -> usize {
|
||||||
let len = script_pubkey.len();
|
let len = script_pubkey.len();
|
||||||
|
|
|
@ -21,6 +21,7 @@ alloc = ["internals/alloc"]
|
||||||
internals = { package = "bitcoin-internals", version = "0.3.0" }
|
internals = { package = "bitcoin-internals", version = "0.3.0" }
|
||||||
|
|
||||||
serde = { version = "1.0.103", default-features = false, features = ["derive"], optional = true }
|
serde = { version = "1.0.103", default-features = false, features = ["derive"], optional = true }
|
||||||
|
arbitrary = { version = "1", optional =true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
internals = { package = "bitcoin-internals", version = "0.3.0", features = ["test-serde"] }
|
internals = { package = "bitcoin-internals", version = "0.3.0", features = ["test-serde"] }
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
|
|
||||||
# Test all these features with "std" enabled.
|
# Test all these features with "std" enabled.
|
||||||
FEATURES_WITH_STD="serde"
|
FEATURES_WITH_STD="serde arbitrary"
|
||||||
|
|
||||||
# Test all these features without "std" enabled.
|
# Test all these features without "std" enabled.
|
||||||
FEATURES_WITHOUT_STD="alloc serde"
|
FEATURES_WITHOUT_STD="alloc serde arbitrary"
|
||||||
|
|
||||||
# Run these examples.
|
# Run these examples.
|
||||||
EXAMPLES=""
|
EXAMPLES=""
|
||||||
|
|
|
@ -16,6 +16,9 @@ use ::serde::{Deserialize, Serialize};
|
||||||
use internals::error::InputString;
|
use internals::error::InputString;
|
||||||
use internals::write_err;
|
use internals::write_err;
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
|
||||||
/// A set of denominations in which amounts can be expressed.
|
/// A set of denominations in which amounts can be expressed.
|
||||||
///
|
///
|
||||||
/// # Accepted Denominations
|
/// # Accepted Denominations
|
||||||
|
@ -1071,6 +1074,14 @@ impl Amount {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
impl<'a> Arbitrary<'a> for Amount {
|
||||||
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
|
let a = u64::arbitrary(u)?;
|
||||||
|
Ok(Amount(a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl default::Default for Amount {
|
impl default::Default for Amount {
|
||||||
fn default() -> Self { Amount::ZERO }
|
fn default() -> Self { Amount::ZERO }
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::amount::Amount;
|
use crate::amount::Amount;
|
||||||
use crate::weight::Weight;
|
use crate::weight::Weight;
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
|
||||||
/// Represents fee rate.
|
/// Represents fee rate.
|
||||||
///
|
///
|
||||||
/// This is an integer newtype representing fee rate in `sat/kwu`. It provides protection against mixing
|
/// This is an integer newtype representing fee rate in `sat/kwu`. It provides protection against mixing
|
||||||
|
@ -107,6 +110,14 @@ impl FeeRate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
impl<'a> Arbitrary<'a> for FeeRate {
|
||||||
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
|
let f = u64::arbitrary(u)?;
|
||||||
|
Ok(FeeRate(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Alternative will display the unit.
|
/// Alternative will display the unit.
|
||||||
impl fmt::Display for FeeRate {
|
impl fmt::Display for FeeRate {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
|
Loading…
Reference in New Issue