diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index 7435bfabe98dd5..c3320366365d30 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -250,17 +250,21 @@ util::Result CoinGrinder(std::vector& utxo_pool, c // Sort the utxo_pool std::sort(utxo_pool.begin(), utxo_pool.end(), descending_effval_weight); std::vector lookahead(utxo_pool.size()); + std::vector 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::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 @@ -336,12 +340,21 @@ util::Result CoinGrinder(std::vector& 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;