mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-09 10:43:19 -05:00
wallet, rpc: Add createwalletdescriptor RPC
This commit is contained in:
parent
8e1a475062
commit
460ae1bf67
2 changed files with 106 additions and 0 deletions
|
@ -278,6 +278,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "gethdkeys", 0, "active_only" },
|
{ "gethdkeys", 0, "active_only" },
|
||||||
{ "gethdkeys", 0, "options" },
|
{ "gethdkeys", 0, "options" },
|
||||||
{ "gethdkeys", 0, "private" },
|
{ "gethdkeys", 0, "private" },
|
||||||
|
{ "createwalletdescriptor", 1, "options" },
|
||||||
|
{ "createwalletdescriptor", 1, "internal" },
|
||||||
// Echo with conversion (For testing only)
|
// Echo with conversion (For testing only)
|
||||||
{ "echojson", 0, "arg0" },
|
{ "echojson", 0, "arg0" },
|
||||||
{ "echojson", 1, "arg1" },
|
{ "echojson", 1, "arg1" },
|
||||||
|
|
|
@ -925,6 +925,109 @@ RPCHelpMan gethdkeys()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static RPCHelpMan createwalletdescriptor()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{"createwalletdescriptor",
|
||||||
|
"Creates the wallet's descriptor for the given address type. "
|
||||||
|
"The address type must be one that the wallet does not already have a descriptor for."
|
||||||
|
+ HELP_REQUIRING_PASSPHRASE,
|
||||||
|
{
|
||||||
|
{"type", RPCArg::Type::STR, RPCArg::Optional::NO, "The address type the descriptor will produce. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
|
||||||
|
{"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
|
||||||
|
{"internal", RPCArg::Type::BOOL, RPCArg::DefaultHint{"Both external and internal will be generated unless this parameter is specified"}, "Whether to only make one descriptor that is internal (if parameter is true) or external (if parameter is false)"},
|
||||||
|
{"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"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
RPCResult{
|
||||||
|
RPCResult::Type::OBJ, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::ARR, "descs", "The public descriptors that were added to the wallet",
|
||||||
|
{{RPCResult::Type::STR, "", ""}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("createwalletdescriptor", "bech32m")
|
||||||
|
+ HelpExampleRpc("createwalletdescriptor", "bech32m")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
|
{
|
||||||
|
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||||
|
if (!pwallet) return UniValue::VNULL;
|
||||||
|
|
||||||
|
// Make sure wallet is a descriptor wallet
|
||||||
|
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "createwalletdescriptor is not available for non-descriptor wallets");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<OutputType> output_type = ParseOutputType(request.params[0].get_str());
|
||||||
|
if (!output_type) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
|
||||||
|
UniValue internal_only{options["internal"]};
|
||||||
|
UniValue hdkey{options["hdkey"]};
|
||||||
|
|
||||||
|
std::vector<bool> internals;
|
||||||
|
if (internal_only.isNull()) {
|
||||||
|
internals.push_back(false);
|
||||||
|
internals.push_back(true);
|
||||||
|
} else {
|
||||||
|
internals.push_back(internal_only.get_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOCK(pwallet->cs_wallet);
|
||||||
|
EnsureWalletIsUnlocked(*pwallet);
|
||||||
|
|
||||||
|
CExtPubKey xpub;
|
||||||
|
if (hdkey.isNull()) {
|
||||||
|
std::set<CExtPubKey> active_xpubs = pwallet->GetActiveHDPubKeys();
|
||||||
|
if (active_xpubs.size() != 1) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");
|
||||||
|
}
|
||||||
|
xpub = *active_xpubs.begin();
|
||||||
|
} else {
|
||||||
|
xpub = DecodeExtPubKey(hdkey.get_str());
|
||||||
|
if (!xpub.pubkey.IsValid()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to parse HD key. Please provide a valid xpub");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CKey> key = pwallet->GetKey(xpub.pubkey.GetID());
|
||||||
|
if (!key) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Private key for %s is not known", EncodeExtPubKey(xpub)));
|
||||||
|
}
|
||||||
|
CExtKey active_hdkey(xpub, *key);
|
||||||
|
|
||||||
|
std::vector<std::reference_wrapper<DescriptorScriptPubKeyMan>> spkms;
|
||||||
|
WalletBatch batch{pwallet->GetDatabase()};
|
||||||
|
for (bool internal : internals) {
|
||||||
|
WalletDescriptor w_desc = GenerateWalletDescriptor(xpub, *output_type, internal);
|
||||||
|
uint256 w_id = DescriptorID(*w_desc.descriptor);
|
||||||
|
if (!pwallet->GetScriptPubKeyMan(w_id)) {
|
||||||
|
spkms.emplace_back(pwallet->SetupDescriptorScriptPubKeyMan(batch, active_hdkey, *output_type, internal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (spkms.empty()) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "Descriptor already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch each descspkm from the wallet in order to get the descriptor strings
|
||||||
|
UniValue descs{UniValue::VARR};
|
||||||
|
for (const auto& spkm : spkms) {
|
||||||
|
std::string desc_str;
|
||||||
|
bool ok = spkm.get().GetDescriptorString(desc_str, false);
|
||||||
|
CHECK_NONFATAL(ok);
|
||||||
|
descs.push_back(desc_str);
|
||||||
|
}
|
||||||
|
UniValue out{UniValue::VOBJ};
|
||||||
|
out.pushKV("descs", std::move(descs));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// addresses
|
// addresses
|
||||||
RPCHelpMan getaddressinfo();
|
RPCHelpMan getaddressinfo();
|
||||||
RPCHelpMan getnewaddress();
|
RPCHelpMan getnewaddress();
|
||||||
|
@ -1008,6 +1111,7 @@ Span<const CRPCCommand> GetWalletRPCCommands()
|
||||||
{"wallet", &bumpfee},
|
{"wallet", &bumpfee},
|
||||||
{"wallet", &psbtbumpfee},
|
{"wallet", &psbtbumpfee},
|
||||||
{"wallet", &createwallet},
|
{"wallet", &createwallet},
|
||||||
|
{"wallet", &createwalletdescriptor},
|
||||||
{"wallet", &restorewallet},
|
{"wallet", &restorewallet},
|
||||||
{"wallet", &dumpprivkey},
|
{"wallet", &dumpprivkey},
|
||||||
{"wallet", &dumpwallet},
|
{"wallet", &dumpwallet},
|
||||||
|
|
Loading…
Add table
Reference in a new issue