mempool_compatibility.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2017-present 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          new_node_mempool.unlink()
  60          old_node_mempool.rename(new_node_mempool)
  61  
  62          self.log.info("Start new node and verify mempool contains the tx")
  63          self.start_node(1, extra_args=["-persistmempoolv1=1"])
  64          assert old_tx_hash in new_node.getrawmempool()
  65  
  66          self.log.info("Add unbroadcasted tx to mempool on new node and shutdown")
  67          unbroadcasted_tx_hash = new_wallet.send_self_transfer(from_node=new_node)['txid']
  68          assert unbroadcasted_tx_hash in new_node.getrawmempool()
  69          assert new_node.getmempoolentry(unbroadcasted_tx_hash)['unbroadcast']
  70          self.stop_node(1)
  71  
  72          self.log.info("Move mempool.dat from new to old node")
  73          new_node_mempool.rename(old_node_mempool)
  74  
  75          self.log.info("Start old node again and verify mempool contains both txs")
  76          self.start_node(0, ['-nowallet'])
  77          assert old_tx_hash in old_node.getrawmempool()
  78          assert unbroadcasted_tx_hash in old_node.getrawmempool()
  79  
  80  
  81  if __name__ == "__main__":
  82      MempoolCompatibilityTest(__file__).main()
  83