70 std::vector<bool> curr_selection;
71 curr_selection.reserve(utxo_pool.size());
72 CAmount actual_target = not_input_fees + target_value;
75 CAmount curr_available_value = 0;
78 assert(utxo.effective_value > 0);
79 curr_available_value += utxo.effective_value;
81 if (curr_available_value < actual_target) {
86 std::sort(utxo_pool.begin(), utxo_pool.end(),
descending);
89 std::vector<bool> best_selection;
95 bool backtrack =
false;
96 if (curr_value + curr_available_value < actual_target ||
97 curr_value > actual_target + cost_of_change ||
98 (curr_waste > best_waste && (utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) > 0)) {
100 }
else if (curr_value >= actual_target) {
101 curr_waste += (curr_value - actual_target);
106 if (curr_waste <= best_waste) {
107 best_selection = curr_selection;
108 best_selection.resize(utxo_pool.size());
109 best_waste = curr_waste;
110 if (best_waste == 0) {
114 curr_waste -= (curr_value - actual_target);
121 while (!curr_selection.empty() && !curr_selection.back()) {
122 curr_selection.pop_back();
123 curr_available_value += utxo_pool.at(curr_selection.size()).effective_value;
126 if (curr_selection.empty()) {
131 curr_selection.back() =
false;
132 OutputGroup& utxo = utxo_pool.at(curr_selection.size() - 1);
136 OutputGroup& utxo = utxo_pool.at(curr_selection.size());
143 if (!curr_selection.empty() && !curr_selection.back() &&
144 utxo.
effective_value == utxo_pool.at(curr_selection.size() - 1).effective_value &&
145 utxo.
fee == utxo_pool.at(curr_selection.size() - 1).fee) {
146 curr_selection.push_back(
false);
149 curr_selection.push_back(
true);
157 if (best_selection.empty()) {
163 for (
size_t i = 0; i < best_selection.size(); ++i) {
164 if (best_selection.at(i)) {
166 value_ret += utxo_pool.at(i).m_value;
174 std::vector<char>& vfBest,
CAmount& nBest,
int iterations = 1000)
176 std::vector<char> vfIncluded;
178 vfBest.assign(groups.size(),
true);
183 for (
int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
185 vfIncluded.assign(groups.size(),
false);
187 bool fReachedTarget =
false;
188 for (
int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
190 for (
unsigned int i = 0; i < groups.size(); i++)
198 if (nPass == 0 ? insecure_rand.
randbool() : !vfIncluded[i])
200 nTotal += groups[i].m_value;
201 vfIncluded[i] =
true;
202 if (nTotal >= nTargetValue)
204 fReachedTarget =
true;
210 nTotal -= groups[i].m_value;
211 vfIncluded[i] =
false;
226 std::vector<OutputGroup> applicable_groups;
232 if (group.m_value == nTargetValue) {
234 nValueRet += group.m_value;
236 }
else if (group.m_value < nTargetValue +
MIN_CHANGE) {
237 applicable_groups.push_back(group);
238 nTotalLower += group.m_value;
239 }
else if (!lowest_larger || group.m_value < lowest_larger->m_value) {
240 lowest_larger = group;
244 if (nTotalLower == nTargetValue) {
245 for (
const auto& group : applicable_groups) {
247 nValueRet += group.m_value;
252 if (nTotalLower < nTargetValue) {
253 if (!lowest_larger)
return false;
255 nValueRet += lowest_larger->m_value;
260 std::sort(applicable_groups.begin(), applicable_groups.end(),
descending);
261 std::vector<char> vfBest;
265 if (nBest != nTargetValue && nTotalLower >= nTargetValue +
MIN_CHANGE) {
272 ((nBest != nTargetValue && nBest < nTargetValue +
MIN_CHANGE) || lowest_larger->m_value <= nBest)) {
274 nValueRet += lowest_larger->m_value;
276 for (
unsigned int i = 0; i < applicable_groups.size(); i++) {
278 util::insert(setCoinsRet, applicable_groups[i].m_outputs);
279 nValueRet += applicable_groups[i].m_value;
285 for (
unsigned int i = 0; i < applicable_groups.size(); i++) {
344 coin.m_fee = coin.m_input_bytes < 0 ? 0 : effective_feerate.
GetFee(coin.m_input_bytes);
347 coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : long_term_feerate.
GetFee(coin.m_input_bytes);
350 coin.effective_value = coin.txout.nValue - coin.m_fee;
#define LogPrint(category,...)
void SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate)
Update the OutputGroup's fee, long_term_fee, and effective_value based on the given feerates...
static const CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
bool SelectCoinsBnB(std::vector< OutputGroup > &utxo_pool, const CAmount &target_value, const CAmount &cost_of_change, std::set< CInputCoin > &out_set, CAmount &value_ret, CAmount not_input_fees)
static constexpr CAmount MIN_CHANGE
target minimum change amount
void insert(Tdst &dst, const Tsrc &src)
Simplification of std insertion.
std::string FormatMoney(const CAmount &n)
Money parsing/formatting utilities.
const uint64_t max_descendants
std::vector< CInputCoin >::iterator Discard(const CInputCoin &output)
int64_t CAmount
Amount in satoshis (Can be negative)
void Insert(const CInputCoin &output, int depth, bool from_me, size_t ancestors, size_t descendants)
const uint64_t max_ancestors
std::vector< CInputCoin > m_outputs
static bool LogAcceptCategory(BCLog::LogFlags category)
Return true if log accepts specified category.
CAmount GetFee(size_t nBytes) const
Return the fee in satoshis for the given size in bytes.
OutputGroup GetPositiveOnlyGroup()
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
Fee rate in satoshis per kilobyte: CAmount / kB.
bool randbool() noexcept
Generate a random boolean.
bool KnapsackSolver(const CAmount &nTargetValue, std::vector< OutputGroup > &groups, std::set< CInputCoin > &setCoinsRet, CAmount &nValueRet)
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)
boost::optional< T > Optional
Substitute for C++17 std::optional.
bool EligibleForSpending(const CoinEligibilityFilter &eligibility_filter) const