diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f4ff753c..7bb9cd11 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: cd bitcoin-coin-selection && ../contrib/test.sh + run: ./contrib/test.sh diff --git a/Cargo.toml b/Cargo.toml index dd464ace..ef422167 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,36 @@ -[workspace] -members = [ - "bitcoin-coin-selection", "fuzz", +[package] +authors = [ + "Yancy Ribbens ", ] -resolver = "1" +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/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d"} +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 + +[patch.crates-io] +bitcoin_hashes = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +base58ck = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-internals = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-io = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-primitives = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-addresses = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-units = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } diff --git a/README.md b/README.md index dbf6c58a..ee285264 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,7 @@ 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` +Fuzz with `cargo fuzz run select_coins_srd`, `cargo fuzz run select_coins_bnb` or `cargo fuzz run select_coins`. ### performance comparison diff --git a/bitcoin-coin-selection/benches/coin_selection.rs b/benches/coin_selection.rs similarity index 100% rename from bitcoin-coin-selection/benches/coin_selection.rs rename to benches/coin_selection.rs diff --git a/bitcoin-coin-selection/Cargo.toml b/bitcoin-coin-selection/Cargo.toml deleted file mode 100644 index 043fd7d7..00000000 --- a/bitcoin-coin-selection/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[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/fuzz/Cargo.toml b/fuzz/Cargo.toml index 101d4d93..5f4b6193 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,23 +1,48 @@ [package] -name = "fuzz-coin-selection" -version = "0.1.0" -edition = "2021" +name = "bitcoin-coin-selection-fuzz" +version = "0.0.0" +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true [dependencies] -honggfuzz = "0.5.56" -bitcoin-coin-selection = { path= "../bitcoin-coin-selection", features=["rand"] } +libfuzzer-sys = "0.4" rand = "0.8.5" -bitcoin = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "edcd2fb5d78be71a60709d18fb367fd56171ff26" } +bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d", features = ["arbitrary"] } arbitrary = { version = "1", features = ["derive"] } +[dependencies.bitcoin-coin-selection] +path = ".." +features = ["rand"] + [[bin]] -name = "single_random_draw_select_coins" -path = "fuzz_targets/single_random_draw_select_coins.rs" +name = "select_coins_srd" +path = "fuzz_targets/select_coins_srd.rs" +test = false +doc = false +bench = false [[bin]] -name = "branch_and_bound_select_coins" -path = "fuzz_targets/branch_and_bound_select_coins.rs" +name = "select_coins_bnb" +path = "fuzz_targets/select_coins_bnb.rs" +test = false +doc = false +bench = false [[bin]] name = "select_coins" path = "fuzz_targets/select_coins.rs" +test = false +doc = false +bench = false + +[patch.crates-io] +bitcoin_hashes = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +base58ck = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-internals = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-io = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-primitives = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-addresses = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } +bitcoin-units = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "cfb53c78667dafe8aea488f104f65a2a29a2f94d" } diff --git a/fuzz/fuzz_targets/branch_and_bound_select_coins.rs b/fuzz/fuzz_targets/branch_and_bound_select_coins.rs index 297ae389..5cb80455 100644 --- a/fuzz/fuzz_targets/branch_and_bound_select_coins.rs +++ b/fuzz/fuzz_targets/branch_and_bound_select_coins.rs @@ -1,18 +1,18 @@ use bitcoin_coin_selection::WeightedUtxo; +use arbitrary::Arbitrary; use bitcoin_coin_selection::select_coins_bnb; use honggfuzz::fuzz; -use arbitrary::Arbitrary; -use bitcoin::TxOut; use bitcoin::Amount; use bitcoin::FeeRate; +use bitcoin::TxOut; use bitcoin::Weight; #[derive(Arbitrary, Debug)] pub struct Utxo { output: TxOut, - satisfaction_weight: Weight + satisfaction_weight: Weight, } impl WeightedUtxo for Utxo { @@ -37,7 +37,13 @@ pub struct Params { 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; + 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 index dc5c8515..020e4cd2 100644 --- a/fuzz/fuzz_targets/select_coins.rs +++ b/fuzz/fuzz_targets/select_coins.rs @@ -1,18 +1,14 @@ -use bitcoin_coin_selection::WeightedUtxo; +#![no_main] -use bitcoin_coin_selection::select_coins; -use honggfuzz::fuzz; -use arbitrary::Arbitrary; - -use bitcoin::TxOut; -use bitcoin::Amount; -use bitcoin::FeeRate; -use bitcoin::Weight; +use arbitrary::{Arbitrary, Unstructured}; +use bitcoin::{TxOut, FeeRate, Amount, Weight}; +use bitcoin_coin_selection::{select_coins, WeightedUtxo}; +use libfuzzer_sys::fuzz_target; #[derive(Arbitrary, Debug)] pub struct Utxo { output: TxOut, - satisfaction_weight: Weight + satisfaction_weight: Weight, } impl WeightedUtxo for Utxo { @@ -25,20 +21,14 @@ impl WeightedUtxo for Utxo { } } -#[derive(Arbitrary, Debug)] -pub struct Params { - target: Amount, - cost_of_change: Amount, - fee_rate: FeeRate, - long_term_fee_rate: FeeRate, - weighted_utxos: Vec, -} +fuzz_target!(|data: &[u8]| { + let mut u = Unstructured::new(&data); -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); - }); - } -} + let target = Amount::arbitrary(&mut u).unwrap(); + let cost_of_change = Amount::arbitrary(&mut u).unwrap(); + let fee_rate = FeeRate::arbitrary(&mut u).unwrap(); + let lt_fee_rate = FeeRate::arbitrary(&mut u).unwrap(); + let wu = Vec::::arbitrary(&mut u).unwrap(); + + select_coins(target, cost_of_change, fee_rate, lt_fee_rate, &wu); +}); diff --git a/fuzz/fuzz_targets/select_coins_bnb.rs b/fuzz/fuzz_targets/select_coins_bnb.rs new file mode 100644 index 00000000..1e160888 --- /dev/null +++ b/fuzz/fuzz_targets/select_coins_bnb.rs @@ -0,0 +1,34 @@ +#![no_main] + +use arbitrary::{Arbitrary, Unstructured}; +use bitcoin::{TxOut, FeeRate, Amount, Weight}; +use bitcoin_coin_selection::{select_coins_bnb, WeightedUtxo}; +use libfuzzer_sys::fuzz_target; + +#[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 + } +} + +fuzz_target!(|data: &[u8]| { + let mut u = Unstructured::new(&data); + + let target = Amount::arbitrary(&mut u).unwrap(); + let cost_of_change = Amount::arbitrary(&mut u).unwrap(); + let fee_rate = FeeRate::arbitrary(&mut u).unwrap(); + let lt_fee_rate = FeeRate::arbitrary(&mut u).unwrap(); + let wu = Vec::::arbitrary(&mut u).unwrap(); + + select_coins_bnb(target, cost_of_change, fee_rate, lt_fee_rate, &wu); +}); diff --git a/fuzz/fuzz_targets/select_coins_srd.rs b/fuzz/fuzz_targets/select_coins_srd.rs new file mode 100644 index 00000000..694d692f --- /dev/null +++ b/fuzz/fuzz_targets/select_coins_srd.rs @@ -0,0 +1,33 @@ +#![no_main] + +use arbitrary::{Arbitrary, Unstructured}; +use bitcoin::{TxOut, FeeRate, Amount, Weight}; +use bitcoin_coin_selection::{select_coins_srd, WeightedUtxo}; +use libfuzzer_sys::fuzz_target; +use rand::thread_rng; + +#[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 + } +} + +fuzz_target!(|data: &[u8]| { + let mut u = Unstructured::new(&data); + + let target = Amount::arbitrary(&mut u).unwrap(); + let fee_rate = FeeRate::arbitrary(&mut u).unwrap(); + let wu = Vec::::arbitrary(&mut u).unwrap(); + + select_coins_srd(target, fee_rate, &wu, &mut thread_rng()); +}); diff --git a/fuzz/fuzz_targets/single_random_draw_select_coins.rs b/fuzz/fuzz_targets/single_random_draw_select_coins.rs index bdad2097..ed24b508 100644 --- a/fuzz/fuzz_targets/single_random_draw_select_coins.rs +++ b/fuzz/fuzz_targets/single_random_draw_select_coins.rs @@ -1,20 +1,20 @@ use bitcoin_coin_selection::WeightedUtxo; +use arbitrary::Arbitrary; 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::FeeRate; +use bitcoin::TxOut; use bitcoin::Weight; #[derive(Arbitrary, Debug)] pub struct Utxo { output: TxOut, - satisfaction_weight: Weight + satisfaction_weight: Weight, } impl WeightedUtxo for Utxo { diff --git a/bitcoin-coin-selection/src/branch_and_bound.rs b/src/branch_and_bound.rs similarity index 96% rename from bitcoin-coin-selection/src/branch_and_bound.rs rename to src/branch_and_bound.rs index 1546c15b..39bbea87 100644 --- a/bitcoin-coin-selection/src/branch_and_bound.rs +++ b/src/branch_and_bound.rs @@ -370,52 +370,43 @@ mod tests { } #[test] - fn select_coins_bnb_one() { assert_coin_select("1 cBTC", &["0.01000000 BTC"]); } + fn select_coins_bnb_one() { assert_coin_select("1 cBTC", &["0.01 BTC"]); } #[test] - fn select_coins_bnb_two() { assert_coin_select("2 cBTC", &["0.02000000 BTC"]); } + fn select_coins_bnb_two() { assert_coin_select("2 cBTC", &["0.02 BTC"]); } #[test] - fn select_coins_bnb_three() { - assert_coin_select("3 cBTC", &["0.02000000 BTC", "0.01000000 BTC"]); - } + fn select_coins_bnb_three() { assert_coin_select("3 cBTC", &["0.02 BTC", "0.01 BTC"]); } #[test] - fn select_coins_bnb_four() { - assert_coin_select("4 cBTC", &["0.03000000 BTC", "0.01000000 BTC"]); - } + fn select_coins_bnb_four() { assert_coin_select("4 cBTC", &["0.03 BTC", "0.01 BTC"]); } #[test] - fn select_coins_bnb_five() { - assert_coin_select("5 cBTC", &["0.03000000 BTC", "0.02000000 BTC"]); - } + fn select_coins_bnb_five() { assert_coin_select("5 cBTC", &["0.03 BTC", "0.02 BTC"]); } #[test] fn select_coins_bnb_six() { - assert_coin_select("6 cBTC", &["0.03000000 BTC", "0.02000000 BTC", "0.01000000 BTC"]); + assert_coin_select("6 cBTC", &["0.03 BTC", "0.02 BTC", "0.01 BTC"]); } #[test] fn select_coins_bnb_seven() { - assert_coin_select("7 cBTC", &["0.04000000 BTC", "0.02000000 BTC", "0.01000000 BTC"]); + assert_coin_select("7 cBTC", &["0.04 BTC", "0.02 BTC", "0.01 BTC"]); } #[test] fn select_coins_bnb_eight() { - assert_coin_select("8 cBTC", &["0.04000000 BTC", "0.03000000 BTC", "0.01000000 BTC"]); + assert_coin_select("8 cBTC", &["0.04 BTC", "0.03 BTC", "0.01 BTC"]); } #[test] fn select_coins_bnb_nine() { - assert_coin_select("9 cBTC", &["0.04000000 BTC", "0.03000000 BTC", "0.02000000 BTC"]); + assert_coin_select("9 cBTC", &["0.04 BTC", "0.03 BTC", "0.02 BTC"]); } #[test] fn select_coins_bnb_ten() { - assert_coin_select( - "10 cBTC", - &["0.04000000 BTC", "0.03000000 BTC", "0.02000000 BTC", "0.01000000 BTC"], - ); + assert_coin_select("10 cBTC", &["0.04 BTC", "0.03 BTC", "0.02 BTC", "0.01 BTC"]); } #[test] diff --git a/bitcoin-coin-selection/src/lib.rs b/src/lib.rs similarity index 97% rename from bitcoin-coin-selection/src/lib.rs rename to src/lib.rs index ec6b6226..e4bab090 100644 --- a/bitcoin-coin-selection/src/lib.rs +++ b/src/lib.rs @@ -94,7 +94,8 @@ pub fn select_coins( long_term_fee_rate: FeeRate, weighted_utxos: &[Utxo], ) -> Option> { - let bnb = select_coins_bnb(target, cost_of_change, fee_rate, long_term_fee_rate, weighted_utxos); + let bnb = + select_coins_bnb(target, cost_of_change, fee_rate, long_term_fee_rate, weighted_utxos); if bnb.is_some() { bnb diff --git a/bitcoin-coin-selection/src/single_random_draw.rs b/src/single_random_draw.rs similarity index 99% rename from bitcoin-coin-selection/src/single_random_draw.rs rename to src/single_random_draw.rs index 337744fe..6e2c297e 100644 --- a/bitcoin-coin-selection/src/single_random_draw.rs +++ b/src/single_random_draw.rs @@ -34,7 +34,7 @@ pub fn select_coins_srd<'a, R: rand::Rng + ?Sized, Utxo: WeightedUtxo>( rng: &mut R, ) -> Option> { if target > Amount::MAX_MONEY { - return None + return None; } let mut result: Vec<_> = weighted_utxos.iter().collect();