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()