Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
yancyribbens committed Oct 14, 2024
1 parent 3d9c979 commit 1da2132
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 166 deletions.
107 changes: 47 additions & 60 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,35 +127,22 @@ mod tests {

pub fn build_pool() -> Vec<Utxo> {
let amts = [
27336,
27_336,
238,
9225,
20540,
35590,
49463,
6331,
35548,
50363,
28009,
9_225,
20_540,
35_590,
49_463,
6_331,
35_548,
50_363,
28_009,
];

let weights = [
25350,
106,
3311,
2633,
21081,
35260,
3896,
6377,
6851,
20236
];

let utxos: Vec<_> = zip(amts, weights)
.map(|(a, w)| {
let amt = Amount::from_sat(a);
let weight = Weight::from_wu(w);
let utxos: Vec<_> = amts.iter()
.map(|a| {
let amt = Amount::from_sat(*a);
let weight = Weight::ZERO;
build_utxo(amt, weight)
}).collect();

Expand All @@ -164,8 +151,8 @@ mod tests {

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

#[derive(Debug, Arbitrary)]
Expand All @@ -184,7 +171,7 @@ mod tests {
let cost_of_change = Amount::ZERO;
let fee_rate = FeeRate::ZERO;
let lt_fee_rate = FeeRate::ZERO;
let pool = build_pool();
let pool = build_pool(); // eff value sum 262643

let result = select_coins(
target,
Expand All @@ -195,7 +182,7 @@ mod tests {
);

// This yields no solution because:
// * BnB fails because the sum overage is greater than ost_of_change
// * BnB fails because the sum overage is greater than cost_of_change
// * SRD fails because the sum is greater the utxo sum + CHANGE_LOWER
assert!(result.is_none());
}
Expand Down Expand Up @@ -248,11 +235,11 @@ mod tests {
assert!(result <= target + cost_of_change);
}

fn build_possible_solutions<'a>(pool: &'a UtxoPool, fee_rate: FeeRate, target: Amount, solutions: &mut Vec<Vec<&'a Utxo>>) {
pub fn build_possible_solutions<'a>(pool: &'a UtxoPool, fee_rate: FeeRate, target: Amount, solutions: &mut Vec<Vec<&'a Utxo>>) {
let mut gen = exhaustigen::Gen::new();
while !gen.done() {
let subset = gen.gen_subset(&pool.utxos).collect::<Vec<_>>();
let effective_value_sum = subset
let effective_values_sum = subset
.iter()
.map(|u| {
effective_value(
Expand All @@ -264,8 +251,8 @@ mod tests {
.map(|u| u.unwrap())
.checked_sum();

if let Some(s) = effective_value_sum {
if let Ok(p) = s.to_unsigned() {
if let Some(s) = effective_values_sum { // if is positive
if let Ok(p) = s.to_unsigned() { // if withon signed_amount bounds
if p >= target {
solutions.push(subset)
}
Expand All @@ -274,6 +261,28 @@ mod tests {
}
}

pub fn assert_proptest_result<'a, T: Iterator<Item = &'a Utxo>>(target: Amount, fee_rate: FeeRate, pool: UtxoPool, result: Option<T>) {
let mut solutions: Vec<Vec<&Utxo>> = Vec::new();
build_possible_solutions(&pool, fee_rate, target, &mut solutions);

if let Some(r) = result {
let utxo_sum: Amount = r.map(|u| {
effective_value(
fee_rate,
u.satisfaction_weight(),
u.value(),
)
.unwrap()
.to_unsigned()
.unwrap()
}).sum();

assert!(utxo_sum >= target);
} else {
assert!(target > Amount::MAX_MONEY || target == Amount::ZERO || solutions.is_empty());
}
}

#[test]
fn select_coins_proptest() {
arbtest(|u| {
Expand All @@ -283,39 +292,17 @@ mod tests {
let fee_rate = FeeRate::arbitrary(u)?;
let lt_fee_rate = FeeRate::arbitrary(u)?;

let mut solutions: Vec<Vec<&Utxo>> = Vec::new();
build_possible_solutions(&pool, fee_rate, target, &mut solutions);

let utxos = pool.utxos.clone();
let result = select_coins(
target,
cost_of_change,
fee_rate,
lt_fee_rate,
&pool.utxos,
&utxos,
);

println!("target: {:?}", target);
println!("cost of change {:?}", cost_of_change);
println!("solutions {:?}", solutions);

if let Some(r) = result {
let utxo_sum: Amount = r.map(|u| {
effective_value(
fee_rate,
u.satisfaction_weight(),
u.value(),
)
.unwrap()
.to_unsigned()
.unwrap()
}).sum();

assert!(utxo_sum >= target);
} else {
assert!(target > Amount::MAX_MONEY || target == Amount::ZERO || solutions.is_empty());
}
assert_proptest_result(target, fee_rate, pool, result);

Ok(())
}).seed(0xba3bc81500000032);
});
}
}
131 changes: 25 additions & 106 deletions src/single_random_draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,55 +102,13 @@ mod tests {
const PROPTEST_MAX_SAT_AMOUNT: u64 = 100_000;
const PROPTEST_MIN_SAT_AMOUNT: u64 = 161; //tx_in base_weight + 1

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

#[derive(Debug, Arbitrary)]
pub struct Utxo {
output: TxOut,
satisfaction_weight: Weight,
}

#[derive(Debug)]
pub struct ParamsStr<'a> {
target: &'a str,
fee_rate: &'a str,
weighted_utxos: Vec<&'a str>,
}

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);

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)
}
}

impl WeightedUtxo for Utxo {
fn satisfaction_weight(&self) -> Weight { self.satisfaction_weight }

fn value(&self) -> Amount { self.output.value }
}

fn build_utxo(amt: Amount, satisfaction_weight: Weight) -> Utxo {
let output = TxOut { value: amt, script_pubkey: ScriptBuf::new() };
Utxo { output, satisfaction_weight }
Expand Down Expand Up @@ -347,68 +305,29 @@ mod tests {
assert_coin_select_params(&params, Some(&["1 cBTC"]));
}

//use bitcoin::SignedAmount;

//#[test]
//fn select_srd_match_proptest() {
//arbtest(|u| {
//let pool = UtxoPool::arbitrary(u)?;
//let target = Amount::arbitrary(u)?;
//let fee_rate = FeeRate::arbitrary(u)?;

//let mut gen = exhaustigen::Gen::new();

//let mut solutions: Vec<_> = Vec::new();
//if target != Amount::ZERO {
//while !gen.done() {
//let subset = gen.gen_subset(&pool.utxos).collect::<Vec<_>>();
//let subset_sum: SignedAmount = subset
//.iter()
//.map(|u| {
//effective_value(
//fee_rate,
//u.satisfaction_weight(),
//u.value()
//)})
//.filter(|e| e.is_some())
//.map(|u| u.unwrap())
//.sum();

//if subset_sum.is_positive() {
//let sum: Amount = subset_sum.to_unsigned().unwrap();

//if sum >= target {
//solutions.push(subset);
//}
//}
//}
//}

//let result = select_coins_srd(
//target,
//fee_rate,
//&pool.utxos,
//&mut get_rng()
//);

//if let Some(iter) = result {
//let sum: Amount = iter.map(|u| {
//effective_value(
//fee_rate,
//u.satisfaction_weight(),
//u.value(),
//)
//.unwrap()
//.to_unsigned()
//.unwrap()
//}).sum();

//assert!(sum >= target);
//} else {
//assert!(solutions.is_empty());
//}

//Ok(())
//});
//}
use bitcoin::SignedAmount;
use crate::tests::build_possible_solutions;
use crate::tests::UtxoPool;
use crate::tests::Utxo;
use crate::tests::assert_proptest_result;

#[test]
fn select_srd_match_proptest() {
arbtest(|u| {
let pool = UtxoPool::arbitrary(u)?;
let target = Amount::arbitrary(u)?;
let fee_rate = FeeRate::arbitrary(u)?;

let utxos = pool.utxos.clone();
let result: Option<_> = select_coins_srd(
target,
fee_rate,
&utxos,
&mut get_rng()
);
assert_proptest_result(target + CHANGE_LOWER, fee_rate, pool, result);

Ok(())
});
}
}

0 comments on commit 1da2132

Please sign in to comment.