Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport: bitcoin#18788, #18974, #19230, #19239, #19441, #19568, #19668, #20226, #21049, #22446 (descriptor wallets part II) #5911

Merged
merged 12 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/release-notes-21049.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Wallet
------

- A new `listdescriptors` RPC is available to inspect the contents of descriptor-enabled wallets.
The RPC returns public versions of all imported descriptors, including their timestamp and flags.
For ranged descriptors, it also returns the range boundaries and the next index to generate addresses from. (dash#5911)
69 changes: 69 additions & 0 deletions src/wallet/rpcdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1908,3 +1908,72 @@ UniValue importdescriptors(const JSONRPCRequest& main_request) {

return response;
}

RPCHelpMan listdescriptors()
{
return RPCHelpMan{
"listdescriptors",
"\nList descriptors imported into a descriptor-enabled wallet.",
{},
RPCResult{
RPCResult::Type::ARR, "", "Response is an array of descriptor objects",
{
{RPCResult::Type::OBJ, "", "", {
{RPCResult::Type::STR, "desc", "Descriptor string representation"},
{RPCResult::Type::NUM, "timestamp", "The creation time of the descriptor"},
{RPCResult::Type::BOOL, "active", "Activeness flag"},
{RPCResult::Type::BOOL, "internal", true, "Whether this is internal or external descriptor; defined only for active descriptors"},
{RPCResult::Type::ARR_FIXED, "range", true, "Defined only for ranged descriptors", {
{RPCResult::Type::NUM, "", "Range start inclusive"},
{RPCResult::Type::NUM, "", "Range end inclusive"},
}},
{RPCResult::Type::NUM, "next", true, "The next index to generate addresses from; defined only for ranged descriptors"},
}},
}
},
RPCExamples{
HelpExampleCli("listdescriptors", "") + HelpExampleRpc("listdescriptors", "")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;

if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
}

LOCK(wallet->cs_wallet);

UniValue response(UniValue::VARR);
const auto active_spk_mans = wallet->GetActiveScriptPubKeyMans();
for (const auto& spk_man : wallet->GetAllScriptPubKeyMans()) {
const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
if (!desc_spk_man) {
throw JSONRPCError(RPC_WALLET_ERROR, "Unexpected ScriptPubKey manager type.");
}
UniValue spk(UniValue::VOBJ);
LOCK(desc_spk_man->cs_desc_man);
const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
spk.pushKV("desc", wallet_descriptor.descriptor->ToString());
spk.pushKV("timestamp", wallet_descriptor.creation_time);
const bool active = active_spk_mans.count(desc_spk_man) != 0;
spk.pushKV("active", active);
const auto& type = wallet_descriptor.descriptor->GetOutputType();
if (active && type != std::nullopt) {
spk.pushKV("internal", wallet->GetScriptPubKeyMan(true) == desc_spk_man);
}
if (wallet_descriptor.descriptor->IsRange()) {
UniValue range(UniValue::VARR);
range.push_back(wallet_descriptor.range_start);
range.push_back(wallet_descriptor.range_end - 1);
spk.pushKV("range", range);
spk.pushKV("next", wallet_descriptor.next_index);
}
response.push_back(spk);
}

return response;
},
};
}
2 changes: 2 additions & 0 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4183,6 +4183,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request);
UniValue removeprunedfunds(const JSONRPCRequest& request);
UniValue importmulti(const JSONRPCRequest& request);
UniValue importdescriptors(const JSONRPCRequest& request);
RPCHelpMan listdescriptors();
UniValue dumphdinfo(const JSONRPCRequest& request);
UniValue importelectrumwallet(const JSONRPCRequest& request);

