23 return util::Error{
_(
"The inputs size exceeds the maximum weight. "
24 "Please try sending a smaller amount or manually consolidating your wallet's UTXOs")};
29 bool operator()(
const OutputGroup& a,
const OutputGroup& b)
const
31 return a.GetSelectionAmount() > b.GetSelectionAmount();
80 std::vector<size_t> curr_selection;
81 int curr_selection_weight = 0;
84 CAmount curr_available_value = 0;
88 assert(utxo.GetSelectionAmount() > 0);
89 curr_available_value += utxo.GetSelectionAmount();
91 if (curr_available_value < selection_target) {
96 std::sort(utxo_pool.begin(), utxo_pool.end(),
descending);
99 std::vector<size_t> best_selection;
102 bool is_feerate_high = utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee;
103 bool max_tx_weight_exceeded =
false;
106 for (
size_t curr_try = 0, utxo_pool_index = 0; curr_try <
TOTAL_TRIES; ++curr_try, ++utxo_pool_index) {
108 bool backtrack =
false;
109 if (curr_value + curr_available_value < selection_target ||
110 curr_value > selection_target + cost_of_change ||
111 (curr_waste > best_waste && is_feerate_high)) {
113 }
else if (curr_selection_weight > max_weight) {
114 max_tx_weight_exceeded =
true;
116 }
else if (curr_value >= selection_target) {
117 curr_waste += (curr_value - selection_target);
122 if (curr_waste <= best_waste) {
123 best_selection = curr_selection;
124 best_waste = curr_waste;
126 curr_waste -= (curr_value - selection_target);
131 if (curr_selection.empty()) {
136 for (--utxo_pool_index; utxo_pool_index > curr_selection.back(); --utxo_pool_index) {
137 curr_available_value += utxo_pool.at(utxo_pool_index).GetSelectionAmount();
141 assert(utxo_pool_index == curr_selection.back());
145 curr_selection_weight -= utxo.
m_weight;
146 curr_selection.pop_back();
153 if (curr_selection.empty() ||
155 (utxo_pool_index - 1) == curr_selection.back() ||
158 utxo.
GetSelectionAmount() != utxo_pool.at(utxo_pool_index - 1).GetSelectionAmount() ||
159 utxo.
fee != utxo_pool.at(utxo_pool_index - 1).fee)
162 curr_selection.push_back(utxo_pool_index);
165 curr_selection_weight += utxo.
m_weight;
171 if (best_selection.empty()) {
176 for (
const size_t& i : best_selection) {
206 std::vector<size_t> indexes;
207 indexes.resize(utxo_pool.size());
208 std::iota(indexes.begin(), indexes.end(), 0);
209 Shuffle(indexes.begin(), indexes.end(), rng);
211 CAmount selected_eff_value = 0;
213 bool max_tx_weight_exceeded =
false;
214 for (
const size_t i : indexes) {
220 selected_eff_value +=
group.GetSelectionAmount();
221 weight +=
group.m_weight;
225 if (weight > max_weight) {
226 max_tx_weight_exceeded =
true;
232 }
while (!heap.empty() && weight > max_weight);
236 if (selected_eff_value >= target_value) {
238 while (!heap.empty()) {
261 std::vector<char>& vfBest,
CAmount& nBest,
int iterations = 1000)
263 std::vector<char> vfIncluded;
266 vfBest.assign(groups.size(),
true);
269 for (
int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
271 vfIncluded.assign(groups.size(),
false);
273 bool fReachedTarget =
false;
274 for (
int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
276 for (
unsigned int i = 0; i < groups.size(); i++)
284 if (nPass == 0 ? insecure_rand.
randbool() : !vfIncluded[i])
286 nTotal += groups[i].GetSelectionAmount();
287 vfIncluded[i] =
true;
288 if (nTotal >= nTargetValue)
290 fReachedTarget =
true;
298 nTotal -= groups[i].GetSelectionAmount();
299 vfIncluded[i] =
false;
313 std::optional<OutputGroup> lowest_larger;
316 std::vector<OutputGroup> applicable_groups;
319 Shuffle(groups.begin(), groups.end(), rng);
322 if (
group.GetSelectionAmount() == nTargetValue) {
325 }
else if (
group.GetSelectionAmount() < nTargetValue + change_target) {
326 applicable_groups.push_back(
group);
327 nTotalLower +=
group.GetSelectionAmount();
328 }
else if (!lowest_larger ||
group.GetSelectionAmount() < lowest_larger->GetSelectionAmount()) {
329 lowest_larger =
group;
333 if (nTotalLower == nTargetValue) {
334 for (
const auto&
group : applicable_groups) {
340 if (nTotalLower < nTargetValue) {
347 std::sort(applicable_groups.begin(), applicable_groups.end(),
descending);
348 std::vector<char> vfBest;
352 if (nBest != nTargetValue && nTotalLower >= nTargetValue + change_target) {
353 ApproximateBestSubset(rng, applicable_groups, nTotalLower, nTargetValue + change_target, vfBest, nBest);
359 ((nBest != nTargetValue && nBest < nTargetValue + change_target) || lowest_larger->GetSelectionAmount() <= nBest)) {
362 for (
unsigned int i = 0; i < applicable_groups.size(); i++) {
364 result.
AddInput(applicable_groups[i]);
379 std::string log_message{
"Coin selection best subset: "};
380 for (
unsigned int i = 0; i < applicable_groups.size(); i++) {
402 fee += coin.GetFee();
420 if (output->input_bytes > 0) {
439 if (
group.m_outputs.empty())
return;
442 if (insert_positive &&
group.GetSelectionAmount() > 0) {
459 CAmount selected_effective_value = 0;
460 for (
const auto& coin_ptr : inputs) {
461 const COutput& coin = *coin_ptr;
470 waste += change_cost;
473 assert(selected_effective_value >= target);
474 waste += selected_effective_value - target;
486 const auto upper_bound = std::min(payment_value * 2,
CHANGE_UPPER);
539 m_weight += std::accumulate(inputs.cbegin(), inputs.cend(), 0, [](
int sum,
const auto& coin) {
540 return sum + std::max(coin->input_bytes, 0) * WITNESS_SCALE_FACTOR;
608 if (change < min_viable_change) {
static constexpr CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
int64_t CAmount
Amount in satoshis (Can be negative)
#define Assert(val)
Identity function.
#define Assume(val)
Assume is the identity function.
CAmount GetFee(uint32_t num_bytes) const
Return the fee in satoshis for the given vsize in vbytes.
bool randbool() noexcept
Generate a random boolean.
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
std::string ToString() const
int operator()(const OutputGroup &group1, const OutputGroup &group2) const
static const int WITNESS_SCALE_FACTOR
#define LogPrint(category,...)
static bool LogAcceptCategory(BCLog::LogFlags category, BCLog::Level level)
Return true if log accepts specified category, at the specified level.
std::string FormatMoney(const CAmount n)
Money parsing/formatting utilities.
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
static constexpr CAmount CHANGE_LOWER
lower bound for randomly-chosen target change amount
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...
struct wallet::@16 descending
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,...
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, FastRandomContext &rng, int max_weight)
Select coins by Single Random Draw.
CAmount GetSelectionWaste(const std::set< std::shared_ptr< 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...
std::string GetAlgorithmName(const SelectionAlgorithm algo)
static const size_t TOTAL_TRIES
static util::Result< SelectionResult > ErrorMaxWeightExceeded()
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
A UTXO under consideration for use in funding a new transaction.
CAmount long_term_fee
The fee required to spend this output at the consolidation feerate.
COutPoint outpoint
The outpoint identifying this UTXO.
int depth
Depth in block chain.
std::string ToString() const
CTxOut txout
The output itself.
CAmount GetEffectiveValue() const
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.
const uint64_t max_descendants
Maximum number of descendants that a single UTXO in the OutputGroup may have.
const int conf_theirs
Minimum number of confirmations for outputs received from a different wallet.
const int conf_mine
Minimum number of confirmations for outputs that we sent to ourselves.
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.
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.
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
std::set< std::shared_ptr< COutput > > m_selected_inputs
Set of inputs selected by the algorithm to use in the transaction.
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.
void Merge(const SelectionResult &other)
Combines the.
const std::set< std::shared_ptr< COutput > > & GetInputSet() const
Get m_selected_inputs.
SelectionAlgorithm m_algo
The algorithm used to produce this result.
void AddInput(const OutputGroup &group)
void AddInputs(const std::set< std::shared_ptr< COutput >> &inputs, bool subtract_fee_outputs)
CAmount GetSelectedEffectiveValue() const
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)
CAmount GetSelectedValue() const
Get the sum of the input values.
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...
std::vector< std::shared_ptr< COutput > > GetShuffledInputVector() const
Get the vector of COutputs that will be used to fill in a CTransaction's vin.
bilingual_str _(const char *psz)
Translation function.