99except ImportError :
1010 pass
1111
12+ import re
13+
1214from test_framework .blocktools import COINBASE_MATURITY
1315from test_framework .test_framework import BitcoinTestFramework
1416from test_framework .util import (
@@ -29,6 +31,54 @@ def skip_test_if_missing_module(self):
2931 self .skip_if_no_wallet ()
3032 self .skip_if_no_py_sqlite3 ()
3133
34+ def test_parent_descriptors (self ):
35+ self .log .info ("Check that parent_descs is the same for all RPCs and is normalized" )
36+ self .nodes [0 ].createwallet (wallet_name = "parent_descs" )
37+ wallet = self .nodes [0 ].get_wallet_rpc ("parent_descs" )
38+ default_wallet = self .nodes [0 ].get_wallet_rpc (self .default_wallet_name )
39+
40+ addr = wallet .getnewaddress ()
41+ parent_desc = wallet .getaddressinfo (addr )["parent_desc" ]
42+
43+ # Verify that the parent descriptor is normalized
44+ # First remove the checksum
45+ desc_verify = parent_desc .split ("#" )[0 ]
46+ # Next extract the xpub
47+ desc_verify = re .sub (r"tpub\w+?(?=/)" , "" , desc_verify )
48+ # Extract origin info
49+ origin_match = re .search (r'\[([\da-fh/]+)\]' , desc_verify )
50+ origin_part = origin_match .group (1 ) if origin_match else ""
51+ # Split on "]" for everything after the origin info
52+ after_origin = desc_verify .split ("]" , maxsplit = 1 )[- 1 ]
53+ # Look for the hardened markers “h” inside each piece
54+ # We don't need to check for aspostrophe as normalization will not output aspostrophe
55+ found_hardened_in_origin = "h" in origin_part
56+ found_hardened_after_origin = "h" in after_origin
57+ assert_equal (found_hardened_in_origin , True )
58+ assert_equal (found_hardened_after_origin , False )
59+
60+ # Send some coins so we can check listunspent, listtransactions, listunspent, and gettransaction
61+ since_block = self .nodes [0 ].getbestblockhash ()
62+ txid = default_wallet .sendtoaddress (addr , 1 )
63+ self .generate (self .nodes [0 ], 1 )
64+
65+ unspent = wallet .listunspent ()
66+ assert_equal (len (unspent ), 1 )
67+ assert_equal (unspent [0 ]["parent_descs" ], [parent_desc ])
68+
69+ txs = wallet .listtransactions ()
70+ assert_equal (len (txs ), 1 )
71+ assert_equal (txs [0 ]["parent_descs" ], [parent_desc ])
72+
73+ txs = wallet .listsinceblock (since_block )["transactions" ]
74+ assert_equal (len (txs ), 1 )
75+ assert_equal (txs [0 ]["parent_descs" ], [parent_desc ])
76+
77+ tx = wallet .gettransaction (txid = txid , verbose = True )
78+ assert_equal (tx ["details" ][0 ]["parent_descs" ], [parent_desc ])
79+
80+ wallet .unloadwallet ()
81+
3282 def run_test (self ):
3383 self .generate (self .nodes [0 ], COINBASE_MATURITY + 1 )
3484
@@ -218,6 +268,7 @@ def run_test(self):
218268 conn .close ()
219269 assert_raises_rpc_error (- 4 , "Unexpected legacy entry in descriptor wallet found." , self .nodes [0 ].loadwallet , "crashme" )
220270
271+ self .test_parent_descriptors ()
221272
222273if __name__ == '__main__' :
223274 WalletDescriptorTest (__file__ ).main ()
0 commit comments