Bitcoin Core  27.99.0
P2P Digital Currency
coinselection.h
Go to the documentation of this file.
1 // Copyright (c) 2017-2022 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 
5 #ifndef BITCOIN_WALLET_COINSELECTION_H
6 #define BITCOIN_WALLET_COINSELECTION_H
7 
8 #include <consensus/amount.h>
9 #include <consensus/consensus.h>
10 #include <outputtype.h>
11 #include <policy/feerate.h>
12 #include <primitives/transaction.h>
13 #include <random.h>
14 #include <util/check.h>
15 #include <util/insert.h>
16 #include <util/result.h>
17 
18 #include <optional>
19 
20 
21 namespace wallet {
23 static constexpr CAmount CHANGE_LOWER{50000};
25 static constexpr CAmount CHANGE_UPPER{1000000};
26 
28 struct COutput {
29 private:
31  std::optional<CAmount> effective_value;
32 
34  std::optional<CAmount> fee;
35 
36 public:
39 
42 
48  int depth;
49 
52 
54  bool spendable;
55 
57  bool solvable;
58 
64  bool safe;
65 
67  int64_t time;
68 
70  bool from_me;
71 
74 
77 
78  COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const std::optional<CFeeRate> feerate = std::nullopt)
79  : outpoint{outpoint},
80  txout{txout},
81  depth{depth},
85  safe{safe},
86  time{time},
88  {
89  if (feerate) {
90  // base fee without considering potential unconfirmed ancestors
91  fee = input_bytes < 0 ? 0 : feerate.value().GetFee(input_bytes);
92  effective_value = txout.nValue - fee.value();
93  }
94  }
95 
96  COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const CAmount fees)
98  {
99  // if input_bytes is unknown, then fees should be 0, if input_bytes is known, then the fees should be a positive integer or 0 (input_bytes known and fees = 0 only happens in the tests)
100  assert((input_bytes < 0 && fees == 0) || (input_bytes > 0 && fees >= 0));
101  fee = fees;
102  effective_value = txout.nValue - fee.value();
103  }
104 
105  std::string ToString() const;
106 
107  bool operator<(const COutput& rhs) const
108  {
109  return outpoint < rhs.outpoint;
110  }
111 
112  void ApplyBumpFee(CAmount bump_fee)
113  {
114  assert(bump_fee >= 0);
115  ancestor_bump_fees = bump_fee;
116  assert(fee);
117  *fee += bump_fee;
118  // Note: assert(effective_value - bump_fee == nValue - fee.value());
119  effective_value = txout.nValue - fee.value();
120  }
121 
122  CAmount GetFee() const
123  {
124  assert(fee.has_value());
125  return fee.value();
126  }
127 
129  {
130  assert(effective_value.has_value());
131  return effective_value.value();
132  }
133 
134  bool HasEffectiveValue() const { return effective_value.has_value(); }
135 };
136 
142  size_t change_output_size = 0;
144  size_t change_spend_size = 0;
165  size_t tx_noinputs_size = 0;
177 
179  CAmount min_change_target, CFeeRate effective_feerate,
180  CFeeRate long_term_feerate, CFeeRate discard_feerate, size_t tx_noinputs_size, bool avoid_partial)
181  : rng_fast{rng_fast},
184  m_min_change_target(min_change_target),
185  m_effective_feerate(effective_feerate),
186  m_long_term_feerate(long_term_feerate),
187  m_discard_feerate(discard_feerate),
189  m_avoid_partial_spends(avoid_partial)
190  {
191  }
193  : rng_fast{rng_fast} {}
194 };
195 
200 {
203  const int conf_mine;
205  const int conf_theirs;
207  const uint64_t max_ancestors;
209  const uint64_t max_descendants;
211  const bool m_include_partial_groups{false};
212 
217 
218  bool operator<(const CoinEligibilityFilter& other) const {
220  < std::tie(other.conf_mine, other.conf_theirs, other.max_ancestors, other.max_descendants, other.m_include_partial_groups);
221  }
222 };
223 
226 {
228  std::vector<std::shared_ptr<COutput>> m_outputs;
232  bool m_from_me{true};
236  int m_depth{999};
239  size_t m_ancestors{0};
241  size_t m_descendants{0};
256  int m_weight{0};
257 
262  {}
263 
264  void Insert(const std::shared_ptr<COutput>& output, size_t ancestors, size_t descendants);
265  bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
266  CAmount GetSelectionAmount() const;
267 };
268 
269 struct Groups {
270  // Stores 'OutputGroup' containing only positive UTXOs (value > 0).
271  std::vector<OutputGroup> positive_group;
272  // Stores 'OutputGroup' which may contain both positive and negative UTXOs.
273  std::vector<OutputGroup> mixed_group;
274 };
275 
278 {
279  // Maps output type to output groups.
280  std::map<OutputType, Groups> groups_by_type;
281  // All inserted groups, no type distinction.
283 
284  // Based on the insert flag; appends group to the 'mixed_group' and, if value > 0, to the 'positive_group'.
285  // This affects both; the groups filtered by type and the overall groups container.
286  void Push(const OutputGroup& group, OutputType type, bool insert_positive, bool insert_mixed);
287  // Different output types count
288  size_t TypesCount() { return groups_by_type.size(); }
289 };
290 
291 typedef std::map<CoinEligibilityFilter, OutputGroupTypeMap> FilteredOutputGroups;
292 
307 [[nodiscard]] CAmount GenerateChangeTarget(const CAmount payment_value, const CAmount change_fee, FastRandomContext& rng);
308 
309 enum class SelectionAlgorithm : uint8_t
310 {
311  BNB = 0,
312  KNAPSACK = 1,
313  SRD = 2,
314  CG = 3,
315  MANUAL = 4,
316 };
317 
318 std::string GetAlgorithmName(const SelectionAlgorithm algo);
319 
321 {
322 private:
324  std::set<std::shared_ptr<COutput>> m_selected_inputs;
330  bool m_use_effective{false};
332  std::optional<CAmount> m_waste;
334  bool m_algo_completed{true};
338  int m_weight{0};
341 
342  template<typename T>
343  void InsertInputs(const T& inputs)
344  {
345  // Store sum of combined input sets to check that the results have no shared UTXOs
346  const size_t expected_count = m_selected_inputs.size() + inputs.size();
348  if (m_selected_inputs.size() != expected_count) {
349  throw std::runtime_error(STR_INTERNAL_BUG("Shared UTXOs among selection results"));
350  }
351  }
352 
367  [[nodiscard]] CAmount GetSelectionWaste(CAmount change_cost, CAmount target, bool use_effective_value = true);
368 
369 public:
370  explicit SelectionResult(const CAmount target, SelectionAlgorithm algo)
371  : m_target(target), m_algo(algo) {}
372 
373  SelectionResult() = delete;
374 
376  [[nodiscard]] CAmount GetSelectedValue() const;
377 
378  [[nodiscard]] CAmount GetSelectedEffectiveValue() const;
379 
380  [[nodiscard]] CAmount GetTotalBumpFees() const;
381 
382  void Clear();
383 
384  void AddInput(const OutputGroup& group);
385  void AddInputs(const std::set<std::shared_ptr<COutput>>& inputs, bool subtract_fee_outputs);
386 
388  void SetBumpFeeDiscount(const CAmount discount);
389 
391  void ComputeAndSetWaste(const CAmount min_viable_change, const CAmount change_cost, const CAmount change_fee);
392  [[nodiscard]] CAmount GetWaste() const;
393 
395  void SetAlgoCompleted(bool algo_completed);
396 
398  bool GetAlgoCompleted() const;
399 
401  void SetSelectionsEvaluated(size_t attempts);
402 
404  size_t GetSelectionsEvaluated() const ;
405 
412  void Merge(const SelectionResult& other);
413 
415  const std::set<std::shared_ptr<COutput>>& GetInputSet() const;
417  std::vector<std::shared_ptr<COutput>> GetShuffledInputVector() const;
418 
419  bool operator<(SelectionResult other) const;
420 
438  CAmount GetChange(const CAmount min_viable_change, const CAmount change_fee) const;
439 
440  CAmount GetTarget() const { return m_target; }
441 
442  SelectionAlgorithm GetAlgo() const { return m_algo; }
443 
444  int GetWeight() const { return m_weight; }
445 };
446 
447 util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change,
448  int max_weight);
449 
450 util::Result<SelectionResult> CoinGrinder(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, CAmount change_target, int max_weight);
451 
461 util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, CAmount change_fee, FastRandomContext& rng,
462  int max_weight);
463 
464 // Original coin selection algorithm as a fallback
465 util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
466  CAmount change_target, FastRandomContext& rng, int max_weight);
467 } // namespace wallet
468 
469 #endif // BITCOIN_WALLET_COINSELECTION_H
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
#define STR_INTERNAL_BUG(msg)
Definition: check.h:60
Fee rate in satoshis per kilovirtualbyte: CAmount / kvB.
Definition: feerate.h:33
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:29
An output of a transaction.
Definition: transaction.h:150
CAmount nValue
Definition: transaction.h:152
Fast randomness source.
Definition: random.h:145
void insert(Tdst &dst, const Tsrc &src)
Simplification of std insertion.
Definition: insert.h:14
util::Result< SelectionResult > SelectCoinsBnB(std::vector< OutputGroup > &utxo_pool, const CAmount &selection_target, const CAmount &cost_of_change, int max_weight)
static constexpr CAmount CHANGE_UPPER
upper bound for randomly-chosen target change amount
Definition: coinselection.h:25
static constexpr CAmount CHANGE_LOWER
lower bound for randomly-chosen target change amount
Definition: coinselection.h:23
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...
util::Result< SelectionResult > CoinGrinder(std::vector< OutputGroup > &utxo_pool, const CAmount &selection_target, CAmount change_target, int max_weight)
SelectionAlgorithm
util::Result< SelectionResult > KnapsackSolver(std::vector< OutputGroup > &groups, const CAmount &nTargetValue, CAmount change_target, FastRandomContext &rng, int max_weight)
util::Result< SelectionResult > SelectCoinsSRD(const std::vector< OutputGroup > &utxo_pool, CAmount target_value, CAmount change_fee, FastRandomContext &rng, int max_weight)
Select coins by Single Random Draw.
std::string GetAlgorithmName(const SelectionAlgorithm algo)
std::map< CoinEligibilityFilter, OutputGroupTypeMap > FilteredOutputGroups
OutputType
Definition: outputtype.h:17
A UTXO under consideration for use in funding a new transaction.
Definition: coinselection.h:28
CAmount long_term_fee
The fee required to spend this output at the consolidation feerate.
Definition: coinselection.h:73
bool from_me
Whether the transaction containing this output is sent from the owning wallet.
Definition: coinselection.h:70
COutPoint outpoint
The outpoint identifying this UTXO.
Definition: coinselection.h:38
std::optional< CAmount > effective_value
The output's value minus fees required to spend it and bump its unconfirmed ancestors to the target f...
Definition: coinselection.h:31
bool solvable
Whether we know how to spend this output, ignoring the lack of keys.
Definition: coinselection.h:57
int64_t time
The time of the transaction containing this output as determined by CWalletTx::nTimeSmart.
Definition: coinselection.h:67
int depth
Depth in block chain.
Definition: coinselection.h:48
std::string ToString() const
bool safe
Whether this output is considered safe to spend.
Definition: coinselection.h:64
bool spendable
Whether we have the private keys to spend this output.
Definition: coinselection.h:54
CTxOut txout
The output itself.
Definition: coinselection.h:41
COutput(const COutPoint &outpoint, const CTxOut &txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const CAmount fees)
Definition: coinselection.h:96
CAmount ancestor_bump_fees
The fee necessary to bump this UTXO's ancestor transactions to the target feerate.
Definition: coinselection.h:76
CAmount GetFee() const
int input_bytes
Pre-computed estimated size of this output as a fully-signed input in a transaction.
Definition: coinselection.h:51
CAmount GetEffectiveValue() const
bool operator<(const COutput &rhs) const
bool HasEffectiveValue() const
void ApplyBumpFee(CAmount bump_fee)
std::optional< CAmount > fee
The fee required to spend this output at the transaction's target feerate and to bump its unconfirmed...
Definition: coinselection.h:34
COutput(const COutPoint &outpoint, const CTxOut &txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const std::optional< CFeeRate > feerate=std::nullopt)
Definition: coinselection.h:78
Parameters for filtering which OutputGroups we may use in coin selection.
const uint64_t max_ancestors
Maximum number of unconfirmed ancestors aggregated across all UTXOs in an OutputGroup.
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants)
const bool m_include_partial_groups
When avoid_reuse=true and there are full groups (OUTPUT_GROUP_MAX_ENTRIES), whether or not to use any...
const uint64_t max_descendants
Maximum number of descendants that a single UTXO in the OutputGroup may have.
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors)
bool operator<(const CoinEligibilityFilter &other) const
const int conf_theirs
Minimum number of confirmations for outputs received from a different wallet.
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants, bool include_partial)
const int conf_mine
Minimum number of confirmations for outputs that we sent to ourselves.
Parameters for one iteration of Coin Selection.
CoinSelectionParams(FastRandomContext &rng_fast, size_t change_output_size, size_t change_spend_size, CAmount min_change_target, CFeeRate effective_feerate, CFeeRate long_term_feerate, CFeeRate discard_feerate, size_t tx_noinputs_size, bool avoid_partial)
FastRandomContext & rng_fast
Randomness to use in the context of coin selection.
CAmount m_min_change_target
Mininmum change to target in Knapsack solver and CoinGrinder: select coins to cover the payment and a...
bool m_subtract_fee_outputs
Indicate that we are subtracting the fee from outputs.
CoinSelectionParams(FastRandomContext &rng_fast)
bool m_include_unsafe_inputs
When true, allow unsafe coins to be selected during Coin Selection.
size_t change_output_size
Size of a change output in bytes, determined by the output type.
CFeeRate m_effective_feerate
The targeted feerate of the transaction being built.
CAmount min_viable_change
Minimum amount for creating a change output.
CAmount m_cost_of_change
Cost of creating the change output + cost of spending the change output in the future.
CAmount m_change_fee
Cost of creating the change output.
CFeeRate m_long_term_feerate
The feerate estimate used to estimate an upper bound on what should be sufficient to spend the change...
CFeeRate m_discard_feerate
If the cost to spend a change output at the discard feerate exceeds its value, drop it to fees.
size_t change_spend_size
Size of the input to spend a change output in virtual bytes.
size_t tx_noinputs_size
Size of the transaction before coin selection, consisting of the header and recipient output(s),...
bool m_avoid_partial_spends
When true, always spend all (up to OUTPUT_GROUP_MAX_ENTRIES) or none of the outputs associated with t...
std::vector< OutputGroup > positive_group
std::vector< OutputGroup > mixed_group
A group of UTXOs paid to the same output script.
CFeeRate m_long_term_feerate
The feerate for spending a created change output eventually (i.e.
bool m_from_me
Whether the UTXOs were sent by the wallet to itself.
OutputGroup(const CoinSelectionParams &params)
CAmount m_value
The total value of the UTXOs in sum.
void Insert(const std::shared_ptr< COutput > &output, size_t ancestors, size_t descendants)
bool m_subtract_fee_outputs
Indicate that we are subtracting the fee from outputs.
CAmount GetSelectionAmount() const
int m_depth
The minimum number of confirmations the UTXOs in the group have.
int m_weight
Total weight of the UTXOs in this group.
bool EligibleForSpending(const CoinEligibilityFilter &eligibility_filter) const
CAmount effective_value
The value of the UTXOs after deducting the cost of spending them at the effective feerate.
size_t m_ancestors
The aggregated count of unconfirmed ancestors of all UTXOs in this group.
CAmount fee
The fee to spend these UTXOs at the effective feerate.
CAmount long_term_fee
The fee to spend these UTXOs at the long term feerate.
size_t m_descendants
The maximum count of descendants of a single UTXO in this output group.
std::vector< std::shared_ptr< COutput > > m_outputs
The list of UTXOs contained in this output group.
Stores several 'Groups' whose were mapped by output type.
void Push(const OutputGroup &group, OutputType type, bool insert_positive, bool insert_mixed)
std::map< OutputType, Groups > groups_by_type
int m_weight
Total weight of the selected inputs.
bool operator<(SelectionResult other) const
size_t m_selections_evaluated
The count of selections that were evaluated by this coin selection attempt.
std::set< std::shared_ptr< COutput > > m_selected_inputs
Set of inputs selected by the algorithm to use in the transaction.
CAmount bump_fee_group_discount
How much individual inputs overestimated the bump fees for the shared ancestry.
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.
CAmount GetSelectionWaste(CAmount change_cost, CAmount target, bool use_effective_value=true)
Compute the waste for this result given the cost of change and the opportunity cost of spending these...
void Merge(const SelectionResult &other)
Combines the.
size_t GetSelectionsEvaluated() const
Get selections_evaluated.
const std::set< std::shared_ptr< COutput > > & GetInputSet() const
Get m_selected_inputs.
SelectionAlgorithm m_algo
The algorithm used to produce this result.
bool GetAlgoCompleted() const
Get m_algo_completed.
void SetBumpFeeDiscount(const CAmount discount)
How much individual inputs overestimated the bump fees for shared ancestries.
void AddInput(const OutputGroup &group)
void AddInputs(const std::set< std::shared_ptr< COutput >> &inputs, bool subtract_fee_outputs)
CAmount GetSelectedEffectiveValue() const
CAmount GetTotalBumpFees() const
bool m_algo_completed
False if algorithm was cut short by hitting limit of attempts and solution is non-optimal.
CAmount m_target
The target the algorithm selected for.
CAmount GetChange(const CAmount min_viable_change, const CAmount change_fee) const
Get the amount for the change output after paying needed fees.
void InsertInputs(const T &inputs)
void SetAlgoCompleted(bool algo_completed)
Tracks that algorithm was able to exhaustively search the entire combination space before hitting lim...
SelectionResult(const CAmount target, SelectionAlgorithm algo)
CAmount GetSelectedValue() const
Get the sum of the input values.
CAmount GetTarget() const
SelectionAlgorithm GetAlgo() const
std::optional< CAmount > m_waste
The computed waste.
bool m_use_effective
Whether the input values for calculations should be the effective value (true) or normal value (false...
void SetSelectionsEvaluated(size_t attempts)
Record the number of selections that were evaluated.
std::vector< std::shared_ptr< COutput > > GetShuffledInputVector() const
Get the vector of COutputs that will be used to fill in a CTransaction's vin.
CAmount GetWaste() const
assert(!tx.IsCoinBase())