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.
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 <rpc/mempool.h>7#include <rpc/register.h> // IWYU pragma: associated9#include <common/args.h>10#include <consensus/amount.h>11#include <consensus/validation.h>12#include <core_io.h>13#include <index/txospenderindex.h>14#include <net.h>15#include <net_processing.h>16#include <netaddress.h>17#include <netbase.h>18#include <node/mempool_persist.h>19#include <node/mempool_persist_args.h>20#include <node/transaction.h>21#include <node/txorphanage.h>22#include <node/types.h>23#include <policy/feerate.h>24#include <policy/packages.h>25#include <policy/policy.h>26#include <policy/rbf.h>27#include <primitives/transaction.h>28#include <rpc/protocol.h>29#include <rpc/request.h>30#include <rpc/server.h>31#include <rpc/server_util.h>32#include <rpc/util.h>33#include <script/script.h>34#include <sync.h>35#include <tinyformat.h>36#include <txgraph.h>37#include <txmempool.h>38#include <uint256.h>39#include <univalue.h>40#include <util/check.h>41#include <util/expected.h>42#include <util/feefrac.h>43#include <util/fs.h>44#include <util/moneystr.h>45#include <util/string.h>46#include <util/time.h>47#include <util/vector.h>48#include <validation.h>50#include <algorithm>51#include <cstddef>52#include <cstdint>53#include <functional>54#include <list>55#include <map>56#include <memory>57#include <optional>58#include <ranges>59#include <set>60#include <string>61#include <string_view>62#include <tuple>63#include <utility>64#include <vector>66namespace node {67struct NodeContext;68} // namespace node70using node::DumpMempool;72using node::DEFAULT_MAX_BURN_AMOUNT;73using node::DEFAULT_MAX_RAW_TX_FEE_RATE;74using node::MempoolPath;75using node::NodeContext;76using node::TransactionError;77using util::ToString;79static RPCMethod sendrawtransaction()80{81return RPCMethod{82"sendrawtransaction",83"Submit a raw transaction (serialized, hex-encoded) to the network.\n"85"\nIf -privatebroadcast is disabled, then the transaction will be put into the\n"86"local mempool of the node and will be sent unconditionally to all currently\n"87"connected peers, so using sendrawtransaction for manual rebroadcast will degrade\n"88"privacy by leaking the transaction's origin, as nodes will normally not\n"89"rebroadcast non-wallet transactions already in their mempool.\n"91"\nIf -privatebroadcast is enabled, then the transaction will be sent via\n"92"dedicated, short-lived connections to Tor or I2P peers, or to IPv4/IPv6 peers\n"93"via the Tor network. This provides best-effort concealment of the transaction's origin.\n"94"Private broadcast is experimental and may change in future releases.\n"95"Submission does not itself add the transaction to the local mempool; normal\n"96"mempool acceptance and relay apply when it is received back from the network.\n"97"The private broadcast queue is bounded: when it is full, this RPC fails and\n"98"the transaction is not scheduled until an existing one completes or is\n"99"aborted. Use getprivatebroadcastinfo to inspect the queue and abortprivatebroadcast to abort.\n"101"\nA specific exception, RPC_TRANSACTION_ALREADY_IN_UTXO_SET, may throw if the transaction cannot be added to the mempool.\n"103"\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",104{105{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},106{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},107"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +108"/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},109{"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},110"Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n"111"If burning funds through unspendable outputs is desired, increase this value.\n"112"This check is based on heuristics and does not guarantee spendability of outputs.\n"},113},114RPCResult{115RPCResult::Type::STR_HEX, "", "The transaction hash in hex"116},117RPCExamples{118"\nCreate a transaction\n"119+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +120"Sign the transaction, and get back the hex\n"121+ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +122"\nSend the transaction (signed hex)\n"123+ HelpExampleCli("sendrawtransaction", "\"signedhex\"") +124"\nAs a JSON-RPC call\n"125+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")126},127[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue128{129const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);131CMutableTransaction mtx;132if (!DecodeHexTx(mtx, request.params[0].get_str())) {133throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");134}136for (const auto& out : mtx.vout) {137if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {138throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);139}140}142CTransactionRef tx(MakeTransactionRef(std::move(mtx)));144const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};146int64_t virtual_size = GetVirtualTransactionSize(*tx);147CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);149std::string err_string;150AssertLockNotHeld(cs_main);151NodeContext& node = EnsureAnyNodeContext(request.context);152const bool private_broadcast_enabled{gArgs.GetBoolArg("-privatebroadcast", DEFAULT_PRIVATE_BROADCAST)};153if (private_broadcast_enabled &&154!g_reachable_nets.Contains(NET_ONION) &&155!g_reachable_nets.Contains(NET_I2P)) {156throw JSONRPCError(RPC_MISC_ERROR,157"-privatebroadcast is enabled, but none of the Tor or I2P networks is "158"reachable. Maybe the location of the Tor proxy couldn't be retrieved "159"from the Tor daemon at startup. Check whether the Tor daemon is running "160"and that -torcontrol, -torpassword and -i2psam are configured properly.");161}162const auto method = private_broadcast_enabled ? node::TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST163: node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL;164const TransactionError err = BroadcastTransaction(node,165tx,166err_string,167max_raw_tx_fee,168method,169/*wait_callback=*/true);170if (TransactionError::OK != err) {171throw JSONRPCTransactionError(err, err_string);172}174return tx->GetHash().GetHex();175},176};177}179static RPCMethod getprivatebroadcastinfo()180{181return RPCMethod{182"getprivatebroadcastinfo",183"Returns information about transactions tracked for private broadcast.\n"184"Transactions that have reached the send-attempt limit remain in the result with attempts_remaining=0.\n"185"This method is only available when running with -privatebroadcast enabled.\n",186{},187RPCResult{188RPCResult::Type::OBJ, "", "",189{190{RPCResult::Type::ARR, "transactions", "",191{192{RPCResult::Type::OBJ, "", "",193{194{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},195{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},196{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"},197{RPCResult::Type::NUM_TIME, "time_added", "The time this transaction was added to the private broadcast queue (seconds since epoch)"},198{RPCResult::Type::NUM, "attempts_remaining", "The number of additional private broadcast send attempts allowed for this transaction"},199{RPCResult::Type::ARR, "peers", "Per-peer send and acknowledgment information for this transaction",200{201{RPCResult::Type::OBJ, "", "",202{203{RPCResult::Type::STR, "address", "The address of the peer to which the transaction was sent"},204{RPCResult::Type::NUM_TIME, "sent", "The time this transaction was picked for sending to this peer via private broadcast (seconds since epoch)"},205{RPCResult::Type::NUM_TIME, "received", /*optional=*/true, "The time this peer acknowledged reception of the transaction (seconds since epoch)"},206}},207}},208}},209}},210}},211RPCExamples{212HelpExampleCli("getprivatebroadcastinfo", "")213+ HelpExampleRpc("getprivatebroadcastinfo", "")214},215[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue216{217const NodeContext& node{EnsureAnyNodeContext(request.context)};218const PeerManager& peerman{EnsurePeerman(node)};219if (!peerman.GetInfo().private_broadcast) {220throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Private broadcast is not enabled. Ensure you're running Bitcoin Core with -privatebroadcast=1.");221}223const auto txs{peerman.GetPrivateBroadcastInfo()};225UniValue transactions(UniValue::VARR);226for (const auto& tx_info : txs) {227UniValue o(UniValue::VOBJ);228o.pushKV("txid", tx_info.tx->GetHash().ToString());229o.pushKV("wtxid", tx_info.tx->GetWitnessHash().ToString());230o.pushKV("hex", EncodeHexTx(*tx_info.tx));231o.pushKV("time_added", TicksSinceEpoch<std::chrono::seconds>(tx_info.time_added));232o.pushKV("attempts_remaining", tx_info.attempts_remaining);233UniValue peers(UniValue::VARR);234for (const auto& peer : tx_info.peers) {235UniValue p(UniValue::VOBJ);236p.pushKV("address", peer.address.ToStringAddrPort());237p.pushKV("sent", TicksSinceEpoch<std::chrono::seconds>(peer.sent));238if (peer.received.has_value()) {239p.pushKV("received", TicksSinceEpoch<std::chrono::seconds>(*peer.received));240}241peers.push_back(std::move(p));242}243o.pushKV("peers", std::move(peers));244transactions.push_back(std::move(o));245}247UniValue ret(UniValue::VOBJ);248ret.pushKV("transactions", std::move(transactions));249return ret;250},