/ test / functional / rpc_blockchain.py
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()