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.
252// if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed253const bool is_p2sh = scriptPubKey.IsPayToScriptHash();254const bool is_p2wsh = scriptPubKey.IsPayToWitnessScriptHash();255if (keystore && (is_p2sh || is_p2wsh)) {256RPCTypeCheckObj(prevOut,257{258{"redeemScript", UniValueType(UniValue::VSTR)},259{"witnessScript", UniValueType(UniValue::VSTR)},260}, true);261const UniValue& rs{prevOut.find_value("redeemScript")};262const UniValue& ws{prevOut.find_value("witnessScript")};263if (rs.isNull() && ws.isNull()) {264throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript/witnessScript");265}267// work from witnessScript when possible268std::vector<unsigned char> scriptData(!ws.isNull() ? ParseHexV(ws, "witnessScript") : ParseHexV(rs, "redeemScript"));269CScript script(scriptData.begin(), scriptData.end());270keystore->scripts.emplace(CScriptID(script), script);271// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).272// This is done for redeemScript only for compatibility, it is encouraged to use the explicit witnessScript field instead.273CScript witness_output_script{GetScriptForDestination(WitnessV0ScriptHash(script))};274keystore->scripts.emplace(CScriptID(witness_output_script), witness_output_script);276if (!ws.isNull() && !rs.isNull()) {277// if both witnessScript and redeemScript are provided,278// they should either be the same (for backwards compat),279// or the redeemScript should be the encoded form of280// the witnessScript (ie, for p2sh-p2wsh)281if (ws.get_str() != rs.get_str()) {282std::vector<unsigned char> redeemScriptData(ParseHexV(rs, "redeemScript"));283CScript redeemScript(redeemScriptData.begin(), redeemScriptData.end());284if (redeemScript != witness_output_script) {285throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript does not correspond to witnessScript");286}287}288}290if (is_p2sh) {291const CTxDestination p2sh{ScriptHash(script)};292const CTxDestination p2sh_p2wsh{ScriptHash(witness_output_script)};293if (scriptPubKey == GetScriptForDestination(p2sh)) {294// traditional p2sh; arguably an error if295// we got here with rs.IsNull(), because296// that means the p2sh script was specified297// via witnessScript param, but for now298// we'll just quietly accept it299} else if (scriptPubKey == GetScriptForDestination(p2sh_p2wsh)) {300// p2wsh encoded as p2sh; ideally the witness301// script was specified in the witnessScript302// param, but also support specifying it via303// redeemScript param for backwards compat304// (in which case ws.IsNull() == true)305} else {306// otherwise, can't generate scriptPubKey from307// either script, so we got unusable parameters308throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey");309}310} else if (is_p2wsh) {311// plain p2wsh; could throw an error if script312// was specified by redeemScript rather than313// witnessScript (ie, ws.IsNull() == true), but314// accept it for backwards compat315const CTxDestination p2wsh{WitnessV0ScriptHash(script)};316if (scriptPubKey != GetScriptForDestination(p2wsh)) {317throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey");318}319}320}321}322}323}325void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)326{327std::optional<int> nHashType = ParseSighashString(hashType);328if (!nHashType) {329nHashType = SIGHASH_DEFAULT;330}332// Script verification errors333std::map<int, bilingual_str> input_errors;335bool complete = SignTransaction(mtx, keystore, coins, {.sighash_type = *nHashType}, input_errors);336SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);337}339void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, bilingual_str>& input_errors, UniValue& result)340{341// Make errors UniValue342UniValue vErrors(UniValue::VARR);343for (const auto& err_pair : input_errors) {344if (err_pair.second.original == "Missing amount") {345// This particular error needs to be an exception for some reason346throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coins.at(mtx.vin.at(err_pair.first).prevout).out.ToString()));347}348TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second.original);349}351result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));352result.pushKV("complete", complete);353if (!vErrors.empty()) {354if (result.exists("errors")) {355vErrors.push_backV(result["errors"].getValues());356}357result.pushKV("errors", std::move(vErrors));358}359}361std::vector<RPCResult> TxDoc(const TxDocOptions& opts)362{363CHECK_NONFATAL(!opts.fee_doc || opts.fee);364CHECK_NONFATAL(!opts.prevout_doc || opts.prevout);365CHECK_NONFATAL(!opts.vin_item_doc || opts.vin_inner_elision);366CHECK_NONFATAL(opts.elision_mode != ElisionMode::WithSummary || opts.elision_summary.has_value());368const std::string fee_doc{opts.fee_doc.value_or(369"transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available")};370const std::string prevout_doc{opts.prevout_doc.value_or(371"The previous output, omitted if block undo data is not available")};372const std::string vin_item_doc{opts.vin_item_doc.value_or("utxo being spent")};374auto vin_inner = std::vector<RPCResult>{375{RPCResult::Type::STR_HEX, "coinbase", /*optional=*/true, "The coinbase value (only if coinbase transaction)"},376{RPCResult::Type::STR_HEX, "txid", /*optional=*/true, "The transaction id (if not coinbase transaction)"},377{RPCResult::Type::NUM, "vout", /*optional=*/true, "The output number (if not coinbase transaction)"},378{RPCResult::Type::OBJ, "scriptSig", /*optional=*/true, "The script (if not coinbase transaction)",379{380{RPCResult::Type::STR, "asm", "Disassembly of the signature script"},381{RPCResult::Type::STR_HEX, "hex", "The raw signature script bytes, hex-encoded"},382}},383{RPCResult::Type::ARR, "txinwitness", /*optional=*/true, "",384{385{RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"},386}},387};388if (opts.prevout) {389vin_inner.emplace_back(390RPCResult::Type::OBJ, "prevout", opts.prevout_optional, prevout_doc,391std::vector<RPCResult>{392{RPCResult::Type::BOOL, "generated", "Coinbase or not"},393{RPCResult::Type::NUM, "height", "The height of the prevout"},394{RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},395{RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()},396}397);398}399vin_inner.emplace_back(RPCResult::Type::NUM, "sequence", "The script sequence number");401if (opts.vin_inner_elision) {402vin_inner = ElideGroup(std::move(vin_inner), *opts.vin_inner_elision);403if (opts.prevout) {404// prevout remains visible even when other fields are elided405std::vector<RPCResult> new_vin;406new_vin.reserve(vin_inner.size());407for (const auto& r : vin_inner) {408if (r.m_key_name == "prevout") {409RPCResultOptions unopts = r.m_opts;410unopts.print_elision = HelpElisionNone{};411new_vin.emplace_back(r, std::move(unopts));412} else {413new_vin.push_back(r);414}415}416vin_inner = std::move(new_vin);417}418}420auto fields = std::vector<RPCResult>{421{RPCResult::Type::STR_HEX, "txid", opts.txid_field_doc},422{RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"},423{RPCResult::Type::NUM, "size", "The serialized transaction size"},424{RPCResult::Type::NUM, "vsize", "The virtual transaction size (differs from size for witness transactions)"},425{RPCResult::Type::NUM, "weight", "The transaction's weight (between vsize*4-3 and vsize*4)"},426{RPCResult::Type::NUM, "version", "The version"},427{RPCResult::Type::NUM_TIME, "locktime", "The lock time"},428{RPCResult::Type::ARR, "vin", "",429{430{RPCResult::Type::OBJ, "", opts.vin_inner_elision ? vin_item_doc : "", std::move(vin_inner)},431}},432{RPCResult::Type::ARR, "vout", "",433{434{RPCResult::Type::OBJ, "", "", Cat(435{436{RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},437{RPCResult::Type::NUM, "n", "index"},438{RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()},439},440opts.wallet ?441std::vector<RPCResult>{{RPCResult::Type::BOOL, "ischange", /*optional=*/true, "Output script is change (only present if true)"}} :442std::vector<RPCResult>{}443)},444}},445};447if (opts.fee) fields.emplace_back(RPCResult::Type::NUM, "fee", /*optional=*/true, fee_doc);448if (opts.hex) fields.emplace_back(RPCResult::Type::STR_HEX, "hex", "The hex-encoded transaction data");450if (opts.elision_mode != ElisionMode::None) {451const bool silent = opts.elision_mode == ElisionMode::Silent;452std::vector<RPCResult> new_fields;453new_fields.reserve(fields.size());454bool first = true;455for (const auto& f : fields) {456if (!silent && f.m_key_name == "fee") {457new_fields.push_back(f);458continue;459}460if (f.m_key_name == "vin" && opts.vin_inner_elision) {461new_fields.push_back(f);462continue;463}464if (!silent && first) {465RPCResultOptions eopts = f.m_opts;466eopts.print_elision = opts.elision_summary.value_or("");467new_fields.emplace_back(f, std::move(eopts));468first = false;469} else {470RPCResultOptions eopts = f.m_opts;471eopts.print_elision = HelpElisionSkip{};472new_fields.emplace_back(f, std::move(eopts));473}474}475fields = std::move(new_fields);476}478return fields;479}