Inside Bitcoin
Opening the selected file…
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.
1// Copyright (c) 2010 Satoshi Nakamoto2// Copyright (c) 2009-present The Bitcoin Core developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <consensus/validation.h>7#include <index/txindex.h>8#include <net.h>9#include <net_processing.h>10#include <node/blockstorage.h>11#include <node/context.h>12#include <node/types.h>13#include <txmempool.h>14#include <validation.h>15#include <validationinterface.h>16#include <node/transaction.h>18namespace node {19static TransactionError HandleATMPError(const TxValidationState& state, std::string& err_string_out)20{21err_string_out = state.ToString();22if (state.IsInvalid()) {23if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {24return TransactionError::MISSING_INPUTS;25}26return TransactionError::MEMPOOL_REJECTED;27} else {28return TransactionError::MEMPOOL_ERROR;29}30}32TransactionError BroadcastTransaction(NodeContext& node,33const CTransactionRef tx,34std::string& err_string,35const CAmount& max_tx_fee,36TxBroadcast broadcast_method,37bool wait_callback)38{39// BroadcastTransaction can be called by RPC or by the wallet.40// chainman, mempool and peerman are initialized before the RPC server and wallet are started41// and reset after the RPC sever and wallet are stopped.42assert(node.chainman);43assert(node.mempool);44assert(node.peerman);46Txid txid = tx->GetHash();47Wtxid wtxid = tx->GetWitnessHash();48bool callback_set = false;50{51LOCK(cs_main);53// If the transaction is already confirmed in the chain, don't do anything54// and return early.55CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();56for (size_t o = 0; o < tx->vout.size(); o++) {57const Coin& existingCoin = view.AccessCoin(COutPoint(txid, o));58// IsSpent doesn't mean the coin is spent, it means the output doesn't exist.59// So if the output does exist, then this transaction exists in the chain.60if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_UTXO_SET;61}63if (auto mempool_tx = node.mempool->get(txid); mempool_tx) {64// There's already a transaction in the mempool with this txid. Don't65// try to submit this transaction to the mempool (since it'll be66// rejected as a TX_CONFLICT), but do attempt to reannounce the mempool67// transaction if broadcast_method is not TxBroadcast::MEMPOOL_NO_BROADCAST.68//69// The mempool transaction may have the same or different witness (and70// wtxid) as this transaction. Use the mempool's wtxid for reannouncement.71wtxid = mempool_tx->GetWitnessHash();72} else {73// Transaction is not already in the mempool.74const bool check_max_fee{max_tx_fee > 0};75if (check_max_fee || broadcast_method == TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST) {76// First, call ATMP with test_accept and check the fee. If ATMP77// fails here, return error immediately.78const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);79if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {80return HandleATMPError(result.m_state, err_string);81} else if (check_max_fee && result.m_base_fees.value() > max_tx_fee) {82return TransactionError::MAX_FEE_EXCEEDED;83}84}86switch (broadcast_method) {87case TxBroadcast::MEMPOOL_NO_BROADCAST:88case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:89// Try to submit the transaction to the mempool.90{91const MempoolAcceptResult result =92node.chainman->ProcessTransaction(tx, /*test_accept=*/false);93if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {94return HandleATMPError(result.m_state, err_string);95}96}97// Transaction was accepted to the mempool.99if (broadcast_method == TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL) {100// the mempool tracks locally submitted transactions to make a101// best-effort of initial broadcast102node.mempool->AddUnbroadcastTx(txid);103}104break;105case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:106break;107}109if (wait_callback && node.validation_signals) {110// For transactions broadcast from outside the wallet, make sure111// that the wallet has been notified of the transaction before112// continuing.113//114// This prevents a race where a user might call sendrawtransaction115// with a transaction to/from their wallet, immediately call some116// wallet RPC, and get a stale result because callbacks have not117// yet been processed.118callback_set = true;119}120}121} // cs_main123if (callback_set) {124// Wait until Validation Interface clients have been notified of the125// transaction entering the mempool.126node.validation_signals->SyncWithValidationInterfaceQueue();127}129switch (broadcast_method) {130case TxBroadcast::MEMPOOL_NO_BROADCAST:131break;132case TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL:133node.peerman->InitiateTxBroadcastToAll(wtxid);134break;135case TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST:136return node.peerman->InitiateTxBroadcastPrivate(tx);137}139return TransactionError::OK;140}142CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const Txid& hash, const BlockManager& blockman, uint256& hashBlock)143{144if (mempool && !block_index) {145CTransactionRef ptx = mempool->get(hash);146if (ptx) return ptx;147}148if (g_txindex) {149if (auto result{g_txindex->FindTx(hash)}) {150if (!block_index || block_index->GetBlockHash() == result->block_hash) {151// Don't return the transaction if the provided block hash doesn't match.152// The case where a transaction appears in multiple blocks (e.g. reorgs or153// BIP30) is handled by the block lookup below.154hashBlock = result->block_hash;155return result->tx;156}157}158}159if (block_index) {160CBlock block;161if (blockman.ReadBlock(block, *block_index)) {162for (const auto& tx : block.vtx) {163if (tx->GetHash() == hash) {164hashBlock = block_index->GetBlockHash();165return tx;166}167}168}169}170return nullptr;171}172} // namespace node