Bitcoin Core 22.99.0
P2P Digital Currency
coinselection.cpp
Go to the documentation of this file.
1// Copyright (c) 2017-2020 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
6
7#include <consensus/amount.h>
8#include <policy/feerate.h>
9#include <util/check.h>
10#include <util/system.h>
11#include <util/moneystr.h>
12
13#include <numeric>
14#include <optional>
15
16// Descending order comparator
17struct {
18 bool operator()(const OutputGroup& a, const OutputGroup& b) const
19 {
21 }
23
24/*
25 * This is the Branch and Bound Coin Selection algorithm designed by Murch. It searches for an input
26 * set that can pay for the spending target and does not exceed the spending target by more than the
27 * cost of creating and spending a change output. The algorithm uses a depth-first search on a binary
28 * tree. In the binary tree, each node corresponds to the inclusion or the omission of a UTXO. UTXOs
29 * are sorted by their effective values and the trees is explored deterministically per the inclusion
30 * branch first. At each node, the algorithm checks whether the selection is within the target range.
31 * While the selection has not reached the target range, more UTXOs are included. When a selection's
32 * value exceeds the target range, the complete subtree deriving from this selection can be omitted.
33 * At that point, the last included UTXO is deselected and the corresponding omission branch explored
34 * instead. The search ends after the complete tree has been searched or after a limited number of tries.
35 *
36 * The search continues to search for better solutions after one solution has been found. The best
37 * solution is chosen by minimizing the waste metric. The waste metric is defined as the cost to
38 * spend the current inputs at the given fee rate minus the long term expected cost to spend the
39 * inputs, plus the amount the selection exceeds the spending target:
40 *
41 * waste = selectionTotal - target + inputs × (currentFeeRate - longTermFeeRate)
42 *
43 * The algorithm uses two additional optimizations. A lookahead keeps track of the total value of
44 * the unexplored UTXOs. A subtree is not explored if the lookahead indicates that the target range
45 * cannot be reached. Further, it is unnecessary to test equivalent combinations. This allows us
46 * to skip testing the inclusion of UTXOs that match the effective value and waste of an omitted
47 * predecessor.
48 *
49 * The Branch and Bound algorithm is described in detail in Murch's Master Thesis:
50 * https://murch.one/wp-content/uploads/2016/11/erhardt2016coinselection.pdf
51 *
52 * @param const std::vector<CInputCoin>& utxo_pool The set of UTXOs that we are choosing from.
53 * These UTXOs will be sorted in descending order by effective value and the CInputCoins'
54 * values are their effective values.
55 * @param const CAmount& selection_target This is the value that we want to select. It is the lower
56 * bound of the range.
57 * @param const CAmount& cost_of_change This is the cost of creating and spending a change output.
58 * This plus selection_target is the upper bound of the range.
59 * @param std::set<CInputCoin>& out_set -> This is an output parameter for the set of CInputCoins
60 * that have been selected.
61 * @param CAmount& value_ret -> This is an output parameter for the total value of the CInputCoins
62 * that were selected.
63 */
64
65static const size_t TOTAL_TRIES = 100000;
66
67bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret)
68{
69 out_set.clear();
70 CAmount curr_value = 0;
71
72 std::vector<bool> curr_selection; // select the utxo at this index
73 curr_selection.reserve(utxo_pool.size());
74
75 // Calculate curr_available_value
76 CAmount curr_available_value = 0;
77 for (const OutputGroup& utxo : utxo_pool) {
78 // Assert that this utxo is not negative. It should never be negative, effective value calculation should have removed it
79 assert(utxo.GetSelectionAmount() > 0);
80 curr_available_value += utxo.GetSelectionAmount();
81 }
82 if (curr_available_value < selection_target) {
83 return false;
84 }
85
86 // Sort the utxo_pool
87 std::sort(utxo_pool.begin(), utxo_pool.end(), descending);
88
89 CAmount curr_waste = 0;
90 std::vector<bool> best_selection;
91 CAmount best_waste = MAX_MONEY;
92
93 // Depth First search loop for choosing the UTXOs
94 for (size_t i = 0; i < TOTAL_TRIES; ++i) {
95 // Conditions for starting a backtrack
96 bool backtrack = false;
97 if (curr_value + curr_available_value < selection_target || // Cannot possibly reach target with the amount remaining in the curr_available_value.
98 curr_value > selection_target + cost_of_change || // Selected value is out of range, go back and try other branch
99 (curr_waste > best_waste && (utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) > 0)) { // Don't select things which we know will be more wasteful if the waste is increasing
100 backtrack = true;
101 } else if (curr_value >= selection_target) { // Selected value is within range
102 curr_waste += (curr_value - selection_target); // This is the excess value which is added to the waste for the below comparison
103 // Adding another UTXO after this check could bring the waste down if the long term fee is higher than the current fee.
104 // However we are not going to explore that because this optimization for the waste is only done when we have hit our target
105 // value. Adding any more UTXOs will be just burning the UTXO; it will go entirely to fees. Thus we aren't going to
106 // explore any more UTXOs to avoid burning money like that.
107 if (curr_waste <= best_waste) {
108 best_selection = curr_selection;
109 best_selection.resize(utxo_pool.size());
110 best_waste = curr_waste;
111 if (best_waste == 0) {
112 break;
113 }
114 }
115 curr_waste -= (curr_value - selection_target); // Remove the excess value as we will be selecting different coins now
116 backtrack = true;
117 }
118
119 // Backtracking, moving backwards
120 if (backtrack) {
121 // Walk backwards to find the last included UTXO that still needs to have its omission branch traversed.
122 while (!curr_selection.empty() && !curr_selection.back()) {
123 curr_selection.pop_back();
124 curr_available_value += utxo_pool.at(curr_selection.size()).GetSelectionAmount();
125 }
126
127 if (curr_selection.empty()) { // We have walked back to the first utxo and no branch is untraversed. All solutions searched
128 break;
129 }
130
131 // Output was included on previous iterations, try excluding now.
132 curr_selection.back() = false;
133 OutputGroup& utxo = utxo_pool.at(curr_selection.size() - 1);
134 curr_value -= utxo.GetSelectionAmount();
135 curr_waste -= utxo.fee - utxo.long_term_fee;
136 } else { // Moving forwards, continuing down this branch
137 OutputGroup& utxo = utxo_pool.at(curr_selection.size());
138
139 // Remove this utxo from the curr_available_value utxo amount
140 curr_available_value -= utxo.GetSelectionAmount();
141
142 // Avoid searching a branch if the previous UTXO has the same value and same waste and was excluded. Since the ratio of fee to
143 // long term fee is the same, we only need to check if one of those values match in order to know that the waste is the same.
144 if (!curr_selection.empty() && !curr_selection.back() &&
145 utxo.GetSelectionAmount() == utxo_pool.at(curr_selection.size() - 1).GetSelectionAmount() &&
146 utxo.fee == utxo_pool.at(curr_selection.size() - 1).fee) {
147 curr_selection.push_back(false);
148 } else {
149 // Inclusion branch first (Largest First Exploration)
150 curr_selection.push_back(true);
151 curr_value += utxo.GetSelectionAmount();
152 curr_waste += utxo.fee - utxo.long_term_fee;
153 }
154 }
155 }
156
157 // Check for solution
158 if (best_selection.empty()) {
159 return false;
160 }
161
162 // Set output set
163 value_ret = 0;
164 for (size_t i = 0; i < best_selection.size(); ++i) {
165 if (best_selection.at(i)) {
166 util::insert(out_set, utxo_pool.at(i).m_outputs);
167 value_ret += utxo_pool.at(i).m_value;
168 }
169 }
170
171 return true;
172}
173
174std::optional<std::pair<std::set<CInputCoin>, CAmount>> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value)
175{
176 std::set<CInputCoin> out_set;
177 CAmount value_ret = 0;
178
179 std::vector<size_t> indexes;
180 indexes.resize(utxo_pool.size());
181 std::iota(indexes.begin(), indexes.end(), 0);
182 Shuffle(indexes.begin(), indexes.end(), FastRandomContext());
183
184 CAmount selected_eff_value = 0;
185 for (const size_t i : indexes) {
186 const OutputGroup& group = utxo_pool.at(i);
187 Assume(group.GetSelectionAmount() > 0);
188 selected_eff_value += group.GetSelectionAmount();
189 value_ret += group.m_value;
190 util::insert(out_set, group.m_outputs);
191 if (selected_eff_value >= target_value) {
192 return std::make_pair(out_set, value_ret);
193 }
194 }
195 return std::nullopt;
196}
197
198static void ApproximateBestSubset(const std::vector<OutputGroup>& groups, const CAmount& nTotalLower, const CAmount& nTargetValue,
199 std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
200{
201 std::vector<char> vfIncluded;
202
203 vfBest.assign(groups.size(), true);
204 nBest = nTotalLower;
205
206 FastRandomContext insecure_rand;
207
208 for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
209 {
210 vfIncluded.assign(groups.size(), false);
211 CAmount nTotal = 0;
212 bool fReachedTarget = false;
213 for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
214 {
215 for (unsigned int i = 0; i < groups.size(); i++)
216 {
217 //The solver here uses a randomized algorithm,
218 //the randomness serves no real security purpose but is just
219 //needed to prevent degenerate behavior and it is important
220 //that the rng is fast. We do not use a constant random sequence,
221 //because there may be some privacy improvement by making
222 //the selection random.
223 if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i])
224 {
225 nTotal += groups[i].GetSelectionAmount();
226 vfIncluded[i] = true;
227 if (nTotal >= nTargetValue)
228 {
229 fReachedTarget = true;
230 if (nTotal < nBest)
231 {
232 nBest = nTotal;
233 vfBest = vfIncluded;
234 }
235 nTotal -= groups[i].GetSelectionAmount();
236 vfIncluded[i] = false;
237 }
238 }
239 }
240 }
241 }
242}
243
244bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet)
245{
246 setCoinsRet.clear();
247 nValueRet = 0;
248
249 // List of values less than target
250 std::optional<OutputGroup> lowest_larger;
251 std::vector<OutputGroup> applicable_groups;
252 CAmount nTotalLower = 0;
253
254 Shuffle(groups.begin(), groups.end(), FastRandomContext());
255
256 for (const OutputGroup& group : groups) {
257 if (group.GetSelectionAmount() == nTargetValue) {
258 util::insert(setCoinsRet, group.m_outputs);
259 nValueRet += group.m_value;
260 return true;
261 } else if (group.GetSelectionAmount() < nTargetValue + MIN_CHANGE) {
262 applicable_groups.push_back(group);
263 nTotalLower += group.GetSelectionAmount();
264 } else if (!lowest_larger || group.GetSelectionAmount() < lowest_larger->GetSelectionAmount()) {
265 lowest_larger = group;
266 }
267 }
268
269 if (nTotalLower == nTargetValue) {
270 for (const auto& group : applicable_groups) {
271 util::insert(setCoinsRet, group.m_outputs);
272 nValueRet += group.m_value;
273 }
274 return true;
275 }
276
277 if (nTotalLower < nTargetValue) {
278 if (!lowest_larger) return false;
279 util::insert(setCoinsRet, lowest_larger->m_outputs);
280 nValueRet += lowest_larger->m_value;
281 return true;
282 }
283
284 // Solve subset sum by stochastic approximation
285 std::sort(applicable_groups.begin(), applicable_groups.end(), descending);
286 std::vector<char> vfBest;
287 CAmount nBest;
288
289 ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue, vfBest, nBest);
290 if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) {
291 ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest);
292 }
293
294 // If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
295 // or the next bigger coin is closer), return the bigger coin
296 if (lowest_larger &&
297 ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->GetSelectionAmount() <= nBest)) {
298 util::insert(setCoinsRet, lowest_larger->m_outputs);
299 nValueRet += lowest_larger->m_value;
300 } else {
301 for (unsigned int i = 0; i < applicable_groups.size(); i++) {
302 if (vfBest[i]) {
303 util::insert(setCoinsRet, applicable_groups[i].m_outputs);
304 nValueRet += applicable_groups[i].m_value;
305 }
306 }
307
309 std::string log_message{"Coin selection best subset: "};
310 for (unsigned int i = 0; i < applicable_groups.size(); i++) {
311 if (vfBest[i]) {
312 log_message += strprintf("%s ", FormatMoney(applicable_groups[i].m_value));
313 }
314 }
315 LogPrint(BCLog::SELECTCOINS, "%stotal %s\n", log_message, FormatMoney(nBest));
316 }
317 }
318
319 return true;
320}
321
322/******************************************************************************
323
324 OutputGroup
325
326 ******************************************************************************/
327
328void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only) {
329 // Compute the effective value first
330 const CAmount coin_fee = output.m_input_bytes < 0 ? 0 : m_effective_feerate.GetFee(output.m_input_bytes);
331 const CAmount ev = output.txout.nValue - coin_fee;
332
333 // Filter for positive only here before adding the coin
334 if (positive_only && ev <= 0) return;
335
336 m_outputs.push_back(output);
337 CInputCoin& coin = m_outputs.back();
338
339 coin.m_fee = coin_fee;
340 fee += coin.m_fee;
341
344
345 coin.effective_value = ev;
347
348 m_from_me &= from_me;
349 m_value += output.txout.nValue;
350 m_depth = std::min(m_depth, depth);
351 // ancestors here express the number of ancestors the new coin will end up having, which is
352 // the sum, rather than the max; this will overestimate in the cases where multiple inputs
353 // have common ancestors
354 m_ancestors += ancestors;
355 // descendants is the count as seen from the top ancestor, not the descendants as seen from the
356 // coin itself; thus, this value is counted as the max, not the sum
357 m_descendants = std::max(m_descendants, descendants);
358}
359
360bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const
361{
362 return m_depth >= (m_from_me ? eligibility_filter.conf_mine : eligibility_filter.conf_theirs)
363 && m_ancestors <= eligibility_filter.max_ancestors
364 && m_descendants <= eligibility_filter.max_descendants;
365}
366
368{
370}
371
372CAmount GetSelectionWaste(const std::set<CInputCoin>& inputs, CAmount change_cost, CAmount target, bool use_effective_value)
373{
374 // This function should not be called with empty inputs as that would mean the selection failed
375 assert(!inputs.empty());
376
377 // Always consider the cost of spending an input now vs in the future.
378 CAmount waste = 0;
379 CAmount selected_effective_value = 0;
380 for (const CInputCoin& coin : inputs) {
381 waste += coin.m_fee - coin.m_long_term_fee;
382 selected_effective_value += use_effective_value ? coin.effective_value : coin.txout.nValue;
383 }
384
385 if (change_cost) {
386 // Consider the cost of making change and spending it in the future
387 // If we aren't making change, the caller should've set change_cost to 0
388 assert(change_cost > 0);
389 waste += change_cost;
390 } else {
391 // When we are not making change (change_cost == 0), consider the excess we are throwing away to fees
392 assert(selected_effective_value >= target);
393 waste += selected_effective_value - target;
394 }
395
396 return waste;
397}
static constexpr CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
Definition: amount.h:26
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
#define Assume(val)
Assume is the identity function.
Definition: check.h:72
CAmount GetFee(uint32_t num_bytes) const
Return the fee in satoshis for the given size in bytes.
Definition: feerate.cpp:23
A UTXO under consideration for use in funding a new transaction.
Definition: coinselection.h:21
int m_input_bytes
Pre-computed estimated size of this output as a fully-signed input in a transaction.
Definition: coinselection.h:59
CTxOut txout
Definition: coinselection.h:53
CAmount m_fee
Definition: coinselection.h:55
CAmount m_long_term_fee
Definition: coinselection.h:56
CAmount effective_value
Definition: coinselection.h:54
CAmount nValue
Definition: transaction.h:131
Fast randomness source.
Definition: random.h:120
bool randbool() noexcept
Generate a random boolean.
Definition: random.h:211
bool KnapsackSolver(const CAmount &nTargetValue, std::vector< OutputGroup > &groups, std::set< CInputCoin > &setCoinsRet, CAmount &nValueRet)
std::optional< std::pair< std::set< CInputCoin >, CAmount > > SelectCoinsSRD(const std::vector< OutputGroup > &utxo_pool, CAmount target_value)
Select coins by Single Random Draw.
bool SelectCoinsBnB(std::vector< OutputGroup > &utxo_pool, const CAmount &selection_target, const CAmount &cost_of_change, std::set< CInputCoin > &out_set, CAmount &value_ret)
static const size_t TOTAL_TRIES
static void ApproximateBestSubset(const std::vector< OutputGroup > &groups, const CAmount &nTotalLower, const CAmount &nTargetValue, std::vector< char > &vfBest, CAmount &nBest, int iterations=1000)
struct @16 descending
CAmount GetSelectionWaste(const std::set< CInputCoin > &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...
static constexpr CAmount MIN_CHANGE
target minimum change amount
Definition: coinselection.h:16
static bool LogAcceptCategory(BCLog::LogFlags category)
Return true if log accepts specified category.
Definition: logging.h:160
#define LogPrint(category,...)
Definition: logging.h:191
std::string FormatMoney(const CAmount n)
Money parsing/formatting utilities.
Definition: moneystr.cpp:15
@ SELECTCOINS
Definition: logging.h:48
void insert(Tdst &dst, const Tsrc &src)
Simplification of std insertion.
Definition: system.h:512
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
Definition: random.h:231
Parameters for filtering which OutputGroups we may use in coin selection.
const int conf_mine
Minimum number of confirmations for outputs that we sent to ourselves.
const uint64_t max_ancestors
Maximum number of unconfirmed ancestors aggregated across all UTXOs in an OutputGroup.
const int conf_theirs
Minimum number of confirmations for outputs received from a different wallet.
const uint64_t max_descendants
Maximum number of descendants that a single UTXO in the OutputGroup may have.
A group of UTXOs paid to the same output script.
bool m_from_me
Whether the UTXOs were sent by the wallet to itself.
std::vector< CInputCoin > m_outputs
The list of UTXOs contained in this output group.
CAmount long_term_fee
The fee to spend these UTXOs at the long term feerate.
CFeeRate m_long_term_feerate
The feerate for spending a created change output eventually (i.e.
CAmount GetSelectionAmount() const
int m_depth
The minimum number of confirmations the UTXOs in the group have.
CFeeRate m_effective_feerate
The target feerate of the transaction we're trying to build.
size_t m_descendants
The maximum count of descendants of a single UTXO in this output group.
CAmount m_value
The total value of the UTXOs in sum.
void Insert(const CInputCoin &output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only)
size_t m_ancestors
The aggregated count of unconfirmed ancestors of all UTXOs in this group.
CAmount effective_value
The value of the UTXOs after deducting the cost of spending them at the effective feerate.
bool EligibleForSpending(const CoinEligibilityFilter &eligibility_filter) const
CAmount fee
The fee to spend these UTXOs at the effective feerate.
bool m_subtract_fee_outputs
Indicate that we are subtracting the fee from outputs.
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
assert(!tx.IsCoinBase())