Skip to content

Commit

Permalink
test: wallet, abandon coinbase txs and their descendants during startup
Browse files Browse the repository at this point in the history
  • Loading branch information
furszy committed Feb 4, 2025
1 parent 0b987ef commit c38266f
Showing 1 changed file with 65 additions and 0 deletions.
65 changes: 65 additions & 0 deletions test/functional/wallet_reorgsrestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error
)

class ReorgsRestoreTest(BitcoinTestFramework):
Expand All @@ -31,6 +32,66 @@ def set_test_params(self):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()

def test_coinbase_automatic_abandon_during_startup(self):
##########################################################################################################
# Verify the wallet marks coinbase transactions, and their descendants, as abandoned during startup when #
# the block is no longer part of the best chain. #
##########################################################################################################
# Sync nodes for the coming test and assert all are at the same block
for i, j in [(0, 1), (1, 2), (2, 0)]:
self.connect_nodes(i, j)
self.sync_blocks()
best_hash = self.nodes[0].getbestblockhash()
assert(all(node.getbestblockhash() == best_hash for node in self.nodes))

# Now disconnect nodes to generate two competing chains.
for i, j in [(0, 1), (1, 2), (2, 0)]:
self.disconnect_nodes(i, j)

# Create a new block in node0, coinbase going to wallet0
self.nodes[0].createwallet(wallet_name="w0", load_on_startup=True)
wallet0 = self.nodes[0].get_wallet_rpc("w0")
self.generatetoaddress(self.nodes[0], 1, wallet0.getnewaddress(), sync_fun=self.no_op)
node0_coinbase_tx_hash = wallet0.getblock(wallet0.getbestblockhash(), verbose=1)['tx'][0]

# Mine 100 blocks on top to mature the coinbase and create a descendant
self.generate(self.nodes[0], 101, sync_fun=self.no_op)
# Make descendant, send-to-self
descendant_tx_id = wallet0.sendtoaddress(wallet0.getnewaddress(), 1)

# Verify balance
wallet0.syncwithvalidationinterfacequeue()
assert(wallet0.getbalances()['mine']['trusted'] > 0)

# Now create a fork in node1. This will be used to replace node0's chain later.
self.nodes[1].createwallet(wallet_name="w1", load_on_startup=True)
wallet1 = self.nodes[1].get_wallet_rpc("w1")
self.generatetoaddress(self.nodes[1], 1, wallet1.getnewaddress(), sync_fun=self.no_op)
wallet1.syncwithvalidationinterfacequeue()

# Verify both nodes are on a different chain
block0_best_hash, block1_best_hash = wallet0.getbestblockhash(), wallet1.getbestblockhash()
assert(block0_best_hash != block1_best_hash)

# Now both nodes and replace node0 chain entirely for the node1 chain
self.stop_nodes()
for path in ["chainstate", "blocks"]:
shutil.rmtree(self.nodes[0].chain_path / path)
shutil.copytree(self.nodes[1].chain_path / path, self.nodes[0].chain_path / path)

# Start node0 and verify that now it has node1 chain and no info about its previous best block
self.start_node(0)
wallet0 = self.nodes[0].get_wallet_rpc("w0")
assert_equal(wallet0.getbestblockhash(), block1_best_hash)
assert_raises_rpc_error(-5, "Block not found", wallet0.getblock, block0_best_hash)

# Verify the coinbase tx was marked as abandoned and balance correctly computed
assert_equal(wallet0.gettransaction(node0_coinbase_tx_hash)['details'][0]['abandoned'], True)
assert(wallet0.getbalances()['mine']['trusted'] == 0)
# Verify the coinbase descendant was also marked as abandoned
assert_equal(wallet0.gettransaction(descendant_tx_id)['details'][0]['abandoned'], True)


def run_test(self):
# Send a tx from which to conflict outputs later
txid_conflict_from = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
Expand Down Expand Up @@ -100,5 +161,9 @@ def run_test(self):
assert_equal(conflicted_after_reorg["confirmations"], 1)
assert conflicting["blockhash"] != conflicted_after_reorg["blockhash"]

# Verify we mark coinbase txs, and their descendants, as abandoned during startup
self.test_coinbase_automatic_abandon_during_startup()


if __name__ == '__main__':
ReorgsRestoreTest(__file__).main()

0 comments on commit c38266f

Please sign in to comment.