mining_basic.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2014-2022 The Limenka 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 RPCs
   6  
   7  - getmininginfo
   8  - getblocktemplate proposal mode
   9  - submitblock"""
  10  
  11  import copy
  12  from decimal import Decimal
  13  
  14  from test_framework.blocktools import (
  15      create_coinbase,
  16      get_witness_script,
  17      NORMAL_GBT_REQUEST_PARAMS,
  18      TIME_GENESIS_BLOCK,
  19      REGTEST_N_BITS,
  20      REGTEST_TARGET,
  21      nbits_str,
  22      target_str,
  23  )
  24  from test_framework.messages import (
  25      BLOCK_HEADER_SIZE,
  26      CBlock,
  27      CBlockHeader,
  28      COIN,
  29      DEFAULT_BLOCK_RESERVED_WEIGHT,
  30      MAX_BLOCK_WEIGHT,
  31      MINIMUM_BLOCK_RESERVED_WEIGHT,
  32      ser_uint256,
  33      WITNESS_SCALE_FACTOR
  34  )
  35  from test_framework.p2p import P2PDataStore
  36  from test_framework.test_framework import LimenkaTestFramework
  37  from test_framework.util import (
  38      assert_equal,
  39      assert_greater_than,
  40      assert_greater_than_or_equal,
  41      assert_raises_rpc_error,
  42      get_fee,
  43  )
  44  from test_framework.wallet import MiniWallet, MiniWalletMode
  45  
  46  
  47  DIFFICULTY_ADJUSTMENT_INTERVAL = 144
  48  MAX_FUTURE_BLOCK_TIME = 2 * 3600
  49  MAX_TIMEWARP = 600
  50  ASSUMED_BLOCK_OVERHEAD_SIZE = 1000
  51  ASSUMED_BLOCK_OVERHEAD_WEIGHT = ASSUMED_BLOCK_OVERHEAD_SIZE * WITNESS_SCALE_FACTOR
  52  VERSIONBITS_TOP_BITS = 0x20000000
  53  VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
  54  DEFAULT_BLOCK_MIN_TX_FEE = 1 # default `-blockmintxfee` setting [sat/kvB]
  55  MAX_SIGOP_COST = 80000
  56  
  57  
  58  def assert_template(node, block, expect, rehash=True):
  59      if rehash:
  60          block.hashMerkleRoot = block.calc_merkle_root()
  61      rsp = node.getblocktemplate(template_request={
  62          'data': block.serialize().hex(),
  63          'mode': 'proposal',
  64          'rules': ['segwit'],
  65      })
  66      assert_equal(rsp, expect)
  67  
  68  
  69  class MiningTest(LimenkaTestFramework):
  70      def set_test_params(self):
  71          self.num_nodes = 3
  72          self.extra_args = [
  73              [],
  74              [],
  75              ["-fastprune", "-prune=1"]
  76          ]
  77          self.setup_clean_chain = True
  78          self.supports_cli = False
  79  
  80      def mine_chain(self):
  81          self.log.info('Create some old blocks')
  82          for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
  83              self.nodes[0].setmocktime(t)
  84              self.generate(self.wallet, 1, sync_fun=self.no_op)
  85          mining_info = self.nodes[0].getmininginfo()
  86          assert_equal(mining_info['blocks'], 200)
  87          assert_equal(mining_info['currentblocktx'], 0)
  88          assert_equal(mining_info['currentblockweight'], DEFAULT_BLOCK_RESERVED_WEIGHT)
  89          assert 'currentblocksize' not in mining_info
  90  
  91          self.log.info('test blockversion')
  92          self.restart_node(0, extra_args=[f'-mocktime={t}', '-blockversion=1337'])
  93          self.connect_nodes(0, 1)
  94          assert_equal(1337, self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)['version'])
  95          self.restart_node(0, extra_args=[f'-mocktime={t}'])
  96          self.connect_nodes(0, 1)
  97          assert_equal(VERSIONBITS_TOP_BITS + (1 << VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT), self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)['version'])
  98          self.restart_node(0)
  99          self.connect_nodes(0, 1)
 100  
 101      def test_blockmintxfee_parameter(self, *, use_rpc=False):
 102          if not use_rpc:
 103              self.log.info("Test -blockmintxfee setting")
 104          self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
 105          node = self.nodes[0]
 106  
 107          # test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB]
 108          for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 5, 10, 50, 100, 500, 1000, 2500, 5000, 21000, 333333, 2500000):
 109              blockmintxfee_btc_kvb = blockmintxfee_sat_kvb / Decimal(COIN)
 110              if use_rpc:
 111                  blockmintxfee_sat_vb = blockmintxfee_sat_kvb / 1000
 112                  self.log.info(f"-> Test RPC param minfeerate={blockmintxfee_sat_vb} ({blockmintxfee_sat_kvb} sat/kvB)...")
 113                  self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
 114                  self.wallet.rescan_utxos()  # to avoid spending outputs of txs that are not in mempool anymore after restart
 115              elif blockmintxfee_sat_kvb == DEFAULT_BLOCK_MIN_TX_FEE:
 116                  self.log.info(f"-> Default -blockmintxfee setting ({blockmintxfee_sat_kvb} sat/kvB)...")
 117              else:
 118                  blockmintxfee_parameter = f"-blockmintxfee={blockmintxfee_btc_kvb:.8f}"
 119                  self.log.info(f"-> Test {blockmintxfee_parameter} ({blockmintxfee_sat_kvb} sat/kvB)...")
 120                  self.restart_node(0, extra_args=[blockmintxfee_parameter, '-minrelaytxfee=0', '-persistmempool=0'])
 121                  self.wallet.rescan_utxos()  # to avoid spending outputs of txs that are not in mempool anymore after restart
 122  
 123              # submit one tx with exactly the blockmintxfee rate, and one slightly below
 124              tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
 125              assert_equal(tx_with_min_feerate["fee"], get_fee(tx_with_min_feerate["tx"].get_vsize(), blockmintxfee_btc_kvb))
 126              if blockmintxfee_sat_kvb > 5:
 127                  lowerfee_btc_kvb = blockmintxfee_btc_kvb - Decimal(10)/COIN  # 0.01 sat/vbyte lower
 128                  tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb, confirmed_only=True)
 129                  assert_equal(tx_below_min_feerate["fee"], get_fee(tx_below_min_feerate["tx"].get_vsize(), lowerfee_btc_kvb))
 130              else:  # go below zero fee by using modified fees
 131                  tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
 132                  node.prioritisetransaction(tx_below_min_feerate["txid"], 0, -1)
 133  
 134              # check that tx below specified fee-rate is neither in template nor in the actual block
 135              req = NORMAL_GBT_REQUEST_PARAMS
 136              if use_rpc:
 137                  req = copy.deepcopy(req)
 138                  req['minfeerate'] = blockmintxfee_sat_vb
 139              block_template = node.getblocktemplate(req)
 140              block_template_txids = [tx['txid'] for tx in block_template['transactions']]
 141  
 142              # Unless blockmintxfee is 0, the template shouldn't contain free transactions.
 143              # Note that the real block assembler uses package feerates, but we didn't create dependent transactions so it's ok to use base feerate.
 144              if blockmintxfee_btc_kvb > 0:
 145                  for txid in block_template_txids:
 146                      tx = node.getmempoolentry(txid)
 147                      assert_greater_than(tx['fees']['base'], 0)
 148  
 149              self.generate(self.wallet, 1, sync_fun=self.no_op)
 150              block = node.getblock(node.getbestblockhash(), verbosity=2)
 151              block_txids = [tx['txid'] for tx in block['tx']]
 152  
 153              assert tx_with_min_feerate['txid'] in block_template_txids
 154              assert tx_below_min_feerate['txid'] not in block_template_txids
 155  
 156              if not use_rpc:
 157                  assert tx_with_min_feerate['txid'] in block_txids
 158                  assert tx_below_min_feerate['txid'] not in block_txids
 159  
 160      def test_rpc_params(self):
 161          self.log.info("Test minfeerate RPC param")
 162          self.test_blockmintxfee_parameter(use_rpc=True)
 163  
 164          node = self.nodes[0]
 165          wallet = MiniWallet(node, mode=MiniWalletMode.RAW_P2PK)
 166          self.wallet.send_to(from_node=node, scriptPubKey=wallet.get_output_script(), amount=40 * COIN)
 167          self.wallet.send_to(from_node=node, scriptPubKey=wallet.get_output_script(), amount=40 * COIN)
 168          self.generate(wallet, 1, sync_fun=self.no_op)
 169  
 170          self.log.info("Preparing mempool")
 171          self.restart_node(0, extra_args=['-limitancestorcount=1000', '-limitancestorsize=7000', '-limitdescendantcount=1000', '-limitdescendantsize=7000'])
 172  
 173          # Fill the mempool
 174          target_mempool_size = 200000
 175          last_tx_size = 0
 176          utxo = wallet.get_utxo()  # save for small coins
 177          while node.getmempoolinfo()['bytes'] < target_mempool_size - last_tx_size:
 178              tx = wallet.send_self_transfer_multi(
 179                  from_node=self.nodes[0],
 180                  num_outputs=1000,
 181              )
 182              last_tx_size = len(tx['hex']) / 2
 183          while node.getmempoolinfo()['bytes'] < 200000:
 184              tx = wallet.send_self_transfer_multi(
 185                  utxos_to_spend=[utxo],
 186                  from_node=node,
 187                  num_outputs=1,
 188              )
 189              utxo = tx['new_utxos'][0]
 190  
 191          self.log.info("Test blockmaxsize RPC param")
 192          req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
 193          normal_size = ASSUMED_BLOCK_OVERHEAD_SIZE + (sum(len(tx['data']) for tx in self.nodes[0].getblocktemplate(req)['transactions']) // 2)
 194          last_size = ASSUMED_BLOCK_OVERHEAD_SIZE
 195          for target_size in (50000, 100000, 150000):
 196              self.log.info(f"-> Test RPC param blockmaxsize={target_size}...")
 197              req['blockmaxsize'] = target_size
 198              tmpl = self.nodes[0].getblocktemplate(req)
 199              blk_size = ASSUMED_BLOCK_OVERHEAD_SIZE + (sum(len(tx['data']) for tx in tmpl['transactions']) // 2)
 200              assert blk_size < normal_size
 201              assert blk_size < target_size
 202              assert blk_size > last_size
 203              last_size = blk_size
 204  
 205          self.log.info("Test blockreservedsize RPC param")
 206          req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
 207          req['blockmaxsize'] = 150000
 208          normal_size = (sum(len(tx['data']) for tx in self.nodes[0].getblocktemplate(req)['transactions']) // 2)
 209          last_size = 0
 210          for reserved_size in (100000, 10000, 100):
 211              self.log.info(f"-> Test RPC param blockreservedsize={reserved_size}...")
 212              req['blockreservedsize'] = reserved_size
 213              tmpl = self.nodes[0].getblocktemplate(req)
 214              blk_size = (sum(len(tx['data']) for tx in tmpl['transactions']) // 2)
 215              assert blk_size < normal_size if reserved_size > 1000 else blk_size > normal_size
 216              assert blk_size + reserved_size <= req['blockmaxsize']
 217              assert blk_size > last_size
 218              last_size = blk_size
 219  
 220          self.log.info("Test blockmaxweight RPC param")
 221          req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
 222          normal_weight = ASSUMED_BLOCK_OVERHEAD_WEIGHT + sum(tx['weight'] for tx in self.nodes[0].getblocktemplate(req)['transactions'])
 223          last_weight = ASSUMED_BLOCK_OVERHEAD_WEIGHT
 224          for target_weight in (200000, 400000, 600000):
 225              self.log.info(f"-> Test RPC param blockmaxweight={target_weight}...")
 226              req['blockmaxweight'] = target_weight
 227              tmpl = self.nodes[0].getblocktemplate(req)
 228              blk_weight = ASSUMED_BLOCK_OVERHEAD_WEIGHT + sum(tx['weight'] for tx in tmpl['transactions'])
 229              assert blk_weight < normal_weight
 230              assert blk_weight < target_weight
 231              assert blk_weight > last_weight
 232              last_weight = blk_weight
 233  
 234          self.log.info("Test blockreservedweight RPC param")
 235          req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
 236          req['blockmaxweight'] = 600000
 237          normal_weight = sum(tx['weight'] for tx in self.nodes[0].getblocktemplate(req)['transactions'])
 238          last_weight = 0
 239          for reserved_weight in (400000, 40000, MINIMUM_BLOCK_RESERVED_WEIGHT):
 240              self.log.info(f"-> Test RPC param blockreservedweight={reserved_weight}...")
 241              req['blockreservedweight'] = reserved_weight
 242              tmpl = self.nodes[0].getblocktemplate(req)
 243              blk_weight = sum(tx['weight'] for tx in tmpl['transactions'])
 244              assert blk_weight < normal_weight if reserved_weight > 4000 else blk_weight > normal_weight
 245              assert blk_weight + reserved_weight <= req['blockmaxweight']
 246              assert blk_weight > last_weight
 247              last_weight = blk_weight
 248  
 249          self.log.info("Test blockreservedsigops RPC param")
 250          req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
 251          normal_sigops = sum(tx['sigops'] for tx in self.nodes[0].getblocktemplate(req)['transactions'])
 252          assert normal_sigops
 253          last_sigops = 0
 254          baseline_sigops = MAX_SIGOP_COST - normal_sigops
 255          for reserved_sigops in (800, 400, 100):
 256              reserved_sigops += baseline_sigops
 257              self.log.info(f"-> Test RPC param blockreservedsigops={reserved_sigops}...")
 258              req['blockreservedsigops'] = reserved_sigops
 259              tmpl = self.nodes[0].getblocktemplate(req)
 260              blk_sigops = sum(tx['sigops'] for tx in tmpl['transactions'])
 261              assert blk_sigops < normal_sigops if reserved_sigops > 400 else blk_sigops > normal_sigops
 262              assert blk_sigops + reserved_sigops <= MAX_SIGOP_COST
 263              assert blk_sigops > last_sigops
 264              last_sigops = blk_sigops
 265  
 266      def test_timewarp(self):
 267          self.log.info("Test timewarp attack mitigation (BIP94)")
 268          node = self.nodes[0]
 269          self.restart_node(0, extra_args=['-test=bip94'])
 270  
 271          self.log.info("Mine until the last block of the retarget period")
 272          blockchain_info = self.nodes[0].getblockchaininfo()
 273          n = DIFFICULTY_ADJUSTMENT_INTERVAL - blockchain_info['blocks'] % DIFFICULTY_ADJUSTMENT_INTERVAL - 2
 274          t = blockchain_info['time']
 275  
 276          for _ in range(n):
 277              t += 600
 278              self.nodes[0].setmocktime(t)
 279              self.generate(self.wallet, 1, sync_fun=self.no_op)
 280  
 281          self.log.info("Create block two hours in the future")
 282          self.nodes[0].setmocktime(t + MAX_FUTURE_BLOCK_TIME)
 283          self.generate(self.wallet, 1, sync_fun=self.no_op)
 284          assert_equal(node.getblock(node.getbestblockhash())['time'], t + MAX_FUTURE_BLOCK_TIME)
 285  
 286          self.log.info("First block template of retarget period can't use wall clock time")
 287          self.nodes[0].setmocktime(t)
 288          # The template will have an adjusted timestamp, which we then modify
 289          tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
 290          assert_greater_than_or_equal(tmpl['curtime'], t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP)
 291          # mintime and curtime should match
 292          assert_equal(tmpl['mintime'], tmpl['curtime'])
 293  
 294          block = CBlock()
 295          block.nVersion = tmpl["version"]
 296          block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
 297          block.nTime = tmpl["curtime"]
 298          block.nBits = int(tmpl["bits"], 16)
 299          block.nNonce = 0
 300          block.vtx = [create_coinbase(height=int(tmpl["height"]))]
 301          block.solve()
 302          assert_template(node, block, None)
 303  
 304          bad_block = copy.deepcopy(block)
 305          bad_block.nTime = t
 306          bad_block.solve()
 307          assert_raises_rpc_error(-25, 'time-timewarp-attack', lambda: node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex()))
 308  
 309          self.log.info("Test timewarp protection boundary")
 310          bad_block.nTime = t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP - 1
 311          bad_block.solve()
 312          assert_raises_rpc_error(-25, 'time-timewarp-attack', lambda: node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex()))
 313  
 314          bad_block.nTime = t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP
 315          bad_block.solve()
 316          node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex())
 317  
 318      def test_pruning(self):
 319          self.log.info("Test that submitblock stores previously pruned block")
 320          prune_node = self.nodes[2]
 321          self.generate(prune_node, 400, sync_fun=self.no_op)
 322          pruned_block = prune_node.getblock(prune_node.getblockhash(2), verbosity=0)
 323          pruned_height = prune_node.pruneblockchain(400)
 324          assert_greater_than_or_equal(pruned_height, 2)
 325          pruned_blockhash = prune_node.getblockhash(2)
 326  
 327          assert_raises_rpc_error(-1, 'Block not available (pruned data)', prune_node.getblock, pruned_blockhash)
 328  
 329          result = prune_node.submitblock(pruned_block)
 330          assert_equal(result, "inconclusive")
 331          assert_equal(prune_node.getblock(pruned_blockhash, verbosity=0), pruned_block)
 332  
 333  
 334      def send_transactions(self, utxos, fee_rate, target_vsize):
 335          """
 336          Helper to create and send transactions with the specified target virtual size and fee rate.
 337          """
 338          for utxo in utxos:
 339              self.wallet.send_self_transfer(
 340                  from_node=self.nodes[0],
 341                  utxo_to_spend=utxo,
 342                  target_vsize=target_vsize,
 343                  fee_rate=fee_rate,
 344              )
 345  
 346      def verify_block_template(self, expected_tx_count, expected_weight):
 347          """
 348          Create a block template and check that it satisfies the expected transaction count and total weight.
 349          """
 350          response = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
 351          self.log.info(f"Testing block template: contains {expected_tx_count} transactions, and total weight <= {expected_weight}")
 352          assert_equal(len(response["transactions"]), expected_tx_count)
 353          total_weight = sum(transaction["weight"] for transaction in response["transactions"])
 354          assert_greater_than_or_equal(expected_weight, total_weight)
 355  
 356      def test_block_max_weight(self):
 357          self.log.info("Testing default and custom -blockmaxweight startup options.")
 358  
 359          # Restart the node to allow large transactions
 360          LARGE_TXS_COUNT = 10
 361          LARGE_VSIZE = int(((MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT) / WITNESS_SCALE_FACTOR) / LARGE_TXS_COUNT)
 362          HIGH_FEERATE = Decimal("0.0003")
 363          self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}"])
 364  
 365          # Ensure the mempool is empty
 366          assert_equal(len(self.nodes[0].getrawmempool()), 0)
 367  
 368          # Generate UTXOs and send 10 large transactions with a high fee rate
 369          utxos = [self.wallet.get_utxo(confirmed_only=True) for _ in range(LARGE_TXS_COUNT + 4)] # Add 4 more utxos that will be used in the test later
 370          self.send_transactions(utxos[:LARGE_TXS_COUNT], HIGH_FEERATE, LARGE_VSIZE)
 371  
 372          # Send 2 normal transactions with a lower fee rate
 373          NORMAL_VSIZE = int(2000 / WITNESS_SCALE_FACTOR)
 374          NORMAL_FEERATE = Decimal("0.0001")
 375          self.send_transactions(utxos[LARGE_TXS_COUNT:LARGE_TXS_COUNT + 2], NORMAL_FEERATE, NORMAL_VSIZE)
 376  
 377          # Check that the mempool contains all transactions
 378          self.log.info(f"Testing that the mempool contains {LARGE_TXS_COUNT + 2} transactions.")
 379          assert_equal(len(self.nodes[0].getrawmempool()), LARGE_TXS_COUNT + 2)
 380  
 381          # Verify the block template includes only the 10 high-fee transactions
 382          self.log.info("Testing that the block template includes only the 10 large transactions.")
 383          self.verify_block_template(
 384              expected_tx_count=LARGE_TXS_COUNT,
 385              expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT,
 386          )
 387  
 388          # Test block template creation with custom -blockmaxweight
 389          custom_block_weight = MAX_BLOCK_WEIGHT - 2000
 390          # Reducing the weight by 2000 units will prevent 1 large transaction from fitting into the block.
 391          self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", f"-blockmaxweight={custom_block_weight}"])
 392  
 393          self.log.info("Testing the block template with custom -blockmaxweight to include 9 large and 2 normal transactions.")
 394          self.verify_block_template(
 395              expected_tx_count=11,
 396              expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT - 2000,
 397          )
 398  
 399          # Ensure the block weight does not exceed the maximum
 400          self.log.info(f"Testing that the block weight will never exceed {MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT}.")
 401          self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", f"-blockmaxweight={MAX_BLOCK_WEIGHT}"])
 402          self.log.info("Sending 2 additional normal transactions to fill the mempool to the maximum block weight.")
 403          self.send_transactions(utxos[LARGE_TXS_COUNT + 2:], NORMAL_FEERATE, NORMAL_VSIZE)
 404          self.log.info(f"Testing that the mempool's weight matches the maximum block weight: {MAX_BLOCK_WEIGHT}.")
 405          assert_equal(self.nodes[0].getmempoolinfo()['bytes'] * WITNESS_SCALE_FACTOR, MAX_BLOCK_WEIGHT)
 406  
 407          self.log.info("Testing that the block template includes only 10 transactions and cannot reach full block weight.")
 408          self.verify_block_template(
 409              expected_tx_count=LARGE_TXS_COUNT,
 410              expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT,
 411          )
 412  
 413          self.log.info("Test -blockreservedweight startup option.")
 414          # Lowering the -blockreservedweight by 4000 will allow for two more transactions.
 415          self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", "-blockreservedweight=4000"])
 416          self.verify_block_template(
 417              expected_tx_count=12,
 418              expected_weight=MAX_BLOCK_WEIGHT - 4000,
 419          )
 420  
 421          self.log.info("Test that node will fail to start when user provide invalid -blockreservedweight")
 422          self.stop_node(0)
 423          self.nodes[0].assert_start_raises_init_error(
 424              extra_args=[f"-blockreservedweight={MAX_BLOCK_WEIGHT + 1}"],
 425              expected_msg=f"Error: Specified -blockreservedweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
 426          )
 427  
 428          self.log.info(f"Test that node will fail to start when user provide -blockreservedweight below {MINIMUM_BLOCK_RESERVED_WEIGHT}")
 429          self.stop_node(0)
 430          self.nodes[0].assert_start_raises_init_error(
 431              extra_args=[f"-blockreservedweight={MINIMUM_BLOCK_RESERVED_WEIGHT - 1}"],
 432              expected_msg=f"Error: Specified -blockreservedweight ({MINIMUM_BLOCK_RESERVED_WEIGHT - 1}) is lower than minimum safety value of ({MINIMUM_BLOCK_RESERVED_WEIGHT})",
 433          )
 434  
 435          self.log.info("Test that node will fail to start when user provide invalid -blockmaxweight")
 436          self.stop_node(0)
 437          self.nodes[0].assert_start_raises_init_error(
 438              extra_args=[f"-blockmaxweight={MAX_BLOCK_WEIGHT + 1}"],
 439              expected_msg=f"Error: Specified -blockmaxweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
 440          )
 441  
 442  
 443      def run_test(self):
 444          node = self.nodes[0]
 445          self.wallet = MiniWallet(node)
 446          self.mine_chain()
 447  
 448          def assert_submitblock(block, result_str_1, result_str_2=None):
 449              block.solve()
 450              result_str_2 = result_str_2 or 'duplicate-invalid'
 451              assert_equal(result_str_1, node.submitblock(hexdata=block.serialize().hex()))
 452              assert_equal(result_str_2, node.submitblock(hexdata=block.serialize().hex()))
 453  
 454          self.log.info('getmininginfo')
 455          mining_info = node.getmininginfo()
 456          assert_equal(mining_info['blocks'], 200)
 457          assert_equal(mining_info['chain'], self.chain)
 458          assert 'currentblocktx' not in mining_info
 459          assert 'currentblockweight' not in mining_info
 460          assert 'currentblocksize' not in mining_info
 461          assert_equal(mining_info['bits'], nbits_str(REGTEST_N_BITS))
 462          assert_equal(mining_info['target'], target_str(REGTEST_TARGET))
 463          assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10'))
 464          assert_equal(mining_info['next'], {
 465              'height': 201,
 466              'target': target_str(REGTEST_TARGET),
 467              'bits': nbits_str(REGTEST_N_BITS),
 468              'difficulty': Decimal('4.656542373906925E-10')
 469          })
 470          assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334'))
 471          assert_equal(mining_info['pooledtx'], 0)
 472  
 473          self.log.info("getblocktemplate: Test default witness commitment")
 474          txid = int(self.wallet.send_self_transfer(from_node=node)['wtxid'], 16)
 475          tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
 476  
 477          # Check that default_witness_commitment is present.
 478          assert 'default_witness_commitment' in tmpl
 479          witness_commitment = tmpl['default_witness_commitment']
 480  
 481          # Check that default_witness_commitment is correct.
 482          witness_root = CBlock.get_merkle_root([ser_uint256(0),
 483                                                 ser_uint256(txid)])
 484          script = get_witness_script(witness_root, 0)
 485          assert_equal(witness_commitment, script.hex())
 486  
 487          # Mine a block to leave initial block download and clear the mempool
 488          self.generatetoaddress(node, 1, node.get_deterministic_priv_key().address)
 489          tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
 490          self.log.info("getblocktemplate: Test capability advertised")
 491          assert 'proposal' in tmpl['capabilities']
 492          assert 'coinbasetxn' not in tmpl
 493  
 494          next_height = int(tmpl["height"])
 495          coinbase_tx = create_coinbase(height=next_height)
 496          # sequence numbers must not be max for nLockTime to have effect
 497          coinbase_tx.vin[0].nSequence = 2**32 - 2
 498          coinbase_tx.rehash()
 499  
 500          block = CBlock()
 501          block.nVersion = tmpl["version"]
 502          block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
 503          block.nTime = tmpl["curtime"]
 504          block.nBits = int(tmpl["bits"], 16)
 505          block.nNonce = 0
 506          block.vtx = [coinbase_tx]
 507  
 508          self.log.info("getblocktemplate: segwit rule must be set")
 509          assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate, {})
 510  
 511          self.log.info("getblocktemplate: Test valid block")
 512          assert_template(node, block, None)
 513  
 514          self.log.info("submitblock: Test block decode failure")
 515          assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex())
 516  
 517          self.log.info("getblocktemplate: Test bad input hash for coinbase transaction")
 518          bad_block = copy.deepcopy(block)
 519          bad_block.vtx[0].vin[0].prevout.hash += 1
 520          bad_block.vtx[0].rehash()
 521          assert_template(node, bad_block, 'bad-cb-missing')
 522  
 523          self.log.info("submitblock: Test bad input hash for coinbase transaction")
 524          bad_block.solve()
 525          assert_equal("bad-cb-missing", node.submitblock(hexdata=bad_block.serialize().hex()))
 526  
 527          self.log.info("submitblock: Test block with no transactions")
 528          no_tx_block = copy.deepcopy(block)
 529          no_tx_block.vtx.clear()
 530          no_tx_block.hashMerkleRoot = 0
 531          no_tx_block.solve()
 532          assert_equal("bad-blk-length", node.submitblock(hexdata=no_tx_block.serialize().hex()))
 533  
 534          self.log.info("submitblock: Test empty block")
 535          assert_equal('high-hash', node.submitblock(hexdata=CBlock().serialize().hex()))
 536  
 537          self.log.info("getblocktemplate: Test truncated final transaction")
 538          assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
 539              'data': block.serialize()[:-1].hex(),
 540              'mode': 'proposal',
 541              'rules': ['segwit'],
 542          })
 543  
 544          self.log.info("getblocktemplate: Test duplicate transaction")
 545          bad_block = copy.deepcopy(block)
 546          bad_block.vtx.append(bad_block.vtx[0])
 547          assert_template(node, bad_block, 'bad-txns-duplicate')
 548          assert_submitblock(bad_block, 'bad-txns-duplicate', 'bad-txns-duplicate')
 549  
 550          self.log.info("getblocktemplate: Test invalid transaction")
 551          bad_block = copy.deepcopy(block)
 552          bad_tx = copy.deepcopy(bad_block.vtx[0])
 553          bad_tx.vin[0].prevout.hash = 255
 554          bad_tx.rehash()
 555          bad_block.vtx.append(bad_tx)
 556          assert_template(node, bad_block, 'bad-txns-inputs-missingorspent')
 557          assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent')
 558  
 559          self.log.info("getblocktemplate: Test nonfinal transaction")
 560          bad_block = copy.deepcopy(block)
 561          bad_block.vtx[0].nLockTime = 2**32 - 1
 562          bad_block.vtx[0].rehash()
 563          assert_template(node, bad_block, 'bad-txns-nonfinal')
 564          assert_submitblock(bad_block, 'bad-txns-nonfinal')
 565  
 566          self.log.info("getblocktemplate: Test bad tx count")
 567          # The tx count is immediately after the block header
 568          bad_block_sn = bytearray(block.serialize())
 569          assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
 570          bad_block_sn[BLOCK_HEADER_SIZE] += 1
 571          assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
 572              'data': bad_block_sn.hex(),
 573              'mode': 'proposal',
 574              'rules': ['segwit'],
 575          })
 576  
 577          self.log.info("getblocktemplate: Test bad bits")
 578          bad_block = copy.deepcopy(block)
 579          bad_block.nBits = 469762303  # impossible in the real world
 580          assert_template(node, bad_block, 'bad-diffbits')
 581  
 582          self.log.info("getblocktemplate: Test bad merkle root")
 583          bad_block = copy.deepcopy(block)
 584          bad_block.hashMerkleRoot += 1
 585          assert_template(node, bad_block, 'bad-txnmrklroot', False)
 586          assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot')
 587  
 588          self.log.info("getblocktemplate: Test bad timestamps")
 589          bad_block = copy.deepcopy(block)
 590          bad_block.nTime = 2**32 - 1
 591          assert_template(node, bad_block, 'time-too-new')
 592          assert_submitblock(bad_block, 'time-too-new', 'time-too-new')
 593          bad_block.nTime = 0
 594          assert_template(node, bad_block, 'time-too-old')
 595          assert_submitblock(bad_block, 'time-too-old', 'time-too-old')
 596  
 597          self.log.info("getblocktemplate: Test not best block")
 598          bad_block = copy.deepcopy(block)
 599          bad_block.hashPrevBlock = 123
 600          assert_template(node, bad_block, 'inconclusive-not-best-prevblk')
 601          assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found')
 602  
 603          self.log.info('submitheader tests')
 604          assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE))
 605          assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE-2)))
 606          assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata=super(CBlock, bad_block).serialize().hex()))
 607  
 608          block.nTime += 1
 609          block.solve()
 610  
 611          def chain_tip(b_hash, *, status='headers-only', branchlen=1):
 612              return {'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status}
 613  
 614          assert chain_tip(block.hash) not in node.getchaintips()
 615          node.submitheader(hexdata=block.serialize().hex())
 616          assert chain_tip(block.hash) in node.getchaintips()
 617          node.submitheader(hexdata=CBlockHeader(block).serialize().hex())  # Noop
 618          assert chain_tip(block.hash) in node.getchaintips()
 619  
 620          bad_block_root = copy.deepcopy(block)
 621          bad_block_root.hashMerkleRoot += 2
 622          bad_block_root.solve()
 623          assert chain_tip(bad_block_root.hash) not in node.getchaintips()
 624          node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
 625          assert chain_tip(bad_block_root.hash) in node.getchaintips()
 626          # Should still reject invalid blocks, even if we have the header:
 627          assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot')
 628          assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot')
 629          assert chain_tip(bad_block_root.hash) in node.getchaintips()
 630          # We know the header for this invalid block, so should just return early without error:
 631          node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
 632          assert chain_tip(bad_block_root.hash) in node.getchaintips()
 633  
 634          bad_block_lock = copy.deepcopy(block)
 635          bad_block_lock.vtx[0].nLockTime = 2**32 - 1
 636          bad_block_lock.vtx[0].rehash()
 637          bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root()
 638          bad_block_lock.solve()
 639          assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'bad-txns-nonfinal')
 640          assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'duplicate-invalid')
 641          # Build a "good" block on top of the submitted bad block
 642          bad_block2 = copy.deepcopy(block)
 643          bad_block2.hashPrevBlock = bad_block_lock.sha256
 644          bad_block2.solve()
 645          assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex()))
 646  
 647          # Should reject invalid header right away
 648          bad_block_time = copy.deepcopy(block)
 649          bad_block_time.nTime = 1
 650          bad_block_time.solve()
 651          assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
 652  
 653          # Should ask for the block from a p2p node, if they announce the header as well:
 654          peer = node.add_p2p_connection(P2PDataStore())
 655          peer.wait_for_getheaders(timeout=5, block_hash=block.hashPrevBlock)
 656          peer.send_blocks_and_test(blocks=[block], node=node)
 657          # Must be active now:
 658          assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips()
 659  
 660          # Building a few blocks should give the same results
 661          self.generatetoaddress(node, 10, node.get_deterministic_priv_key().address)
 662          assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
 663          assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex()))
 664          node.submitheader(hexdata=CBlockHeader(block).serialize().hex())
 665          node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
 666          assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate')  # valid
 667  
 668          self.test_blockmintxfee_parameter()
 669          self.test_block_max_weight()
 670          self.test_rpc_params()
 671          self.test_timewarp()
 672          self.test_pruning()
 673  
 674  
 675  if __name__ == '__main__':
 676      MiningTest(__file__).main()
 677