Skip to content

Commit

Permalink
Write lowest fee test that hits important branch
Browse files Browse the repository at this point in the history
That proptest is not hitting for some reason.
  • Loading branch information
LLFourn committed Dec 22, 2023
1 parent 17cc8f2 commit 7360052
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/metrics/lowest_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl BnbMetric for LowestFee {

if let Some((_, low_sats_per_wu_candidate)) = cs.unselected().next_back() {
let ev = low_sats_per_wu_candidate.effective_value(self.target.feerate);
if ev < 0.0 {
if ev < -0.0 {
// we can only reduce excess if ev is negative
let value_per_negative_effective_value =
low_sats_per_wu_candidate.value as f32 / ev.abs();
Expand All @@ -85,7 +85,7 @@ impl BnbMetric for LowestFee {
.drain_weights
.waste(self.target.feerate, self.long_term_feerate);
let best_score_without_change = Ordf32(
current_score.0 - cost_of_change + cost_of_getting_rid_of_change,
current_score.0 + cost_of_getting_rid_of_change - cost_of_change,
);
if best_score_without_change < current_score {
return Some(best_score_without_change);
Expand Down
84 changes: 83 additions & 1 deletion tests/lowest_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

mod common;
use bdk_coin_select::metrics::{Changeless, LowestFee};
use bdk_coin_select::ChangePolicy;
use bdk_coin_select::{BnbMetric, Candidate, CoinSelector};
use bdk_coin_select::{ChangePolicy, Drain, DrainWeights, FeeRate, Target};
use proptest::prelude::*;

proptest! {
Expand Down Expand Up @@ -160,3 +160,85 @@ fn combined_changeless_metric() {

assert!(combined_rounds >= rounds);
}

/// This test considers the case where you could actually lower your long term fee by adding another input.
#[test]
fn adding_another_input_to_remove_change() {
let target = Target {
feerate: FeeRate::from_sat_per_vb(1.0),
min_fee: 0,
value: 99_870,
};

let candidates = vec![
Candidate {
value: 100_000,
weight: 100,
input_count: 1,
is_segwit: true,
},
Candidate {
value: 50_000,
weight: 100,
input_count: 1,
is_segwit: true,
},
// NOTE: this input has negative effective value
Candidate {
value: 10,
weight: 100,
input_count: 1,
is_segwit: true,
},
];

let mut cs = CoinSelector::new(&candidates, 200);

let best_solution = {
let mut cs = cs.clone();
cs.select(0);
cs.select(2);
cs.excess(target, Drain::none());
assert!(cs.is_target_met(target));
cs
};

let drain_weights = DrainWeights {
output_weight: 100,
spend_weight: 1_000,
};

let excess_to_make_first_candidate_satisfy_but_have_change = {
let mut cs = cs.clone();
cs.select(0);
assert!(cs.is_target_met(target));
let with_change_excess = cs.excess(
target,
Drain {
value: 0,
weights: drain_weights,
},
);
assert!(with_change_excess > 0);
with_change_excess as u64
};

let change_policy = ChangePolicy {
min_value: excess_to_make_first_candidate_satisfy_but_have_change - 10,
drain_weights,
};

let mut metric = LowestFee {
target,
long_term_feerate: FeeRate::from_sat_per_vb(1.0),
change_policy,
};

let (score, _) = common::bnb_search(&mut cs, metric, 10).expect("finds solution");
let best_solution_score = metric.score(&best_solution).expect("must be a solution");

assert_eq!(best_solution.drain(target, change_policy), Drain::none());

assert!(score <= best_solution_score);
assert_eq!(cs.selected_indices(), best_solution.selected_indices());
}

0 comments on commit 7360052

Please sign in to comment.