Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2014-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Test the listdescriptors RPC."""7from test_framework.blocktools import (8TIME_GENESIS_BLOCK,9)10from test_framework.descriptors import (11descsum_create,12)13from test_framework.extendedkey import ExtendedPrivateKey14from test_framework.test_framework import BitcoinTestFramework15from test_framework.util import (16assert_not_equal,17assert_equal,18assert_raises_rpc_error,19)20from test_framework.wallet_util import generate_keypair23class ListDescriptorsTest(BitcoinTestFramework):24def set_test_params(self):25self.num_nodes = 127def skip_test_if_missing_module(self):28self.skip_if_no_wallet()30# do not create any wallet by default31def init_wallet(self, *, node):32return34def run_test(self):35node = self.nodes[0]36assert_raises_rpc_error(-18, 'No wallet is loaded.', node.listdescriptors)38self.log.info('Test the command for empty descriptors wallet.')39node.createwallet(wallet_name='w2', blank=True)40assert_equal(0, len(node.get_wallet_rpc('w2').listdescriptors()['descriptors']))42self.log.info('Test the command for a default descriptors wallet.')43node.createwallet(wallet_name='w3')44result = node.get_wallet_rpc('w3').listdescriptors()45assert_equal("w3", result['wallet_name'])46assert_equal(8, len(result['descriptors']))47assert_equal(8, len([d for d in result['descriptors'] if d['active']]))48assert_equal(4, len([d for d in result['descriptors'] if d['internal']]))49for item in result['descriptors']:50assert_not_equal(item['desc'], '')51assert_equal(item['next_index'], 0)52assert_equal(item['range'], [0, 0])53assert item['timestamp'] is not None55self.log.info('Test that descriptor strings are returned in lexicographically sorted order.')56descriptor_strings = [descriptor['desc'] for descriptor in result['descriptors']]57assert_equal(descriptor_strings, sorted(descriptor_strings))59self.log.info('Test descriptors with hardened derivations are listed in importable form.')60extended_key = ExtendedPrivateKey.generate()61xprv = extended_key.to_string()62xprv_fingerprint = extended_key._fingerprint().hex()63hardened_path = '/84h/1h/0h'64xpub_acc = extended_key.derive_path(f"m{hardened_path}").pubkey().to_string()65wallet = node.get_wallet_rpc('w2')66wallet.importdescriptors([{67'desc': descsum_create('wpkh(' + xprv + hardened_path + '/0/*)'),68'timestamp': TIME_GENESIS_BLOCK,69}])70expected = {71'wallet_name': 'w2',72'descriptors': [73{'desc': descsum_create('wpkh([' + xprv_fingerprint + hardened_path + ']' + xpub_acc + '/0/*)'),74'timestamp': TIME_GENESIS_BLOCK,75'active': False,76'range': [0, 0],77'next': 0,78'next_index': 0},79],80}81assert_equal(expected, wallet.listdescriptors())82assert_equal(expected, wallet.listdescriptors(False))84self.log.info('Test list private descriptors')85expected_private = {86'wallet_name': 'w2',87'descriptors': [88{'desc': descsum_create('wpkh(' + xprv + hardened_path + '/0/*)'),89'timestamp': TIME_GENESIS_BLOCK,90'active': False,91'range': [0, 0],92'next': 0,93'next_index': 0},94],95}96assert_equal(expected_private, wallet.listdescriptors(True))98self.log.info("Test listdescriptors with encrypted wallet")99wallet.encryptwallet("pass")100assert_equal(expected, wallet.listdescriptors())102self.log.info('Test list private descriptors with encrypted wallet')103assert_raises_rpc_error(-13, 'Please enter the wallet passphrase with walletpassphrase first.', wallet.listdescriptors, True)104wallet.walletpassphrase(passphrase="pass", timeout=1000000)105assert_equal(expected_private, wallet.listdescriptors(True))107self.log.info('Test list private descriptors with watch-only wallet')108node.createwallet(wallet_name='watch-only', disable_private_keys=True)109watch_only_wallet = node.get_wallet_rpc('watch-only')110watch_only_wallet.importdescriptors([{111'desc': descsum_create('wpkh(' + xpub_acc + ')'),112'timestamp': TIME_GENESIS_BLOCK,113}])114assert_raises_rpc_error(-4, 'Can\'t get private descriptor string for watch-only wallets', watch_only_wallet.listdescriptors, True)116self.log.info('Test non-active non-range combo descriptor')117node.createwallet(wallet_name='w4', blank=True)118wallet = node.get_wallet_rpc('w4')119wallet.importdescriptors([{120'desc': descsum_create('combo(' + node.get_deterministic_priv_key().key + ')'),121'timestamp': TIME_GENESIS_BLOCK,122}])123expected = {124'wallet_name': 'w4',125'descriptors': [126{'active': False,127'desc': 'combo(0227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3f)#np574htj',128'timestamp': TIME_GENESIS_BLOCK},129],130}131assert_equal(expected, wallet.listdescriptors())133self.log.info('Test taproot descriptor do not have mixed hardened derivation marker')134node.createwallet(wallet_name='w5', descriptors=True, disable_private_keys=True)135wallet = node.get_wallet_rpc('w5')136derivation_path = "/48'/1'/0'/2'"137xprvs = [ExtendedPrivateKey.generate() for _ in range(0, 2)]138xprv_fingerprints = [xprv._fingerprint().hex() for xprv in xprvs]139desc_xpubs = [xprv.derive_path(f"m{derivation_path}").pubkey().to_string() for xprv in xprvs]140wallet.importdescriptors([{141'desc': descsum_create(f"tr([{xprv_fingerprints[0]}{derivation_path}]{desc_xpubs[0]}/0/*,and_v(v:pk([{xprv_fingerprints[1]}{derivation_path}]{desc_xpubs[1]}/0/*),older(65535)))"),142'timestamp': TIME_GENESIS_BLOCK,143}])144derivation_path_alternate = "/48h/1h/0h/2h"145expected = {146'wallet_name': 'w5',147'descriptors': [148{'active': False,149'desc': descsum_create(f'tr([{xprv_fingerprints[0]}{derivation_path_alternate}]{desc_xpubs[0]}/0/*,and_v(v:pk([{xprv_fingerprints[1]}{derivation_path_alternate}]{desc_xpubs[1]}/0/*),older(65535)))'),150'timestamp': TIME_GENESIS_BLOCK,151'range': [0, 0],152'next': 0,153'next_index': 0},154]155}156assert_equal(expected, wallet.listdescriptors())158self.log.info('Test descriptor with missing private keys')159node.createwallet(wallet_name='w6', blank=True)160wallet = node.get_wallet_rpc('w6')162xprvs = []163xpubs = []164for _ in range(0, 8):165xprvs.append(ExtendedPrivateKey.generate().to_string())166xpubs.append(ExtendedPrivateKey.generate().pubkey().to_string())168_, pubkey_bytes = generate_keypair()169pubkey = pubkey_bytes.hex()171expected_descs = {172descsum_create(f'tr({node.get_deterministic_priv_key().key},{{pk({pubkey}),pk([d34db33f/44h/0h/0h]{xpubs[0]}/0)}})'),173descsum_create(f'wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,{node.get_deterministic_priv_key().key},{xpubs[1]}/0)))'),174descsum_create(f'tr({pubkey},pk(musig({xprvs[0]},{xpubs[2]})/7/8/*))'),175descsum_create(f'tr({pubkey},pk(musig({xprvs[1]}/10,{xpubs[3]}/11)/*))'),176descsum_create(f'tr({pubkey},{{pk(musig({xpubs[4]},{xprvs[2]})/12/*),pk(musig({xprvs[3]},{xpubs[4]})/13/*)}})'),177descsum_create(f'tr({pubkey},{{pk(musig({xpubs[5]},{xpubs[6]})/12/*),pk(musig({xprvs[4]},{xpubs[7]})/13/*)}})')178}180descs_to_import = []181for desc in expected_descs:182descs_to_import.append({'desc': desc, 'timestamp': TIME_GENESIS_BLOCK})184wallet.importdescriptors(descs_to_import)185result = wallet.listdescriptors(True)186actual_descs = [d['desc'] for d in result['descriptors']]188assert_equal(len(actual_descs), len(expected_descs))189for desc in actual_descs:190if desc not in expected_descs:191raise AssertionError(f"{desc} missing")195if __name__ == '__main__':196ListDescriptorsTest(__file__).main()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.