mining_mainnet.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2025-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 mining on an alternate mainnet
   6  
   7  Test mining related RPCs that involve difficulty adjustment, which
   8  regtest doesn't have.
   9  
  10  It uses an alternate mainnet chain. See data/README.md for how it was generated.
  11  
  12  Mine one retarget period worth of blocks with a short interval in
  13  order to maximally raise the difficulty. Verify this using the getmininginfo RPC.
  14  
  15  """
  16  
  17  from test_framework.test_framework import BitcoinTestFramework
  18  from test_framework.util import (
  19      assert_equal,
  20  )
  21  from test_framework.blocktools import (
  22      DIFF_1_N_BITS,
  23      DIFF_1_TARGET,
  24      DIFF_4_N_BITS,
  25      DIFF_4_TARGET,
  26      create_coinbase,
  27      nbits_str,
  28      target_str
  29  )
  30  
  31  from test_framework.messages import (
  32      CBlock,
  33      SEQUENCE_FINAL,
  34  )
  35  
  36  import json
  37  import os
  38  
  39  # See data/README.md
  40  COINBASE_SCRIPT_PUBKEY="76a914eadbac7f36c37e39361168b7aaee3cb24a25312d88ac"
  41  
  42  class MiningMainnetTest(BitcoinTestFramework):
  43  
  44      def set_test_params(self):
  45          self.num_nodes = 1
  46          self.setup_clean_chain = True
  47          self.chain = "" # main
  48  
  49      def add_options(self, parser):
  50          parser.add_argument(
  51              '--datafile',
  52              default='data/mainnet_alt.json',
  53              help='Block data file (default: %(default)s)',
  54          )
  55  
  56      def mine(self, height, prev_hash, blocks, node):
  57          self.log.debug(f"height={height}")
  58          block = CBlock()
  59          block.nVersion = 0x20000000
  60          block.hashPrevBlock = int(prev_hash, 16)
  61          block.nTime = blocks['timestamps'][height - 1]
  62          block.nBits = DIFF_1_N_BITS if height < 2016 else DIFF_4_N_BITS
  63          block.nNonce = blocks['nonces'][height - 1]
  64          block.vtx = [create_coinbase(height=height, script_pubkey=bytes.fromhex(COINBASE_SCRIPT_PUBKEY), halving_period=210000)]
  65          # The alternate mainnet chain was mined with non-timelocked coinbase txs.
  66          block.vtx[0].nLockTime = 0
  67          block.vtx[0].vin[0].nSequence = SEQUENCE_FINAL
  68          block.hashMerkleRoot = block.calc_merkle_root()
  69          block_hex = block.serialize(with_witness=False).hex()
  70          self.log.debug(block_hex)
  71          assert_equal(node.submitblock(block_hex), None)
  72          prev_hash = node.getbestblockhash()
  73          assert_equal(prev_hash, block.hash_hex)
  74          return prev_hash
  75  
  76  
  77      def run_test(self):
  78          node = self.nodes[0]
  79          # Clear disk space warning
  80          node.stderr.seek(0)
  81          node.stderr.truncate()
  82          self.log.info("Load alternative mainnet blocks")
  83          path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.options.datafile)
  84          prev_hash = node.getbestblockhash()
  85          blocks = None
  86          with open(path) as f:
  87              blocks = json.load(f)
  88              n_blocks = len(blocks['timestamps'])
  89              assert_equal(n_blocks, 2016)
  90  
  91          # Mine up to the last block of the first retarget period
  92          for i in range(2015):
  93              prev_hash = self.mine(i + 1, prev_hash, blocks, node)
  94  
  95          assert_equal(node.getblockcount(), 2015)
  96  
  97          self.log.info("Check difficulty adjustment with getmininginfo")
  98          mining_info = node.getmininginfo()
  99          assert_equal(mining_info['difficulty'], 1)
 100          assert_equal(mining_info['bits'], nbits_str(DIFF_1_N_BITS))
 101          assert_equal(mining_info['target'], target_str(DIFF_1_TARGET))
 102  
 103          assert_equal(mining_info['next']['height'], 2016)
 104          assert_equal(mining_info['next']['difficulty'], 4)
 105          assert_equal(mining_info['next']['bits'], nbits_str(DIFF_4_N_BITS))
 106          assert_equal(mining_info['next']['target'], target_str(DIFF_4_TARGET))
 107  
 108          # Mine first block of the second retarget period
 109          height = 2016
 110          prev_hash = self.mine(height, prev_hash, blocks, node)
 111          assert_equal(node.getblockcount(), height)
 112  
 113          mining_info = node.getmininginfo()
 114          assert_equal(mining_info['difficulty'], 4)
 115  
 116          self.log.info("getblock RPC should show historical target")
 117          block_info = node.getblock(node.getblockhash(1))
 118  
 119          assert_equal(block_info['difficulty'], 1)
 120          assert_equal(block_info['bits'], nbits_str(DIFF_1_N_BITS))
 121          assert_equal(block_info['target'], target_str(DIFF_1_TARGET))
 122  
 123  
 124  if __name__ == '__main__':
 125      MiningMainnetTest(__file__).main()
 126