Skip to content

Commit

Permalink
wip: Add loop iteration limit
Browse files Browse the repository at this point in the history
  • Loading branch information
yancyribbens committed Dec 9, 2023
1 parent c98543c commit 743fd3e
Showing 1 changed file with 149 additions and 22 deletions.
171 changes: 149 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const CHANGE_LOWER: Amount = Amount::from_sat(50_000);
/// The idea of using a WeightUtxo type was inspired by the BDK implementation:
/// <https://github.com/bitcoindevkit/bdk/blob/feafaaca31a0a40afc03ce98591d151c48c74fa2/crates/bdk/src/types.rs#L181>
#[derive(Clone, Debug, PartialEq)]
// note, change this to private? No good reason to be public.
pub struct WeightedUtxo {
/// The satisfaction_weight is the size of the required params to satisfy the UTXO.
pub satisfaction_weight: Weight,
Expand Down Expand Up @@ -150,9 +151,19 @@ pub fn select_coins_bnb(
target: Amount,
_fee_rate: FeeRate,
weighted_utxos: &mut [WeightedUtxo],
) -> Option<Vec<WeightedUtxo>> {
) -> Option<Vec<usize>> {
// Total_Tries in Core:
// https://github.com/bitcoin/bitcoin/blob/1d9da8da309d1dbf9aef15eb8dc43b4a2dc3d309/src/wallet/coinselection.cpp#L74
const ITERATION_LIMIT: i32 = 100_000;

let mut iteration = 0;
let mut index = 0;

let mut value = Amount::ZERO;
let mut selection: Vec<WeightedUtxo> = vec![];
let waste = Amount::MAX_MONEY;

let mut selection: Vec<usize> = vec![];
let mut best_selection: Vec<usize> = vec![];
let available_value: Amount = weighted_utxos.iter().map(|u| u.utxo.value).sum();

if available_value < target {
Expand All @@ -161,29 +172,96 @@ pub fn select_coins_bnb(

weighted_utxos.sort_by(|a, b| b.utxo.value.cmp(&a.utxo.value));

for (_i, utxo) in weighted_utxos.iter().enumerate() {
while iteration < ITERATION_LIMIT {
//println!("iteration: {} index: {} value: {} selection: {:?} best_selection {:?}", iteration, index, value, selection, best_selection);
// backtrack
//
// There are three conditions for backtracking:
//
// * value meets or exceeded target
// * leaf node (nothing left to explorer)
// * not enough value to make it to target
if value >= target
|| available_value + value < target
|| utxo == weighted_utxos.last().unwrap()
{
// backtrack
// * value meets or exceeded target.
// * leaf node (nothing left to explorer).
// * not enough value to make it to target.

// value meets or exceeds the target:
//
// For example:
// o
// /
// 4
// /
// 3
//
// Transform to:
// o
// /
// 4
// / \
// 3
//
// The search index is decremented by 1 and the value
// is subtracted by the current search index. Therefore
// the omission branch at the search index is tested
// next.
if value >= target {
let current_waste = value - target;

if current_waste <= waste {
best_selection = selection.clone();
}

selection.pop();

if selection.is_empty() {
return Some(best_selection);
}

let utxo = &weighted_utxos[index];
value -= utxo.utxo.value;
index -= 1;
}
// value is a leaf node, therefore, we remove the root
// and try the exclusion branch of that root node.
// o
// / \
// 4
// / \
// 3
// / \
// 2
// /
// 1
//
// We try excluding 4 now
// o
// / \
// 4
// / \
// 3
//
// A new subtree is checked by resetting the search index
// to the root that was just removed. Therefore, the next
// iteration, the search index is incremented by 1 and we
// start with a root node that is the next in list.
else if index == weighted_utxos.len() {
index = selection[0];
selection = vec![];
}
//if available_value + value < target {
//}

// proceed with depth first search.
else {
selection.push(utxo.clone());
let utxo = &weighted_utxos[index];
selection.push(index);
value += utxo.utxo.value;
}

index += 1;
iteration += 1;
}

Some(selection)
println!("all solutions checked, returning best_selection: {:?}", best_selection);
Some(best_selection)
}

#[cfg(test)]
Expand Down Expand Up @@ -216,17 +294,66 @@ mod tests {
},
};

vec![utxo_one, utxo_two]
let utxo_three = WeightedUtxo {
satisfaction_weight: SATISFACTION_SIZE,
utxo: TxOut {
value: Amount::from_str("3 cBTC").unwrap(),
script_pubkey: ScriptBuf::new(),
},
};

let utxo_four = WeightedUtxo {
satisfaction_weight: SATISFACTION_SIZE,
utxo: TxOut {
value: Amount::from_str("4 cBTC").unwrap(),
script_pubkey: ScriptBuf::new(),
},
};

vec![utxo_one, utxo_two, utxo_three, utxo_four]
}

#[test]
fn one_solution_one_coin() {
let target = Amount::from_str("1 cBTC").unwrap();

let mut weighted_utxos = vec![WeightedUtxo {
satisfaction_weight: SATISFACTION_SIZE,
utxo: TxOut {
value: Amount::from_str("1 cBTC").unwrap(),
script_pubkey: ScriptBuf::new(),
},
}];

let index_list = select_coins_bnb(target, FEE_RATE, &mut weighted_utxos).unwrap();
let expected_index_list = vec![0];
assert_eq!(index_list, expected_index_list);
}

#[test]
fn find_solution_one_cbtc() {
let _target = Amount::from_str("1.5 cBTC").unwrap();
let _weighted_utxos: Vec<WeightedUtxo> = create_weighted_utxos();
fn one_solution_two_coins() {
let target = Amount::from_str("4 cBTC").unwrap();

let mut weighted_utxos = vec![
WeightedUtxo {
satisfaction_weight: SATISFACTION_SIZE,
utxo: TxOut {
value: Amount::from_str("4 cBTC").unwrap(),
script_pubkey: ScriptBuf::new(),
},
},
WeightedUtxo {
satisfaction_weight: SATISFACTION_SIZE,
utxo: TxOut {
value: Amount::from_str("3 cBTC").unwrap(),
script_pubkey: ScriptBuf::new(),
},
},
];

//let utxo_match = select_coins_bnb(target, FEE_RATE, &mut weighted_utxos);
//let expected_bool_vec = vec![false, false, false, true];
//assert_eq!(expected_bool_vec, utxo_match);
let index_list = select_coins_bnb(target, FEE_RATE, &mut weighted_utxos).unwrap();
let expected_index_list = vec![0];
assert_eq!(index_list, expected_index_list);
}

//#[test]
Expand Down Expand Up @@ -299,8 +426,8 @@ mod tests {
//}

#[test]
fn select_coins_bnb_no_solution() {
let target: Amount = Amount::from_str("4 cBTC").unwrap();
fn select_coins_bnb_not_enough_value() {
let target: Amount = Amount::from_str("11 cBTC").unwrap();
let mut weighted_utxos: Vec<WeightedUtxo> = create_weighted_utxos();

let result = select_coins_bnb(target, FEE_RATE, &mut weighted_utxos);
Expand Down

0 comments on commit 743fd3e

Please sign in to comment.