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