Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
yancyribbens committed Oct 6, 2024
1 parent 2e657df commit 6c0e553
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 121 deletions.
18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ keywords = ["bitcoin", "coin-selection", "coin", "coinselection", "utxo"]
readme = "README.md"

[dependencies]
bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "894f82e7cc9eb459a297d43e82734621e0824610" }
bitcoin = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "3e73807c70c85be2d684846cb7fb83633a263323" }
rand = {version = "0.8.5", default-features = false, optional = true}

[dev-dependencies]
bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "894f82e7cc9eb459a297d43e82734621e0824610", features = ["arbitrary"] }
bitcoin = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "3e73807c70c85be2d684846cb7fb83633a263323", features = ["arbitrary"] }
criterion = "0.3"
bitcoin-coin-selection = {path = ".", features = ["rand"]}
rand = "0.8.5"
Expand All @@ -31,10 +31,10 @@ name = "coin_selection"
harness = false

[patch.crates-io]
bitcoin_hashes = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "894f82e7cc9eb459a297d43e82734621e0824610" }
base58ck = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "894f82e7cc9eb459a297d43e82734621e0824610" }
bitcoin-internals = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "894f82e7cc9eb459a297d43e82734621e0824610" }
bitcoin-io = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "894f82e7cc9eb459a297d43e82734621e0824610" }
bitcoin-primitives = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "894f82e7cc9eb459a297d43e82734621e0824610" }
bitcoin-addresses = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "894f82e7cc9eb459a297d43e82734621e0824610" }
bitcoin-units = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "894f82e7cc9eb459a297d43e82734621e0824610" }
bitcoin_hashes = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "3e73807c70c85be2d684846cb7fb83633a263323" }
base58ck = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "3e73807c70c85be2d684846cb7fb83633a263323" }
bitcoin-internals = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "3e73807c70c85be2d684846cb7fb83633a263323" }
bitcoin-io = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "3e73807c70c85be2d684846cb7fb83633a263323" }
bitcoin-primitives = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "3e73807c70c85be2d684846cb7fb83633a263323" }
bitcoin-addresses = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "3e73807c70c85be2d684846cb7fb83633a263323" }
bitcoin-units = { git = "https://github.com/yancyribbens/rust-bitcoin.git", rev = "3e73807c70c85be2d684846cb7fb83633a263323" }
160 changes: 77 additions & 83 deletions src/branch_and_bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
//! Bitcoin Branch and Bound Coin Selection.
//!
//! This module introduces the Branch and Bound Coin Selection Algorithm.
//! This module introduces the Branch and Bound Coin-Selection Algorithm.

use bitcoin::amount::CheckedSum;
use bitcoin::{Amount, FeeRate, SignedAmount};
Expand Down Expand Up @@ -196,7 +196,9 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
w_utxos.sort_by_key(|u| u.0);
w_utxos.reverse();

println!("calc avail values");
let mut available_value = w_utxos.clone().into_iter().map(|(ev, _, _)| ev).checked_sum()?;
println!("done");

