Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
501curr_amount += utxo.GetSelectionAmount();502curr_weight += utxo.m_weight;503curr_selection.push_back(next_utxo);504++next_utxo;505++curr_try;507// EVALUATE current selection: check for solutions and see whether we can CUT or SHIFT before EXPLORING further508auto curr_tail = curr_selection.back();509if (curr_amount + lookahead[curr_tail] < total_target) {510// Insufficient funds with lookahead: CUT511should_cut = true;512} else if (curr_weight > best_selection_weight) {513// best_selection_weight is initialized to max_selection_weight514if (curr_weight > max_selection_weight) max_tx_weight_exceeded = true;515// Worse weight than best solution. More UTXOs only increase weight:516// CUT if last selected group had minimal weight, else SHIFT517if (utxo_pool[curr_tail].m_weight <= min_tail_weight[curr_tail]) {518should_cut = true;519} else {520should_shift = true;521}522} else if (curr_amount >= total_target) {523// Success, adding more weight cannot be better: SHIFT524should_shift = true;525if (curr_weight < best_selection_weight || (curr_weight == best_selection_weight && curr_amount < best_selection_amount)) {526// New lowest weight, or same weight with fewer funds tied up527best_selection = curr_selection;528best_selection_weight = curr_weight;529best_selection_amount = curr_amount;530}531} else if (!best_selection.empty() && curr_weight + int64_t{min_tail_weight[curr_tail]} * ((total_target - curr_amount + utxo_pool[curr_tail].GetSelectionAmount() - 1) / utxo_pool[curr_tail].GetSelectionAmount()) > best_selection_weight) {532// Compare minimal tail weight and last selected amount with the amount missing to gauge whether a better weight is still possible.533if (utxo_pool[curr_tail].m_weight <= min_tail_weight[curr_tail]) {534should_cut = true;535} else {536should_shift = true;537}538}540if (curr_try >= TOTAL_TRIES) {541// Solution is not guaranteed to be optimal if `curr_try` hit TOTAL_TRIES542result.SetAlgoCompleted(false);543break;544}546if (next_utxo == utxo_pool.size()) {547// Last added UTXO was end of UTXO pool, nothing left to add on inclusion or omission branch: CUT548should_cut = true;549}551if (should_cut) {552// Neither adding to the current selection nor exploring the omission branch of the last selected UTXO can553// find any solutions. Redirect to exploring the Omission branch of the penultimate selected UTXO (i.e.554// set `next_utxo` to one after the penultimate selected, then deselect the last two selected UTXOs)555deselect_last();556should_shift = true;557}559while (should_shift) {560// Set `next_utxo` to one after last selected, then deselect last selected UTXO561if (curr_selection.empty()) {562// Exhausted search space before running into attempt limit563is_done = true;564result.SetAlgoCompleted(true);565break;566}567next_utxo = curr_selection.back() + 1;568deselect_last();569should_shift = false;571// After SHIFTing to an omission branch, the `next_utxo` might have the same effective value as the UTXO we572// just omitted. Since lower weight is our tiebreaker on UTXOs with equal effective value for sorting, if it573// ties on the effective value, it _must_ have the same weight (i.e. be a "clone" of the prior UTXO) or a574// higher weight. If so, selecting `next_utxo` would produce an equivalent or worse selection as one we575// previously evaluated. In that case, increment `next_utxo` until we find a UTXO with a differing amount.576while (utxo_pool[next_utxo - 1].GetSelectionAmount() == utxo_pool[next_utxo].GetSelectionAmount()) {577if (next_utxo >= utxo_pool.size() - 1) {578// Reached end of UTXO pool skipping clones: SHIFT instead579should_shift = true;580break;581}582// Skip clone: previous UTXO is equivalent and unselected583++next_utxo;584}585}586}588result.SetSelectionsEvaluated(curr_try);590if (best_selection.empty()) {591return max_tx_weight_exceeded ? ErrorMaxWeightExceeded() : util::Error();592}594for (const size_t& i : best_selection) {595result.AddInput(utxo_pool[i]);596}598return result;599}601class MinOutputGroupComparator602{603public:604int operator() (const OutputGroup& group1, const OutputGroup& group2) const605{606return descending_effval_weight(group1, group2);607}608};610util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, CAmount change_fee, FastRandomContext& rng,611int max_selection_weight)612{613SelectionResult result(target_value, SelectionAlgorithm::SRD);614std::priority_queue<OutputGroup, std::vector<OutputGroup>, MinOutputGroupComparator> heap;616// Include change for SRD as we want to avoid making really small change if the selection just617// barely meets the target. Just use the lower bound change target instead of the randomly618// generated one, since SRD will result in a random change amount anyway; avoid making the619// target needlessly large.620target_value += CHANGE_LOWER + change_fee;622std::vector<size_t> indexes;623indexes.resize(utxo_pool.size());624std::iota(indexes.begin(), indexes.end(), 0);625std::shuffle(indexes.begin(), indexes.end(), rng);627CAmount selected_eff_value = 0;628int weight = 0;629bool max_tx_weight_exceeded = false;630for (const size_t i : indexes) {631const OutputGroup& group = utxo_pool.at(i);632Assume(group.GetSelectionAmount() > 0);634// Add group to selection635heap.push(group);636selected_eff_value += group.GetSelectionAmount();637weight += group.m_weight;639// If the selection weight exceeds the maximum allowed size, remove the least valuable inputs until we640// are below max weight.641if (weight > max_selection_weight) {642max_tx_weight_exceeded = true; // mark it in case we don't find any useful result.643do {644const OutputGroup& to_remove_group = heap.top();645selected_eff_value -= to_remove_group.GetSelectionAmount();646weight -= to_remove_group.m_weight;647heap.pop();648} while (!heap.empty() && weight > max_selection_weight);649}651// Now check if we are above the target652if (selected_eff_value >= target_value) {653// Result found, add it.654while (!heap.empty()) {655result.AddInput(heap.top());656heap.pop();657}658return result;659}660}661return max_tx_weight_exceeded ? ErrorMaxWeightExceeded() : util::Error();662}664/** Find a subset of the OutputGroups that is at least as large as, but as close as possible to, the665* target amount; solve subset sum.666* @param[in] groups OutputGroups to choose from, sorted by value in descending order.667* @param[in] nTotalLower Total (effective) value of the UTXOs in groups.668* @param[in] nTargetValue Subset sum target, not including change.669* @param[out] vfBest Boolean vector representing the subset chosen that is closest to670* nTargetValue, with indices corresponding to groups. If the ith671* entry is true, that means the ith group in groups was selected.672* @param[out] nBest Total amount of subset chosen that is closest to nTargetValue.673* @param[in] max_selection_weight The maximum allowed weight for a selection result to be valid.674* @param[in] iterations Maximum number of tries.675*/676static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::vector<OutputGroup>& groups,677const CAmount& nTotalLower, const CAmount& nTargetValue,678std::vector<char>& vfBest, CAmount& nBest, int max_selection_weight, int iterations = 1000)679{680std::vector<char> vfIncluded;682// Worst case "best" approximation is just all of the groups.683vfBest.assign(groups.size(), true);684nBest = nTotalLower;686for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)687{688vfIncluded.assign(groups.size(), false);689CAmount nTotal = 0;690int selected_coins_weight{0};691bool fReachedTarget = false;692for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)693{694for (unsigned int i = 0; i < groups.size(); i++)695{696//The solver here uses a randomized algorithm,697//the randomness serves no real security purpose but is just698//needed to prevent degenerate behavior and it is important699//that the rng is fast. We do not use a constant random sequence,700//because there may be some privacy improvement by making701//the selection random.702if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i])703{704nTotal += groups[i].GetSelectionAmount();705selected_coins_weight += groups[i].m_weight;706vfIncluded[i] = true;707if (nTotal >= nTargetValue && selected_coins_weight <= max_selection_weight) {708fReachedTarget = true;709// If the total is between nTargetValue and nBest, it's our new best710// approximation.711if (nTotal < nBest)712{713nBest = nTotal;714vfBest = vfIncluded;715}716nTotal -= groups[i].GetSelectionAmount();717selected_coins_weight -= groups[i].m_weight;718vfIncluded[i] = false;719}720}721}722}723}724}726util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,727CAmount change_target, FastRandomContext& rng, int max_selection_weight)728{729SelectionResult result(nTargetValue, SelectionAlgorithm::KNAPSACK);731bool max_weight_exceeded{false};732// List of values less than target733std::optional<OutputGroup> lowest_larger;734// Groups with selection amount smaller than the target and any change we might produce.735// Don't include groups larger than this, because they will only cause us to overshoot.736std::vector<OutputGroup> applicable_groups;737CAmount nTotalLower = 0;739std::shuffle(groups.begin(), groups.end(), rng);741for (const OutputGroup& group : groups) {742if (group.m_weight > max_selection_weight) {743max_weight_exceeded = true;744continue;745}746if (group.GetSelectionAmount() == nTargetValue) {747result.AddInput(group);748return result;749} else if (group.GetSelectionAmount() < nTargetValue + change_target) {750applicable_groups.push_back(group);