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