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.
751{"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"The HD key used by all other active descriptors"}, "The HD key that the wallet knows the private key of, listed using 'gethdkeys', to use for this descriptor's key"},752}},753},754RPCResult{755RPCResult::Type::OBJ, "", "",756{757{RPCResult::Type::ARR, "descs", "The public descriptors that were added to the wallet",758{{RPCResult::Type::STR, "", ""}}759}760},761},762RPCExamples{763HelpExampleCli("createwalletdescriptor", "bech32m")764+ HelpExampleRpc("createwalletdescriptor", R"("bech32m")")765},766[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue767{768std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);769if (!pwallet) return UniValue::VNULL;771std::optional<OutputType> output_type = ParseOutputType(request.params[0].get_str());772if (!output_type) {773throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));774}776UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};777UniValue internal_only{options["internal"]};778UniValue hdkey{options["hdkey"]};780std::vector<bool> internals;781if (internal_only.isNull()) {782internals.push_back(false);783internals.push_back(true);784} else {785internals.push_back(internal_only.get_bool());786}788LOCK(pwallet->cs_wallet);789EnsureWalletIsUnlocked(*pwallet);791CExtPubKey xpub;792if (hdkey.isNull()) {793HDPubKeyMap active_xpubs = pwallet->GetHDPubKeys(HDKeyFilter::Active);794if (active_xpubs.size() != 1) {795throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");796}797xpub = active_xpubs.begin()->first;798} else {799xpub = DecodeExtPubKey(hdkey.get_str());800if (!xpub.pubkey.IsValid()) {801throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to parse HD key. Please provide a valid xpub");802}803}805std::optional<CKey> key = pwallet->GetKey(xpub.pubkey.GetID());806if (!key) {807throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Private key for %s is not known", EncodeExtPubKey(xpub)));808}809CExtKey active_hdkey(xpub, *key);811std::vector<std::reference_wrapper<DescriptorScriptPubKeyMan>> spkms;812WalletBatch batch{pwallet->GetDatabase()};813for (bool internal : internals) {814WalletDescriptor w_desc = GenerateWalletDescriptor(xpub, *output_type, internal);815if (!pwallet->GetDescriptorScriptPubKeyMan(w_desc)) {816spkms.emplace_back(pwallet->SetupDescriptorScriptPubKeyMan(batch, active_hdkey, *output_type, internal));817}818}819if (spkms.empty()) {820throw JSONRPCError(RPC_WALLET_ERROR, "Descriptor already exists");821}823// Fetch each descspkm from the wallet in order to get the descriptor strings824UniValue descs{UniValue::VARR};825for (const auto& spkm : spkms) {826std::string desc_str;827bool ok = spkm.get().GetDescriptorString(desc_str, false);828CHECK_NONFATAL(ok);829descs.push_back(desc_str);830}831UniValue out{UniValue::VOBJ};832out.pushKV("descs", std::move(descs));833return out;834}835};836}838RPCMethod addhdkey()839{840return RPCMethod{841"addhdkey",842"Add a BIP 32 HD key to the wallet that can be used with 'createwalletdescriptor'\n",843{844{"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"Automatically generated new key"}, "The BIP 32 extended private key to add. If none is provided, a randomly generated one will be added."},845},846RPCResult{847RPCResult::Type::OBJ, "", "",848{849{RPCResult::Type::STR, "xpub", "The xpub of the HD key that was added to the wallet"}850},851},852RPCExamples{853HelpExampleCli("addhdkey", "xprv") + HelpExampleRpc("addhdkey", R"("xprv")")854},855[&](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue856{857std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);858if (!wallet) return UniValue::VNULL;860if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {861throw JSONRPCError(RPC_WALLET_ERROR, "addhdkey is not available for wallets without private keys");862}864EnsureWalletIsUnlocked(*wallet);866CExtKey hdkey;867if (request.params[0].isNull()) {868CKey seed_key = GenerateRandomKey();869hdkey.SetSeed(seed_key);870} else {871hdkey = DecodeExtKey(request.params[0].get_str());872if (!hdkey.key.IsValid()) {873// Check if the user gave us an xpub and give a more descriptive error if so874CExtPubKey xpub = DecodeExtPubKey(request.params[0].get_str());875if (xpub.pubkey.IsValid()) {876throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Extended public key (xpub) provided, but extended private key (xprv) is required");877} else {878throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Could not parse HD key");879}880}881}883LOCK(wallet->cs_wallet);884std::string desc_str = "unused(" + EncodeExtKey(hdkey) + ")";885FlatSigningProvider keys;886std::string error;887std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, error, false);888CHECK_NONFATAL(!descs.empty());889WalletDescriptor w_desc(std::move(descs.at(0)), GetTime(), 0, 0, 0);890if (wallet->GetDescriptorScriptPubKeyMan(w_desc) != nullptr) {891throw JSONRPCError(RPC_WALLET_ERROR, "HD key already exists");892}894auto spkm = wallet->AddWalletDescriptor(w_desc, keys, /*label=*/"", /*internal=*/false);895if (!spkm) {896throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(spkm).original);897}899UniValue response(UniValue::VOBJ);900const DescriptorScriptPubKeyMan& desc_spkm = spkm->get();901LOCK(desc_spkm.cs_desc_man);902std::set<CPubKey> pubkeys;903std::set<CExtPubKey> extpubs;904desc_spkm.GetWalletDescriptor().descriptor->GetPubKeys(pubkeys, extpubs);905CHECK_NONFATAL(pubkeys.size() == 0);906CHECK_NONFATAL(extpubs.size() == 1);907response.pushKV("xpub", EncodeExtPubKey(*extpubs.begin()));909return response;910},911};912}914static RPCMethod exportwatchonlywallet()915{916return RPCMethod{"exportwatchonlywallet",917"Creates a wallet file at the specified destination containing a watchonly version "918"of the current wallet. This watchonly wallet contains the wallet's public descriptors, "919"its transactions, and address book data. Descriptors that use hardened derivation will "920"only have a limited number of derived keys included in the export due to hardened "921"derivation requiring private keys. Descriptors with unhardened derivation do not have "922"this limitation. The watchonly wallet can be imported into another node using 'restorewallet'.",923{924{"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The path to the filename the exported watchonly wallet will be saved to"},925},926RPCResult{927RPCResult::Type::OBJ, "", "",928{929{RPCResult::Type::STR, "exported_file", "The full path that the file has been exported to"},930},931},932RPCExamples{933HelpExampleCli("exportwatchonlywallet", "\"/path/to/export.dat\"")934+ HelpExampleRpc("exportwatchonlywallet", "\"/path/to/export.dat\"")935},936[&](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue937{938std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);939if (!pwallet) return UniValue::VNULL;940WalletContext& context = EnsureWalletContext(request.context);942std::string dest = request.params[0].get_str();944LOCK(pwallet->cs_wallet);945pwallet->TopUpKeyPool();946util::Result<std::string> exported = ExportWatchOnlyWallet(*pwallet, fs::PathFromString(dest), context);947if (!exported) {948throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(exported).original);949}950UniValue out{UniValue::VOBJ};951out.pushKV("exported_file", *exported);952return out;953}954};955}957RPCMethod derivehdkey()958{959return RPCMethod{960"derivehdkey",961"Derive extended public or private key from HD key in the wallet at a given path.\n"962"Derivation uses wallet private key material.\n"963+ HELP_REQUIRING_PASSPHRASE,964{965{"path", RPCArg::Type::STR, RPCArg::Optional::NO, "BIP 32 derivation path with at least one hardened step."},966{"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {967{"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private key"},968{"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"Either the HD key of an unused(KEY) descriptor, or any other active descriptor."}, "The HD key that the wallet knows the private key of, listed using 'gethdkeys', to use for derivation"},969}},970},971RPCResult{972RPCResult::Type::OBJ, "", "", {973{RPCResult::Type::STR, "origin", "Fingerprint and path for use in descriptors"},974{RPCResult::Type::STR, "xpub", "The extended public key"},975{RPCResult::Type::STR, "xprv", /*optional=*/true, "The extended private key if \"private\" is true"},976},977},978RPCExamples{979HelpExampleCli("derivehdkey", "m/87h/0h/0h") + HelpExampleRpc("derivehdkey", "\"m/87h/0h/0h\"")980+ HelpExampleCliNamed("derivehdkey", {{"path", "m/87h/0h/0h"}, {"private", "true"}})981+ HelpExampleRpcNamed("derivehdkey", {{"path", "m/87h/0h/0h"}, {"private", "true"}})982},983[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue984{985const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);986if (!wallet) return UniValue::VNULL;988if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {989// Watch-only wallets can't contain unused(KEY) descriptors990throw JSONRPCError(RPC_WALLET_ERROR, "derivehdkey is not available for watch-only wallets");991}993std::vector<uint32_t> path = ParsePathBIP32(request.params[0].get_str());994UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};995const bool priv{options.exists("private") ? options["private"].get_bool() : false};996UniValue hdkey{options["hdkey"]};997if (!HasHardenedDerivation(path)) {998throw JSONRPCError(RPC_INVALID_PARAMETER, "Derivation path requires at least one hardened step");999}