Expand Down Expand Up @@ -4443,6 +4444,7 @@ static const CRPCCommand commands[] =
{ "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} },
{ "wallet", "listaddressbalances", &listaddressbalances, {"minamount"} },
{ "wallet", "listaddressgroupings", &listaddressgroupings, {} },
{ "wallet", "listdescriptors", &listdescriptors, {} },
{ "wallet", "listlabels", &listlabels, {"purpose"} },
{ "wallet", "listlockunspent", &listlockunspent, {} },
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","addlocked","include_empty","include_watchonly","address_filter"} },
Expand Down
20 changes: 12 additions & 8 deletions src/wallet/scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan&
//!
//! @param keystore legacy key and script store
//! @param script script to solve
//! @param sigversion script type (top-level / redeemscript / witnessscript)
//! @param recurse_scripthash whether to recurse into nested p2sh and p2wsh
//! @param sigversion script type (top-level / redeemscript)
//! @param recurse_scripthash whether to recurse into nested p2sh
//! scripts or simply treat any script that has been
//! stored in the keystore as spendable
IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true)
Expand Down Expand Up @@ -299,6 +299,7 @@ bool LegacyScriptPubKeyMan::GetReservedDestination(bool internal, CTxDestination
if (!ReserveKeyFromKeyPool(index, keypool, internal)) {
return false;
}
// TODO: unify with bitcoin and use here GetDestinationForKey even if we have no type
address = PKHash(keypool.vchPubKey);
return true;
}
Expand Down Expand Up @@ -610,7 +611,7 @@ bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const
LOCK(cs_KeyStore);
// Check if the keypool has keys
bool keypool_has_keys;
if (internal && m_storage.CanSupportFeature(FEATURE_HD)) {
if (internal) {
keypool_has_keys = setInternalKeyPool.size() > 0;
} else {
keypool_has_keys = KeypoolCountExternalKeys() > 0;
Expand Down Expand Up @@ -1214,13 +1215,13 @@ bool LegacyScriptPubKeyMan::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& inf
return true;
}

bool LegacyScriptPubKeyMan::AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info)
bool LegacyScriptPubKeyMan::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info)
{
LOCK(cs_KeyStore);
std::copy(info.fingerprint, info.fingerprint + 4, mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint);
mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path;
mapKeyMetadata[pubkey.GetID()].has_key_origin = true;
return WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
}

bool LegacyScriptPubKeyMan::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
Expand Down Expand Up @@ -1367,6 +1368,9 @@ void LegacyScriptPubKeyMan::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
bool LegacyScriptPubKeyMan::CanGenerateKeys() const
{
LOCK(cs_KeyStore);
// TODO : unify with bitcoin after backporting SetupGeneration
// return IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD);

if (m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) || m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) {
return false;
}
Expand Down Expand Up @@ -1713,6 +1717,9 @@ bool LegacyScriptPubKeyMan::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey
bool LegacyScriptPubKeyMan::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp)
{
WalletBatch batch(m_storage.GetDatabase());
for (const auto& entry : key_origins) {
AddKeyOriginWithDB(batch, entry.second.first, entry.second.second);
}
for (const CKeyID& id : ordered_pubkeys) {
auto entry = pubkey_map.find(id);
if (entry == pubkey_map.end()) {
Expand All @@ -1735,9 +1742,6 @@ bool LegacyScriptPubKeyMan::ImportPubKeys(const std::vector<CKeyID>& ordered_pub
NotifyCanGetAddressesChanged();
}
}
for (const auto& entry : key_origins) {
AddKeyOrigin(entry.second.first, entry.second.second);
}
return true;
}

Expand Down
2 changes: 0 additions & 2 deletions src/wallet/scriptpubkeyman.h
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,6 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
bool AddCScript(const CScript& redeemScript) override;
/** Implement lookup of key origin information through wallet key metadata. */
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
/** Add a KeyOriginInfo to the wallet */
bool AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info);

void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
bool NewKeyPool();
Expand Down
17 changes: 0 additions & 17 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2960,23 +2960,6 @@ bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint,
}

// At this point, one input was not fully signed otherwise we would have exited already
// Find that input and figure out what went wrong.
for (unsigned int i = 0; i < tx.vin.size(); i++) {
// Get the prevout
CTxIn& txin = tx.vin[i];
auto coin = coins.find(txin.prevout);
if (coin == coins.end() || coin->second.IsSpent()) {
input_errors[i] = "Input not found or already spent";
continue;
}

// Check if this input is complete
SignatureData sigdata = DataFromTransaction(tx, i, coin->second.out);
if (!sigdata.complete) {
input_errors[i] = "Unable to sign input, missing keys";
continue;
}
}
return false;
}

Expand Down
3 changes: 0 additions & 3 deletions src/wallet/walletdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -605,9 +605,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssValue >> ser_xpub;
CExtPubKey xpub;
xpub.Decode(ser_xpub.data());
if (wss.m_descriptor_caches.count(desc_id)) {
wss.m_descriptor_caches[desc_id] = DescriptorCache();
}
if (parent) {
wss.m_descriptor_caches[desc_id].CacheParentExtPubKey(key_exp_index, xpub);
} else {
Expand Down
Loading
Loading