@@ -394,6 +394,62 @@ def test_dump_createfromdump(self):
394
394
self .assert_raises_tool_error ('Error: Checksum is not the correct size' , '-wallet=badload' , '-dumpfile={}' .format (bad_sum_wallet_dump ), 'createfromdump' )
395
395
assert not (self .nodes [0 ].wallets_path / "badload" ).is_dir ()
396
396
397
+ def test_chainless_conflicts (self ):
398
+ self .log .info ("Test wallet tool when wallet contains conflicting transactions" )
399
+ self .restart_node (0 )
400
+ self .generate (self .nodes [0 ], 101 )
401
+
402
+ def_wallet = self .nodes [0 ].get_wallet_rpc (self .default_wallet_name )
403
+
404
+ self .nodes [0 ].createwallet ("conflicts" )
405
+ wallet = self .nodes [0 ].get_wallet_rpc ("conflicts" )
406
+ def_wallet .sendtoaddress (wallet .getnewaddress (), 10 )
407
+ self .generate (self .nodes [0 ], 1 )
408
+
409
+ # parent tx
410
+ parent_txid = wallet .sendtoaddress (wallet .getnewaddress (), 9 )
411
+ parent_txid_bytes = bytes .fromhex (parent_txid )[::- 1 ]
412
+ conflict_utxo = wallet .gettransaction (txid = parent_txid , verbose = True )["decoded" ]["vin" ][0 ]
413
+
414
+ # The specific assertion in MarkConflicted being tested requires that the parent tx is already loaded
415
+ # by the time the child tx is loaded. Since transactions end up being loaded in txid order due to how both
416
+ # and sqlite store things, we can just grind the child tx until it has a txid that is greater than the parent's.
417
+ locktime = 500000000 # Use locktime as nonce, starting at unix timestamp minimum
418
+ addr = wallet .getnewaddress ()
419
+ while True :
420
+ child_send_res = wallet .send (outputs = [{addr : 8 }], add_to_wallet = False , locktime = locktime )
421
+ child_txid = child_send_res ["txid" ]
422
+ child_txid_bytes = bytes .fromhex (child_txid )[::- 1 ]
423
+ if (child_txid_bytes > parent_txid_bytes ):
424
+ wallet .sendrawtransaction (child_send_res ["hex" ])
425
+ break
426
+ locktime += 1
427
+
428
+ # conflict with parent
429
+ conflict_unsigned = self .nodes [0 ].createrawtransaction (inputs = [conflict_utxo ], outputs = [{wallet .getnewaddress (): 9.9999 }])
430
+ conflict_signed = wallet .signrawtransactionwithwallet (conflict_unsigned )["hex" ]
431
+ conflict_txid = self .nodes [0 ].sendrawtransaction (conflict_signed )
432
+ self .generate (self .nodes [0 ], 1 )
433
+ assert_equal (wallet .gettransaction (txid = parent_txid )["confirmations" ], - 1 )
434
+ assert_equal (wallet .gettransaction (txid = child_txid )["confirmations" ], - 1 )
435
+ assert_equal (wallet .gettransaction (txid = conflict_txid )["confirmations" ], 1 )
436
+
437
+ self .stop_node (0 )
438
+
439
+ # Wallet tool should successfully give info for this wallet
440
+ expected_output = textwrap .dedent (f'''\
441
+ Wallet info
442
+ ===========
443
+ Name: conflicts
444
+ Format: { "sqlite" if self .options .descriptors else "bdb" }
445
+ Descriptors: { "yes" if self .options .descriptors else "no" }
446
+ Encrypted: no
447
+ HD (hd seed available): yes
448
+ Keypool Size: { "8" if self .options .descriptors else "1" }
449
+ Transactions: 4
450
+ Address Book: 4
451
+ ''' )
452
+ self .assert_tool_output (expected_output , "-wallet=conflicts" , "info" )
397
453
398
454
def run_test (self ):
399
455
self .wallet_path = os .path .join (self .nodes [0 ].wallets_path , self .default_wallet_name , self .wallet_data_filename )
@@ -407,6 +463,7 @@ def run_test(self):
407
463
# Salvage is a legacy wallet only thing
408
464
self .test_salvage ()
409
465
self .test_dump_createfromdump ()
466
+ self .test_chainless_conflicts ()
410
467
411
468
if __name__ == '__main__' :
412
469
ToolWalletTest ().main ()
0 commit comments