mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-25 10:43:19 -03:00
wallet, rpc: add listdescriptors command
This commit is contained in:
parent
f1f26b8d5b
commit
647b81b709
4 changed files with 139 additions and 0 deletions
|
@ -1740,3 +1740,72 @@ RPCHelpMan importdescriptors()
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
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 != nullopt) {
|
||||
spk.pushKV("internal", wallet->GetScriptPubKeyMan(*type, 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;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4537,6 +4537,7 @@ RPCHelpMan importprunedfunds();
|
|||
RPCHelpMan removeprunedfunds();
|
||||
RPCHelpMan importmulti();
|
||||
RPCHelpMan importdescriptors();
|
||||
RPCHelpMan listdescriptors();
|
||||
|
||||
Span<const CRPCCommand> GetWalletRPCCommands()
|
||||
{
|
||||
|
@ -4575,6 +4576,7 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "importwallet", &importwallet, {"filename"} },
|
||||
{ "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} },
|
||||
{ "wallet", "listaddressgroupings", &listaddressgroupings, {} },
|
||||
{ "wallet", "listdescriptors", &listdescriptors, {} },
|
||||
{ "wallet", "listlabels", &listlabels, {"purpose"} },
|
||||
{ "wallet", "listlockunspent", &listlockunspent, {} },
|
||||
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
|
||||
|
|
|
@ -237,6 +237,7 @@ BASE_SCRIPTS = [
|
|||
'rpc_named_arguments.py',
|
||||
'wallet_listsinceblock.py',
|
||||
'wallet_listsinceblock.py --descriptors',
|
||||
'wallet_listdescriptors.py --descriptors',
|
||||
'p2p_leak.py',
|
||||
'wallet_encryption.py',
|
||||
'wallet_encryption.py --descriptors',
|
||||
|
|
67
test/functional/wallet_listdescriptors.py
Executable file
67
test/functional/wallet_listdescriptors.py
Executable file
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2021 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test the listdescriptors RPC."""
|
||||
|
||||
from test_framework.descriptors import (
|
||||
descsum_create
|
||||
)
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
|
||||
|
||||
class ListDescriptorsTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
self.skip_if_no_sqlite()
|
||||
|
||||
# do not create any wallet by default
|
||||
def init_wallet(self, i):
|
||||
return
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
assert_raises_rpc_error(-18, 'No wallet is loaded.', node.listdescriptors)
|
||||
|
||||
self.log.info('Test that the command is not available for legacy wallets.')
|
||||
node.createwallet(wallet_name='w1', descriptors=False)
|
||||
assert_raises_rpc_error(-4, 'listdescriptors is not available for non-descriptor wallets', node.listdescriptors)
|
||||
|
||||
self.log.info('Test the command for empty descriptors wallet.')
|
||||
node.createwallet(wallet_name='w2', blank=True, descriptors=True)
|
||||
assert_equal(0, len(node.get_wallet_rpc('w2').listdescriptors()))
|
||||
|
||||
self.log.info('Test the command for a default descriptors wallet.')
|
||||
node.createwallet(wallet_name='w3', descriptors=True)
|
||||
result = node.get_wallet_rpc('w3').listdescriptors()
|
||||
assert_equal(6, len(result))
|
||||
assert_equal(6, len([d for d in result if d['active']]))
|
||||
assert_equal(3, len([d for d in result if d['internal']]))
|
||||
for item in result:
|
||||
assert item['desc'] != ''
|
||||
assert item['next'] == 0
|
||||
assert item['range'] == [0, 0]
|
||||
assert item['timestamp'] is not None
|
||||
|
||||
self.log.info('Test non-active non-range combo descriptor')
|
||||
node.createwallet(wallet_name='w4', blank=True, descriptors=True)
|
||||
wallet = node.get_wallet_rpc('w4')
|
||||
wallet.importdescriptors([{
|
||||
'desc': descsum_create('combo(' + node.get_deterministic_priv_key().key + ')'),
|
||||
'timestamp': 1296688602,
|
||||
}])
|
||||
expected = [{'active': False,
|
||||
'desc': 'combo(0227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3f)#np574htj',
|
||||
'timestamp': 1296688602}]
|
||||
assert_equal(expected, wallet.listdescriptors())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ListDescriptorsTest().main()
|
Loading…
Add table
Reference in a new issue