/ test / functional / mempool_compatibility.py
mempool_compatibility.py
 1  #!/usr/bin/env python3
 2  # Copyright (c) 2017-2022 The Bitcoin Core 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 BitcoinTestFramework
15  from test_framework.wallet import (
16      MiniWallet,
17      MiniWalletMode,
18  )
19  
20  
21  class MempoolCompatibilityTest(BitcoinTestFramework):
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().main()