mining_mainnet.py
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()