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