1 #!/usr/bin/env python3
2 # Copyright (c) 2017-2022 The Limenka developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test that mempool.dat is both backward and forward compatible between versions
6 7 NOTE: The test is designed to prevent cases when compatibility is broken accidentally.
8 In case we need to break mempool compatibility we can continue to use the test by just bumping the version number.
9 10 Previous releases are required by this test, see test/README.md.
11 """
12 13 from test_framework.blocktools import COINBASE_MATURITY
14 from test_framework.test_framework import LimenkaTestFramework
15 from test_framework.wallet import (
16 MiniWallet,
17 MiniWalletMode,
18 )
19 20 21 class MempoolCompatibilityTest(LimenkaTestFramework):
22 def set_test_params(self):
23 self.num_nodes = 2
24 self.setup_clean_chain = True
25 26 def skip_test_if_missing_module(self):
27 self.skip_if_no_previous_releases()
28 29 def setup_network(self):
30 self.add_nodes(self.num_nodes, versions=[
31 200100, # Last release without unbroadcast serialization and without XOR
32 None,
33 ])
34 self.start_nodes()
35 36 def run_test(self):
37 self.log.info("Test that mempool.dat is compatible between versions")
38 39 old_node, new_node = self.nodes
40 assert "unbroadcastcount" not in old_node.getmempoolinfo()
41 new_wallet = MiniWallet(new_node, mode=MiniWalletMode.RAW_P2PK)
42 self.generate(new_wallet, 1, sync_fun=self.no_op)
43 self.generate(new_node, COINBASE_MATURITY, sync_fun=self.no_op)
44 # Sync the nodes to ensure old_node has the block that contains the coinbase that new_wallet will spend.
45 # Otherwise, because coinbases are only valid in a block and not as loose txns, if the nodes aren't synced
46 # unbroadcasted_tx won't pass old_node's `MemPoolAccept::PreChecks`.
47 self.connect_nodes(0, 1)
48 self.sync_blocks()
49 50 self.log.info("Add a transaction to mempool on old node and shutdown")
51 old_tx_hash = new_wallet.send_self_transfer(from_node=old_node)["txid"]
52 assert old_tx_hash in old_node.getrawmempool()
53 self.stop_node(0)
54 self.stop_node(1)
55 56 self.log.info("Move mempool.dat from old to new node")
57 old_node_mempool = old_node.chain_path / "mempool.dat"
58 new_node_mempool = new_node.chain_path / "mempool.dat"
59 old_node_mempool.rename(new_node_mempool)
60 61 self.log.info("Start new node and verify mempool contains the tx")
62 self.start_node(1, extra_args=["-persistmempoolv1=1"])
63 assert old_tx_hash in new_node.getrawmempool()
64 65 self.log.info("Add unbroadcasted tx to mempool on new node and shutdown")
66 unbroadcasted_tx_hash = new_wallet.send_self_transfer(from_node=new_node)['txid']
67 assert unbroadcasted_tx_hash in new_node.getrawmempool()
68 assert new_node.getmempoolentry(unbroadcasted_tx_hash)['unbroadcast']
69 self.stop_node(1)
70 71 self.log.info("Move mempool.dat from new to old node")
72 new_node_mempool.rename(old_node_mempool)
73 74 self.log.info("Start old node again and verify mempool contains both txs")
75 self.start_node(0, ['-nowallet'])
76 assert old_tx_hash in old_node.getrawmempool()
77 assert unbroadcasted_tx_hash in old_node.getrawmempool()
78 79 80 if __name__ == "__main__":
81 MempoolCompatibilityTest(__file__).main()
82