Skip to content

Commit

Permalink
opt: Cut if last addition was minimal weight
Browse files Browse the repository at this point in the history
In situations where we have UTXO groups of various weight, we can CUT
rather than SHIFT when we exceeded the max_weight or the best
selection’s weight while the last step was equal to the minimum weight
in the lookahead.
  • Loading branch information
murchandamus committed Dec 22, 2023
1 parent 36f085f commit 8ec3b6e
Showing 1 changed file with 17 additions and 4 deletions.
21 changes: 17 additions & 4 deletions src/wallet/coinselection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,17 +250,21 @@ util::Result<SelectionResult> CoinGrinder(std::vector<OutputGroup>& utxo_pool, c
// Sort the utxo_pool
std::sort(utxo_pool.begin(), utxo_pool.end(), descending_effval_weight);
std::vector<CAmount> lookahead(utxo_pool.size());
std::vector<int> min_tail_weight(utxo_pool.size());

// Calculate lookahead and check that there are sufficient funds
CAmount total_available = 0;
int min_group_weight = std::numeric_limits<int>::max();
size_t i = utxo_pool.size();
while (i > 0) {
--i;
lookahead.at(i) = total_available;
min_tail_weight.at(i) = min_group_weight;
// Assert that this utxo is not negative. It should never be negative,
// effective value calculation should have removed it
assert(utxo_pool.at(i).GetSelectionAmount() > 0);
total_available += utxo_pool.at(i).GetSelectionAmount();
min_group_weight = std::min(min_group_weight, utxo_pool.at(i).m_weight);
}
if (total_available < selection_target + change_target) {
// Insufficient funds
Expand Down Expand Up @@ -336,12 +340,21 @@ util::Result<SelectionResult> CoinGrinder(std::vector<OutputGroup>& utxo_pool, c
// Cannot succeed due to insufficient funds in lookahead: CUT (deselect latest, SHIFT)
next_op = operations::cut;
} else if (curr_weight > max_weight) {
// max_weight exceeded: SHIFT
// max_weight exceeded: CUT if last selected group had minimal weight, else SHIFT
max_tx_weight_exceeded = true;
next_op = operations::shift;
if (utxo_pool.at(curr_selection.back()).m_weight <= min_tail_weight.at(curr_selection.back())) {
next_op = operations::cut;
} else {
next_op = operations::shift;
}
} else if (curr_weight > best_selection_weight) {
// Worse weight than best solution. More UTXOs only increase weight: SHIFT
next_op = operations::shift;
// Worse weight than best solution. More UTXOs only increase weight:
// CUT if last selected group had minimal weight, else SHIFT
if (utxo_pool.at(curr_selection.back()).m_weight <= min_tail_weight.at(curr_selection.back())) {
next_op = operations::cut;
} else {
next_op = operations::shift;
}
} else if (curr_amount >= selection_target + change_target) {
// Target exceeded: check if new best, then SHIFT (deselect latest, select next)
next_op = operations::shift;
Expand Down

0 comments on commit 8ec3b6e

Please sign in to comment.