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.
1252// vouts to the payees1253txNew.vout.reserve(vecSend.size() + 1); // + 1 because of possible later insert1254for (const auto& recipient : vecSend)1255{1256txNew.vout.emplace_back(recipient.nAmount, GetScriptForDestination(recipient.dest));1257}1258const CAmount change_amount = result.GetChange(coin_selection_params.min_viable_change, coin_selection_params.m_change_fee);1259if (change_amount > 0) {1260CTxOut newTxOut(change_amount, scriptChange);1261if (!change_pos) {1262// Insert change txn at random position:1263change_pos = rng_fast.randrange(txNew.vout.size() + 1);1264} else if ((unsigned int)*change_pos > txNew.vout.size()) {1265return util::Error{_("Transaction change output index out of range")};1266}1267txNew.vout.insert(txNew.vout.begin() + *change_pos, newTxOut);1268} else {1269change_pos = std::nullopt;1270}1272// Shuffle selected coins and fill in final vin1273std::vector<std::shared_ptr<COutput>> selected_coins = result.GetShuffledInputVector();1275if (coin_control.HasSelected() && coin_control.HasSelectedOrder()) {1276// When there are preselected inputs, we need to move them to be the first UTXOs1277// and have them be in the order selected. We can use stable_sort for this, where we1278// compare with the positions stored in coin_control. The COutputs that have positions1279// will be placed before those that don't, and those positions will be in order.1280std::stable_sort(selected_coins.begin(), selected_coins.end(),1281[&coin_control](const std::shared_ptr<COutput>& a, const std::shared_ptr<COutput>& b) {1282auto a_pos = coin_control.GetSelectionPos(a->outpoint);1283auto b_pos = coin_control.GetSelectionPos(b->outpoint);1284if (a_pos.has_value() && b_pos.has_value()) {1285return a_pos.value() < b_pos.value();1286} else if (a_pos.has_value() && !b_pos.has_value()) {1287return true;1288} else {1289return false;1290}1291});1292}1294// The sequence number is set to non-maxint so that DiscourageFeeSniping1295// works.1296//1297// BIP125 defines opt-in RBF as any nSequence < maxint-1, so1298// we use the highest possible value in that range (maxint-2)1299// to avoid conflicting with other possible uses of nSequence,1300// and in the spirit of "smallest possible change from prior1301// behavior."1302bool use_anti_fee_sniping = true;1303const uint32_t default_sequence{coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : CTxIn::MAX_SEQUENCE_NONFINAL};1304txNew.vin.reserve(selected_coins.size());1305for (const auto& coin : selected_coins) {1306std::optional<uint32_t> sequence = coin_control.GetSequence(coin->outpoint);1307if (sequence) {1308// If an input has a preset sequence, we can't do anti-fee-sniping1309use_anti_fee_sniping = false;1310}1311txNew.vin.emplace_back(coin->outpoint, CScript{}, sequence.value_or(default_sequence));1313auto scripts = coin_control.GetScripts(coin->outpoint);1314if (scripts.first) {1315txNew.vin.back().scriptSig = *scripts.first;1316}1317if (scripts.second) {1318txNew.vin.back().scriptWitness = *scripts.second;1319}1320}1321if (coin_control.m_locktime) {1322txNew.nLockTime = coin_control.m_locktime.value();1323// If we have a locktime set, we can't use anti-fee-sniping1324use_anti_fee_sniping = false;1325}1326if (use_anti_fee_sniping) {1327DiscourageFeeSniping(txNew, rng_fast, wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight());1328}1330// Calculate the transaction fee1331TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);1332int nBytes = tx_sizes.vsize;1333if (nBytes == -1) {1334return util::Error{_("Missing solving data for estimating transaction size")};1335}1336CAmount fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes) + result.GetTotalBumpFees();1337const CAmount output_value = CalculateOutputValue(txNew);1338Assume(recipients_sum + change_amount == output_value);1339CAmount current_fee = result.GetSelectedValue() - output_value;1341// Sanity check that the fee cannot be negative as that means we have more output value than input value1342if (current_fee < 0) {1343return util::Error{Untranslated(STR_INTERNAL_BUG("Fee paid < 0"))};1344}1346// If there is a change output and we overpay the fees then increase the change to match the fee needed1347if (change_pos && fee_needed < current_fee) {1348auto& change = txNew.vout.at(*change_pos);1349change.nValue += current_fee - fee_needed;1350current_fee = result.GetSelectedValue() - CalculateOutputValue(txNew);1351if (fee_needed != current_fee) {1352return util::Error{Untranslated(STR_INTERNAL_BUG("Change adjustment: Fee needed != fee paid"))};1353}1354}1356// Reduce output values for subtractFeeFromAmount1357if (coin_selection_params.m_subtract_fee_outputs) {1358CAmount to_reduce = fee_needed - current_fee;1359unsigned int i = 0;1360bool fFirst = true;1361for (const auto& recipient : vecSend)1362{1363if (change_pos && i == *change_pos) {1364++i;1365}1366CTxOut& txout = txNew.vout[i];1368if (recipient.fSubtractFeeFromAmount)1369{1370txout.nValue -= to_reduce / outputs_to_subtract_fee_from; // Subtract fee equally from each selected recipient1372if (fFirst) // first receiver pays the remainder not divisible by output count1373{1374fFirst = false;1375txout.nValue -= to_reduce % outputs_to_subtract_fee_from;1376}1378// Error if this output is reduced to be below dust1379if (IsDust(txout, wallet.chain().relayDustFee())) {1380if (txout.nValue < 0) {1381return util::Error{_("The transaction amount is too small to pay the fee")};1382} else {1383return util::Error{_("The transaction amount is too small to send after the fee has been deducted")};1384}1385}1386}1387++i;1388}1389current_fee = result.GetSelectedValue() - CalculateOutputValue(txNew);1390if (fee_needed != current_fee) {1391return util::Error{Untranslated(STR_INTERNAL_BUG("SFFO: Fee needed != fee paid"))};1392}1393}1395// fee_needed should now always be less than or equal to the current fees that we pay.1396// If it is not, it is a bug.1397if (fee_needed > current_fee) {1398return util::Error{Untranslated(STR_INTERNAL_BUG("Fee needed > fee paid"))};1399}1401// Give up if change keypool ran out and change is required1402if (scriptChange.empty() && change_pos) {1403return util::Error{error};1404}1406if (sign && !wallet.SignTransaction(txNew)) {1407return util::Error{_("Signing transaction failed")};1408}1410// Return the constructed transaction data.1411CTransactionRef tx = MakeTransactionRef(std::move(txNew));1413// Limit size1414if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||1415(!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))1416{1417return util::Error{_("Transaction too large")};1418}1420if (current_fee > wallet.m_default_max_tx_fee) {1421return util::Error{TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED)};1422}1424if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {1425// Lastly, ensure this tx will pass the mempool's chain limits1426auto result = wallet.chain().checkChainLimits(tx);1427if (!result) {1428return util::Error{util::ErrorString(result)};1429}1430}1432// Before we return success, we assume any change key will be used to prevent1433// accidental reuse.1434reservedest.KeepDestination();1436wallet.WalletLogPrintf("Coin Selection: Algorithm:%s, Waste Metric Score:%d\n", GetAlgorithmName(result.GetAlgo()), result.GetWaste());1437wallet.WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u, Source: %s\n",1438current_fee, nBytes, StringForFeeReason(min_fee_rate.fee_reason));1439return CreatedTransactionResult(tx, current_fee, change_pos, min_fee_rate.fee_reason);1440}1442util::Result<CreatedTransactionResult> CreateTransaction(1443CWallet& wallet,1444const std::vector<CRecipient>& vecSend,1445std::optional<unsigned int> change_pos,1446const CCoinControl& coin_control,1447bool sign)1448{1449if (vecSend.empty()) {1450return util::Error{_("Transaction must have at least one recipient")};1451}1453if (std::any_of(vecSend.cbegin(), vecSend.cend(), [](const auto& recipient){ return recipient.nAmount < 0; })) {1454return util::Error{_("Transaction amounts must not be negative")};1455}1457LOCK(wallet.cs_wallet);1459auto res = CreateTransactionInternal(wallet, vecSend, change_pos, coin_control, sign);1460TRACEPOINT(coin_selection, normal_create_tx_internal,1461wallet.GetName().c_str(),1462bool(res),1463res ? res->fee : 0,1464res && res->change_pos.has_value() ? int32_t(*res->change_pos) : -1);1465if (!res) return res;1466const auto& txr_ungrouped = *res;1467// try with avoidpartialspends unless it's enabled already1468if (txr_ungrouped.fee > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {1469TRACEPOINT(coin_selection, attempting_aps_create_tx, wallet.GetName().c_str());1470CCoinControl tmp_cc = coin_control;1471tmp_cc.m_avoid_partial_spends = true;1473// Reuse the change destination from the first creation attempt to avoid skipping BIP44 indexes1474if (txr_ungrouped.change_pos) {1475ExtractDestination(txr_ungrouped.tx->vout[*txr_ungrouped.change_pos].scriptPubKey, tmp_cc.destChange);1476}1478auto txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, tmp_cc, sign);1479// if fee of this alternative one is within the range of the max fee, we use this one1480const bool use_aps{txr_grouped.has_value() ? (txr_grouped->fee <= txr_ungrouped.fee + wallet.m_max_aps_fee) : false};1481TRACEPOINT(coin_selection, aps_create_tx_internal,1482wallet.GetName().c_str(),1483use_aps,1484txr_grouped.has_value(),1485txr_grouped.has_value() ? txr_grouped->fee : 0,1486txr_grouped.has_value() && txr_grouped->change_pos.has_value() ? int32_t(*txr_grouped->change_pos) : -1);1487if (txr_grouped) {1488wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n",1489txr_ungrouped.fee, txr_grouped->fee, use_aps ? "grouped" : "non-grouped");1490if (use_aps) return txr_grouped;1491}1492}1493return res;1494}1496util::Result<CreatedTransactionResult> FundTransaction(CWallet& wallet, const CMutableTransaction& tx, const std::vector<CRecipient>& vecSend, std::optional<unsigned int> change_pos, bool lockUnspents, CCoinControl coinControl)1497{1498// We want to make sure tx.vout is not used now that we are passing outputs as a vector of recipients.1499// This sets us up to remove tx completely in a future PR in favor of passing the inputs directly.1500assert(tx.vout.empty());