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:
    ACK 3e034d5ede
  Kixunil:
    ACK 3e034d5ede
  apoelstra:
    ACK 3e034d5ede successfully ran local tests

Tree-SHA512: accd565815de3b37730d2ff12a24fcfc84e52ad357e5c940b1500a1e0bb17f4ff5fd6e52d31e8e96bb5290ee4fa050cfd2a9bbd6bbae13fc378f43093b64177f
This commit is contained in:
merge-script 2024-08-25 13:16:49 +00:00
commit 51af258eaa
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
10 changed files with 69 additions and 4 deletions

View File

@ -8,6 +8,12 @@ version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
[[package]]
name = "arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
[[package]]
name = "arrayvec"
version = "0.7.4"
@ -49,6 +55,7 @@ dependencies = [
name = "bitcoin"
version = "0.32.0-rc1"
dependencies = [
"arbitrary",
"base58ck",
"base64",
"bech32",
@ -115,6 +122,7 @@ dependencies = [
name = "bitcoin-units"
version = "0.1.1"
dependencies = [
"arbitrary",
"bitcoin-internals",
"serde",
"serde_json",

View File

@ -8,6 +8,12 @@ version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
[[package]]
name = "arrayvec"
version = "0.7.4"
@ -48,6 +54,7 @@ dependencies = [
name = "bitcoin"
version = "0.32.0-rc1"
dependencies = [
"arbitrary",
"base58ck",
"base64",
"bech32",
@ -114,6 +121,7 @@ dependencies = [
name = "bitcoin-units"
version = "0.1.1"
dependencies = [
"arbitrary",
"bitcoin-internals",
"serde",
"serde_json",

View File

@ -22,6 +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"]
[dependencies]
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 }
serde = { version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true }
arbitrary = { version = "1", optional = true }
[dev-dependencies]
internals = { package = "bitcoin-internals", version = "0.3.0", features = ["test-serde"] }

View File

@ -5,10 +5,10 @@
# shellcheck disable=SC2034
# 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.
FEATURES_WITHOUT_STD="rand serde secp-recovery bitcoinconsensus base64 ordered"
FEATURES_WITHOUT_STD="rand serde secp-recovery bitcoinconsensus base64 ordered arbitrary"
# 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"

View File

@ -11,6 +11,9 @@ use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
use crate::prelude::{Box, Vec};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
/// An owned, growable script.
///
/// `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! {
pub(crate) trait ScriptBufExtPriv impl for ScriptBuf {
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.

View File

@ -29,6 +29,9 @@ use crate::sighash::{EcdsaSighashType, TapSighashType};
use crate::witness::Witness;
use crate::{Amount, FeeRate, SignedAmount, VarInt};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
hashes::hash_newtype! {
/// 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.
fn size_from_script_pubkey(script_pubkey: &Script) -> usize {
let len = script_pubkey.len();

View File

@ -21,6 +21,7 @@ alloc = ["internals/alloc"]
internals = { package = "bitcoin-internals", version = "0.3.0" }
serde = { version = "1.0.103", default-features = false, features = ["derive"], optional = true }
arbitrary = { version = "1", optional =true }
[dev-dependencies]
internals = { package = "bitcoin-internals", version = "0.3.0", features = ["test-serde"] }

View File

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

View File

@ -16,6 +16,9 @@ use ::serde::{Deserialize, Serialize};
use internals::error::InputString;
use internals::write_err;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
/// A set of denominations in which amounts can be expressed.
///
/// # 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 {
fn default() -> Self { Amount::ZERO }
}

View File

@ -11,6 +11,9 @@ use serde::{Deserialize, Serialize};
use crate::amount::Amount;
use crate::weight::Weight;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
/// Represents fee rate.
///
/// 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.
impl fmt::Display for FeeRate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {