Skip to content

Commit

Permalink
opt: Skip over combinations of sand
Browse files Browse the repository at this point in the history
If you have a lot of small value UTXOs, they may in sum add up to a
sufficient lookahead, but we can gauge from the amount of funds we are
still missing, the maximal amount in the tail, and the minimal weight in
the tail whether any sufficient combination would exceed the best weight
and in that case cut the subtree.
  • Loading branch information
murchandamus committed Jan 8, 2024
1 parent 4081439 commit 6cf3d57
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 4 deletions.
5 changes: 5 additions & 0 deletions src/wallet/coinselection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <util/check.h>
#include <util/moneystr.h>

#include <cmath>
#include <numeric>
#include <optional>
#include <queue>
Expand Down Expand Up @@ -335,7 +336,11 @@ util::Result<SelectionResult> CoinGrinder(std::vector<OutputGroup>& utxo_pool, c
best_selection = curr_selection;
best_selection_weight = curr_weight;
best_selection_amount = curr_amount;
std::cout << "NEW BEST!" << std::endl;
}
} else if (!best_selection.empty() && curr_weight + min_tail_weight[curr_selection.back()] * std::ceil((selection_target + change_target - curr_amount) / utxo_pool[curr_selection.back()].GetSelectionAmount()) > best_selection_weight) {
// Compare minimal tail weight and last selected amount with the amount missing to gauge whether a better weight is still possible.
should_cut = true;
}

if (curr_try >= TOTAL_TRIES) {
Expand Down
8 changes: 4 additions & 4 deletions src/wallet/test/coinselector_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1178,7 +1178,7 @@ BOOST_AUTO_TEST_CASE(coin_grinder_tests)
});
BOOST_CHECK(res);
// If this takes more attempts, the implementation has regressed
size_t expected_attempts = 184;
size_t expected_attempts = 37;
BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts, "Expected " + std::to_string(expected_attempts) + " attempts, but got " + std::to_string(res->GetSelectionsEvaluated()));
}

Expand Down Expand Up @@ -1247,7 +1247,7 @@ BOOST_AUTO_TEST_CASE(coin_grinder_tests)

{
// #################################################################################################################
// 5) Lots of tiny UTXOs of different amounts quickly exhausts the search attempts
// 5) Test that lots of tiny UTXOs can be skipped even if there are enough funds in lookahead
// #################################################################################################################
SelectionResult expected_result(CAmount(0), SelectionAlgorithm::CG);
CAmount target = 1.9L * COIN;
Expand All @@ -1264,11 +1264,11 @@ BOOST_AUTO_TEST_CASE(coin_grinder_tests)
return available_coins;
});
expected_result.Clear();
add_coin(1.8 * COIN, 1, expected_result);
add_coin(1 * COIN, 1, expected_result);
add_coin(1 * COIN, 2, expected_result);
BOOST_CHECK(EquivalentResult(expected_result, *res));
// If this takes more attempts, the implementation has regressed
size_t expected_attempts = 100000;
size_t expected_attempts = 7;
BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts, "Expected " + std::to_string(expected_attempts) + " attempts, but got " + std::to_string(res->GetSelectionsEvaluated()));
}

Expand Down

0 comments on commit 6cf3d57

Please sign in to comment.