Skip to content

Commit 781e3c3

Browse files
evanlinjinclaude
andcommitted
perf: make LowestFee's BnB bound max_weight-aware
The bound was deliberately cap-blind (admissible but loose): it credited improvements the weight budget can't actually afford. Tighten it in the cap-binding regime by refusing those improvements: - Not-funded ("slurp") branch: reaching the feerate needs a perfect input weighing `scale * to_resize.weight`. `to_resize` is the best value-per-weight input available, so if even that (fractionally) can't fit the remaining weight budget, no within-cap selection down this branch reaches the target -> prune. This is the fractional relaxation, so it never prunes a branch that has an integer within-cap solution. - Changeless "recover value by adding change" branch: only credit the improvement if a change output fits the cap. Clearing the dust threshold needs more (heavier) inputs and the change output adds weight, so if change doesn't fit now it never will down this branch -> keep `current_score`. Both terms stay admissible (validated by `ensure_bound_is_not_too_tight`) and BnB still finds the optimum (`can_eventually_find_best_solution`). Only takes effect when `max_weight` is set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ef8f37a commit 781e3c3

1 file changed

Lines changed: 24 additions & 1 deletion

File tree

src/metrics/lowest_fee.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,17 @@ impl BnbMetric for LowestFee {
161161

162162
let best_score_with_change =
163163
Ordf32(current_score.0 - cost_of_no_change as f32 + cost_of_adding_change);
164-
if best_score_with_change < current_score {
164+
// max_weight-aware: recovering that value requires a change output (and more
165+
// inputs to clear the dust threshold), which only makes the tx heavier. If a change
166+
// output can't fit the cap now it never will down this branch, so don't credit the
167+
// improvement — keep `current_score` (a tighter, still-admissible bound).
168+
let change_output = Drain {
169+
weights: self.drain_weights,
170+
value: 0,
171+
};
172+
if cs.is_within_max_weight(target, change_output)
173+
&& best_score_with_change < current_score
174+
{
165175
return Some(best_score_with_change);
166176
}
167177
}
@@ -243,6 +253,19 @@ impl BnbMetric for LowestFee {
243253
}
244254
}
245255

256+
// max_weight-aware: reaching the feerate needs a perfect input weighing
257+
// `scale * to_resize.weight`. `to_resize` is the best value-per-weight input available,
258+
// so if even that (fractionally) can't fit the remaining weight budget, no within-cap
259+
// selection down this branch reaches the target -> prune. This is the fractional
260+
// relaxation, so it never prunes a branch that has an (integer) within-cap solution.
261+
if let Some(max_weight) = target.max_weight {
262+
let budget =
263+
max_weight.saturating_sub(cs.weight(target.outputs, DrainWeights::NONE));
264+
if scale.0 * to_resize.weight as f32 > budget as f32 {
265+
return None;
266+
}
267+
}
268+
246269
// `scale` could be 0 even if `is_funded` is `false` due to the latter being based on
247270
// rounded-up vbytes.
248271
let ideal_fee = scale.0 * to_resize.value as f32 + cs.selected_value() as f32

0 commit comments

Comments
 (0)