opt: Skip UTXOs with worse waste, same eff_value

When two successive UTXOs differ in waste but match in effective value,
we can skip the second if the first is not selected, because all input
sets we can generate by swapping out a less wasteful UTXOs with a more
wastefull UTXO of matching effective value would be strictly worse.
This commit is contained in:
Murch 2025-03-26 18:59:30 -07:00
parent b8ebcb039d
commit 44bab80284
No known key found for this signature in database
GPG key ID: 7BA035CA5B901713
2 changed files with 7 additions and 6 deletions

View file

@ -216,12 +216,13 @@ util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool
deselect_last();
should_shift = false;
// After SHIFTing to an omission branch, the `next_utxo` might have the same value and same weight as the
// UTXO we just omitted (i.e. it is a "clone"). If so, selecting `next_utxo` would produce an equivalent
// After SHIFTing to an omission branch, the `next_utxo` might have the same effective value as the
// UTXO we just omitted. Since lower waste is our tiebreaker on UTXOs with equal effective value for sorting, if it
// ties on the effective value, it _must_ have the same waste (i.e. be a "clone" of the prior UTXO) or a
// higher waste. If so, selecting `next_utxo` would produce an equivalent or worse
// selection as one we previously evaluated. In that case, increment `next_utxo` until we find a UTXO with a
// differing amount or weight.
while (utxo_pool[next_utxo - 1].GetSelectionAmount() == utxo_pool[next_utxo].GetSelectionAmount()
&& utxo_pool[next_utxo - 1].m_weight == utxo_pool[next_utxo].m_weight) {
// differing amount.
while (utxo_pool[next_utxo - 1].GetSelectionAmount() == utxo_pool[next_utxo].GetSelectionAmount()) {
if (next_utxo >= utxo_pool.size() - 1) {
// Reached end of UTXO pool skipping clones: SHIFT instead
should_shift = true;

View file

@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
add_coin(3 * CENT, 2, expected_result);
BOOST_CHECK(EquivalentResult(expected_result, *res));
// Demonstrate how following improvements reduce iteration count and catch any regressions in the future.
expected_attempts = 25;
expected_attempts = 22;
BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts, strprintf("Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));
}
}