diff --git a/src/branch_and_bound.rs b/src/branch_and_bound.rs index 75940d3..9cb51b9 100644 --- a/src/branch_and_bound.rs +++ b/src/branch_and_bound.rs @@ -138,7 +138,7 @@ pub fn select_coins_bnb( ) -> Option> { // Total_Tries in Core: // https://github.com/bitcoin/bitcoin/blob/1d9da8da309d1dbf9aef15eb8dc43b4a2dc3d309/src/wallet/coinselection.cpp#L74 - const ITERATION_LIMIT: i32 = 100_000; + const ITERATION_LIMIT: i32 = 200_000; let mut iteration = 0; let mut index = 0; @@ -179,6 +179,7 @@ pub fn select_coins_bnb( } while iteration < ITERATION_LIMIT { + println!("{} {:?}", iteration, index_selection); backtrack = false; // * If any of the conditions are met, backtrack. @@ -310,6 +311,8 @@ mod tests { use bitcoin::TxOut; use bitcoin::Weight; use core::str::FromStr; + use std::iter::once; + use std::iter::zip; fn create_weighted_utxos(fee: Amount) -> Vec { let amts = [ @@ -719,4 +722,122 @@ mod tests { assert_eq!(list[1].utxo.value, Amount::from_str("6 cBTC").unwrap()); assert_eq!(list[2].utxo.value, Amount::from_str("2 cBTC").unwrap()); } + + #[test] + fn select_coins_bnb_exhaust() { + // Recreate make_hard from bitcoin core test suit. + // Takes 327,661 to find a solution. + let base: usize = 2; + let alpha = (0..17).enumerate().map(|(i, _)| base.pow(17 + i as u32)); + let target = Amount::from_sat(alpha.clone().sum::() as u64); + + let beta = (0..17).enumerate().map(|(i, _)| { + let a = base.pow(17 + i as u32); + let b = base.pow(16 - i as u32); + a + b + }); + + let vals: Vec<_> = zip(alpha, beta) + // flatten requires iterable types. + // use once() to make tuple iterable. + .flat_map(|tup| once(tup.0).chain(once(tup.1))) + .map(|a| Amount::from_sat(a as u64)) + .collect(); + + let weighted_utxos = create_weighted_utxos_from_values(vals); + let list = select_coins_bnb( + target, + Amount::ONE_SAT, + FeeRate::ZERO, + FeeRate::ZERO, + &weighted_utxos, + ); + + assert!(list.is_none()); + } + + #[test] + fn select_coins_bnb_exhaust_v2() { + // Takes 163,819 iterations to find a solution. + let base: usize = 2; + let mut target = 0; + let vals = (0..15) + .enumerate() + .flat_map(|(i, _)| { + let a = base.pow(15 + i as u32) as u64; + target += a; + vec![a, a + 2] + }); + + let vals: Vec<_> = vals.map(Amount::from_sat).collect(); + let weighted_utxos = create_weighted_utxos_from_values(vals); + let list = select_coins_bnb( + Amount::from_sat(target), + Amount::ONE_SAT, + FeeRate::ZERO, + FeeRate::ZERO, + &weighted_utxos, + ); + + assert!(list.is_none()); + } + + #[test] + fn select_coins_bnb_exhaust_v3() { + // Takes 163,819 iterations to find a solution. + let base: usize = 2; + let mut target = 0; + let vals = (0..3) + .enumerate() + .flat_map(|(i, _)| { + let a = base.pow(4 + i as u32) as u64; + target += a; + vec![a, a + 2] + }); + + let vals: Vec<_> = vals.map(Amount::from_sat).collect(); + let weighted_utxos = create_weighted_utxos_from_values(vals); + let list = select_coins_bnb( + Amount::from_sat(target), + Amount::ONE_SAT, + FeeRate::ZERO, + FeeRate::ZERO, + &weighted_utxos, + ); + + assert!(list.is_none()); + } + + #[test] + fn select_coins_bnb_exhaust_v4() { + // Recreate make_hard from bitcoin core test suit. + // Takes 327,661 to find a solution. + let base: usize = 2; + let alpha = (0..4).enumerate().map(|(i, _)| base.pow(17 + i as u32)); + let target = Amount::from_sat(alpha.clone().sum::() as u64); + + let beta = (0..4).enumerate().map(|(i, _)| { + let a = base.pow(4 + i as u32); + let b = base.pow(3 - i as u32); + a + b + }); + + let vals: Vec<_> = zip(alpha, beta) + // flatten requires iterable types. + // use once() to make tuple iterable. + .flat_map(|tup| once(tup.0).chain(once(tup.1))) + .map(|a| Amount::from_sat(a as u64)) + .collect(); + + let weighted_utxos = create_weighted_utxos_from_values(vals); + let list = select_coins_bnb( + target, + Amount::ONE_SAT, + FeeRate::ZERO, + FeeRate::ZERO, + &weighted_utxos, + ); + + assert!(list.is_none()); + } }