PageRenderTime 25ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/test/functional/rpc_blockchain.py

http://github.com/bitcoin/bitcoin
Python | 338 lines | 291 code | 19 blank | 28 comment | 2 complexity | e74e792981cda1d116fbcf8a181d98e2 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, MIT
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2014-2020 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. Test the following RPCs:
  7. - getblockchaininfo
  8. - gettxoutsetinfo
  9. - getdifficulty
  10. - getbestblockhash
  11. - getblockhash
  12. - getblockheader
  13. - getchaintxstats
  14. - getnetworkhashps
  15. - verifychain
  16. Tests correspond to code in rpc/blockchain.cpp.
  17. """
  18. from decimal import Decimal
  19. import http.client
  20. import subprocess
  21. from test_framework.test_framework import BitcoinTestFramework
  22. from test_framework.util import (
  23. assert_equal,
  24. assert_greater_than,
  25. assert_greater_than_or_equal,
  26. assert_raises,
  27. assert_raises_rpc_error,
  28. assert_is_hex_string,
  29. assert_is_hash_string,
  30. )
  31. from test_framework.blocktools import (
  32. create_block,
  33. create_coinbase,
  34. TIME_GENESIS_BLOCK,
  35. )
  36. from test_framework.messages import (
  37. msg_block,
  38. )
  39. from test_framework.mininode import (
  40. P2PInterface,
  41. )
  42. class BlockchainTest(BitcoinTestFramework):
  43. def set_test_params(self):
  44. self.setup_clean_chain = True
  45. self.num_nodes = 1
  46. self.supports_cli = False
  47. def run_test(self):
  48. self.mine_chain()
  49. self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1']) # Set extra args with pruning after rescan is complete
  50. self._test_getblockchaininfo()
  51. self._test_getchaintxstats()
  52. self._test_gettxoutsetinfo()
  53. self._test_getblockheader()
  54. self._test_getdifficulty()
  55. self._test_getnetworkhashps()
  56. self._test_stopatheight()
  57. self._test_waitforblockheight()
  58. assert self.nodes[0].verifychain(4, 0)
  59. def mine_chain(self):
  60. self.log.info('Create some old blocks')
  61. address = self.nodes[0].get_deterministic_priv_key().address
  62. for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
  63. # ten-minute steps from genesis block time
  64. self.nodes[0].setmocktime(t)
  65. self.nodes[0].generatetoaddress(1, address)
  66. assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
  67. def _test_getblockchaininfo(self):
  68. self.log.info("Test getblockchaininfo")
  69. keys = [
  70. 'bestblockhash',
  71. 'blocks',
  72. 'chain',
  73. 'chainwork',
  74. 'difficulty',
  75. 'headers',
  76. 'initialblockdownload',
  77. 'mediantime',
  78. 'pruned',
  79. 'size_on_disk',
  80. 'softforks',
  81. 'verificationprogress',
  82. 'warnings',
  83. ]
  84. res = self.nodes[0].getblockchaininfo()
  85. # result should have these additional pruning keys if manual pruning is enabled
  86. assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning'] + keys))
  87. # size_on_disk should be > 0
  88. assert_greater_than(res['size_on_disk'], 0)
  89. # pruneheight should be greater or equal to 0
  90. assert_greater_than_or_equal(res['pruneheight'], 0)
  91. # check other pruning fields given that prune=1
  92. assert res['pruned']
  93. assert not res['automatic_pruning']
  94. self.restart_node(0, ['-stopatheight=207'])
  95. res = self.nodes[0].getblockchaininfo()
  96. # should have exact keys
  97. assert_equal(sorted(res.keys()), keys)
  98. self.restart_node(0, ['-stopatheight=207', '-prune=550'])
  99. res = self.nodes[0].getblockchaininfo()
  100. # result should have these additional pruning keys if prune=550
  101. assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning', 'prune_target_size'] + keys))
  102. # check related fields
  103. assert res['pruned']
  104. assert_equal(res['pruneheight'], 0)
  105. assert res['automatic_pruning']
  106. assert_equal(res['prune_target_size'], 576716800)
  107. assert_greater_than(res['size_on_disk'], 0)
  108. assert_equal(res['softforks'], {
  109. 'bip34': {'type': 'buried', 'active': False, 'height': 500},
  110. 'bip66': {'type': 'buried', 'active': False, 'height': 1251},
  111. 'bip65': {'type': 'buried', 'active': False, 'height': 1351},
  112. 'csv': {'type': 'buried', 'active': False, 'height': 432},
  113. 'segwit': {'type': 'buried', 'active': True, 'height': 0},
  114. 'testdummy': {
  115. 'type': 'bip9',
  116. 'bip9': {
  117. 'status': 'started',
  118. 'bit': 28,
  119. 'start_time': 0,
  120. 'timeout': 0x7fffffffffffffff, # testdummy does not have a timeout so is set to the max int64 value
  121. 'since': 144,
  122. 'statistics': {
  123. 'period': 144,
  124. 'threshold': 108,
  125. 'elapsed': 57,
  126. 'count': 57,
  127. 'possible': True,
  128. },
  129. },
  130. 'active': False}
  131. })
  132. def _test_getchaintxstats(self):
  133. self.log.info("Test getchaintxstats")
  134. # Test `getchaintxstats` invalid extra parameters
  135. assert_raises_rpc_error(-1, 'getchaintxstats', self.nodes[0].getchaintxstats, 0, '', 0)
  136. # Test `getchaintxstats` invalid `nblocks`
  137. assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].getchaintxstats, '')
  138. assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, -1)
  139. 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())
  140. # Test `getchaintxstats` invalid `blockhash`
  141. assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getchaintxstats, blockhash=0)
  142. assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 1, for '0')", self.nodes[0].getchaintxstats, blockhash='0')
  143. assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getchaintxstats, blockhash='ZZZ0000000000000000000000000000000000000000000000000000000000000')
  144. assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0000000000000000000000000000000000000000000000000000000000000000')
  145. blockhash = self.nodes[0].getblockhash(200)
  146. self.nodes[0].invalidateblock(blockhash)
  147. assert_raises_rpc_error(-8, "Block is not in main chain", self.nodes[0].getchaintxstats, blockhash=blockhash)
  148. self.nodes[0].reconsiderblock(blockhash)
  149. chaintxstats = self.nodes[0].getchaintxstats(nblocks=1)
  150. # 200 txs plus genesis tx
  151. assert_equal(chaintxstats['txcount'], 201)
  152. # tx rate should be 1 per 10 minutes, or 1/600
  153. # we have to round because of binary math
  154. assert_equal(round(chaintxstats['txrate'] * 600, 10), Decimal(1))
  155. b1_hash = self.nodes[0].getblockhash(1)
  156. b1 = self.nodes[0].getblock(b1_hash)
  157. b200_hash = self.nodes[0].getblockhash(200)
  158. b200 = self.nodes[0].getblock(b200_hash)
  159. time_diff = b200['mediantime'] - b1['mediantime']
  160. chaintxstats = self.nodes[0].getchaintxstats()
  161. assert_equal(chaintxstats['time'], b200['time'])
  162. assert_equal(chaintxstats['txcount'], 201)
  163. assert_equal(chaintxstats['window_final_block_hash'], b200_hash)
  164. assert_equal(chaintxstats['window_final_block_height'], 200)
  165. assert_equal(chaintxstats['window_block_count'], 199)
  166. assert_equal(chaintxstats['window_tx_count'], 199)
  167. assert_equal(chaintxstats['window_interval'], time_diff)
  168. assert_equal(round(chaintxstats['txrate'] * time_diff, 10), Decimal(199))
  169. chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1_hash)
  170. assert_equal(chaintxstats['time'], b1['time'])
  171. assert_equal(chaintxstats['txcount'], 2)
  172. assert_equal(chaintxstats['window_final_block_hash'], b1_hash)
  173. assert_equal(chaintxstats['window_final_block_height'], 1)
  174. assert_equal(chaintxstats['window_block_count'], 0)
  175. assert 'window_tx_count' not in chaintxstats
  176. assert 'window_interval' not in chaintxstats
  177. assert 'txrate' not in chaintxstats
  178. def _test_gettxoutsetinfo(self):
  179. node = self.nodes[0]
  180. res = node.gettxoutsetinfo()
  181. assert_equal(res['total_amount'], Decimal('8725.00000000'))
  182. assert_equal(res['transactions'], 200)
  183. assert_equal(res['height'], 200)
  184. assert_equal(res['txouts'], 200)
  185. assert_equal(res['bogosize'], 15000),
  186. assert_equal(res['bestblock'], node.getblockhash(200))
  187. size = res['disk_size']
  188. assert size > 6400
  189. assert size < 64000
  190. assert_equal(len(res['bestblock']), 64)
  191. assert_equal(len(res['hash_serialized_2']), 64)
  192. self.log.info("Test that gettxoutsetinfo() works for blockchain with just the genesis block")
  193. b1hash = node.getblockhash(1)
  194. node.invalidateblock(b1hash)
  195. res2 = node.gettxoutsetinfo()
  196. assert_equal(res2['transactions'], 0)
  197. assert_equal(res2['total_amount'], Decimal('0'))
  198. assert_equal(res2['height'], 0)
  199. assert_equal(res2['txouts'], 0)
  200. assert_equal(res2['bogosize'], 0),
  201. assert_equal(res2['bestblock'], node.getblockhash(0))
  202. assert_equal(len(res2['hash_serialized_2']), 64)
  203. self.log.info("Test that gettxoutsetinfo() returns the same result after invalidate/reconsider block")
  204. node.reconsiderblock(b1hash)
  205. res3 = node.gettxoutsetinfo()
  206. # The field 'disk_size' is non-deterministic and can thus not be
  207. # compared between res and res3. Everything else should be the same.
  208. del res['disk_size'], res3['disk_size']
  209. assert_equal(res, res3)
  210. def _test_getblockheader(self):
  211. node = self.nodes[0]
  212. assert_raises_rpc_error(-8, "hash must be of length 64 (not 8, for 'nonsense')", node.getblockheader, "nonsense")
  213. assert_raises_rpc_error(-8, "hash must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", node.getblockheader, "ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844")
  214. assert_raises_rpc_error(-5, "Block not found", node.getblockheader, "0cf7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844")
  215. besthash = node.getbestblockhash()
  216. secondbesthash = node.getblockhash(199)
  217. header = node.getblockheader(blockhash=besthash)
  218. assert_equal(header['hash'], besthash)
  219. assert_equal(header['height'], 200)
  220. assert_equal(header['confirmations'], 1)
  221. assert_equal(header['previousblockhash'], secondbesthash)
  222. assert_is_hex_string(header['chainwork'])
  223. assert_equal(header['nTx'], 1)
  224. assert_is_hash_string(header['hash'])
  225. assert_is_hash_string(header['previousblockhash'])
  226. assert_is_hash_string(header['merkleroot'])
  227. assert_is_hash_string(header['bits'], length=None)
  228. assert isinstance(header['time'], int)
  229. assert isinstance(header['mediantime'], int)
  230. assert isinstance(header['nonce'], int)
  231. assert isinstance(header['version'], int)
  232. assert isinstance(int(header['versionHex'], 16), int)
  233. assert isinstance(header['difficulty'], Decimal)
  234. def _test_getdifficulty(self):
  235. difficulty = self.nodes[0].getdifficulty()
  236. # 1 hash in 2 should be valid, so difficulty should be 1/2**31
  237. # binary => decimal => binary math is why we do this check
  238. assert abs(difficulty * 2**31 - 1) < 0.0001
  239. def _test_getnetworkhashps(self):
  240. hashes_per_second = self.nodes[0].getnetworkhashps()
  241. # This should be 2 hashes every 10 minutes or 1/300
  242. assert abs(hashes_per_second * 300 - 1) < 0.0001
  243. def _test_stopatheight(self):
  244. assert_equal(self.nodes[0].getblockcount(), 200)
  245. self.nodes[0].generatetoaddress(6, self.nodes[0].get_deterministic_priv_key().address)
  246. assert_equal(self.nodes[0].getblockcount(), 206)
  247. self.log.debug('Node should not stop at this height')
  248. assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3))
  249. try:
  250. self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address)
  251. except (ConnectionError, http.client.BadStatusLine):
  252. pass # The node already shut down before response
  253. self.log.debug('Node should stop at this height...')
  254. self.nodes[0].wait_until_stopped()
  255. self.start_node(0)
  256. assert_equal(self.nodes[0].getblockcount(), 207)
  257. def _test_waitforblockheight(self):
  258. self.log.info("Test waitforblockheight")
  259. node = self.nodes[0]
  260. node.add_p2p_connection(P2PInterface())
  261. current_height = node.getblock(node.getbestblockhash())['height']
  262. # Create a fork somewhere below our current height, invalidate the tip
  263. # of that fork, and then ensure that waitforblockheight still
  264. # works as expected.
  265. #
  266. # (Previously this was broken based on setting
  267. # `rpc/blockchain.cpp:latestblock` incorrectly.)
  268. #
  269. b20hash = node.getblockhash(20)
  270. b20 = node.getblock(b20hash)
  271. def solve_and_send_block(prevhash, height, time):
  272. b = create_block(prevhash, create_coinbase(height), time)
  273. b.solve()
  274. node.p2p.send_and_ping(msg_block(b))
  275. return b
  276. b21f = solve_and_send_block(int(b20hash, 16), 21, b20['time'] + 1)
  277. b22f = solve_and_send_block(b21f.sha256, 22, b21f.nTime + 1)
  278. node.invalidateblock(b22f.hash)
  279. def assert_waitforheight(height, timeout=2):
  280. assert_equal(
  281. node.waitforblockheight(height=height, timeout=timeout)['height'],
  282. current_height)
  283. assert_waitforheight(0)
  284. assert_waitforheight(current_height - 1)
  285. assert_waitforheight(current_height)
  286. assert_waitforheight(current_height + 1)
  287. if __name__ == '__main__':
  288. BlockchainTest().main()