@@ -929,6 +929,82 @@ static RPCHelpMan createwalletdescriptor()
929929 };
930930}
931931
932+ RPCHelpMan addhdkey ()
933+ {
934+ return RPCHelpMan{
935+ " addhdkey" ,
936+ " Add a BIP 32 HD key to the wallet that can be used with 'createwalletdescriptor'\n " ,
937+ {
938+ {" 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." },
939+ },
940+ RPCResult{
941+ RPCResult::Type::OBJ, " " , " " ,
942+ {
943+ {RPCResult::Type::STR, " xpub" , " The xpub of the HD key that was added to the wallet" }
944+ },
945+ },
946+ RPCExamples{
947+ HelpExampleCli (" addhdkey" , " xprv" ) + HelpExampleRpc (" addhdkey" , " xprv" )
948+ },
949+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
950+ {
951+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest (request);
952+ if (!wallet) return UniValue::VNULL;
953+
954+ if (wallet->IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
955+ throw JSONRPCError (RPC_WALLET_ERROR, " addhdkey is not available for wallets without private keys" );
956+ }
957+
958+ EnsureWalletIsUnlocked (*wallet);
959+
960+ CExtKey hdkey;
961+ if (request.params [0 ].isNull ()) {
962+ CKey seed_key = GenerateRandomKey ();
963+ hdkey.SetSeed (seed_key);
964+ } else {
965+ hdkey = DecodeExtKey (request.params [0 ].get_str ());
966+ if (!hdkey.key .IsValid ()) {
967+ // Check if the user gave us an xpub and give a more descriptive error if so
968+ CExtPubKey xpub = DecodeExtPubKey (request.params [0 ].get_str ());
969+ if (xpub.pubkey .IsValid ()) {
970+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Extended public key (xpub) provided, but extended private key (xprv) is required" );
971+ } else {
972+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Could not parse HD key" );
973+ }
974+ }
975+ }
976+
977+ LOCK (wallet->cs_wallet );
978+ std::string desc_str = " unused(" + EncodeExtKey (hdkey) + " )" ;
979+ FlatSigningProvider keys;
980+ std::string error;
981+ std::vector<std::unique_ptr<Descriptor>> descs = Parse (desc_str, keys, error, false );
982+ CHECK_NONFATAL (!descs.empty ());
983+ WalletDescriptor w_desc (std::move (descs.at (0 )), GetTime (), 0 , 0 , 0 );
984+ if (wallet->GetDescriptorScriptPubKeyMan (w_desc) != nullptr ) {
985+ throw JSONRPCError (RPC_WALLET_ERROR, " HD key already exists" );
986+ }
987+
988+ auto spkm = wallet->AddWalletDescriptor (w_desc, keys, /* label=*/ " " , /* internal=*/ false );
989+ if (!spkm) {
990+ throw JSONRPCError (RPC_WALLET_ERROR, util::ErrorString (spkm).original );
991+ }
992+
993+ UniValue response (UniValue::VOBJ);
994+ const DescriptorScriptPubKeyMan& desc_spkm = spkm->get ();
995+ LOCK (desc_spkm.cs_desc_man );
996+ std::set<CPubKey> pubkeys;
997+ std::set<CExtPubKey> extpubs;
998+ desc_spkm.GetWalletDescriptor ().descriptor ->GetPubKeys (pubkeys, extpubs);
999+ CHECK_NONFATAL (pubkeys.size () == 0 );
1000+ CHECK_NONFATAL (extpubs.size () == 1 );
1001+ response.pushKV (" xpub" , EncodeExtPubKey (*extpubs.begin ()));
1002+
1003+ return response;
1004+ },
1005+ };
1006+ }
1007+
9321008// addresses
9331009RPCHelpMan getaddressinfo ();
9341010RPCHelpMan getnewaddress ();
@@ -998,6 +1074,7 @@ std::span<const CRPCCommand> GetWalletRPCCommands()
9981074 {" rawtransactions" , &fundrawtransaction},
9991075 {" wallet" , &abandontransaction},
10001076 {" wallet" , &abortrescan},
1077+ {" wallet" , &addhdkey},
10011078 {" wallet" , &backupwallet},
10021079 {" wallet" , &bumpfee},
10031080 {" wallet" , &psbtbumpfee},
0 commit comments