5
5
"""Test generation and spending of P2TR addresses."""
6
6
7
7
import random
8
+ import uuid
8
9
9
10
from decimal import Decimal
10
11
from test_framework .address import output_key_to_p2tr
@@ -226,104 +227,162 @@ def make_addr(treefn, keys, i):
226
227
227
228
def do_test_addr (self , comment , pattern , privmap , treefn , keys ):
228
229
self .log .info ("Testing %s address derivation" % comment )
230
+
231
+ # Create wallets
232
+ wallet_uuid = uuid .uuid4 ().hex
233
+ self .nodes [0 ].createwallet (wallet_name = f"privs_tr_enabled_{ wallet_uuid } " , descriptors = True , blank = True )
234
+ self .nodes [0 ].createwallet (wallet_name = f"pubs_tr_enabled_{ wallet_uuid } " , descriptors = True , blank = True , disable_private_keys = True )
235
+ self .nodes [0 ].createwallet (wallet_name = f"addr_gen_{ wallet_uuid } " , descriptors = True , disable_private_keys = True , blank = True )
236
+ privs_tr_enabled = self .nodes [0 ].get_wallet_rpc (f"privs_tr_enabled_{ wallet_uuid } " )
237
+ pubs_tr_enabled = self .nodes [0 ].get_wallet_rpc (f"pubs_tr_enabled_{ wallet_uuid } " )
238
+ addr_gen = self .nodes [0 ].get_wallet_rpc (f"addr_gen_{ wallet_uuid } " )
239
+
229
240
desc = self .make_desc (pattern , privmap , keys , False )
230
241
desc_pub = self .make_desc (pattern , privmap , keys , True )
231
242
assert_equal (self .nodes [0 ].getdescriptorinfo (desc )['descriptor' ], desc_pub )
232
- result = self . addr_gen .importdescriptors ([{"desc" : desc_pub , "active" : True , "timestamp" : "now" }])
243
+ result = addr_gen .importdescriptors ([{"desc" : desc_pub , "active" : True , "timestamp" : "now" }])
233
244
assert (result [0 ]['success' ])
245
+ address_type = "bech32m" if "tr" in pattern else "bech32"
234
246
for i in range (4 ):
235
- addr_g = self . addr_gen .getnewaddress (address_type = 'bech32m' )
247
+ addr_g = addr_gen .getnewaddress (address_type = address_type )
236
248
if treefn is not None :
237
249
addr_r = self .make_addr (treefn , keys , i )
238
250
assert_equal (addr_g , addr_r )
239
- desc_a = self . addr_gen .getaddressinfo (addr_g )['desc' ]
251
+ desc_a = addr_gen .getaddressinfo (addr_g )['desc' ]
240
252
if desc .startswith ("tr(" ):
241
253
assert desc_a .startswith ("tr(" )
242
254
rederive = self .nodes [1 ].deriveaddresses (desc_a )
243
255
assert_equal (len (rederive ), 1 )
244
256
assert_equal (rederive [0 ], addr_g )
245
257
246
258
# tr descriptors can be imported
247
- result = self . privs_tr_enabled .importdescriptors ([{"desc" : desc , "timestamp" : "now" }])
259
+ result = privs_tr_enabled .importdescriptors ([{"desc" : desc , "timestamp" : "now" }])
248
260
assert (result [0 ]["success" ])
249
- result = self . pubs_tr_enabled .importdescriptors ([{"desc" : desc_pub , "timestamp" : "now" }])
261
+ result = pubs_tr_enabled .importdescriptors ([{"desc" : desc_pub , "timestamp" : "now" }])
250
262
assert (result [0 ]["success" ])
251
263
264
+ # Cleanup
265
+ privs_tr_enabled .unloadwallet ()
266
+ pubs_tr_enabled .unloadwallet ()
267
+ addr_gen .unloadwallet ()
268
+
252
269
def do_test_sendtoaddress (self , comment , pattern , privmap , treefn , keys_pay , keys_change ):
253
270
self .log .info ("Testing %s through sendtoaddress" % comment )
271
+
272
+ # Create wallets
273
+ wallet_uuid = uuid .uuid4 ().hex
274
+ self .nodes [0 ].createwallet (wallet_name = f"rpc_online_{ wallet_uuid } " , descriptors = True , blank = True )
275
+ rpc_online = self .nodes [0 ].get_wallet_rpc (f"rpc_online_{ wallet_uuid } " )
276
+
254
277
desc_pay = self .make_desc (pattern , privmap , keys_pay )
255
278
desc_change = self .make_desc (pattern , privmap , keys_change )
256
279
desc_pay_pub = self .make_desc (pattern , privmap , keys_pay , True )
257
280
desc_change_pub = self .make_desc (pattern , privmap , keys_change , True )
258
281
assert_equal (self .nodes [0 ].getdescriptorinfo (desc_pay )['descriptor' ], desc_pay_pub )
259
282
assert_equal (self .nodes [0 ].getdescriptorinfo (desc_change )['descriptor' ], desc_change_pub )
260
- result = self . rpc_online .importdescriptors ([{"desc" : desc_pay , "active" : True , "timestamp" : "now" }])
283
+ result = rpc_online .importdescriptors ([{"desc" : desc_pay , "active" : True , "timestamp" : "now" }])
261
284
assert (result [0 ]['success' ])
262
- result = self . rpc_online .importdescriptors ([{"desc" : desc_change , "active" : True , "timestamp" : "now" , "internal" : True }])
285
+ result = rpc_online .importdescriptors ([{"desc" : desc_change , "active" : True , "timestamp" : "now" , "internal" : True }])
263
286
assert (result [0 ]['success' ])
287
+ address_type = "bech32m" if "tr" in pattern else "bech32"
264
288
for i in range (4 ):
265
- addr_g = self . rpc_online .getnewaddress (address_type = 'bech32m' )
289
+ addr_g = rpc_online .getnewaddress (address_type = address_type )
266
290
if treefn is not None :
267
291
addr_r = self .make_addr (treefn , keys_pay , i )
268
292
assert_equal (addr_g , addr_r )
269
293
boring_balance = int (self .boring .getbalance () * 100000000 )
270
294
to_amnt = random .randrange (1000000 , boring_balance )
271
295
self .boring .sendtoaddress (address = addr_g , amount = Decimal (to_amnt ) / 100000000 , subtractfeefromamount = True )
272
296
self .generatetoaddress (self .nodes [0 ], 1 , self .boring .getnewaddress (), sync_fun = self .no_op )
273
- test_balance = int (self . rpc_online .getbalance () * 100000000 )
297
+ test_balance = int (rpc_online .getbalance () * 100000000 )
274
298
ret_amnt = random .randrange (100000 , test_balance )
275
299
# Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends.
276
- res = self . rpc_online .sendtoaddress (address = self .boring .getnewaddress (), amount = Decimal (ret_amnt ) / 100000000 , subtractfeefromamount = True , fee_rate = 200 )
300
+ res = rpc_online .sendtoaddress (address = self .boring .getnewaddress (), amount = Decimal (ret_amnt ) / 100000000 , subtractfeefromamount = True , fee_rate = 200 )
277
301
self .generatetoaddress (self .nodes [0 ], 1 , self .boring .getnewaddress (), sync_fun = self .no_op )
278
- assert (self .rpc_online .gettransaction (res )["confirmations" ] > 0 )
302
+ assert (rpc_online .gettransaction (res )["confirmations" ] > 0 )
303
+
304
+ # Cleanup
305
+ txid = rpc_online .sendall (recipients = [self .boring .getnewaddress ()])["txid" ]
306
+ self .generatetoaddress (self .nodes [0 ], 1 , self .boring .getnewaddress (), sync_fun = self .no_op )
307
+ assert (rpc_online .gettransaction (txid )["confirmations" ] > 0 )
308
+ rpc_online .unloadwallet ()
279
309
280
310
def do_test_psbt (self , comment , pattern , privmap , treefn , keys_pay , keys_change ):
281
311
self .log .info ("Testing %s through PSBT" % comment )
312
+
313
+ # Create wallets
314
+ wallet_uuid = uuid .uuid4 ().hex
315
+ self .nodes [0 ].createwallet (wallet_name = f"psbt_online_{ wallet_uuid } " , descriptors = True , disable_private_keys = True , blank = True )
316
+ self .nodes [1 ].createwallet (wallet_name = f"psbt_offline_{ wallet_uuid } " , descriptors = True , blank = True )
317
+ self .nodes [1 ].createwallet (f"key_only_wallet_{ wallet_uuid } " , descriptors = True , blank = True )
318
+ psbt_online = self .nodes [0 ].get_wallet_rpc (f"psbt_online_{ wallet_uuid } " )
319
+ psbt_offline = self .nodes [1 ].get_wallet_rpc (f"psbt_offline_{ wallet_uuid } " )
320
+ key_only_wallet = self .nodes [1 ].get_wallet_rpc (f"key_only_wallet_{ wallet_uuid } " )
321
+
282
322
desc_pay = self .make_desc (pattern , privmap , keys_pay , False )
283
323
desc_change = self .make_desc (pattern , privmap , keys_change , False )
284
324
desc_pay_pub = self .make_desc (pattern , privmap , keys_pay , True )
285
325
desc_change_pub = self .make_desc (pattern , privmap , keys_change , True )
286
326
assert_equal (self .nodes [0 ].getdescriptorinfo (desc_pay )['descriptor' ], desc_pay_pub )
287
327
assert_equal (self .nodes [0 ].getdescriptorinfo (desc_change )['descriptor' ], desc_change_pub )
288
- result = self . psbt_online .importdescriptors ([{"desc" : desc_pay_pub , "active" : True , "timestamp" : "now" }])
328
+ result = psbt_online .importdescriptors ([{"desc" : desc_pay_pub , "active" : True , "timestamp" : "now" }])
289
329
assert (result [0 ]['success' ])
290
- result = self . psbt_online .importdescriptors ([{"desc" : desc_change_pub , "active" : True , "timestamp" : "now" , "internal" : True }])
330
+ result = psbt_online .importdescriptors ([{"desc" : desc_change_pub , "active" : True , "timestamp" : "now" , "internal" : True }])
291
331
assert (result [0 ]['success' ])
292
- result = self . psbt_offline .importdescriptors ([{"desc" : desc_pay , "active" : True , "timestamp" : "now" }])
332
+ result = psbt_offline .importdescriptors ([{"desc" : desc_pay , "active" : True , "timestamp" : "now" }])
293
333
assert (result [0 ]['success' ])
294
- result = self . psbt_offline .importdescriptors ([{"desc" : desc_change , "active" : True , "timestamp" : "now" , "internal" : True }])
334
+ result = psbt_offline .importdescriptors ([{"desc" : desc_change , "active" : True , "timestamp" : "now" , "internal" : True }])
295
335
assert (result [0 ]['success' ])
336
+ for key in keys_pay + keys_change :
337
+ result = key_only_wallet .importdescriptors ([{"desc" : descsum_create (f"wpkh({ key ['xprv' ]} /*)" ), "timestamp" :"now" }])
338
+ assert (result [0 ]["success" ])
339
+ address_type = "bech32m" if "tr" in pattern else "bech32"
296
340
for i in range (4 ):
297
- addr_g = self . psbt_online .getnewaddress (address_type = 'bech32m' )
341
+ addr_g = psbt_online .getnewaddress (address_type = address_type )
298
342
if treefn is not None :
299
343
addr_r = self .make_addr (treefn , keys_pay , i )
300
344
assert_equal (addr_g , addr_r )
301
345
boring_balance = int (self .boring .getbalance () * 100000000 )
302
346
to_amnt = random .randrange (1000000 , boring_balance )
303
347
self .boring .sendtoaddress (address = addr_g , amount = Decimal (to_amnt ) / 100000000 , subtractfeefromamount = True )
304
348
self .generatetoaddress (self .nodes [0 ], 1 , self .boring .getnewaddress (), sync_fun = self .no_op )
305
- test_balance = int (self . psbt_online .getbalance () * 100000000 )
349
+ test_balance = int (psbt_online .getbalance () * 100000000 )
306
350
ret_amnt = random .randrange (100000 , test_balance )
307
351
# Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends.
308
- psbt = self .psbt_online .walletcreatefundedpsbt ([], [{self .boring .getnewaddress (): Decimal (ret_amnt ) / 100000000 }], None , {"subtractFeeFromOutputs" :[0 ], "fee_rate" : 200 , "change_type" : "bech32m" })['psbt' ]
309
- res = self .psbt_offline .walletprocesspsbt (psbt = psbt , finalize = False )
310
-
311
- decoded = self .psbt_offline .decodepsbt (res ["psbt" ])
312
- if pattern .startswith ("tr(" ):
313
- for psbtin in decoded ["inputs" ]:
314
- assert "non_witness_utxo" not in psbtin
315
- assert "witness_utxo" in psbtin
316
- assert "taproot_internal_key" in psbtin
317
- assert "taproot_bip32_derivs" in psbtin
318
- assert "taproot_key_path_sig" in psbtin or "taproot_script_path_sigs" in psbtin
319
- if "taproot_script_path_sigs" in psbtin :
320
- assert "taproot_merkle_root" in psbtin
321
- assert "taproot_scripts" in psbtin
322
-
323
- rawtx = self .nodes [0 ].finalizepsbt (res ['psbt' ])['hex' ]
352
+ psbt = psbt_online .walletcreatefundedpsbt ([], [{self .boring .getnewaddress (): Decimal (ret_amnt ) / 100000000 }], None , {"subtractFeeFromOutputs" :[0 ], "fee_rate" : 200 , "change_type" : address_type })['psbt' ]
353
+ res = psbt_offline .walletprocesspsbt (psbt = psbt , finalize = False )
354
+ for wallet in [psbt_offline , key_only_wallet ]:
355
+ res = wallet .walletprocesspsbt (psbt = psbt , finalize = False )
356
+
357
+ decoded = wallet .decodepsbt (res ["psbt" ])
358
+ if pattern .startswith ("tr(" ):
359
+ for psbtin in decoded ["inputs" ]:
360
+ assert "non_witness_utxo" not in psbtin
361
+ assert "witness_utxo" in psbtin
362
+ assert "taproot_internal_key" in psbtin
363
+ assert "taproot_bip32_derivs" in psbtin
364
+ assert "taproot_key_path_sig" in psbtin or "taproot_script_path_sigs" in psbtin
365
+ if "taproot_script_path_sigs" in psbtin :
366
+ assert "taproot_merkle_root" in psbtin
367
+ assert "taproot_scripts" in psbtin
368
+
369
+ rawtx = self .nodes [0 ].finalizepsbt (res ['psbt' ])['hex' ]
370
+ res = self .nodes [0 ].testmempoolaccept ([rawtx ])
371
+ assert res [0 ]["allowed" ]
372
+
324
373
txid = self .nodes [0 ].sendrawtransaction (rawtx )
325
374
self .generatetoaddress (self .nodes [0 ], 1 , self .boring .getnewaddress (), sync_fun = self .no_op )
326
- assert (self .psbt_online .gettransaction (txid )['confirmations' ] > 0 )
375
+ assert (psbt_online .gettransaction (txid )['confirmations' ] > 0 )
376
+
377
+ # Cleanup
378
+ psbt = psbt_online .sendall (recipients = [self .boring .getnewaddress ()], options = {"psbt" : True })["psbt" ]
379
+ res = psbt_offline .walletprocesspsbt (psbt = psbt , finalize = False )
380
+ rawtx = self .nodes [0 ].finalizepsbt (res ['psbt' ])['hex' ]
381
+ txid = self .nodes [0 ].sendrawtransaction (rawtx )
382
+ self .generatetoaddress (self .nodes [0 ], 1 , self .boring .getnewaddress (), sync_fun = self .no_op )
383
+ assert (psbt_online .gettransaction (txid )['confirmations' ] > 0 )
384
+ psbt_online .unloadwallet ()
385
+ psbt_offline .unloadwallet ()
327
386
328
387
def do_test (self , comment , pattern , privmap , treefn ):
329
388
nkeys = len (privmap )
@@ -333,21 +392,8 @@ def do_test(self, comment, pattern, privmap, treefn):
333
392
self .do_test_psbt (comment , pattern , privmap , treefn , keys [2 * nkeys :3 * nkeys ], keys [3 * nkeys :4 * nkeys ])
334
393
335
394
def run_test (self ):
336
- self .log .info ("Creating wallets..." )
337
- self .nodes [0 ].createwallet (wallet_name = "privs_tr_enabled" , descriptors = True , blank = True )
338
- self .privs_tr_enabled = self .nodes [0 ].get_wallet_rpc ("privs_tr_enabled" )
339
- self .nodes [0 ].createwallet (wallet_name = "pubs_tr_enabled" , descriptors = True , blank = True , disable_private_keys = True )
340
- self .pubs_tr_enabled = self .nodes [0 ].get_wallet_rpc ("pubs_tr_enabled" )
341
395
self .nodes [0 ].createwallet (wallet_name = "boring" )
342
- self .nodes [0 ].createwallet (wallet_name = "addr_gen" , descriptors = True , disable_private_keys = True , blank = True )
343
- self .nodes [0 ].createwallet (wallet_name = "rpc_online" , descriptors = True , blank = True )
344
- self .nodes [0 ].createwallet (wallet_name = "psbt_online" , descriptors = True , disable_private_keys = True , blank = True )
345
- self .nodes [1 ].createwallet (wallet_name = "psbt_offline" , descriptors = True , blank = True )
346
396
self .boring = self .nodes [0 ].get_wallet_rpc ("boring" )
347
- self .addr_gen = self .nodes [0 ].get_wallet_rpc ("addr_gen" )
348
- self .rpc_online = self .nodes [0 ].get_wallet_rpc ("rpc_online" )
349
- self .psbt_online = self .nodes [0 ].get_wallet_rpc ("psbt_online" )
350
- self .psbt_offline = self .nodes [1 ].get_wallet_rpc ("psbt_offline" )
351
397
352
398
self .log .info ("Mining blocks..." )
353
399
gen_addr = self .boring .getnewaddress ()
@@ -457,18 +503,5 @@ def run_test(self):
457
503
lambda k1 : key (k1 )
458
504
)
459
505
460
- self .log .info ("Sending everything back..." )
461
-
462
- txid = self .rpc_online .sendall (recipients = [self .boring .getnewaddress ()])["txid" ]
463
- self .generatetoaddress (self .nodes [0 ], 1 , self .boring .getnewaddress (), sync_fun = self .no_op )
464
- assert (self .rpc_online .gettransaction (txid )["confirmations" ] > 0 )
465
-
466
- psbt = self .psbt_online .sendall (recipients = [self .boring .getnewaddress ()], options = {"psbt" : True })["psbt" ]
467
- res = self .psbt_offline .walletprocesspsbt (psbt = psbt , finalize = False )
468
- rawtx = self .nodes [0 ].finalizepsbt (res ['psbt' ])['hex' ]
469
- txid = self .nodes [0 ].sendrawtransaction (rawtx )
470
- self .generatetoaddress (self .nodes [0 ], 1 , self .boring .getnewaddress (), sync_fun = self .no_op )
471
- assert (self .psbt_online .gettransaction (txid )['confirmations' ] > 0 )
472
-
473
506
if __name__ == '__main__' :
474
507
WalletTaprootTest ().main ()
0 commit comments