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) 2009-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <chain.h>6#include <clientversion.h>7#include <core_io.h>8#include <hash.h>9#include <interfaces/chain.h>10#include <key_io.h>11#include <merkleblock.h>12#include <node/types.h>13#include <rpc/util.h>14#include <script/descriptor.h>15#include <script/script.h>16#include <script/solver.h>17#include <sync.h>18#include <uint256.h>19#include <util/bip32.h>20#include <util/check.h>21#include <util/fs.h>22#include <util/time.h>23#include <util/translation.h>24#include <wallet/export.h>25#include <wallet/rpc/util.h>26#include <wallet/wallet.h>28#include <cstdint>29#include <fstream>30#include <tuple>31#include <string>33#include <univalue.h>37using interfaces::FoundBlock;39namespace wallet {40RPCMethod importprunedfunds()41{42return RPCMethod{43"importprunedfunds",44"Imports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",45{46{"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},47{"txoutproof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex output from gettxoutproof that contains the transaction"},48},49RPCResult{RPCResult::Type::NONE, "", ""},50RPCExamples{""},51[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue52{53std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);54if (!pwallet) return UniValue::VNULL;56CMutableTransaction tx;57if (!DecodeHexTx(tx, request.params[0].get_str())) {58throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");59}61CMerkleBlock merkleBlock;62SpanReader{ParseHexV(request.params[1], "proof")} >> merkleBlock;64//Search partial merkle tree in proof for our transaction and index in valid block65std::vector<Txid> vMatch;66std::vector<unsigned int> vIndex;67if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {68throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");69}71LOCK(pwallet->cs_wallet);72int height;73if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {74throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");75}77std::vector<Txid>::const_iterator it;78if ((it = std::find(vMatch.begin(), vMatch.end(), tx.GetHash())) == vMatch.end()) {79throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");80}82unsigned int txnIndex = vIndex[it - vMatch.begin()];84CTransactionRef tx_ref = MakeTransactionRef(tx);85if (pwallet->IsMine(*tx_ref)) {86pwallet->AddToWallet(std::move(tx_ref), TxStateConfirmed{merkleBlock.header.GetHash(), height, static_cast<int>(txnIndex)});87return UniValue::VNULL;88}90throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");91},92};93}95RPCMethod removeprunedfunds()96{97return RPCMethod{98"removeprunedfunds",99"(DEPRECATED) This feature will be removed in the next major release. Start bitcoind with the `-deprecatedrpc=removeprunedfunds` option in order to use this.\n"100"Deletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",101{102{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},103},104RPCResult{RPCResult::Type::NONE, "", ""},105RPCExamples{106HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +107"\nAs a JSON-RPC call\n"108+ HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")109},110[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue111{112std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);113if (!pwallet) return UniValue::VNULL;115if (!pwallet->chain().rpcEnableDeprecated("removeprunedfunds")) {116throw JSONRPCError(RPC_METHOD_DEPRECATED, "DEPRECATION WARNING: This feature will be removed in the next major release. Start bitcoind with the `-deprecatedrpc=removeprunedfunds` option in order to use this.");117}119LOCK(pwallet->cs_wallet);121Txid hash{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};122std::vector<Txid> vHash;123vHash.push_back(hash);124if (auto res = pwallet->RemoveTxs(vHash); !res) {125throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original);126}128return UniValue::VNULL;129},130};131}133static int64_t GetImportTimestamp(const UniValue& data, int64_t now)134{135if (data.exists("timestamp")) {136const UniValue& timestamp = data["timestamp"];137if (timestamp.isNum()) {138return timestamp.getInt<int64_t>();139} else if (timestamp.isStr() && timestamp.get_str() == "now") {140return now;141}142throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp.type())));143}144throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");145}147static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)148{149UniValue warnings(UniValue::VARR);150UniValue result(UniValue::VOBJ);152try {153if (!data.exists("desc")) {154throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor not found.");155}157const std::string& descriptor = data["desc"].get_str();158const bool active = data.exists("active") ? data["active"].get_bool() : false;159const std::string label{LabelFromValue(data["label"])};161// Parse descriptor string162FlatSigningProvider keys;163std::string error;164auto parsed_descs = Parse(descriptor, keys, error, /* require_checksum = */ true);165if (parsed_descs.empty()) {166throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);167}168std::optional<bool> internal;169if (data.exists("internal")) {170if (parsed_descs.size() > 1) {171throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot have multipath descriptor while also specifying \'internal\'");172}173internal = data["internal"].get_bool();174}176// Range check177std::optional<bool> is_ranged;178int64_t range_start = 0, range_end = 1, next_index = 0;179if (!parsed_descs.at(0)->IsRange() && data.exists("range")) {180throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");181} else if (parsed_descs.at(0)->IsRange()) {182if (data.exists("range")) {183auto range = ParseDescriptorRange(data["range"]);184range_start = range.first;185range_end = range.second + 1; // Specified range end is inclusive, but we need range end as exclusive186} else {187warnings.push_back("Range not given, using default keypool range");188range_start = 0;189range_end = wallet.m_keypool_size;190}191next_index = range_start;192is_ranged = true;194if (data.exists("next_index")) {195next_index = data["next_index"].getInt<int64_t>();196// bound checks197if (next_index < range_start || next_index >= range_end) {198throw JSONRPCError(RPC_INVALID_PARAMETER, "next_index is out of range");199}200}201}203// Active descriptors must be ranged204if (active && !parsed_descs.at(0)->IsRange()) {205throw JSONRPCError(RPC_INVALID_PARAMETER, "Active descriptors must be ranged");206}208// Multipath descriptors should not have a label209if (parsed_descs.size() > 1 && data.exists("label")) {210throw JSONRPCError(RPC_INVALID_PARAMETER, "Multipath descriptors should not have a label");211}213// Ranged descriptors should not have a label214if (is_ranged.has_value() && is_ranged.value() && data.exists("label")) {215throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptors should not have a label");216}218bool desc_internal = internal.has_value() && internal.value();219// Internal addresses should not have a label either220if (desc_internal && data.exists("label")) {221throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");222}224// Combo descriptor check225if (active && !parsed_descs.at(0)->IsSingleType()) {226throw JSONRPCError(RPC_WALLET_ERROR, "Combo descriptors cannot be set to active");227}229// If the wallet disabled private keys, abort if private keys exist230if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) {231throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");232}234for (size_t j = 0; j < parsed_descs.size(); ++j) {235auto parsed_desc = std::move(parsed_descs[j]);236if (parsed_descs.size() == 2) {237desc_internal = j == 1;238} else if (parsed_descs.size() > 2) {239CHECK_NONFATAL(!desc_internal);240}241// Expand to check whether the descriptor can be derived at the first index.242FlatSigningProvider expand_keys;243std::vector<CScript> scripts;244if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {245throw JSONRPCError(RPC_WALLET_ERROR, "Cannot expand descriptor. Probably because of hardened derivations without private keys provided");246}248for (const auto& w : parsed_desc->Warnings()) {249warnings.push_back(w);250}