diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 7bb9cd11..f4ff753c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -39,4 +39,4 @@ jobs: override: true - name: Running test script env: ${{ matrix.env }} - run: ./contrib/test.sh + run: cd bitcoin-coin-selection && ../contrib/test.sh diff --git a/Cargo.toml b/Cargo.toml index 428aab76..dd464ace 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,5 @@ -[package] -authors = [ - "Yancy Ribbens ", +[workspace] +members = [ + "bitcoin-coin-selection", "fuzz", ] -edition = "2018" -homepage = "https://github.com/rust-bitcoin/rust-bitcoin-coin-selection/" -license = "CC0-1.0" -name = "bitcoin-coin-selection" -repository = "https://github.com/rust-bitcoin/rust-bitcoin-coin-selection/" -version = "0.5.0" -# documentation = "https://docs.rs/bitcoin-coin-selection/" -description = "Libary providing utility functions to efficiently select a set of UTXOs." -keywords = ["bitcoin", "coin-selection", "coin", "coinselection", "utxo"] -readme = "README.md" - -[dependencies] -bitcoin = "0.32.2" -rand = {version = "0.8.5", default-features = false, optional = true} - -[dev-dependencies] -criterion = "0.3" -bitcoin-coin-selection = {path = ".", features = ["rand"]} -rand = "0.8.5" - -[[bench]] -name = "coin_selection" -harness = false +resolver = "1" diff --git a/README.md b/README.md index 36580bc1..dbf6c58a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,14 @@ To run the benchmarks use: `cargo bench`. Note: criterion requires rustc version 1.65 to run the benchmarks. +## Fuzz + +To run fuzzer: + +Single Random Draw: `cargo hfuzz run single_random_draw_select_coins` +Branch and Bound: `cargo hfuzz run branch_and_bound_select_coins` +Select Coins (both SRD and BNB): `cargo hfuzz run select_coins` + ### performance comparison A basic performance comparison between this Rust BnB implementation and [Bitcoin Core](https://github.com/bitcoin/bitcoin/blob/4b1196a9855dcd188a24f393aa2fa21e2d61f061/src/wallet/coinselection.cpp#L76) using commodity hardware (My rather old laptop). diff --git a/bitcoin-coin-selection/Cargo.toml b/bitcoin-coin-selection/Cargo.toml new file mode 100644 index 00000000..043fd7d7 --- /dev/null +++ b/bitcoin-coin-selection/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = [ + "Yancy Ribbens ", +] +edition = "2018" +homepage = "https://github.com/rust-bitcoin/rust-bitcoin-coin-selection/" +license = "CC0-1.0" +name = "bitcoin-coin-selection" +repository = "https://github.com/rust-bitcoin/rust-bitcoin-coin-selection/" +version = "0.5.0" +# documentation = "https://docs.rs/bitcoin-coin-selection/" +description = "Libary providing utility functions to efficiently select a set of UTXOs." +keywords = ["bitcoin", "coin-selection", "coin", "coinselection", "utxo"] +readme = "README.md" + +[dependencies] +bitcoin = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "edcd2fb5d78be71a60709d18fb367fd56171ff26" } +rand = {version = "0.8.5", default-features = false, optional = true} + +[dev-dependencies] +criterion = "0.3" +bitcoin-coin-selection = {path = ".", features = ["rand"]} +rand = "0.8.5" + +[[bench]] +name = "coin_selection" +harness = false diff --git a/benches/coin_selection.rs b/bitcoin-coin-selection/benches/coin_selection.rs similarity index 100% rename from benches/coin_selection.rs rename to bitcoin-coin-selection/benches/coin_selection.rs diff --git a/src/branch_and_bound.rs b/bitcoin-coin-selection/src/branch_and_bound.rs similarity index 100% rename from src/branch_and_bound.rs rename to bitcoin-coin-selection/src/branch_and_bound.rs diff --git a/src/lib.rs b/bitcoin-coin-selection/src/lib.rs similarity index 100% rename from src/lib.rs rename to bitcoin-coin-selection/src/lib.rs diff --git a/src/single_random_draw.rs b/bitcoin-coin-selection/src/single_random_draw.rs similarity index 100% rename from src/single_random_draw.rs rename to bitcoin-coin-selection/src/single_random_draw.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 00000000..101d4d93 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "fuzz-coin-selection" +version = "0.1.0" +edition = "2021" + +[dependencies] +honggfuzz = "0.5.56" +bitcoin-coin-selection = { path= "../bitcoin-coin-selection", features=["rand"] } +rand = "0.8.5" +bitcoin = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "edcd2fb5d78be71a60709d18fb367fd56171ff26" } +arbitrary = { version = "1", features = ["derive"] } + +[[bin]] +name = "single_random_draw_select_coins" +path = "fuzz_targets/single_random_draw_select_coins.rs" + +[[bin]] +name = "branch_and_bound_select_coins" +path = "fuzz_targets/branch_and_bound_select_coins.rs" + +[[bin]] +name = "select_coins" +path = "fuzz_targets/select_coins.rs" diff --git a/fuzz/fuzz_targets/branch_and_bound_select_coins.rs b/fuzz/fuzz_targets/branch_and_bound_select_coins.rs new file mode 100644 index 00000000..297ae389 --- /dev/null +++ b/fuzz/fuzz_targets/branch_and_bound_select_coins.rs @@ -0,0 +1,44 @@ +use bitcoin_coin_selection::WeightedUtxo; + +use bitcoin_coin_selection::select_coins_bnb; +use honggfuzz::fuzz; +use arbitrary::Arbitrary; + +use bitcoin::TxOut; +use bitcoin::Amount; +use bitcoin::FeeRate; +use bitcoin::Weight; + +#[derive(Arbitrary, Debug)] +pub struct Utxo { + output: TxOut, + satisfaction_weight: Weight +} + +impl WeightedUtxo for Utxo { + fn satisfaction_weight(&self) -> Weight { + self.satisfaction_weight + } + + fn value(&self) -> Amount { + self.output.value + } +} + +#[derive(Arbitrary, Debug)] +pub struct Params { + target: Amount, + cost_of_change: Amount, + fee_rate: FeeRate, + long_term_fee_rate: FeeRate, + weighted_utxos: Vec, +} + +fn main() { + loop { + fuzz!(|params: Params| { + let Params { target: t, cost_of_change: c, fee_rate: f, long_term_fee_rate: ltf, weighted_utxos: wu } = params; + select_coins_bnb(t, c, f, ltf, &wu); + }); + } +} diff --git a/fuzz/fuzz_targets/select_coins.rs b/fuzz/fuzz_targets/select_coins.rs new file mode 100644 index 00000000..dc5c8515 --- /dev/null +++ b/fuzz/fuzz_targets/select_coins.rs @@ -0,0 +1,44 @@ +use bitcoin_coin_selection::WeightedUtxo; + +use bitcoin_coin_selection::select_coins; +use honggfuzz::fuzz; +use arbitrary::Arbitrary; + +use bitcoin::TxOut; +use bitcoin::Amount; +use bitcoin::FeeRate; +use bitcoin::Weight; + +#[derive(Arbitrary, Debug)] +pub struct Utxo { + output: TxOut, + satisfaction_weight: Weight +} + +impl WeightedUtxo for Utxo { + fn satisfaction_weight(&self) -> Weight { + self.satisfaction_weight + } + + fn value(&self) -> Amount { + self.output.value + } +} + +#[derive(Arbitrary, Debug)] +pub struct Params { + target: Amount, + cost_of_change: Amount, + fee_rate: FeeRate, + long_term_fee_rate: FeeRate, + weighted_utxos: Vec, +} + +fn main() { + loop { + fuzz!(|params: Params| { + let Params { target: t, cost_of_change: c, fee_rate: f, long_term_fee_rate: ltf, weighted_utxos: wu } = params; + select_coins(t, c, f, ltf, &wu); + }); + } +} diff --git a/fuzz/fuzz_targets/single_random_draw_select_coins.rs b/fuzz/fuzz_targets/single_random_draw_select_coins.rs new file mode 100644 index 00000000..bdad2097 --- /dev/null +++ b/fuzz/fuzz_targets/single_random_draw_select_coins.rs @@ -0,0 +1,44 @@ +use bitcoin_coin_selection::WeightedUtxo; + +use bitcoin_coin_selection::select_coins_srd; +use honggfuzz::fuzz; +use arbitrary::Arbitrary; + +use rand::thread_rng; + +use bitcoin::TxOut; +use bitcoin::FeeRate; +use bitcoin::Amount; +use bitcoin::Weight; + +#[derive(Arbitrary, Debug)] +pub struct Utxo { + output: TxOut, + satisfaction_weight: Weight +} + +impl WeightedUtxo for Utxo { + fn satisfaction_weight(&self) -> Weight { + self.satisfaction_weight + } + + fn value(&self) -> Amount { + self.output.value + } +} + +#[derive(Arbitrary, Debug)] +pub struct Params { + target: Amount, + fee_rate: FeeRate, + weighted_utxos: Vec, +} + +fn main() { + loop { + fuzz!(|params: Params| { + let Params { target: t, fee_rate: f, weighted_utxos: wu } = params; + select_coins_srd(t, f, &wu, &mut thread_rng()); + }); + } +}