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