Skip to content

Commit e4dd5a3

Browse files
committed
test: wallet, abandon coinbase txs and their descendants during startup
1 parent 474139a commit e4dd5a3

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

test/functional/wallet_reorgsrestore.py

+64
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from test_framework.test_framework import BitcoinTestFramework
2020
from test_framework.util import (
2121
assert_equal,
22+
assert_raises_rpc_error
2223
)
2324

2425
class ReorgsRestoreTest(BitcoinTestFramework):
@@ -31,6 +32,65 @@ def set_test_params(self):
3132
def skip_test_if_missing_module(self):
3233
self.skip_if_no_wallet()
3334

35+
def test_coinbase_automatic_abandon_during_startup(self):
36+
##########################################################################################################
37+
# Verify the wallet marks coinbase transactions, and their descendants, as abandoned during startup when #
38+
# the block is no longer part of the best chain. #
39+
##########################################################################################################
40+
self.log.info("Test automatic coinbase abandonment during startup")
41+
# Test setup: Sync nodes for the coming test, ensuring both are at the same block, then disconnect them to
42+
# generate two competing chains. After disconnection, verify no other peer connection exists.
43+
self.connect_nodes(1, 0)
44+
self.sync_blocks(self.nodes[:2])
45+
self.disconnect_nodes(1, 0)
46+
assert all(len(node.getpeerinfo()) == 0 for node in self.nodes[:2])
47+
48+
# Create a new block in node0, coinbase going to wallet0
49+
self.nodes[0].createwallet(wallet_name="w0", load_on_startup=True)
50+
wallet0 = self.nodes[0].get_wallet_rpc("w0")
51+
self.generatetoaddress(self.nodes[0], 1, wallet0.getnewaddress(), sync_fun=self.no_op)
52+
node0_coinbase_tx_hash = wallet0.getblock(wallet0.getbestblockhash(), verbose=1)['tx'][0]
53+
54+
# Mine 100 blocks on top to mature the coinbase and create a descendant
55+
self.generate(self.nodes[0], 101, sync_fun=self.no_op)
56+
# Make descendant, send-to-self
57+
descendant_tx_id = wallet0.sendtoaddress(wallet0.getnewaddress(), 1)
58+
59+
# Verify balance
60+
wallet0.syncwithvalidationinterfacequeue()
61+
assert(wallet0.getbalances()['mine']['trusted'] > 0)
62+
63+
# Now create a fork in node1. This will be used to replace node0's chain later.
64+
self.nodes[1].createwallet(wallet_name="w1", load_on_startup=True)
65+
wallet1 = self.nodes[1].get_wallet_rpc("w1")
66+
self.generatetoaddress(self.nodes[1], 1, wallet1.getnewaddress(), sync_fun=self.no_op)
67+
wallet1.syncwithvalidationinterfacequeue()
68+
69+
# Verify both nodes are on a different chain
70+
block0_best_hash, block1_best_hash = wallet0.getbestblockhash(), wallet1.getbestblockhash()
71+
assert(block0_best_hash != block1_best_hash)
72+
73+
# Stop both nodes and replace node0 chain entirely for the node1 chain
74+
self.stop_nodes()
75+
for path in ["chainstate", "blocks"]:
76+
shutil.rmtree(self.nodes[0].chain_path / path)
77+
shutil.copytree(self.nodes[1].chain_path / path, self.nodes[0].chain_path / path)
78+
79+
# Start node0 and verify that now it has node1 chain and no info about its previous best block
80+
self.start_node(0)
81+
wallet0 = self.nodes[0].get_wallet_rpc("w0")
82+
assert_equal(wallet0.getbestblockhash(), block1_best_hash)
83+
assert_raises_rpc_error(-5, "Block not found", wallet0.getblock, block0_best_hash)
84+
85+
# Verify the coinbase tx was marked as abandoned and balance correctly computed
86+
tx_info = wallet0.gettransaction(node0_coinbase_tx_hash)['details'][0]
87+
assert_equal(tx_info['abandoned'], True)
88+
assert_equal(tx_info['category'], 'orphan')
89+
assert(wallet0.getbalances()['mine']['trusted'] == 0)
90+
# Verify the coinbase descendant was also marked as abandoned
91+
assert_equal(wallet0.gettransaction(descendant_tx_id)['details'][0]['abandoned'], True)
92+
93+
3494
def run_test(self):
3595
# Send a tx from which to conflict outputs later
3696
txid_conflict_from = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
@@ -100,5 +160,9 @@ def run_test(self):
100160
assert_equal(conflicted_after_reorg["confirmations"], 1)
101161
assert conflicting["blockhash"] != conflicted_after_reorg["blockhash"]
102162

163+
# Verify we mark coinbase txs, and their descendants, as abandoned during startup
164+
self.test_coinbase_automatic_abandon_during_startup()
165+
166+
103167
if __name__ == '__main__':
104168
ReorgsRestoreTest(__file__).main()

0 commit comments

Comments
 (0)