rpc_blockchain.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2014-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 RPCs related to blockchainstate.
   6  
   7  Test the following RPCs:
   8      - getblockchaininfo
   9      - getdeploymentinfo
  10      - getchaintxstats
  11      - gettxoutsetinfo
  12      - gettxout
  13      - getblockheader
  14      - getdifficulty
  15      - getnetworkhashps
  16      - waitforblockheight
  17      - getblock
  18      - getblockhash
  19      - getbestblockhash
  20      - verifychain
  21  
  22  Tests correspond to code in rpc/blockchain.cpp.
  23  """
  24  
  25  from decimal import Decimal
  26  import http.client
  27  import os
  28  import subprocess
  29  import textwrap
  30  
  31  from test_framework.blocktools import (
  32      MAX_FUTURE_BLOCK_TIME,
  33      TIME_GENESIS_BLOCK,
  34      REGTEST_N_BITS,
  35      REGTEST_TARGET,
  36      create_block,
  37      create_coinbase,
  38      create_tx_with_script,
  39      nbits_str,
  40      target_str,
  41  )
  42  from test_framework.messages import (
  43      CBlockHeader,
  44      COIN,
  45      from_hex,
  46      msg_block,
  47  )
  48  from test_framework.p2p import P2PInterface
  49  from test_framework.script import hash256, OP_TRUE
  50  from test_framework.test_framework import BitcoinTestFramework
  51  from test_framework.util import (
  52      assert_not_equal,
  53      assert_equal,
  54      assert_greater_than,
  55      assert_greater_than_or_equal,
  56      assert_raises,
  57      assert_raises_rpc_error,
  58      assert_is_hex_string,
  59      assert_is_hash_string,
  60  )
  61  from test_framework.wallet import MiniWallet
  62  
  63  
  64  HEIGHT = 200  # blocks mined
  65  TIME_RANGE_STEP = 600  # ten-minute steps
  66  TIME_RANGE_MTP = TIME_GENESIS_BLOCK + (HEIGHT - 6) * TIME_RANGE_STEP
  67  TIME_RANGE_TIP = TIME_GENESIS_BLOCK + (HEIGHT - 1) * TIME_RANGE_STEP
  68  TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP
  69  DIFFICULTY_ADJUSTMENT_INTERVAL = 144
  70  
  71  
  72  class BlockchainTest(BitcoinTestFramework):
  73      def set_test_params(self):
  74          self.setup_clean_chain = True
  75          self.num_nodes = 1
  76          self.supports_cli = False
  77  
  78      def run_test(self):
  79          self.wallet = MiniWallet(self.nodes[0])
  80          self._test_prune_disk_space()
  81          self.mine_chain()
  82          self._test_max_future_block_time()
  83          self.restart_node(
  84              0,
  85              extra_args=[
  86                  "-stopatheight=207",
  87                  "-checkblocks=-1",  # Check all blocks
  88                  "-prune=1",  # Set pruning after rescan is complete
  89              ],
  90          )
  91  
  92          self._test_getblockchaininfo()
  93          self._test_getchaintxstats()
  94          self._test_gettxoutsetinfo()
  95          self._test_gettxout()
  96          self._test_getblockheader()
  97          self._test_getdifficulty()
  98          self._test_getnetworkhashps()
  99          self._test_stopatheight()
 100          self._test_waitforblock() # also tests waitfornewblock
 101          self._test_waitforblockheight()
 102          self._test_getblock()
 103          self._test_getdeploymentinfo()
 104          self._test_verificationprogress()
 105          self._test_y2106()
 106          assert self.nodes[0].verifychain(4, 0)
 107  
 108      def mine_chain(self):
 109          self.log.info(f"Generate {HEIGHT} blocks after the genesis block in ten-minute steps")
 110          for t in range(TIME_GENESIS_BLOCK, TIME_RANGE_END, TIME_RANGE_STEP):
 111              self.nodes[0].setmocktime(t)
 112              self.generate(self.wallet, 1)
 113          assert_equal(self.nodes[0].getblockchaininfo()['blocks'], HEIGHT)
 114  
 115      def _test_prune_disk_space(self):
 116          self.log.info("Test that a manually pruned node does not run into "
 117                        "integer overflow on first start up")
 118          self.restart_node(0, extra_args=["-prune=1"])
 119          self.log.info("Avoid warning when assumed chain size is enough")
 120          self.restart_node(0, extra_args=["-prune=123456789"])
 121  
 122      def _test_max_future_block_time(self):
 123          self.stop_node(0)
 124          self.log.info("A block tip of more than MAX_FUTURE_BLOCK_TIME in the future raises an error")
 125          self.nodes[0].assert_start_raises_init_error(
 126              extra_args=[f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME - 1}"],
 127              expected_msg="The block database contains a block which appears to be from the future."
 128              " This may be due to your computer's date and time being set incorrectly."
 129              f" Only rebuild the block database if you are sure that your computer's date and time are correct.{os.linesep}"
 130              "Please restart with -reindex or -reindex-chainstate to recover.",
 131          )
 132          self.log.info("A block tip of MAX_FUTURE_BLOCK_TIME in the future is fine")
 133          self.start_node(0, extra_args=[f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME}"])
 134  
 135      def _test_getblockchaininfo(self):
 136          self.log.info("Test getblockchaininfo")
 137  
 138          keys = [
 139              'bestblockhash',
 140              'bits',
 141              'blocks',
 142              'chain',
 143              'chainwork',
 144              'difficulty',
 145              'headers',
 146              'initialblockdownload',
 147              'mediantime',
 148              'pruned',
 149              'size_on_disk',
 150              'target',
 151              'time',
 152              'verificationprogress',
 153              'warnings',
 154          ]
 155          res = self.nodes[0].getblockchaininfo()
 156  
 157          assert_equal(res['time'], TIME_RANGE_END - TIME_RANGE_STEP)
 158          assert_equal(res['mediantime'], TIME_RANGE_MTP)
 159  
 160          # result should have these additional pruning keys if manual pruning is enabled
 161          assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning'] + keys))
 162  
 163          # size_on_disk should be > 0
 164          assert_greater_than(res['size_on_disk'], 0)
 165  
 166          # pruneheight should be greater or equal to 0
 167          assert_greater_than_or_equal(res['pruneheight'], 0)
 168  
 169          # check other pruning fields given that prune=1
 170          assert res['pruned']
 171          assert not res['automatic_pruning']
 172  
 173          # check background validation is not present when we are not using assumeutxo
 174          assert "backgroundvalidation" not in res.keys()
 175  
 176          self.restart_node(0, ['-stopatheight=207'])
 177          res = self.nodes[0].getblockchaininfo()
 178          # should have exact keys
 179          assert_equal(sorted(res.keys()), keys)
 180  
 181          self.stop_node(0)
 182          self.nodes[0].assert_start_raises_init_error(
 183              extra_args=['-testactivationheight=name@2'],
 184              expected_msg='Error: Invalid name (name@2) for -testactivationheight=name@height.',
 185          )
 186          self.nodes[0].assert_start_raises_init_error(
 187              extra_args=['-testactivationheight=bip34@-2'],
 188              expected_msg='Error: Invalid height value (bip34@-2) for -testactivationheight=name@height.',
 189          )
 190          self.nodes[0].assert_start_raises_init_error(
 191              extra_args=['-testactivationheight='],
 192              expected_msg='Error: Invalid format () for -testactivationheight=name@height.',
 193          )
 194          self.start_node(0, extra_args=[
 195              '-stopatheight=207',
 196              '-prune=550',
 197          ])
 198  
 199          res = self.nodes[0].getblockchaininfo()
 200          # result should have these additional pruning keys if prune=550
 201          assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning', 'prune_target_size'] + keys))
 202  
 203          # check related fields
 204          assert res['pruned']
 205          assert_equal(res['pruneheight'], 0)
 206          assert res['automatic_pruning']
 207          assert_equal(res['prune_target_size'], 576716800)
 208          assert_greater_than(res['size_on_disk'], 0)
 209  
 210          assert_equal(res['bits'], nbits_str(REGTEST_N_BITS))
 211          assert_equal(res['target'], target_str(REGTEST_TARGET))
 212  
 213      def check_signalling_deploymentinfo_result(self, gdi_result, height, blockhash, status_next):
 214          assert height >= 144 and height <= 287
 215  
 216          assert_equal(gdi_result, {
 217            "hash": blockhash,
 218            "height": height,
 219            "script_flags": ["CHECKLOCKTIMEVERIFY","CHECKSEQUENCEVERIFY","DERSIG","NULLDUMMY","P2SH","TAPROOT","WITNESS"],
 220            "deployments": {
 221              'bip34': {'type': 'buried', 'active': True, 'height': 2},
 222              'bip66': {'type': 'buried', 'active': True, 'height': 3},
 223              'bip65': {'type': 'buried', 'active': True, 'height': 4},
 224              'csv': {'type': 'buried', 'active': True, 'height': 5},
 225              'segwit': {'type': 'buried', 'active': True, 'height': 6},
 226              'testdummy': {
 227                  'type': 'bip9',
 228                  'bip9': {
 229                      'bit': 28,
 230                      'start_time': 0,
 231                      'timeout': 0x7fffffffffffffff,  # testdummy does not have a timeout so is set to the max int64 value
 232                      'min_activation_height': 0,
 233                      'status': 'started',
 234                      'status_next': status_next,
 235                      'since': 144,
 236                      'statistics': {
 237                          'period': 144,
 238                          'threshold': 108,
 239                          'elapsed': height - 143,
 240                          'count': height - 143,
 241                          'possible': True,
 242                      },
 243                      'signalling': '#'*(height-143),
 244                  },
 245                  'active': False
 246              },
 247            }
 248          })
 249  
 250      def _test_getdeploymentinfo(self):
 251          # Note: continues past -stopatheight height, so must be invoked
 252          # after _test_stopatheight
 253  
 254          self.log.info("Test getdeploymentinfo")
 255          self.stop_node(0)
 256          self.start_node(0, extra_args=[
 257              '-testactivationheight=bip34@2',
 258              '-testactivationheight=dersig@3',
 259              '-testactivationheight=cltv@4',
 260              '-testactivationheight=csv@5',
 261              '-testactivationheight=segwit@6',
 262          ])
 263  
 264          gbci207 = self.nodes[0].getblockchaininfo()
 265          self.check_signalling_deploymentinfo_result(self.nodes[0].getdeploymentinfo(), gbci207["blocks"], gbci207["bestblockhash"], "started")
 266  
 267          # block just prior to lock in
 268          self.generate(self.wallet, 287 - gbci207["blocks"])
 269          gbci287 = self.nodes[0].getblockchaininfo()
 270          self.check_signalling_deploymentinfo_result(self.nodes[0].getdeploymentinfo(), gbci287["blocks"], gbci287["bestblockhash"], "locked_in")
 271  
 272          # calling with an explicit hash works
 273          self.check_signalling_deploymentinfo_result(self.nodes[0].getdeploymentinfo(gbci207["bestblockhash"]), gbci207["blocks"], gbci207["bestblockhash"], "started")
 274  
 275      def _test_verificationprogress(self):
 276          self.log.info("Check that verificationprogress is less than 1 when the block tip is old")
 277          future = 2 * 60 * 60
 278          self.nodes[0].setmocktime(self.nodes[0].getblockchaininfo()["time"] + future + 1)
 279          assert_greater_than(1, self.nodes[0].getblockchaininfo()["verificationprogress"])
 280  
 281          self.log.info("Check that verificationprogress is exactly 1 for a recent block tip")
 282          self.nodes[0].setmocktime(self.nodes[0].getblockchaininfo()["time"] + future)
 283          assert_equal(1, self.nodes[0].getblockchaininfo()["verificationprogress"])
 284  
 285          self.log.info("Check that verificationprogress is less than 1 as soon as a new header comes in")
 286          self.nodes[0].submitheader(self.generateblock(self.nodes[0], output="raw(55)", transactions=[], submit=False, sync_fun=self.no_op)["hex"])
 287          assert_greater_than(1, self.nodes[0].getblockchaininfo()["verificationprogress"])
 288  
 289      def _test_y2106(self):
 290          self.log.info("Check that block timestamps work until year 2106")
 291          self.generate(self.nodes[0], 8)[-1]
 292          time_2106 = 2**32 - 1
 293          assert_raises_rpc_error(-8, f"Mocktime must be in the range [0, {time_2106}], not -1.", self.nodes[0].setmocktime, -1)
 294          assert_raises_rpc_error(-8, f"Mocktime must be in the range [0, {time_2106}], not {time_2106 + 1}.", self.nodes[0].setmocktime, time_2106 + 1)
 295          self.nodes[0].setmocktime(time_2106)
 296          last = self.generate(self.nodes[0], 6)[-1]
 297          assert_equal(self.nodes[0].getblockheader(last)["mediantime"], time_2106)
 298  
 299      def _test_getchaintxstats(self):
 300          self.log.info("Test getchaintxstats")
 301  
 302          # Test `getchaintxstats` invalid extra parameters
 303          assert_raises_rpc_error(-1, 'getchaintxstats', self.nodes[0].getchaintxstats, 0, '', 0)
 304  
 305          # Test `getchaintxstats` invalid `nblocks`
 306          assert_raises_rpc_error(-3, "JSON value of type string is not of expected type number", self.nodes[0].getchaintxstats, '')
 307          assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, -1)
 308          assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, self.nodes[0].getblockcount())
 309  
 310          # Test `getchaintxstats` invalid `blockhash`
 311          assert_raises_rpc_error(-3, "JSON value of type number is not of expected type string", self.nodes[0].getchaintxstats, blockhash=0)
 312          assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 1, for '0')", self.nodes[0].getchaintxstats, blockhash='0')
 313          assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getchaintxstats, blockhash='ZZZ0000000000000000000000000000000000000000000000000000000000000')
 314          assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0000000000000000000000000000000000000000000000000000000000000000')
 315          blockhash = self.nodes[0].getblockhash(HEIGHT)
 316          self.nodes[0].invalidateblock(blockhash)
 317          assert_raises_rpc_error(-8, "Block is not in main chain", self.nodes[0].getchaintxstats, blockhash=blockhash)
 318          self.nodes[0].reconsiderblock(blockhash)
 319  
 320          chaintxstats = self.nodes[0].getchaintxstats(nblocks=1)
 321          # 200 txs plus genesis tx
 322          assert_equal(chaintxstats['txcount'], HEIGHT + 1)
 323          # tx rate should be 1 per 10 minutes, or 1/600
 324          # we have to round because of binary math
 325          assert_equal(round(chaintxstats['txrate'] * TIME_RANGE_STEP, 10), Decimal(1))
 326  
 327          b1_hash = self.nodes[0].getblockhash(1)
 328          b1 = self.nodes[0].getblock(b1_hash)
 329          b200_hash = self.nodes[0].getblockhash(HEIGHT)
 330          b200 = self.nodes[0].getblock(b200_hash)
 331          time_diff = b200['mediantime'] - b1['mediantime']
 332  
 333          chaintxstats = self.nodes[0].getchaintxstats()
 334          assert_equal(chaintxstats['time'], b200['time'])
 335          assert_equal(chaintxstats['txcount'], HEIGHT + 1)
 336          assert_equal(chaintxstats['window_final_block_hash'], b200_hash)
 337          assert_equal(chaintxstats['window_final_block_height'], HEIGHT )
 338          assert_equal(chaintxstats['window_block_count'], HEIGHT - 1)
 339          assert_equal(chaintxstats['window_tx_count'], HEIGHT - 1)
 340          assert_equal(chaintxstats['window_interval'], time_diff)
 341          assert_equal(round(chaintxstats['txrate'] * time_diff, 10), Decimal(HEIGHT - 1))
 342  
 343          chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1_hash)
 344          assert_equal(chaintxstats['time'], b1['time'])
 345          assert_equal(chaintxstats['txcount'], 2)
 346          assert_equal(chaintxstats['window_final_block_hash'], b1_hash)
 347          assert_equal(chaintxstats['window_final_block_height'], 1)
 348          assert_equal(chaintxstats['window_block_count'], 0)
 349          assert 'window_tx_count' not in chaintxstats
 350          assert 'window_interval' not in chaintxstats
 351          assert 'txrate' not in chaintxstats
 352  
 353      def _test_gettxoutsetinfo(self):
 354          node = self.nodes[0]
 355          res = node.gettxoutsetinfo()
 356  
 357          assert_equal(res['total_amount'], Decimal('8725.00000000'))
 358          assert_equal(res['transactions'], HEIGHT)
 359          assert_equal(res['height'], HEIGHT)
 360          assert_equal(res['txouts'], HEIGHT)
 361          assert_equal(res['bogosize'], 16800),
 362          assert_equal(res['bestblock'], node.getblockhash(HEIGHT))
 363          size = res['disk_size']
 364          assert size > 6400
 365          assert size < 64000
 366          assert_equal(len(res['bestblock']), 64)
 367          assert_equal(len(res['hash_serialized_3']), 64)
 368  
 369          self.log.info("Test gettxoutsetinfo works for blockchain with just the genesis block")
 370          b1hash = node.getblockhash(1)
 371          node.invalidateblock(b1hash)
 372  
 373          res2 = node.gettxoutsetinfo()
 374          assert_equal(res2['transactions'], 0)
 375          assert_equal(res2['total_amount'], Decimal('0'))
 376          assert_equal(res2['height'], 0)
 377          assert_equal(res2['txouts'], 0)
 378          assert_equal(res2['bogosize'], 0),
 379          assert_equal(res2['bestblock'], node.getblockhash(0))
 380          assert_equal(len(res2['hash_serialized_3']), 64)
 381  
 382          self.log.info("Test gettxoutsetinfo returns the same result after invalidate/reconsider block")
 383          node.reconsiderblock(b1hash)
 384  
 385          res3 = node.gettxoutsetinfo()
 386          # The field 'disk_size' is non-deterministic and can thus not be
 387          # compared between res and res3.  Everything else should be the same.
 388          del res['disk_size'], res3['disk_size']
 389          assert_equal(res, res3)
 390  
 391          self.log.info("Test gettxoutsetinfo hash_type option")
 392          # Adding hash_type 'hash_serialized_3', which is the default, should
 393          # not change the result.
 394          res4 = node.gettxoutsetinfo(hash_type='hash_serialized_3')
 395          del res4['disk_size']
 396          assert_equal(res, res4)
 397  
 398          # hash_type none should not return a UTXO set hash.
 399          res5 = node.gettxoutsetinfo(hash_type='none')
 400          assert 'hash_serialized_3' not in res5
 401  
 402          # hash_type muhash should return a different UTXO set hash.
 403          res6 = node.gettxoutsetinfo(hash_type='muhash')
 404          assert 'muhash' in res6
 405          assert_not_equal(res['hash_serialized_3'], res6['muhash'])
 406  
 407          # muhash should not be returned unless requested.
 408          for r in [res, res2, res3, res4, res5]:
 409              assert 'muhash' not in r
 410  
 411          # Unknown hash_type raises an error
 412          assert_raises_rpc_error(-8, "'foo hash' is not a valid hash_type", node.gettxoutsetinfo, "foo hash")
 413  
 414      def _test_gettxout(self):
 415          self.log.info("Validating gettxout RPC response")
 416          node = self.nodes[0]
 417  
 418          # Get the best block hash and the block, which
 419          # should only include the coinbase transaction.
 420          best_block_hash = node.getbestblockhash()
 421          block = node.getblock(best_block_hash)
 422          assert_equal(block['nTx'], 1)
 423  
 424          # Get the transaction ID of the coinbase tx and
 425          # the transaction output.
 426          txid = block['tx'][0]
 427          txout = node.gettxout(txid, 0)
 428  
 429          # Validate the gettxout response
 430          assert_equal(txout['bestblock'], best_block_hash)
 431          assert_equal(txout['confirmations'], 1)
 432          assert_equal(txout['value'], 25)
 433          assert_equal(txout['scriptPubKey']['address'], self.wallet.get_address())
 434          assert_equal(txout['scriptPubKey']['hex'], self.wallet.get_output_script().hex())
 435          decoded_script = node.decodescript(self.wallet.get_output_script().hex())
 436          assert_equal(txout['scriptPubKey']['asm'], decoded_script['asm'])
 437          assert_equal(txout['scriptPubKey']['desc'], decoded_script['desc'])
 438          assert_equal(txout['scriptPubKey']['type'], decoded_script['type'])
 439          assert_equal(txout['coinbase'], True)
 440  
 441      def _test_getblockheader(self):
 442          self.log.info("Test getblockheader")
 443          node = self.nodes[0]
 444  
 445          assert_raises_rpc_error(-8, "hash must be of length 64 (not 8, for 'nonsense')", node.getblockheader, "nonsense")
 446          assert_raises_rpc_error(-8, "hash must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", node.getblockheader, "ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844")
 447          assert_raises_rpc_error(-5, "Block not found", node.getblockheader, "0cf7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844")
 448  
 449          besthash = node.getbestblockhash()
 450          secondbesthash = node.getblockhash(HEIGHT - 1)
 451          header = node.getblockheader(blockhash=besthash)
 452  
 453          assert_equal(header['hash'], besthash)
 454          assert_equal(header['height'], HEIGHT)
 455          assert_equal(header['confirmations'], 1)
 456          assert_equal(header['previousblockhash'], secondbesthash)
 457          assert_is_hex_string(header['chainwork'])
 458          assert_equal(header['nTx'], 1)
 459          assert_is_hash_string(header['hash'])
 460          assert_is_hash_string(header['previousblockhash'])
 461          assert_is_hash_string(header['merkleroot'])
 462          assert_equal(header['bits'], nbits_str(REGTEST_N_BITS))
 463          assert_equal(header['target'], target_str(REGTEST_TARGET))
 464          assert isinstance(header['time'], int)
 465          assert_equal(header['mediantime'], TIME_RANGE_MTP)
 466          assert isinstance(header['nonce'], int)
 467          assert isinstance(header['version'], int)
 468          assert isinstance(int(header['versionHex'], 16), int)
 469          assert isinstance(header['difficulty'], Decimal)
 470  
 471          # Test with verbose=False, which should return the header as hex.
 472          header_hex = node.getblockheader(blockhash=besthash, verbose=False)
 473          assert_is_hex_string(header_hex)
 474  
 475          header = from_hex(CBlockHeader(), header_hex)
 476          assert_equal(header.hash_hex, besthash)
 477  
 478          assert 'previousblockhash' not in node.getblockheader(node.getblockhash(0))
 479          assert 'nextblockhash' not in node.getblockheader(node.getbestblockhash())
 480  
 481      def _test_getdifficulty(self):
 482          self.log.info("Test getdifficulty")
 483          difficulty = self.nodes[0].getdifficulty()
 484          # 1 hash in 2 should be valid, so difficulty should be 1/2**31
 485          # binary => decimal => binary math is why we do this check
 486          assert abs(difficulty * 2**31 - 1) < 0.0001
 487  
 488      def _test_getnetworkhashps(self):
 489          self.log.info("Test getnetworkhashps")
 490          assert_raises_rpc_error(
 491              -3,
 492              textwrap.dedent("""
 493              Wrong type passed:
 494              {
 495                  "Position 1 (nblocks)": "JSON value of type string is not of expected type number",
 496                  "Position 2 (height)": "JSON value of type array is not of expected type number"
 497              }
 498              """).strip(),
 499              lambda: self.nodes[0].getnetworkhashps("a", []),
 500          )
 501          assert_raises_rpc_error(
 502              -8,
 503              "Block does not exist at specified height",
 504              lambda: self.nodes[0].getnetworkhashps(100, self.nodes[0].getblockcount() + 1),
 505          )
 506          assert_raises_rpc_error(
 507              -8,
 508              "Block does not exist at specified height",
 509              lambda: self.nodes[0].getnetworkhashps(100, -10),
 510          )
 511          assert_raises_rpc_error(
 512              -8,
 513              "Invalid nblocks. Must be a positive number or -1.",
 514              lambda: self.nodes[0].getnetworkhashps(-100),
 515          )
 516          assert_raises_rpc_error(
 517              -8,
 518              "Invalid nblocks. Must be a positive number or -1.",
 519              lambda: self.nodes[0].getnetworkhashps(0),
 520          )
 521  
 522          # Genesis block height estimate should return 0
 523          hashes_per_second = self.nodes[0].getnetworkhashps(100, 0)
 524          assert_equal(hashes_per_second, 0)
 525  
 526          # This should be 2 hashes every 10 minutes or 1/300
 527          hashes_per_second = self.nodes[0].getnetworkhashps()
 528          assert abs(hashes_per_second * 300 - 1) < 0.0001
 529  
 530          # Test setting the first param of getnetworkhashps to -1 returns the average network
 531          # hashes per second from the last difficulty change.
 532          current_block_height = self.nodes[0].getmininginfo()['blocks']
 533          blocks_since_last_diff_change = current_block_height % DIFFICULTY_ADJUSTMENT_INTERVAL + 1
 534          expected_hashes_per_second_since_diff_change = self.nodes[0].getnetworkhashps(blocks_since_last_diff_change)
 535  
 536          assert_equal(self.nodes[0].getnetworkhashps(-1), expected_hashes_per_second_since_diff_change)
 537  
 538          # Ensure long lookups get truncated to chain length
 539          hashes_per_second = self.nodes[0].getnetworkhashps(self.nodes[0].getblockcount() + 1000)
 540          assert hashes_per_second > 0.003
 541  
 542      def _test_stopatheight(self):
 543          self.log.info("Test stopping at height")
 544          assert_equal(self.nodes[0].getblockcount(), HEIGHT)
 545          self.generate(self.wallet, 6)
 546          assert_equal(self.nodes[0].getblockcount(), HEIGHT + 6)
 547          self.log.debug('Node should not stop at this height')
 548          assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3))
 549          try:
 550              self.generatetoaddress(self.nodes[0], 1, self.wallet.get_address(), sync_fun=self.no_op)
 551          except (ConnectionError, http.client.BadStatusLine):
 552              pass  # The node already shut down before response
 553          self.log.debug('Node should stop at this height...')
 554          self.nodes[0].wait_until_stopped()
 555          self.start_node(0)
 556          assert_equal(self.nodes[0].getblockcount(), HEIGHT + 7)
 557  
 558      def _test_waitforblock(self):
 559          self.log.info("Test waitforblock and waitfornewblock")
 560          node = self.nodes[0]
 561  
 562          current_height = node.getblock(node.getbestblockhash())['height']
 563          current_hash = node.getblock(node.getbestblockhash())['hash']
 564  
 565          self.log.debug("Roll the chain back a few blocks and then reconsider it")
 566          rollback_height = current_height - 100
 567          rollback_hash = node.getblockhash(rollback_height)
 568          rollback_header = node.getblockheader(rollback_hash)
 569  
 570          node.invalidateblock(rollback_hash)
 571          assert_equal(node.getblockcount(), rollback_height - 1)
 572  
 573          self.log.debug("waitforblock should return the same block after its timeout")
 574          assert_equal(node.waitforblock(blockhash=current_hash, timeout=1)['hash'], rollback_header['previousblockhash'])
 575          assert_raises_rpc_error(-1, "Negative timeout", node.waitforblock, current_hash, -1)
 576  
 577          node.reconsiderblock(rollback_hash)
 578          # The chain has probably already been restored by the time reconsiderblock returns,
 579          # but poll anyway.
 580          self.wait_until(lambda: node.waitforblock(blockhash=current_hash, timeout=100)['hash'] == current_hash)
 581  
 582          # roll back again
 583          node.invalidateblock(rollback_hash)
 584          assert_equal(node.getblockcount(), rollback_height - 1)
 585  
 586          node.reconsiderblock(rollback_hash)
 587          # The chain has probably already been restored by the time reconsiderblock returns,
 588          # but poll anyway.
 589          self.wait_until(lambda: node.waitfornewblock(current_tip=rollback_header['previousblockhash'])['hash'] == current_hash)
 590  
 591          assert_raises_rpc_error(-1, "Negative timeout", node.waitfornewblock, -1)
 592  
 593      def _test_waitforblockheight(self):
 594          self.log.info("Test waitforblockheight")
 595          node = self.nodes[0]
 596          peer = node.add_p2p_connection(P2PInterface())
 597  
 598          current_height = node.getblock(node.getbestblockhash())['height']
 599  
 600          # Create a fork somewhere below our current height, invalidate the tip
 601          # of that fork, and then ensure that waitforblockheight still
 602          # works as expected.
 603          #
 604          # (Previously this was broken based on setting
 605          # `rpc/blockchain.cpp:latestblock` incorrectly.)
 606          #
 607          fork_height = current_height - 100 # choose something vaguely near our tip
 608          fork_hash = node.getblockhash(fork_height)
 609          fork_block = node.getblock(fork_hash)
 610  
 611          def solve_and_send_block(prevhash, height, time):
 612              b = create_block(prevhash, height=height, ntime=time)
 613              b.solve()
 614              peer.send_and_ping(msg_block(b))
 615              return b
 616  
 617          b1 = solve_and_send_block(int(fork_hash, 16), fork_height+1, fork_block['time'] + 1)
 618          b2 = solve_and_send_block(b1.hash_int, fork_height+2, b1.nTime + 1)
 619  
 620          node.invalidateblock(b2.hash_hex)
 621  
 622          def assert_waitforheight(height, timeout=2):
 623              assert_equal(
 624                  node.waitforblockheight(height=height, timeout=timeout)['height'],
 625                  current_height)
 626  
 627          assert_waitforheight(0)
 628          assert_waitforheight(current_height - 1)
 629          assert_waitforheight(current_height)
 630          assert_waitforheight(current_height + 1)
 631          assert_raises_rpc_error(-1, "Negative timeout", node.waitforblockheight, current_height, -1)
 632  
 633      def _test_getblock(self):
 634          node = self.nodes[0]
 635          fee_per_byte = Decimal('0.00000010')
 636          fee_per_kb = 1000 * fee_per_byte
 637  
 638          self.wallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
 639          blockhash = self.generate(node, 1)[0]
 640  
 641          def assert_coinbase_metadata(hash, verbosity):
 642              block = node.getblock(hash, verbosity)
 643              coinbase_tx = node.getblock(hash, 2)["tx"][0]
 644  
 645              expected_keys = {"version", "locktime", "sequence", "coinbase"}
 646              if "txinwitness" in coinbase_tx["vin"][0]:
 647                  expected_keys.add("witness")
 648              assert_equal(set(block["coinbase_tx"].keys()), expected_keys)
 649  
 650              assert_equal(block["coinbase_tx"]["version"], coinbase_tx["version"])
 651              assert_equal(block["coinbase_tx"]["locktime"], coinbase_tx["locktime"])
 652              assert_equal(block["coinbase_tx"]["sequence"], coinbase_tx["vin"][0]["sequence"])
 653              assert_equal(block["coinbase_tx"]["coinbase"], coinbase_tx["vin"][0]["coinbase"])
 654  
 655              witness_stack = coinbase_tx["vin"][0].get("txinwitness")
 656              if witness_stack is None:
 657                  assert "witness" not in block["coinbase_tx"]
 658              else:
 659                  assert_equal(block["coinbase_tx"]["witness"], witness_stack[0])
 660  
 661          def assert_hexblock_hashes(verbosity):
 662              block = node.getblock(blockhash, verbosity)
 663              assert_equal(blockhash, hash256(bytes.fromhex(block[:160]))[::-1].hex())
 664  
 665          def assert_fee_not_in_block(hash, verbosity):
 666              block = node.getblock(hash, verbosity)
 667              assert 'fee' not in block['tx'][1]
 668  
 669          def assert_fee_in_block(hash, verbosity):
 670              block = node.getblock(hash, verbosity)
 671              tx = block['tx'][1]
 672              assert 'fee' in tx
 673              assert_equal(tx['fee'], tx['vsize'] * fee_per_byte)
 674  
 675          def assert_vin_contains_prevout(verbosity):
 676              block = node.getblock(blockhash, verbosity)
 677              tx = block["tx"][1]
 678              total_vin = Decimal("0.00000000")
 679              total_vout = Decimal("0.00000000")
 680              for vin in tx["vin"]:
 681                  assert "prevout" in vin
 682                  assert_equal(set(vin["prevout"].keys()), set(("value", "height", "generated", "scriptPubKey")))
 683                  assert_equal(vin["prevout"]["generated"], True)
 684                  total_vin += vin["prevout"]["value"]
 685              for vout in tx["vout"]:
 686                  total_vout += vout["value"]
 687              assert_equal(total_vin, total_vout + tx["fee"])
 688  
 689          def assert_vin_does_not_contain_prevout(hash, verbosity):
 690              block = node.getblock(hash, verbosity)
 691              tx = block["tx"][1]
 692              if isinstance(tx, str):
 693                  # In verbosity level 1, only the transaction hashes are written
 694                  pass
 695              else:
 696                  for vin in tx["vin"]:
 697                      assert "prevout" not in vin
 698  
 699          self.log.info("Test that getblock with verbosity 0 hashes to expected value")
 700          assert_hexblock_hashes(0)
 701          assert_hexblock_hashes(False)
 702  
 703          self.log.info("Test that getblock with verbosity 1 doesn't include fee")
 704          assert_fee_not_in_block(blockhash, 1)
 705          assert_fee_not_in_block(blockhash, True)
 706  
 707          self.log.info("Test getblock coinbase metadata fields")
 708          assert_coinbase_metadata(blockhash, 1)
 709  
 710          self.log.info('Test that getblock with verbosity 2 and 3 includes expected fee')
 711          assert_fee_in_block(blockhash, 2)
 712          assert_fee_in_block(blockhash, 3)
 713  
 714          self.log.info("Test that getblock with verbosity 1 and 2 does not include prevout")
 715          assert_vin_does_not_contain_prevout(blockhash, 1)
 716          assert_vin_does_not_contain_prevout(blockhash, 2)
 717  
 718          self.log.info("Test that getblock with verbosity 3 includes prevout")
 719          assert_vin_contains_prevout(3)
 720  
 721          self.log.info("Test getblock with invalid verbosity type returns proper error message")
 722          assert_raises_rpc_error(-3, "JSON value of type string is not of expected type number", node.getblock, blockhash, "2")
 723  
 724          self.log.info("Test that getblock doesn't work with deleted Undo data")
 725  
 726          def move_block_file(old, new):
 727              old_path = self.nodes[0].blocks_path / old
 728              new_path = self.nodes[0].blocks_path / new
 729              old_path.rename(new_path)
 730  
 731          # Move instead of deleting so we can restore chain state afterwards
 732          move_block_file('rev00000.dat', 'rev_wrong')
 733  
 734          assert_raises_rpc_error(-32603, "Undo data expected but can't be read. This could be due to disk corruption or a conflict with a pruning event.", lambda: node.getblock(blockhash, 2))
 735          assert_raises_rpc_error(-32603, "Undo data expected but can't be read. This could be due to disk corruption or a conflict with a pruning event.", lambda: node.getblock(blockhash, 3))
 736  
 737          # Restore chain state
 738          move_block_file('rev_wrong', 'rev00000.dat')
 739  
 740          assert 'previousblockhash' not in node.getblock(node.getblockhash(0))
 741          assert 'nextblockhash' not in node.getblock(node.getbestblockhash())
 742  
 743          self.log.info("Test getblock when only header is known")
 744          current_height = node.getblock(node.getbestblockhash())['height']
 745          block_time = node.getblock(node.getbestblockhash())['time'] + 1
 746          block = create_block(int(blockhash, 16), create_coinbase(current_height + 1, nValue=100), ntime=block_time)
 747          block.solve()
 748          node.submitheader(block.serialize().hex())
 749          assert_raises_rpc_error(-1, "Block not available (not fully downloaded)", lambda: node.getblock(block.hash_hex))
 750  
 751          self.log.info("Test getblock when block data is available but undo data isn't")
 752          # Submits a block building on the header-only block, so it can't be connected and has no undo data
 753          tx = create_tx_with_script(block.vtx[0], 0, script_sig=bytes([OP_TRUE]), amount=50 * COIN)
 754          block_noundo = create_block(block.hash_int, create_coinbase(current_height + 2, nValue=100), ntime=block_time + 1, txlist=[tx])
 755          block_noundo.solve()
 756          node.submitblock(block_noundo.serialize().hex())
 757  
 758          assert_fee_not_in_block(block_noundo.hash_hex, 2)
 759          assert_fee_not_in_block(block_noundo.hash_hex, 3)
 760          assert_vin_does_not_contain_prevout(block_noundo.hash_hex, 2)
 761          assert_vin_does_not_contain_prevout(block_noundo.hash_hex, 3)
 762  
 763          self.log.info("Test getblock when block is missing")
 764          move_block_file('blk00000.dat', 'blk00000.dat.bak')
 765          assert_raises_rpc_error(-1, "Block not found on disk", node.getblock, blockhash)
 766          move_block_file('blk00000.dat.bak', 'blk00000.dat')
 767  
 768  
 769  if __name__ == '__main__':
 770      BlockchainTest(__file__).main()
 771