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 <rpc/rawtransaction_util.h>8#include <coins.h>9#include <consensus/amount.h>10#include <core_io.h>11#include <crypto/hex_base.h>12#include <key_io.h>13#include <policy/feerate.h>14#include <policy/policy.h>15#include <primitives/transaction.h>16#include <rpc/protocol.h>17#include <rpc/request.h>18#include <rpc/util.h>19#include <script/interpreter.h>20#include <script/script.h>21#include <script/sign.h>22#include <script/signingprovider.h>23#include <tinyformat.h>24#include <univalue.h>25#include <util/check.h>26#include <util/rbf.h>27#include <util/translation.h>28#include <util/vector.h>30#include <cstddef>31#include <set>32#include <span>33#include <variant>35void AddInputs(CMutableTransaction& rawTx, const UniValue& inputs_in, std::optional<bool> rbf)36{37UniValue inputs;38if (inputs_in.isNull()) {39inputs = UniValue::VARR;40} else {41inputs = inputs_in.get_array();42}44for (unsigned int idx = 0; idx < inputs.size(); idx++) {45const UniValue& input = inputs[idx];46const UniValue& o = input.get_obj();48Txid txid = Txid::FromUint256(ParseHashO(o, "txid"));50const UniValue& vout_v = o.find_value("vout");51if (!vout_v.isNum())52throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");53int nOutput = vout_v.getInt<int>();54if (nOutput < 0)55throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");57uint32_t nSequence;59if (rbf.value_or(true)) {60nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */61} else if (rawTx.nLockTime) {62nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; /* CTxIn::SEQUENCE_FINAL - 1 */63} else {64nSequence = CTxIn::SEQUENCE_FINAL;65}67// set the sequence number if passed in the parameters object68const UniValue& sequenceObj = o.find_value("sequence");69if (sequenceObj.isNum()) {70int64_t seqNr64 = sequenceObj.getInt<int64_t>();71if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {72throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");73} else {74nSequence = (uint32_t)seqNr64;75}76}78CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);80rawTx.vin.push_back(in);81}82}84UniValue NormalizeOutputs(const UniValue& outputs_in)85{86if (outputs_in.isNull()) {87throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");88}90const bool outputs_is_obj = outputs_in.isObject();91UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();93if (!outputs_is_obj) {94// Translate array of key-value pairs into dict95UniValue outputs_dict = UniValue(UniValue::VOBJ);96for (size_t i = 0; i < outputs.size(); ++i) {97const UniValue& output = outputs[i];98if (!output.isObject()) {99throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");100}101if (output.size() != 1) {102throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");103}104outputs_dict.pushKVs(output);105}106outputs = std::move(outputs_dict);107}108return outputs;109}111std::vector<std::pair<CTxDestination, CAmount>> ParseOutputs(const UniValue& outputs)112{113// Duplicate checking114std::set<CTxDestination> destinations;115std::vector<std::pair<CTxDestination, CAmount>> parsed_outputs;116bool has_data{false};117const auto& keys{outputs.getKeys()};118const auto& values{outputs.getValues()};119for (size_t i{0}; i < keys.size(); ++i) {120const auto& name_{keys[i]};121const auto& value{values[i]};122if (name_ == "data") {123if (has_data) {124throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");125}126has_data = true;127std::vector<unsigned char> data = ParseHexV(value.getValStr(), "Data");128CTxDestination destination{CNoDestination{CScript() << OP_RETURN << data}};129CAmount amount{0};130parsed_outputs.emplace_back(destination, amount);131} else {132CTxDestination destination{DecodeDestination(name_)};133CAmount amount{AmountFromValue(value)};134if (!IsValidDestination(destination)) {135throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);136}138if (!destinations.insert(destination).second) {139throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);140}141parsed_outputs.emplace_back(destination, amount);142}143}144return parsed_outputs;145}147void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in)148{149UniValue outputs(UniValue::VOBJ);150outputs = NormalizeOutputs(outputs_in);152std::vector<std::pair<CTxDestination, CAmount>> parsed_outputs = ParseOutputs(outputs);153for (const auto& [destination, nAmount] : parsed_outputs) {154CScript scriptPubKey = GetScriptForDestination(destination);156CTxOut out(nAmount, scriptPubKey);157rawTx.vout.push_back(out);158}159}161CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf, const uint32_t version)162{163CMutableTransaction rawTx;165if (!locktime.isNull()) {166int64_t nLockTime = locktime.getInt<int64_t>();167if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)168throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");169rawTx.nLockTime = nLockTime;170}172if (version < TX_MIN_STANDARD_VERSION || version > TX_MAX_STANDARD_VERSION) {173throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, version out of range(%d~%d)", TX_MIN_STANDARD_VERSION, TX_MAX_STANDARD_VERSION));174}175rawTx.version = version;177AddInputs(rawTx, inputs_in, rbf);178AddOutputs(rawTx, outputs_in);180if (rbf.has_value() && rbf.value() && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) {181throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");182}184return rawTx;185}187/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */188static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)189{190UniValue entry(UniValue::VOBJ);191entry.pushKV("txid", txin.prevout.hash.ToString());192entry.pushKV("vout", txin.prevout.n);193UniValue witness(UniValue::VARR);194for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {195witness.push_back(HexStr(txin.scriptWitness.stack[i]));196}197entry.pushKV("witness", std::move(witness));198entry.pushKV("scriptSig", HexStr(txin.scriptSig));199entry.pushKV("sequence", txin.nSequence);200entry.pushKV("error", strMessage);201vErrorsRet.push_back(std::move(entry));202}204void ParsePrevouts(const UniValue& prevTxsUnival, FlatSigningProvider* keystore, std::map<COutPoint, Coin>& coins)205{206if (!prevTxsUnival.isNull()) {207const UniValue& prevTxs = prevTxsUnival.get_array();208for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {209const UniValue& p = prevTxs[idx];210if (!p.isObject()) {211throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");212}214const UniValue& prevOut = p.get_obj();216RPCTypeCheckObj(prevOut,217{218{"txid", UniValueType(UniValue::VSTR)},219{"vout", UniValueType(UniValue::VNUM)},220{"scriptPubKey", UniValueType(UniValue::VSTR)},221});223Txid txid = Txid::FromUint256(ParseHashO(prevOut, "txid"));225int nOut = prevOut.find_value("vout").getInt<int>();226if (nOut < 0) {227throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout cannot be negative");228}230COutPoint out(txid, nOut);231std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));232CScript scriptPubKey(pkData.begin(), pkData.end());234{235auto coin = coins.find(out);236if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) {237std::string err("Previous output scriptPubKey mismatch:\n");238err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+239ScriptToAsmStr(scriptPubKey);240throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);241}242Coin newcoin;243newcoin.out.scriptPubKey = scriptPubKey;244newcoin.out.nValue = MAX_MONEY;245if (prevOut.exists("amount")) {246newcoin.out.nValue = AmountFromValue(prevOut.find_value("amount"));247}248newcoin.nHeight = 1;249coins[out] = std::move(newcoin);250}