24 return util::Error{
_(
"The inputs size exceeds the maximum weight. " 25 "Please try sending a smaller amount or manually consolidating your wallet's UTXOs")};
30 bool operator()(
const OutputGroup& a,
const OutputGroup& b)
const 32 return a.GetSelectionAmount() > b.GetSelectionAmount();
81 std::vector<size_t> curr_selection;
82 int curr_selection_weight = 0;
85 CAmount curr_available_value = 0;
89 assert(utxo.GetSelectionAmount() > 0);
90 curr_available_value += utxo.GetSelectionAmount();
92 if (curr_available_value < selection_target) {
97 std::sort(utxo_pool.begin(), utxo_pool.end(),
descending);
100 std::vector<size_t> best_selection;
103 bool is_feerate_high = utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee;
104 bool max_tx_weight_exceeded =
false;
107 for (
size_t curr_try = 0, utxo_pool_index = 0; curr_try <
TOTAL_TRIES; ++curr_try, ++utxo_pool_index) {
109 bool backtrack =
false;
110 if (curr_value + curr_available_value < selection_target ||
111 curr_value > selection_target + cost_of_change ||
112 (curr_waste > best_waste && is_feerate_high)) {
114 }
else if (curr_selection_weight > max_weight) {
115 max_tx_weight_exceeded =
true;
117 }
else if (curr_value >= selection_target) {
118 curr_waste += (curr_value - selection_target);
123 if (curr_waste <= best_waste) {
124 best_selection = curr_selection;
125 best_waste = curr_waste;
127 curr_waste -= (curr_value - selection_target);
132 if (curr_selection.empty()) {
137 for (--utxo_pool_index; utxo_pool_index > curr_selection.back(); --utxo_pool_index) {
138 curr_available_value += utxo_pool.at(utxo_pool_index).GetSelectionAmount();
142 assert(utxo_pool_index == curr_selection.back());
145 curr_waste -= utxo.fee - utxo.long_term_fee;
146 curr_selection_weight -= utxo.m_weight;
147 curr_selection.pop_back();
154 if (curr_selection.empty() ||
156 (utxo_pool_index - 1) == curr_selection.back() ||
159 utxo.
GetSelectionAmount() != utxo_pool.at(utxo_pool_index - 1).GetSelectionAmount() ||
160 utxo.
fee != utxo_pool.at(utxo_pool_index - 1).fee)
163 curr_selection.push_back(utxo_pool_index);
166 curr_selection_weight += utxo.
m_weight;
172 if (best_selection.empty()) {
177 for (
const size_t& i : best_selection) {
207 std::vector<size_t> indexes;
208 indexes.resize(utxo_pool.size());
209 std::iota(indexes.begin(), indexes.end(), 0);
210 Shuffle(indexes.begin(), indexes.end(), rng);
212 CAmount selected_eff_value = 0;
214 bool max_tx_weight_exceeded =
false;
215 for (
const size_t i : indexes) {
221 selected_eff_value +=
group.GetSelectionAmount();
222 weight +=
group.m_weight;
226 if (weight > max_weight) {
227 max_tx_weight_exceeded =
true;
233 }
while (!heap.empty() && weight > max_weight);
237 if (selected_eff_value >= target_value) {
239 while (!heap.empty()) {
262 std::vector<char>& vfBest,
CAmount& nBest,
int iterations = 1000)
264 std::vector<char> vfIncluded;
267 vfBest.assign(groups.size(),
true);
270 for (
int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
272 vfIncluded.assign(groups.size(),
false);
274 bool fReachedTarget =
false;
275 for (
int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
277 for (
unsigned int i = 0; i < groups.size(); i++)
285 if (nPass == 0 ? insecure_rand.
randbool() : !vfIncluded[i])
287 nTotal += groups[i].GetSelectionAmount();
288 vfIncluded[i] =
true;
289 if (nTotal >= nTargetValue)
291 fReachedTarget =
true;
299 nTotal -= groups[i].GetSelectionAmount();
300 vfIncluded[i] =
false;
314 std::optional<OutputGroup> lowest_larger;
317 std::vector<OutputGroup> applicable_groups;
320 Shuffle(groups.begin(), groups.end(), rng);
323 if (
group.GetSelectionAmount() == nTargetValue) {
326 }
else if (
group.GetSelectionAmount() < nTargetValue + change_target) {
327 applicable_groups.push_back(
group);
328 nTotalLower +=
group.GetSelectionAmount();
329 }
else if (!lowest_larger ||
group.GetSelectionAmount() < lowest_larger->GetSelectionAmount()) {
330 lowest_larger =
group;
334 if (nTotalLower == nTargetValue) {
335 for (
const auto&
group : applicable_groups) {
341 if (nTotalLower < nTargetValue) {
348 std::sort(applicable_groups.begin(), applicable_groups.end(),
descending);
349 std::vector<char> vfBest;
353 if (nBest != nTargetValue && nTotalLower >= nTargetValue + change_target) {
354 ApproximateBestSubset(rng, applicable_groups, nTotalLower, nTargetValue + change_target, vfBest, nBest);
360 ((nBest != nTargetValue && nBest < nTargetValue + change_target) || lowest_larger->GetSelectionAmount() <= nBest)) {
363 for (
unsigned int i = 0; i < applicable_groups.size(); i++) {
365 result.
AddInput(applicable_groups[i]);
380 std::string log_message{
"Coin selection best subset: "};
381 for (
unsigned int i = 0; i < applicable_groups.size(); i++) {
403 fee += coin.GetFee();
421 if (output->input_bytes > 0) {
440 if (
group.m_outputs.empty())
return;
443 if (insert_positive &&
group.GetSelectionAmount() > 0) {
461 const COutput& coin = *coin_ptr;
471 waste += change_cost;
475 assert(selected_effective_value >= target);
476 waste += selected_effective_value - target;
488 const auto upper_bound = std::min(payment_value * 2,
CHANGE_UPPER);
554 m_weight += std::accumulate(inputs.cbegin(), inputs.cend(), 0, [](
int sum,
const auto& coin) {
623 if (change < min_viable_change) {
bool m_subtract_fee_outputs
Indicate that we are subtracting the fee from outputs.
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.
COutPoint outpoint
The outpoint identifying this UTXO.
CAmount m_value
The total value of the UTXOs in sum.
std::map< OutputType, Groups > groups_by_type
static const int WITNESS_SCALE_FACTOR
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)
util::Result< SelectionResult > SelectCoinsBnB(std::vector< OutputGroup > &utxo_pool, const CAmount &selection_target, const CAmount &cost_of_change, int max_weight)
const uint64_t max_ancestors
Maximum number of unconfirmed ancestors aggregated across all UTXOs in an OutputGroup.
void AddInputs(const std::set< std::shared_ptr< COutput >> &inputs, bool subtract_fee_outputs)
int m_weight
Total weight of the UTXOs in this group.
static constexpr CAmount CHANGE_LOWER
lower bound for randomly-chosen target change amount
bool operator<(SelectionResult other) const
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...
void Push(const OutputGroup &group, OutputType type, bool insert_positive, bool insert_mixed)
CTxOut txout
The output itself.
int64_t CAmount
Amount in satoshis (Can be negative)
CAmount bump_fee_group_discount
How much individual inputs overestimated the bump fees for the shared ancestry.
std::string ToString() const
CAmount long_term_fee
The fee required to spend this output at the consolidation feerate.
CAmount GetTotalBumpFees() const
bool EligibleForSpending(const CoinEligibilityFilter &eligibility_filter) const
static util::Result< SelectionResult > ErrorMaxWeightExceeded()
size_t m_descendants
The maximum count of descendants of a single UTXO in this output group.
std::vector< OutputGroup > positive_group
CAmount GetSelectedEffectiveValue() const
std::set< std::shared_ptr< COutput > > m_selected_inputs
Set of inputs selected by the algorithm to use in the transaction.
bilingual_str _(const char *psz)
Translation function.
std::optional< CAmount > m_waste
The computed waste.
int depth
Depth in block chain.
const std::set< std::shared_ptr< COutput > > & GetInputSet() const
Get m_selected_inputs.
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...
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...
util::Result< SelectionResult > KnapsackSolver(std::vector< OutputGroup > &groups, const CAmount &nTargetValue, CAmount change_target, FastRandomContext &rng, int max_weight)
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.
void InsertInputs(const T &inputs)
#define Assume(val)
Assume is the identity function.
int m_weight
Total weight of the selected inputs.
std::vector< std::shared_ptr< COutput > > m_outputs
The list of UTXOs contained in this output group.
Parameters for filtering which OutputGroups we may use in coin selection.
std::vector< OutputGroup > mixed_group
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.
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.
int operator()(const OutputGroup &group1, const OutputGroup &group2) const
CAmount GetFee(uint32_t num_bytes) const
Return the fee in satoshis for the given vsize in vbytes.
void Insert(const std::shared_ptr< COutput > &output, size_t ancestors, size_t descendants)
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.
static constexpr CAmount CHANGE_UPPER
upper bound for randomly-chosen target change amount
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.
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 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.
void SetBumpFeeDiscount(const CAmount discount)
How much individual inputs overestimated the bump fees for shared ancestries.
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
void Merge(const SelectionResult &other)
Combines the.
CAmount m_target
The target the algorithm selected for.
#define Assert(val)
Identity function.