From 709a917c26bf048de454760c7189278b965ca7f3 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 1 Nov 2021 15:47:33 -0400 Subject: [PATCH] rpc: Add getxpub command --- src/wallet/keyman.cpp | 8 +++++++ src/wallet/keyman.h | 1 + src/wallet/rpcwallet.cpp | 45 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/src/wallet/keyman.cpp b/src/wallet/keyman.cpp index 978ea07b1c549..c77b85e42cdf7 100644 --- a/src/wallet/keyman.cpp +++ b/src/wallet/keyman.cpp @@ -74,6 +74,14 @@ std::optional KeyManager::GetActiveHDKey() const return master_key; } +std::optional KeyManager::GetActiveHDPubKey() const +{ + if (!m_active_xpub.pubkey.IsValid()) { + return std::nullopt; + } + return m_active_xpub; +} + bool KeyManager::AddKeyInner(WalletBatch& batch, const CKey& key, const CPubKey& pubkey) { AssertLockHeld(cs_keyman); diff --git a/src/wallet/keyman.h b/src/wallet/keyman.h index cdebe232b85cd..03b369136f0b0 100644 --- a/src/wallet/keyman.h +++ b/src/wallet/keyman.h @@ -43,6 +43,7 @@ class KeyManager void GenerateAndSetHDKey(); std::optional GetActiveHDKey() const EXCLUSIVE_LOCKS_REQUIRED(cs_keyman); + std::optional GetActiveHDPubKey() const EXCLUSIVE_LOCKS_REQUIRED(cs_keyman); void SetActiveHDKey(const CExtPubKey& extpub); void LoadActiveHDKey(const CExtPubKey& extpub); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 86bfa10d888a7..62ba5b130758c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4839,6 +4839,50 @@ static RPCHelpMan walletdisplayaddress() } #endif // ENABLE_EXTERNAL_SIGNER +static RPCHelpMan getxpub() +{ + return RPCHelpMan{"getxpub", + "Returns the xpub most recently used to generate descriptors for this descriptor wallet. " + "Not entirely useful right now as it returns the xpub of the root, and there are " + "hardened derivation steps involved in normal key derivation.\n", + {}, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + { + {RPCResult::Type::STR, "xpub", "The xpub"}, + }}, + }, + RPCExamples{ + HelpExampleCli("getxpub", "") + + HelpExampleRpc("getxpub", "") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const std::shared_ptr pwallet = GetWalletForJSONRPCRequest(request); + if (!pwallet) return NullUniValue; + + if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { + throw JSONRPCError(RPC_WALLET_ERROR, "getxpub is not available for non-descriptor wallets"); + } + + const KeyManager& keyman = pwallet->GetKeyManager(); + LOCK2(pwallet->cs_wallet, keyman.cs_keyman); + + std::optional extpub = keyman.GetActiveHDPubKey(); + if (extpub == std::nullopt) { + throw JSONRPCError(RPC_WALLET_ERROR, "This wallet does not have an active xpub"); + } + std::string xpub = EncodeExtPubKey(*extpub); + + UniValue obj(UniValue::VOBJ); + obj.pushKV("xpub", xpub); + + return obj; +}, + }; +} + RPCHelpMan abortrescan(); RPCHelpMan dumpprivkey(); RPCHelpMan importprivkey(); @@ -4880,6 +4924,7 @@ static const CRPCCommand commands[] = { "wallet", &gettransaction, }, { "wallet", &getunconfirmedbalance, }, { "wallet", &getbalances, }, + { "wallet", &getxpub, }, { "wallet", &getwalletinfo, }, { "wallet", &importaddress, }, { "wallet", &importdescriptors, },