if available_value < target || target == Amount::ZERO {
return None;
Expand Down Expand Up @@ -336,9 +338,6 @@ mod tests {
use arbtest::arbtest;

const TX_IN_BASE_WEIGHT: u64 = 160;
const PROPTEST_POOL_SIZE: usize = 10;
const PROPTEST_MAX_SAT_AMOUNT: u64 = 100_000;
const PROPTEST_MIN_SAT_AMOUNT: u64 = 161; //tx_in base_weight + 1

#[derive(Debug)]
pub struct ParamsStr<'a> {
Expand All @@ -349,22 +348,6 @@ mod tests {
weighted_utxos: Vec<&'a str>,
}

//#[derive(Debug, Clone, PartialEq, Ord, Eq, PartialOrd, Arbitrary)]
//pub struct Utxo {
//output: TxOut,
//satisfaction_weight: Weight,
//}

//#[derive(Debug)]
//pub struct UtxoPool {
//utxos: Vec<Utxo>,
//}

//fn build_utxo(amt: Amount, satisfaction_weight: Weight) -> Utxo {
//let output = TxOut { value: amt, script_pubkey: ScriptBuf::new() };
//Utxo { output, satisfaction_weight }
//}

fn build_pool(fee: Amount) -> Vec<Utxo> {
let amts = [
Amount::from_str("1 cBTC").unwrap() + fee,
Expand Down Expand Up @@ -450,7 +433,9 @@ mod tests {
}

fn calculate_max_fee_rate(amount: Amount, weight: Weight) -> FeeRate {
(amount - Amount::from_sat(1)) / (weight + Weight::from_wu(TX_IN_BASE_WEIGHT))
let amt = Amount::from_sat(18446744073709551);
let weight = weight + Weight::from_wu(TX_IN_BASE_WEIGHT);
amt.checked_div_by_weight(weight).unwrap() - FeeRate::from_sat_per_kwu(1)
}

#[test]
Expand Down Expand Up @@ -771,50 +756,44 @@ mod tests {
let max_fee_rate = calculate_max_fee_rate(utxo.value(), utxo.satisfaction_weight());
let fee_rate = arb_fee_rate_in_range(u, 1..=max_fee_rate.to_sat_per_kwu());

let target = effective_value(
let target_effective_value = effective_value(
fee_rate,
utxo.satisfaction_weight(),
utxo.value(),
)
.unwrap()
.to_unsigned()
.unwrap();

let eff_value = effective_value(
fee_rate,
utxo.satisfaction_weight(),
utxo.value(),
)
.unwrap();

assert!(eff_value > SignedAmount::ZERO);

let coins: Vec<_> = select_coins_bnb(
target,
Amount::ZERO,
fee_rate,
fee_rate,
&pool.utxos,
).unwrap().collect();

let mut effective_value_sum = SignedAmount::ZERO;
for coin in &coins {
let eff_value = effective_value(
fee_rate,
coin.satisfaction_weight(),
coin.value(),
)
.unwrap();

effective_value_sum += eff_value;
);

// if the effective_value was greater than i64 max, then don't use as a target.
if let Some(positive_signed) = target_effective_value {
// If the effective_value was negative it can't be used as a target.
if let Ok(target) = positive_signed.to_unsigned() {
let coins = select_coins_bnb(
target,
Amount::ZERO,
fee_rate,
fee_rate,
&pool.utxos,
);

// If results are some then check the results
// else verify the error.
if let Some(c) = coins {
let result: SignedAmount = c.map(|u| {
effective_value(
fee_rate,
u.satisfaction_weight(),
u.value()
).unwrap()
}).sum();
assert_eq!(result.to_unsigned().unwrap(), target);
} else {
let pool_sum = pool.utxos.iter().cloned().map(|u| u.value()).checked_sum();
assert!(pool_sum.is_none()); // assert error when added.
}
}
}

let result = effective_value_sum.to_unsigned().unwrap();
assert_eq!(result, target);

Ok(())
});
}).seed(0x13de95f300000373);
}

#[test]
Expand Down Expand Up @@ -843,36 +822,51 @@ mod tests {
let min_fee_rate = fee_rates.first().unwrap_or(&FeeRate::ZERO).to_sat_per_kwu();
let fee_rate = arb_fee_rate_in_range(u, 0..=min_fee_rate);

// sum the effective_values and use it asa the target
let target: SignedAmount = selection.iter().map(|u| {
effective_value(
let effective_values: Vec<SignedAmount> = selection.iter().map(|u| {
let e = effective_value(
fee_rate,
u.satisfaction_weight(),
u.value()
).unwrap()
}).sum();
);

let coins = select_coins_bnb(
target.to_unsigned().unwrap(),
Amount::ZERO,
fee_rate,
fee_rate,
&pool.utxos,
);
if e.is_some() {
e.unwrap()
} else {
SignedAmount::ZERO
}
}).collect();

if selection.is_empty() {
assert!(coins.is_none());
} else {
let result: SignedAmount = coins.unwrap().map(|u| {
effective_value(
fee_rate,
u.satisfaction_weight(),
u.value(),
)
.unwrap()
}).sum();
let eff_values_sum = effective_values.into_iter().checked_sum();

// if None, then this random subset is an invalid target
if let Some(s) = eff_values_sum {
let target = s.to_unsigned().unwrap();

let coins = select_coins_bnb(
target,
Amount::ZERO,
fee_rate,
fee_rate,
&pool.utxos,
);

if selection.is_empty() || target == Amount::ZERO {
assert!(coins.is_none());
} else {
let result: Amount = coins.unwrap().map(|u| {
effective_value(
fee_rate,
u.satisfaction_weight(),
u.value(),
)
.unwrap()
.to_unsigned()
.unwrap()
}).sum();

assert_eq!(result, target);
}

assert_eq!(result, target);
}

Ok(())
Expand Down Expand Up @@ -943,6 +937,6 @@ mod tests {
}

Ok(())
});
}).seed(0x6388c4b200000020);
}
}
39 changes: 13 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ mod tests {
use arbtest::arbtest;

const PROPTEST_POOL_SIZE: usize = 10;
const PROPTEST_MAX_SAT_AMOUNT: u64 = 100_000;
const PROPTEST_MAX_SAT_AMOUNT: u64 = u64::MAX;
const PROPTEST_MIN_SAT_AMOUNT: u64 = 161; //tx_in base_weight + 1

pub fn build_utxo(amt: Amount, satisfaction_weight: Weight) -> Utxo {
Expand Down Expand Up @@ -167,7 +167,7 @@ mod tests {
satisfaction_weight: Weight,
}

#[derive(Debug)]
#[derive(Debug, Arbitrary)]
pub struct UtxoPool {
pub utxos: Vec<Utxo>,
}
Expand All @@ -177,30 +177,18 @@ mod tests {
fn value(&self) -> Amount { self.output.value }
}

impl<'a> Arbitrary<'a> for UtxoPool
where
Utxo: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
let mut p = Vec::with_capacity(PROPTEST_POOL_SIZE);
//use bitcoin::amount::CheckedSum;
//impl<'a> Arbitrary<'a> for UtxoPool
//where
//Utxo: Arbitrary<'a>,
//{
//fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
//let pool: Vec<Utxo> = Vec::arbitrary(u)?;
//let p = UtxoPool { utxos: pool };

for _ in 0..PROPTEST_POOL_SIZE {
let amount_int = u.int_in_range::<u64>(PROPTEST_MIN_SAT_AMOUNT..=PROPTEST_MAX_SAT_AMOUNT).unwrap();
let amount = Amount::from_sat(amount_int);

let weight_int = u.int_in_range::<u64>(1..=amount_int).unwrap();
let weight = Weight::from_wu(weight_int);

let utxos = build_utxo(amount, weight);

p.push(utxos);
}

let pool = UtxoPool { utxos: p };

Ok(pool)
}
}
//Ok(p)
//}
//}

#[test]
fn select_coins_no_solution() {
Expand Down Expand Up @@ -340,4 +328,3 @@ mod tests {
});
}
}

9 changes: 6 additions & 3 deletions src/single_random_draw.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! This library provides efficient algorithms to compose a set of unspent transaction outputs
//! (UTXOs).
// SPDX-License-Identifier: CC0-1.0
//
//! Single Random Draw Algorithem.
//!
//! This module introduces the Single Random Draw Coin-Selection Algorithm.

use bitcoin::blockdata::transaction::effective_value;
use bitcoin::{Amount, FeeRate};
Expand Down Expand Up @@ -86,7 +89,7 @@ mod tests {

use super::*;
use crate::single_random_draw::select_coins_srd;
use crate::{WeightedUtxo, CHANGE_LOWER};
use crate::WeightedUtxo;

use arbitrary::{Arbitrary, Result, Unstructured};
use arbtest::arbtest;
Expand Down

0 comments on commit 6c0e553

Please sign in to comment.