/ test / functional / mining_mainnet.py
mining_mainnet.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) 2025 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  )
 34  
 35  import json
 36  import os
 37  
 38  # See data/README.md
 39  COINBASE_SCRIPT_PUBKEY="76a914eadbac7f36c37e39361168b7aaee3cb24a25312d88ac"
 40  
 41  class MiningMainnetTest(BitcoinTestFramework):
 42  
 43      def set_test_params(self):
 44          self.num_nodes = 1
 45          self.setup_clean_chain = True
 46          self.chain = "" # main
 47  
 48      def add_options(self, parser):
 49          parser.add_argument(
 50              '--datafile',
 51              default='data/mainnet_alt.json',
 52              help='Block data file (default: %(default)s)',
 53          )
 54  
 55      def mine(self, height, prev_hash, blocks, node, fees=0):
 56          self.log.debug(f"height={height}")
 57          block = CBlock()
 58          block.nVersion = 0x20000000
 59          block.hashPrevBlock = int(prev_hash, 16)
 60          block.nTime = blocks['timestamps'][height - 1]
 61          block.nBits = DIFF_1_N_BITS
 62          block.nNonce = blocks['nonces'][height - 1]
 63          block.vtx = [create_coinbase(height=height, script_pubkey=bytes.fromhex(COINBASE_SCRIPT_PUBKEY), retarget_period=2016)]
 64          block.hashMerkleRoot = block.calc_merkle_root()
 65          block.rehash()
 66          block_hex = block.serialize(with_witness=False).hex()
 67          self.log.debug(block_hex)
 68          assert_equal(node.submitblock(block_hex), None)
 69          prev_hash = node.getbestblockhash()
 70          assert_equal(prev_hash, block.hash)
 71          return prev_hash
 72  
 73  
 74      def run_test(self):
 75          node = self.nodes[0]
 76          # Clear disk space warning
 77          node.stderr.seek(0)
 78          node.stderr.truncate()
 79          self.log.info("Load alternative mainnet blocks")
 80          path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.options.datafile)
 81          prev_hash = node.getbestblockhash()
 82          with open(path, encoding='utf-8') as f:
 83              blocks = json.load(f)
 84              n_blocks = len(blocks['timestamps'])
 85              assert_equal(n_blocks, 2015)
 86              for i in range(2015):
 87                  prev_hash = self.mine(i + 1, prev_hash, blocks, node)
 88  
 89          assert_equal(node.getblockcount(), 2015)
 90  
 91          self.log.info("Check difficulty adjustment with getmininginfo")
 92          mining_info = node.getmininginfo()
 93          assert_equal(mining_info['difficulty'], 1)
 94          assert_equal(mining_info['bits'], nbits_str(DIFF_1_N_BITS))
 95          assert_equal(mining_info['target'], target_str(DIFF_1_TARGET))
 96  
 97          assert_equal(mining_info['next']['height'], 2016)
 98          assert_equal(mining_info['next']['difficulty'], 4)
 99          assert_equal(mining_info['next']['bits'], nbits_str(DIFF_4_N_BITS))
100          assert_equal(mining_info['next']['target'], target_str(DIFF_4_TARGET))
101  
102  if __name__ == '__main__':
103      MiningMainnetTest(__file__).main()