Skip to content

Commit dc46db3

Browse files
committed
Write lowest fee test that hits important branch
That proptest is not hitting for some reason.
1 parent 50b0299 commit dc46db3

File tree

2 files changed

+82
-3
lines changed

2 files changed

+82
-3
lines changed

src/metrics/lowest_fee.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl BnbMetric for LowestFee {
6767

6868
if let Some((_, low_sats_per_wu_candidate)) = cs.unselected().next_back() {
6969
let ev = low_sats_per_wu_candidate.effective_value(self.target.feerate);
70-
if ev < 0.0 {
70+
if ev < -0.0 {
7171
// we can only reduce excess if ev is negative
7272
let value_per_negative_effective_value =
7373
low_sats_per_wu_candidate.value_pwu() / ev.abs();
@@ -81,7 +81,7 @@ impl BnbMetric for LowestFee {
8181
.drain_weights
8282
.waste(self.target.feerate, self.long_term_feerate);
8383
let best_score_without_change = Ordf32(
84-
current_score.0 - cost_of_change + cost_of_getting_rid_of_change,
84+
current_score.0 + cost_of_getting_rid_of_change - cost_of_change,
8585
);
8686
if best_score_without_change < current_score {
8787
return Some(best_score_without_change);

tests/lowest_fee.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
mod common;
44
use bdk_coin_select::metrics::{Changeless, LowestFee};
5-
use bdk_coin_select::ChangePolicy;
65
use bdk_coin_select::{BnbMetric, Candidate, CoinSelector};
6+
use bdk_coin_select::{ChangePolicy, Drain, DrainWeights, FeeRate, Target};
77
use proptest::prelude::*;
88

99
proptest! {
@@ -160,3 +160,82 @@ fn combined_changeless_metric() {
160160

161161
assert!(combined_rounds >= rounds);
162162
}
163+
164+
/// This test considers the case where you could actually lower your long term fee by adding another input.
165+
#[test]
166+
fn adding_another_input_to_remove_change() {
167+
let target = Target {
168+
feerate: FeeRate::from_sat_per_vb(1.0),
169+
min_fee: 0,
170+
value: 99_700,
171+
};
172+
173+
let candidates = vec![
174+
Candidate {
175+
value: 100_000,
176+
weight: 100,
177+
input_count: 1,
178+
is_segwit: true,
179+
},
180+
Candidate {
181+
value: 50_000,
182+
weight: 100,
183+
input_count: 1,
184+
is_segwit: true,
185+
},
186+
Candidate {
187+
value: 10,
188+
weight: 100,
189+
input_count: 1,
190+
is_segwit: true,
191+
},
192+
];
193+
194+
let mut cs = CoinSelector::new(&candidates, 200);
195+
196+
let best_solution = {
197+
let mut cs = cs.clone();
198+
cs.select(0);
199+
cs.select(2);
200+
cs
201+
};
202+
203+
let drain_weights = DrainWeights {
204+
output_weight: 100,
205+
spend_weight: 1_000,
206+
};
207+
208+
let excess_to_make_first_candidate_satisfy_but_have_change = {
209+
let mut cs = cs.clone();
210+
cs.select(0);
211+
assert!(cs.is_target_met(target));
212+
let with_change_excess = cs.excess(
213+
target,
214+
Drain {
215+
value: 0,
216+
weights: drain_weights,
217+
},
218+
);
219+
assert!(with_change_excess > 0);
220+
with_change_excess as u64
221+
};
222+
223+
let change_policy = ChangePolicy {
224+
min_value: excess_to_make_first_candidate_satisfy_but_have_change - 1,
225+
drain_weights,
226+
};
227+
228+
let mut metric = LowestFee {
229+
target,
230+
long_term_feerate: FeeRate::from_sat_per_vb(1.0),
231+
change_policy,
232+
};
233+
234+
let (score, _) = common::bnb_search(&mut cs, metric, 10).expect("finds solution");
235+
let best_solution_score = metric.score(&best_solution).expect("must be a solution");
236+
237+
assert_eq!(best_solution.drain(target, change_policy), Drain::none());
238+
239+
assert!(score <= best_solution_score);
240+
assert_eq!(cs.selected_indices(), best_solution.selected_indices());
241+
}

0 commit comments

Comments
 (0)