Skip to content

Commit 52d4f79

Browse files
evanlinjinclaude
andcommitted
fix: allow zero-fee transactions in LowestFee metric
Change assertion from fee > 0 to fee >= 0 to handle valid zero-fee transactions. Add test case to verify the metric works correctly when target fee rate is zero. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent dda8638 commit 52d4f79

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

src/metrics/lowest_fee.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ impl BnbMetric for LowestFee {
3232
let drain = cs.drain(self.target, self.change_policy);
3333
let fee_for_the_tx = cs.fee(self.target.value(), drain.value);
3434
assert!(
35-
fee_for_the_tx > 0,
36-
"must not be called unless selection has met target"
35+
fee_for_the_tx >= 0,
36+
"must not be called unless selection has met target: fee={}",
37+
fee_for_the_tx
3738
);
3839
// `spend_fee` rounds up here. We could use floats but I felt it was just better to
3940
// accept the extra 1 sat penality to having a change output

tests/lowest_fee.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,55 @@ fn adding_another_input_to_remove_change() {
267267
assert!(score <= best_solution_score);
268268
assert_eq!(cs.selected_indices(), best_solution.selected_indices());
269269
}
270+
271+
#[test]
272+
fn zero_fee_tx() {
273+
let target_feerate = FeeRate::ZERO;
274+
let long_term_feerate = FeeRate::DEFAULT_MIN_RELAY;
275+
276+
let target = Target {
277+
fee: TargetFee {
278+
rate: target_feerate,
279+
replace: None,
280+
},
281+
outputs: TargetOutputs {
282+
value_sum: 99_870,
283+
weight_sum: 200 - TX_FIXED_FIELD_WEIGHT - 1,
284+
n_outputs: 1,
285+
},
286+
};
287+
288+
let candidates = vec![
289+
Candidate {
290+
value: 100_000,
291+
weight: 100,
292+
input_count: 1,
293+
is_segwit: true,
294+
},
295+
Candidate {
296+
value: 50_000,
297+
weight: 100,
298+
input_count: 1,
299+
is_segwit: true,
300+
},
301+
];
302+
303+
let drain_weights = DrainWeights {
304+
output_weight: 100,
305+
spend_weight: 1_000,
306+
n_outputs: 1,
307+
};
308+
309+
let mut cs = CoinSelector::new(&candidates);
310+
let metric = LowestFee {
311+
target,
312+
long_term_feerate,
313+
change_policy: ChangePolicy::min_value_and_waste(
314+
drain_weights,
315+
50,
316+
target_feerate,
317+
long_term_feerate,
318+
),
319+
};
320+
let (_score, _rounds) = common::bnb_search(&mut cs, metric, 1000).expect("must find solution");
321+
}

0 commit comments

Comments
 (0)