19 bool operator()(
const OutputGroup& a,
const OutputGroup& b)
const 21 return a.GetSelectionAmount() > b.GetSelectionAmount();
69 std::vector<size_t> curr_selection;
72 CAmount curr_available_value = 0;
76 assert(utxo.GetSelectionAmount() > 0);
77 curr_available_value += utxo.GetSelectionAmount();
79 if (curr_available_value < selection_target) {
84 std::sort(utxo_pool.begin(), utxo_pool.end(),
descending);
87 std::vector<size_t> best_selection;
91 for (
size_t curr_try = 0, utxo_pool_index = 0; curr_try <
TOTAL_TRIES; ++curr_try, ++utxo_pool_index) {
93 bool backtrack =
false;
94 if (curr_value + curr_available_value < selection_target ||
95 curr_value > selection_target + cost_of_change ||
96 (curr_waste > best_waste && (utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) > 0)) {
98 }
else if (curr_value >= selection_target) {
99 curr_waste += (curr_value - selection_target);
104 if (curr_waste <= best_waste) {
105 best_selection = curr_selection;
106 best_waste = curr_waste;
108 curr_waste -= (curr_value - selection_target);
113 if (curr_selection.empty()) {
118 for (--utxo_pool_index; utxo_pool_index > curr_selection.back(); --utxo_pool_index) {
119 curr_available_value += utxo_pool.at(utxo_pool_index).GetSelectionAmount();
123 assert(utxo_pool_index == curr_selection.back());
126 curr_waste -= utxo.fee - utxo.long_term_fee;
127 curr_selection.pop_back();
134 if (curr_selection.empty() ||
136 (utxo_pool_index - 1) == curr_selection.back() ||
139 utxo.
GetSelectionAmount() != utxo_pool.at(utxo_pool_index - 1).GetSelectionAmount() ||
140 utxo.
fee != utxo_pool.at(utxo_pool_index - 1).fee)
143 curr_selection.push_back(utxo_pool_index);
151 if (best_selection.empty()) {
156 for (
const size_t& i : best_selection) {
175 std::vector<size_t> indexes;
176 indexes.resize(utxo_pool.size());
177 std::iota(indexes.begin(), indexes.end(), 0);
178 Shuffle(indexes.begin(), indexes.end(), rng);
180 CAmount selected_eff_value = 0;
181 for (
const size_t i : indexes) {
186 if (selected_eff_value >= target_value) {
206 std::vector<char>& vfBest,
CAmount& nBest,
int iterations = 1000)
208 std::vector<char> vfIncluded;
211 vfBest.assign(groups.size(),
true);
214 for (
int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
216 vfIncluded.assign(groups.size(),
false);
218 bool fReachedTarget =
false;
219 for (
int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
221 for (
unsigned int i = 0; i < groups.size(); i++)
229 if (nPass == 0 ? insecure_rand.
randbool() : !vfIncluded[i])
231 nTotal += groups[i].GetSelectionAmount();
232 vfIncluded[i] =
true;
233 if (nTotal >= nTargetValue)
235 fReachedTarget =
true;
243 nTotal -= groups[i].GetSelectionAmount();
244 vfIncluded[i] =
false;
258 std::optional<OutputGroup> lowest_larger;
261 std::vector<OutputGroup> applicable_groups;
264 Shuffle(groups.begin(), groups.end(), rng);
267 if (group.GetSelectionAmount() == nTargetValue) {
270 }
else if (group.GetSelectionAmount() < nTargetValue + change_target) {
271 applicable_groups.push_back(group);
272 nTotalLower += group.GetSelectionAmount();
273 }
else if (!lowest_larger || group.GetSelectionAmount() < lowest_larger->GetSelectionAmount()) {
274 lowest_larger = group;
278 if (nTotalLower == nTargetValue) {
279 for (
const auto& group : applicable_groups) {
285 if (nTotalLower < nTargetValue) {
286 if (!lowest_larger)
return std::nullopt;
292 std::sort(applicable_groups.begin(), applicable_groups.end(),
descending);
293 std::vector<char> vfBest;
297 if (nBest != nTargetValue && nTotalLower >= nTargetValue + change_target) {
298 ApproximateBestSubset(rng, applicable_groups, nTotalLower, nTargetValue + change_target, vfBest, nBest);
304 ((nBest != nTargetValue && nBest < nTargetValue + change_target) || lowest_larger->GetSelectionAmount() <= nBest)) {
307 for (
unsigned int i = 0; i < applicable_groups.size(); i++) {
309 result.
AddInput(applicable_groups[i]);
314 std::string log_message{
"Coin selection best subset: "};
315 for (
unsigned int i = 0; i < applicable_groups.size(); i++) {
378 CAmount selected_effective_value = 0;
379 for (
const COutput& coin : inputs) {
380 waste += coin.GetFee() - coin.long_term_fee;
381 selected_effective_value += use_effective_value ? coin.GetEffectiveValue() : coin.txout.nValue;
388 waste += change_cost;
391 assert(selected_effective_value >= target);
392 waste += selected_effective_value - target;
404 const auto upper_bound = std::min(payment_value * 2,
CHANGE_UPPER);
507 if (change < min_viable_change) {
bool m_subtract_fee_outputs
Indicate that we are subtracting the fee from outputs.
COutPoint outpoint
The outpoint identifying this UTXO.
CAmount m_value
The total value of the UTXOs in sum.
CAmount GetSelectionWaste(const std::set< COutput > &inputs, CAmount change_cost, CAmount target, bool use_effective_value)
Compute the waste for this result given the cost of change and the opportunity cost of spending these...
CAmount GetEffectiveValue() const
struct wallet::@16 descending
#define LogPrint(category,...)
CAmount fee
The fee to spend these UTXOs at the effective feerate.
const uint64_t max_descendants
Maximum number of descendants that a single UTXO in the OutputGroup may have.
size_t m_ancestors
The aggregated count of unconfirmed ancestors of all UTXOs in this group.
void AddInput(const OutputGroup &group)
const uint64_t max_ancestors
Maximum number of unconfirmed ancestors aggregated across all UTXOs in an OutputGroup.
static constexpr CAmount CHANGE_LOWER
lower bound for randomly-chosen target change amount
void Insert(const COutput &output, size_t ancestors, size_t descendants, bool positive_only)
void insert(Tdst &dst, const Tsrc &src)
Simplification of std insertion.
bool operator<(SelectionResult other) const
std::optional< SelectionResult > SelectCoinsBnB(std::vector< OutputGroup > &utxo_pool, const CAmount &selection_target, const CAmount &cost_of_change)
bool m_use_effective
Whether the input values for calculations should be the effective value (true) or normal value (false...
CAmount GetChange(const CAmount min_viable_change, const CAmount change_fee) const
Get the amount for the change output after paying needed fees.
CAmount effective_value
The value of the UTXOs after deducting the cost of spending them at the effective feerate...
CTxOut txout
The output itself.
int64_t CAmount
Amount in satoshis (Can be negative)
std::string ToString() const
CAmount long_term_fee
The fee required to spend this output at the consolidation feerate.
std::set< COutput > m_selected_inputs
Set of inputs selected by the algorithm to use in the transaction.
bool EligibleForSpending(const CoinEligibilityFilter &eligibility_filter) const
size_t m_descendants
The maximum count of descendants of a single UTXO in this output group.
bool from_me
Whether the transaction containing this output is sent from the owning wallet.
CAmount GetSelectedEffectiveValue() const
std::optional< CAmount > m_waste
The computed waste.
int depth
Depth in block chain.
CFeeRate m_long_term_feerate
The feerate for spending a created change output eventually (i.e.
static const size_t TOTAL_TRIES
static void ApproximateBestSubset(FastRandomContext &insecure_rand, const std::vector< OutputGroup > &groups, const CAmount &nTotalLower, const CAmount &nTargetValue, std::vector< char > &vfBest, CAmount &nBest, int iterations=1000)
Find a subset of the OutputGroups that is at least as large as, but as close as possible to...
CAmount GetSelectionAmount() const
SelectionAlgorithm m_algo
The algorithm used to produce this result.
std::string ToString() const
A group of UTXOs paid to the same output script.
void ComputeAndSetWaste(const CAmount min_viable_change, const CAmount change_cost, const CAmount change_fee)
Calculates and stores the waste for this selection via GetSelectionWaste.
#define Assume(val)
Assume is the identity function.
const std::set< COutput > & GetInputSet() const
Get m_selected_inputs.
Parameters for filtering which OutputGroups we may use in coin selection.
std::string FormatMoney(const CAmount n)
Money parsing/formatting utilities.
const int conf_mine
Minimum number of confirmations for outputs that we sent to ourselves.
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
std::optional< SelectionResult > SelectCoinsSRD(const std::vector< OutputGroup > &utxo_pool, CAmount target_value, FastRandomContext &rng)
Select coins by Single Random Draw.
int input_bytes
Pre-computed estimated size of this output as a fully-signed input in a transaction.
int m_depth
The minimum number of confirmations the UTXOs in the group have.
std::string GetAlgorithmName(const SelectionAlgorithm algo)
static constexpr CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
bool randbool() noexcept
Generate a random boolean.
CAmount GetFee(uint32_t num_bytes) const
Return the fee in satoshis for the given vsize in vbytes.
static bool LogAcceptCategory(BCLog::LogFlags category, BCLog::Level level)
Return true if log accepts specified category, at the specified level.
CAmount GetSelectedValue() const
Get the sum of the input values.
CAmount long_term_fee
The fee to spend these UTXOs at the long term feerate.
std::vector< COutput > GetShuffledInputVector() const
Get the vector of COutputs that will be used to fill in a CTransaction's vin.
static constexpr CAmount CHANGE_UPPER
upper bound for randomly-chosen target change amount
std::optional< SelectionResult > KnapsackSolver(std::vector< OutputGroup > &groups, const CAmount &nTargetValue, CAmount change_target, FastRandomContext &rng)
bool m_from_me
Whether the UTXOs were sent by the wallet to itself.
A UTXO under consideration for use in funding a new transaction.
CAmount GenerateChangeTarget(const CAmount payment_value, const CAmount change_fee, FastRandomContext &rng)
Choose a random change target for each transaction to make it harder to fingerprint the Core wallet b...
const int conf_theirs
Minimum number of confirmations for outputs received from a different wallet.
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
void Merge(const SelectionResult &other)
CAmount m_target
The target the algorithm selected for.
#define Assert(val)
Identity function.
std::vector< COutput > m_outputs
The list of UTXOs contained in this output group.