2016-03-19 20:58:06 +01:00
#!/usr/bin/env python3
2022-12-25 00:49:50 +01:00
# Copyright (c) 2014-2022 The Bitcoin Core developers
2015-10-08 10:22:50 +02:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-18 00:34:40 +01:00
""" Test RPCs related to blockchainstate.
2015-10-08 10:22:50 +02:00
2017-01-18 00:34:40 +01:00
Test the following RPCs :
2017-09-06 20:05:28 +02:00
- getblockchaininfo
2021-12-01 08:46:21 +01:00
- getdeploymentinfo
2021-07-21 18:11:19 +02:00
- getchaintxstats
2017-01-18 00:34:40 +01:00
- gettxoutsetinfo
2017-04-18 22:11:06 +02:00
- getblockheader
2021-07-21 18:11:19 +02:00
- getdifficulty
2017-04-20 20:43:58 +02:00
- getnetworkhashps
2021-07-21 18:11:19 +02:00
- waitforblockheight
- getblock
- getblockhash
- getbestblockhash
2017-01-18 00:34:40 +01:00
- verifychain
Tests correspond to code in rpc / blockchain . cpp .
"""
2015-10-08 10:22:50 +02:00
2015-12-02 18:12:23 +01:00
from decimal import Decimal
2017-06-23 09:17:18 +02:00
import http . client
2020-10-09 17:50:49 +02:00
import os
2017-06-19 21:40:38 +02:00
import subprocess
2023-01-20 12:50:25 +01:00
import textwrap
2015-10-08 10:22:50 +02:00
2018-02-14 04:37:36 +01:00
from test_framework . blocktools import (
2021-11-30 16:03:55 +01:00
MAX_FUTURE_BLOCK_TIME ,
TIME_GENESIS_BLOCK ,
2018-02-14 04:37:36 +01:00
create_block ,
create_coinbase ,
)
from test_framework . messages import (
2020-08-14 20:19:14 +02:00
CBlockHeader ,
2021-06-20 14:01:36 +02:00
from_hex ,
2018-02-14 04:37:36 +01:00
msg_block ,
)
2020-08-17 11:45:44 +02:00
from test_framework . p2p import P2PInterface
2022-09-01 20:47:36 +02:00
from test_framework . script import hash256
2020-08-17 11:45:44 +02:00
from test_framework . test_framework import BitcoinTestFramework
from test_framework . util import (
assert_equal ,
assert_greater_than ,
assert_greater_than_or_equal ,
assert_raises ,
assert_raises_rpc_error ,
assert_is_hex_string ,
assert_is_hash_string ,
2018-02-14 04:37:36 +01:00
)
2020-10-09 17:50:49 +02:00
from test_framework . wallet import MiniWallet
2018-02-14 04:37:36 +01:00
2015-10-08 10:22:50 +02:00
2021-07-21 17:15:06 +02:00
HEIGHT = 200 # blocks mined
2021-07-21 17:05:01 +02:00
TIME_RANGE_STEP = 600 # ten-minute steps
2021-07-21 17:15:06 +02:00
TIME_RANGE_MTP = TIME_GENESIS_BLOCK + ( HEIGHT - 6 ) * TIME_RANGE_STEP
2021-11-30 16:03:55 +01:00
TIME_RANGE_TIP = TIME_GENESIS_BLOCK + ( HEIGHT - 1 ) * TIME_RANGE_STEP
2021-07-21 17:15:06 +02:00
TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP
2023-06-10 22:54:13 +02:00
DIFFICULTY_ADJUSTMENT_INTERVAL = 2016
2021-07-21 17:05:01 +02:00
2015-10-08 10:22:50 +02:00
class BlockchainTest ( BitcoinTestFramework ) :
2017-06-10 00:21:21 +02:00
def set_test_params ( self ) :
2019-02-11 17:18:36 +01:00
self . setup_clean_chain = True
2017-05-24 00:08:04 +02:00
self . num_nodes = 1
2019-12-06 15:37:49 +01:00
self . supports_cli = False
2015-10-08 10:22:50 +02:00
def run_test ( self ) :
2021-10-27 13:25:28 +02:00
self . wallet = MiniWallet ( self . nodes [ 0 ] )
2023-03-13 12:55:03 +01:00
self . _test_prune_disk_space ( )
2019-02-11 17:18:36 +01:00
self . mine_chain ( )
2021-11-30 16:03:55 +01:00
self . _test_max_future_block_time ( )
2022-02-21 10:41:08 +01:00
self . restart_node (
0 ,
extra_args = [
" -stopatheight=207 " ,
" -checkblocks=-1 " , # Check all blocks
" -prune=1 " , # Set pruning after rescan is complete
] ,
)
2018-09-10 22:58:15 +02:00
2017-09-20 01:45:39 +02:00
self . _test_getblockchaininfo ( )
2017-06-03 06:48:32 +02:00
self . _test_getchaintxstats ( )
2015-12-09 18:02:59 +01:00
self . _test_gettxoutsetinfo ( )
self . _test_getblockheader ( )
2017-04-18 22:11:06 +02:00
self . _test_getdifficulty ( )
2017-04-20 20:43:58 +02:00
self . _test_getnetworkhashps ( )
2017-06-19 21:40:38 +02:00
self . _test_stopatheight ( )
2018-02-14 04:37:36 +01:00
self . _test_waitforblockheight ( )
2020-10-09 17:50:49 +02:00
self . _test_getblock ( )
2021-11-13 22:55:34 +01:00
self . _test_getdeploymentinfo ( )
2022-10-05 16:58:18 +02:00
self . _test_y2106 ( )
2017-06-19 21:40:38 +02:00
assert self . nodes [ 0 ] . verifychain ( 4 , 0 )
2015-12-09 18:02:59 +01:00
2019-02-11 17:18:36 +01:00
def mine_chain ( self ) :
2021-07-21 17:15:06 +02:00
self . log . info ( f " Generate { HEIGHT } blocks after the genesis block in ten-minute steps " )
2021-07-21 17:05:01 +02:00
for t in range ( TIME_GENESIS_BLOCK , TIME_RANGE_END , TIME_RANGE_STEP ) :
2019-02-11 17:18:36 +01:00
self . nodes [ 0 ] . setmocktime ( t )
2021-10-27 13:25:28 +02:00
self . generate ( self . wallet , 1 )
2021-07-21 17:15:06 +02:00
assert_equal ( self . nodes [ 0 ] . getblockchaininfo ( ) [ ' blocks ' ] , HEIGHT )
2019-02-11 17:18:36 +01:00
2023-03-13 12:55:03 +01:00
def _test_prune_disk_space ( self ) :
self . log . info ( " Test that a manually pruned node does not run into "
" integer overflow on first start up " )
self . restart_node ( 0 , extra_args = [ " -prune=1 " ] )
self . log . info ( " Avoid warning when assumed chain size is enough " )
self . restart_node ( 0 , extra_args = [ " -prune=123456789 " ] )
2021-11-30 16:03:55 +01:00
def _test_max_future_block_time ( self ) :
self . stop_node ( 0 )
self . log . info ( " A block tip of more than MAX_FUTURE_BLOCK_TIME in the future raises an error " )
self . nodes [ 0 ] . assert_start_raises_init_error (
extra_args = [ f " -mocktime= { TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME - 1 } " ] ,
expected_msg = " : The block database contains a block which appears to be from the future. "
" This may be due to your computer ' s date and time being set incorrectly. "
f " Only rebuild the block database if you are sure that your computer ' s date and time are correct. { os . linesep } "
" Please restart with -reindex or -reindex-chainstate to recover. " ,
)
self . log . info ( " A block tip of MAX_FUTURE_BLOCK_TIME in the future is fine " )
self . start_node ( 0 , extra_args = [ f " -mocktime= { TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME } " ] )
2017-09-20 01:45:39 +02:00
def _test_getblockchaininfo ( self ) :
self . log . info ( " Test getblockchaininfo " )
keys = [
' bestblockhash ' ,
' blocks ' ,
' chain ' ,
' chainwork ' ,
' difficulty ' ,
' headers ' ,
2017-09-06 20:05:28 +02:00
' initialblockdownload ' ,
2017-09-20 01:45:39 +02:00
' mediantime ' ,
' pruned ' ,
2017-09-21 01:52:20 +02:00
' size_on_disk ' ,
2021-07-05 23:39:36 +02:00
' time ' ,
2017-09-20 01:45:39 +02:00
' verificationprogress ' ,
2017-07-18 00:43:12 +02:00
' warnings ' ,
2017-09-20 01:45:39 +02:00
]
res = self . nodes [ 0 ] . getblockchaininfo ( )
2017-09-21 01:52:20 +02:00
2021-07-21 17:05:01 +02:00
assert_equal ( res [ ' time ' ] , TIME_RANGE_END - TIME_RANGE_STEP )
2021-07-21 17:07:18 +02:00
assert_equal ( res [ ' mediantime ' ] , TIME_RANGE_MTP )
2021-07-05 23:39:36 +02:00
2017-09-21 01:52:20 +02:00
# result should have these additional pruning keys if manual pruning is enabled
assert_equal ( sorted ( res . keys ( ) ) , sorted ( [ ' pruneheight ' , ' automatic_pruning ' ] + keys ) )
# size_on_disk should be > 0
assert_greater_than ( res [ ' size_on_disk ' ] , 0 )
2017-09-20 01:45:39 +02:00
# pruneheight should be greater or equal to 0
2017-09-21 01:52:20 +02:00
assert_greater_than_or_equal ( res [ ' pruneheight ' ] , 0 )
# check other pruning fields given that prune=1
assert res [ ' pruned ' ]
assert not res [ ' automatic_pruning ' ]
2017-09-20 01:45:39 +02:00
self . restart_node ( 0 , [ ' -stopatheight=207 ' ] )
res = self . nodes [ 0 ] . getblockchaininfo ( )
# should have exact keys
assert_equal ( sorted ( res . keys ( ) ) , keys )
2021-09-24 15:40:07 +02:00
self . stop_node ( 0 )
self . nodes [ 0 ] . assert_start_raises_init_error (
extra_args = [ ' -testactivationheight=name@2 ' ] ,
expected_msg = ' Error: Invalid name (name@2) for -testactivationheight=name@height. ' ,
)
self . nodes [ 0 ] . assert_start_raises_init_error (
extra_args = [ ' -testactivationheight=bip34@-2 ' ] ,
expected_msg = ' Error: Invalid height value (bip34@-2) for -testactivationheight=name@height. ' ,
)
self . nodes [ 0 ] . assert_start_raises_init_error (
extra_args = [ ' -testactivationheight= ' ] ,
expected_msg = ' Error: Invalid format () for -testactivationheight=name@height. ' ,
)
self . start_node ( 0 , extra_args = [
' -stopatheight=207 ' ,
' -prune=550 ' ,
] )
2017-09-21 01:52:20 +02:00
res = self . nodes [ 0 ] . getblockchaininfo ( )
# result should have these additional pruning keys if prune=550
assert_equal ( sorted ( res . keys ( ) ) , sorted ( [ ' pruneheight ' , ' automatic_pruning ' , ' prune_target_size ' ] + keys ) )
# check related fields
assert res [ ' pruned ' ]
assert_equal ( res [ ' pruneheight ' ] , 0 )
assert res [ ' automatic_pruning ' ]
assert_equal ( res [ ' prune_target_size ' ] , 576716800 )
assert_greater_than ( res [ ' size_on_disk ' ] , 0 )
2021-11-13 22:55:34 +01:00
def check_signalling_deploymentinfo_result ( self , gdi_result , height , blockhash , status_next ) :
assert height > = 144 and height < = 287
2021-12-01 08:46:21 +01:00
2021-11-13 22:55:34 +01:00
assert_equal ( gdi_result , {
2021-11-13 23:44:40 +01:00
" hash " : blockhash ,
" height " : height ,
2021-12-01 08:46:21 +01:00
" deployments " : {
2021-09-24 15:40:07 +02:00
' bip34 ' : { ' type ' : ' buried ' , ' active ' : True , ' height ' : 2 } ,
' bip66 ' : { ' type ' : ' buried ' , ' active ' : True , ' height ' : 3 } ,
' bip65 ' : { ' type ' : ' buried ' , ' active ' : True , ' height ' : 4 } ,
' csv ' : { ' type ' : ' buried ' , ' active ' : True , ' height ' : 5 } ,
' segwit ' : { ' type ' : ' buried ' , ' active ' : True , ' height ' : 6 } ,
2018-02-13 15:35:22 +01:00
' testdummy ' : {
' type ' : ' bip9 ' ,
' bip9 ' : {
' bit ' : 28 ,
2019-09-06 13:30:15 +02:00
' start_time ' : 0 ,
2018-02-13 15:35:22 +01:00
' timeout ' : 0x7fffffffffffffff , # testdummy does not have a timeout so is set to the max int64 value
2021-11-13 22:55:34 +01:00
' min_activation_height ' : 0 ,
' status ' : ' started ' ,
2022-03-11 10:21:48 +01:00
' status_next ' : status_next ,
2018-02-13 15:35:22 +01:00
' since ' : 144 ,
' statistics ' : {
' period ' : 144 ,
' threshold ' : 108 ,
2021-11-13 22:55:34 +01:00
' elapsed ' : height - 143 ,
' count ' : height - 143 ,
2018-02-13 15:35:22 +01:00
' possible ' : True ,
} ,
2021-11-11 22:14:21 +01:00
' signalling ' : ' # ' * ( height - 143 ) ,
2018-02-13 15:35:22 +01:00
} ,
2020-09-11 23:34:17 +02:00
' active ' : False
} ,
' taproot ' : {
' type ' : ' bip9 ' ,
' bip9 ' : {
' start_time ' : - 1 ,
' timeout ' : 9223372036854775807 ,
2021-03-06 09:18:49 +01:00
' min_activation_height ' : 0 ,
2021-11-13 22:55:34 +01:00
' status ' : ' active ' ,
2022-03-11 10:21:48 +01:00
' status_next ' : ' active ' ,
2021-11-13 22:55:34 +01:00
' since ' : 0 ,
2020-09-11 23:34:17 +02:00
} ,
' height ' : 0 ,
' active ' : True
}
2021-12-01 08:46:21 +01:00
}
2018-02-13 15:35:22 +01:00
} )
2021-11-13 22:55:34 +01:00
def _test_getdeploymentinfo ( self ) :
# Note: continues past -stopatheight height, so must be invoked
# after _test_stopatheight
self . log . info ( " Test getdeploymentinfo " )
self . stop_node ( 0 )
self . start_node ( 0 , extra_args = [
' -testactivationheight=bip34@2 ' ,
' -testactivationheight=dersig@3 ' ,
' -testactivationheight=cltv@4 ' ,
' -testactivationheight=csv@5 ' ,
' -testactivationheight=segwit@6 ' ,
] )
gbci207 = self . nodes [ 0 ] . getblockchaininfo ( )
self . check_signalling_deploymentinfo_result ( self . nodes [ 0 ] . getdeploymentinfo ( ) , gbci207 [ " blocks " ] , gbci207 [ " bestblockhash " ] , " started " )
# block just prior to lock in
self . generate ( self . wallet , 287 - gbci207 [ " blocks " ] )
gbci287 = self . nodes [ 0 ] . getblockchaininfo ( )
self . check_signalling_deploymentinfo_result ( self . nodes [ 0 ] . getdeploymentinfo ( ) , gbci287 [ " blocks " ] , gbci287 [ " bestblockhash " ] , " locked_in " )
# calling with an explicit hash works
self . check_signalling_deploymentinfo_result ( self . nodes [ 0 ] . getdeploymentinfo ( gbci207 [ " bestblockhash " ] ) , gbci207 [ " blocks " ] , gbci207 [ " bestblockhash " ] , " started " )
2022-10-05 16:58:18 +02:00
def _test_y2106 ( self ) :
self . log . info ( " Check that block timestamps work until year 2106 " )
self . generate ( self . nodes [ 0 ] , 8 ) [ - 1 ]
time_2106 = 2 * * 32 - 1
self . nodes [ 0 ] . setmocktime ( time_2106 )
last = self . generate ( self . nodes [ 0 ] , 6 ) [ - 1 ]
assert_equal ( self . nodes [ 0 ] . getblockheader ( last ) [ " mediantime " ] , time_2106 )
2017-06-03 06:48:32 +02:00
def _test_getchaintxstats ( self ) :
2018-01-31 19:47:06 +01:00
self . log . info ( " Test getchaintxstats " )
2018-01-04 02:01:05 +01:00
# Test `getchaintxstats` invalid extra parameters
assert_raises_rpc_error ( - 1 , ' getchaintxstats ' , self . nodes [ 0 ] . getchaintxstats , 0 , ' ' , 0 )
# Test `getchaintxstats` invalid `nblocks`
2022-09-14 17:13:58 +02:00
assert_raises_rpc_error ( - 3 , " JSON value of type string is not of expected type number " , self . nodes [ 0 ] . getchaintxstats , ' ' )
2018-01-04 02:01:05 +01:00
assert_raises_rpc_error ( - 8 , " Invalid block count: should be between 0 and the block ' s height - 1 " , self . nodes [ 0 ] . getchaintxstats , - 1 )
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 ( ) )
# Test `getchaintxstats` invalid `blockhash`
2022-09-14 17:13:58 +02:00
assert_raises_rpc_error ( - 3 , " JSON value of type number is not of expected type string " , self . nodes [ 0 ] . getchaintxstats , blockhash = 0 )
2018-06-08 20:16:07 +02:00
assert_raises_rpc_error ( - 8 , " blockhash must be of length 64 (not 1, for ' 0 ' ) " , self . nodes [ 0 ] . getchaintxstats , blockhash = ' 0 ' )
assert_raises_rpc_error ( - 8 , " blockhash must be hexadecimal string (not ' ZZZ0000000000000000000000000000000000000000000000000000000000000 ' ) " , self . nodes [ 0 ] . getchaintxstats , blockhash = ' ZZZ0000000000000000000000000000000000000000000000000000000000000 ' )
assert_raises_rpc_error ( - 5 , " Block not found " , self . nodes [ 0 ] . getchaintxstats , blockhash = ' 0000000000000000000000000000000000000000000000000000000000000000 ' )
2021-07-21 17:15:06 +02:00
blockhash = self . nodes [ 0 ] . getblockhash ( HEIGHT )
2018-01-04 02:01:05 +01:00
self . nodes [ 0 ] . invalidateblock ( blockhash )
assert_raises_rpc_error ( - 8 , " Block is not in main chain " , self . nodes [ 0 ] . getchaintxstats , blockhash = blockhash )
self . nodes [ 0 ] . reconsiderblock ( blockhash )
2018-11-13 19:42:36 +01:00
chaintxstats = self . nodes [ 0 ] . getchaintxstats ( nblocks = 1 )
2017-06-03 06:48:32 +02:00
# 200 txs plus genesis tx
2021-07-21 17:15:06 +02:00
assert_equal ( chaintxstats [ ' txcount ' ] , HEIGHT + 1 )
2017-06-03 06:48:32 +02:00
# tx rate should be 1 per 10 minutes, or 1/600
# we have to round because of binary math
2021-07-21 17:15:06 +02:00
assert_equal ( round ( chaintxstats [ ' txrate ' ] * TIME_RANGE_STEP , 10 ) , Decimal ( 1 ) )
2017-06-03 06:48:32 +02:00
2018-01-31 19:47:06 +01:00
b1_hash = self . nodes [ 0 ] . getblockhash ( 1 )
b1 = self . nodes [ 0 ] . getblock ( b1_hash )
2021-07-21 17:15:06 +02:00
b200_hash = self . nodes [ 0 ] . getblockhash ( HEIGHT )
2018-01-31 19:47:06 +01:00
b200 = self . nodes [ 0 ] . getblock ( b200_hash )
2017-08-14 09:29:00 +02:00
time_diff = b200 [ ' mediantime ' ] - b1 [ ' mediantime ' ]
chaintxstats = self . nodes [ 0 ] . getchaintxstats ( )
assert_equal ( chaintxstats [ ' time ' ] , b200 [ ' time ' ] )
2021-07-21 17:15:06 +02:00
assert_equal ( chaintxstats [ ' txcount ' ] , HEIGHT + 1 )
2018-01-31 19:47:06 +01:00
assert_equal ( chaintxstats [ ' window_final_block_hash ' ] , b200_hash )
2021-07-21 17:15:06 +02:00
assert_equal ( chaintxstats [ ' window_final_block_height ' ] , HEIGHT )
assert_equal ( chaintxstats [ ' window_block_count ' ] , HEIGHT - 1 )
assert_equal ( chaintxstats [ ' window_tx_count ' ] , HEIGHT - 1 )
2017-08-14 09:29:00 +02:00
assert_equal ( chaintxstats [ ' window_interval ' ] , time_diff )
2021-07-21 17:15:06 +02:00
assert_equal ( round ( chaintxstats [ ' txrate ' ] * time_diff , 10 ) , Decimal ( HEIGHT - 1 ) )
2017-08-14 09:29:00 +02:00
2018-01-31 19:47:06 +01:00
chaintxstats = self . nodes [ 0 ] . getchaintxstats ( blockhash = b1_hash )
2017-08-14 09:29:00 +02:00
assert_equal ( chaintxstats [ ' time ' ] , b1 [ ' time ' ] )
assert_equal ( chaintxstats [ ' txcount ' ] , 2 )
2018-01-31 19:47:06 +01:00
assert_equal ( chaintxstats [ ' window_final_block_hash ' ] , b1_hash )
2019-08-23 16:42:28 +02:00
assert_equal ( chaintxstats [ ' window_final_block_height ' ] , 1 )
2017-08-14 09:29:00 +02:00
assert_equal ( chaintxstats [ ' window_block_count ' ] , 0 )
2019-02-19 23:43:44 +01:00
assert ' window_tx_count ' not in chaintxstats
assert ' window_interval ' not in chaintxstats
assert ' txrate ' not in chaintxstats
2017-08-14 09:29:00 +02:00
2015-12-09 18:02:59 +01:00
def _test_gettxoutsetinfo ( self ) :
2015-10-08 10:22:50 +02:00
node = self . nodes [ 0 ]
res = node . gettxoutsetinfo ( )
2016-03-19 20:58:06 +01:00
assert_equal ( res [ ' total_amount ' ] , Decimal ( ' 8725.00000000 ' ) )
2021-07-21 17:15:06 +02:00
assert_equal ( res [ ' transactions ' ] , HEIGHT )
assert_equal ( res [ ' height ' ] , HEIGHT )
assert_equal ( res [ ' txouts ' ] , HEIGHT )
2021-02-16 17:23:06 +01:00
assert_equal ( res [ ' bogosize ' ] , 16800 ) ,
2021-07-21 17:15:06 +02:00
assert_equal ( res [ ' bestblock ' ] , node . getblockhash ( HEIGHT ) )
2017-05-13 00:19:19 +02:00
size = res [ ' disk_size ' ]
assert size > 6400
assert size < 64000
2016-03-19 20:58:06 +01:00
assert_equal ( len ( res [ ' bestblock ' ] ) , 64 )
2023-10-19 14:54:41 +02:00
assert_equal ( len ( res [ ' hash_serialized_3 ' ] ) , 64 )
2015-10-08 10:22:50 +02:00
2021-07-21 18:11:19 +02:00
self . log . info ( " Test gettxoutsetinfo works for blockchain with just the genesis block " )
2017-05-24 00:08:04 +02:00
b1hash = node . getblockhash ( 1 )
node . invalidateblock ( b1hash )
res2 = node . gettxoutsetinfo ( )
assert_equal ( res2 [ ' transactions ' ] , 0 )
assert_equal ( res2 [ ' total_amount ' ] , Decimal ( ' 0 ' ) )
assert_equal ( res2 [ ' height ' ] , 0 )
assert_equal ( res2 [ ' txouts ' ] , 0 )
2017-05-19 01:29:29 +02:00
assert_equal ( res2 [ ' bogosize ' ] , 0 ) ,
2017-05-24 00:08:04 +02:00
assert_equal ( res2 [ ' bestblock ' ] , node . getblockhash ( 0 ) )
2023-10-19 14:54:41 +02:00
assert_equal ( len ( res2 [ ' hash_serialized_3 ' ] ) , 64 )
2017-05-24 00:08:04 +02:00
2021-07-21 18:11:19 +02:00
self . log . info ( " Test gettxoutsetinfo returns the same result after invalidate/reconsider block " )
2017-05-24 00:08:04 +02:00
node . reconsiderblock ( b1hash )
res3 = node . gettxoutsetinfo ( )
2018-08-09 16:16:25 +02:00
# The field 'disk_size' is non-deterministic and can thus not be
# compared between res and res3. Everything else should be the same.
del res [ ' disk_size ' ] , res3 [ ' disk_size ' ]
assert_equal ( res , res3 )
2017-05-24 00:08:04 +02:00
2021-07-21 18:11:19 +02:00
self . log . info ( " Test gettxoutsetinfo hash_type option " )
2023-10-19 14:54:41 +02:00
# Adding hash_type 'hash_serialized_3', which is the default, should
2020-06-19 15:21:35 +02:00
# not change the result.
2023-10-19 14:54:41 +02:00
res4 = node . gettxoutsetinfo ( hash_type = ' hash_serialized_3 ' )
2020-06-19 15:21:35 +02:00
del res4 [ ' disk_size ' ]
assert_equal ( res , res4 )
# hash_type none should not return a UTXO set hash.
res5 = node . gettxoutsetinfo ( hash_type = ' none ' )
2023-10-19 14:54:41 +02:00
assert ' hash_serialized_3 ' not in res5
2020-06-19 15:21:35 +02:00
2020-06-02 23:55:32 +02:00
# hash_type muhash should return a different UTXO set hash.
res6 = node . gettxoutsetinfo ( hash_type = ' muhash ' )
assert ' muhash ' in res6
2023-10-19 14:54:41 +02:00
assert res [ ' hash_serialized_3 ' ] != res6 [ ' muhash ' ]
2020-06-02 23:55:32 +02:00
2021-04-30 20:20:05 +02:00
# muhash should not be returned unless requested.
2020-06-02 23:55:32 +02:00
for r in [ res , res2 , res3 , res4 , res5 ] :
assert ' muhash ' not in r
# Unknown hash_type raises an error
2021-12-13 09:52:13 +01:00
assert_raises_rpc_error ( - 8 , " ' foo hash ' is not a valid hash_type " , node . gettxoutsetinfo , " foo hash " )
2020-06-02 23:55:32 +02:00
2015-12-09 18:02:59 +01:00
def _test_getblockheader ( self ) :
2021-07-21 18:11:19 +02:00
self . log . info ( " Test getblockheader " )
2015-12-09 18:02:59 +01:00
node = self . nodes [ 0 ]
2018-06-08 20:16:07 +02:00
assert_raises_rpc_error ( - 8 , " hash must be of length 64 (not 8, for ' nonsense ' ) " , node . getblockheader , " nonsense " )
assert_raises_rpc_error ( - 8 , " hash must be hexadecimal string (not ' ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844 ' ) " , node . getblockheader , " ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844 " )
assert_raises_rpc_error ( - 5 , " Block not found " , node . getblockheader , " 0cf7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844 " )
2015-12-09 18:02:59 +01:00
besthash = node . getbestblockhash ( )
2021-07-21 17:15:06 +02:00
secondbesthash = node . getblockhash ( HEIGHT - 1 )
2018-11-13 19:42:36 +01:00
header = node . getblockheader ( blockhash = besthash )
2015-12-09 18:02:59 +01:00
assert_equal ( header [ ' hash ' ] , besthash )
2021-07-21 17:15:06 +02:00
assert_equal ( header [ ' height ' ] , HEIGHT )
2015-12-09 18:02:59 +01:00
assert_equal ( header [ ' confirmations ' ] , 1 )
assert_equal ( header [ ' previousblockhash ' ] , secondbesthash )
assert_is_hex_string ( header [ ' chainwork ' ] )
2018-06-12 22:39:29 +02:00
assert_equal ( header [ ' nTx ' ] , 1 )
2015-12-09 18:02:59 +01:00
assert_is_hash_string ( header [ ' hash ' ] )
assert_is_hash_string ( header [ ' previousblockhash ' ] )
assert_is_hash_string ( header [ ' merkleroot ' ] )
assert_is_hash_string ( header [ ' bits ' ] , length = None )
assert isinstance ( header [ ' time ' ] , int )
2021-07-21 17:07:18 +02:00
assert_equal ( header [ ' mediantime ' ] , TIME_RANGE_MTP )
2015-12-09 18:02:59 +01:00
assert isinstance ( header [ ' nonce ' ] , int )
assert isinstance ( header [ ' version ' ] , int )
2016-04-05 00:21:00 +02:00
assert isinstance ( int ( header [ ' versionHex ' ] , 16 ) , int )
2016-01-18 15:17:48 +01:00
assert isinstance ( header [ ' difficulty ' ] , Decimal )
2015-10-08 10:22:50 +02:00
2020-08-14 20:19:14 +02:00
# Test with verbose=False, which should return the header as hex.
header_hex = node . getblockheader ( blockhash = besthash , verbose = False )
assert_is_hex_string ( header_hex )
2021-06-20 14:01:36 +02:00
header = from_hex ( CBlockHeader ( ) , header_hex )
2020-08-14 20:19:14 +02:00
header . calc_sha256 ( )
assert_equal ( header . hash , besthash )
2021-01-29 01:07:18 +01:00
assert ' previousblockhash ' not in node . getblockheader ( node . getblockhash ( 0 ) )
assert ' nextblockhash ' not in node . getblockheader ( node . getbestblockhash ( ) )
2017-04-18 22:11:06 +02:00
def _test_getdifficulty ( self ) :
2021-07-21 18:11:19 +02:00
self . log . info ( " Test getdifficulty " )
2017-04-18 22:11:06 +02:00
difficulty = self . nodes [ 0 ] . getdifficulty ( )
# 1 hash in 2 should be valid, so difficulty should be 1/2**31
# binary => decimal => binary math is why we do this check
assert abs ( difficulty * 2 * * 31 - 1 ) < 0.0001
2017-04-20 20:43:58 +02:00
def _test_getnetworkhashps ( self ) :
2021-07-21 18:11:19 +02:00
self . log . info ( " Test getnetworkhashps " )
2023-01-20 12:50:25 +01:00
assert_raises_rpc_error (
- 3 ,
textwrap . dedent ( """
Wrong type passed :
{
" Position 1 (nblocks) " : " JSON value of type string is not of expected type number " ,
" Position 2 (height) " : " JSON value of type array is not of expected type number "
}
""" ).strip(),
lambda : self . nodes [ 0 ] . getnetworkhashps ( " a " , [ ] ) ,
)
2023-09-30 21:06:36 +02:00
assert_raises_rpc_error (
- 8 ,
" Block does not exist at specified height " ,
lambda : self . nodes [ 0 ] . getnetworkhashps ( 100 , self . nodes [ 0 ] . getblockcount ( ) + 1 ) ,
)
assert_raises_rpc_error (
- 8 ,
" Block does not exist at specified height " ,
lambda : self . nodes [ 0 ] . getnetworkhashps ( 100 , - 10 ) ,
)
assert_raises_rpc_error (
- 8 ,
" Invalid nblocks. Must be a positive number or -1. " ,
lambda : self . nodes [ 0 ] . getnetworkhashps ( - 100 ) ,
)
assert_raises_rpc_error (
- 8 ,
" Invalid nblocks. Must be a positive number or -1. " ,
lambda : self . nodes [ 0 ] . getnetworkhashps ( 0 ) ,
)
# Genesis block height estimate should return 0
hashes_per_second = self . nodes [ 0 ] . getnetworkhashps ( 100 , 0 )
assert_equal ( hashes_per_second , 0 )
2017-04-20 20:43:58 +02:00
# This should be 2 hashes every 10 minutes or 1/300
2023-09-30 21:06:36 +02:00
hashes_per_second = self . nodes [ 0 ] . getnetworkhashps ( )
2017-04-20 20:43:58 +02:00
assert abs ( hashes_per_second * 300 - 1 ) < 0.0001
2023-09-30 21:06:36 +02:00
# Test setting the first param of getnetworkhashps to -1 returns the average network
2023-06-10 22:54:13 +02:00
# hashes per second from the last difficulty change.
current_block_height = self . nodes [ 0 ] . getmininginfo ( ) [ ' blocks ' ]
blocks_since_last_diff_change = current_block_height % DIFFICULTY_ADJUSTMENT_INTERVAL + 1
expected_hashes_per_second_since_diff_change = self . nodes [ 0 ] . getnetworkhashps ( blocks_since_last_diff_change )
assert_equal ( self . nodes [ 0 ] . getnetworkhashps ( - 1 ) , expected_hashes_per_second_since_diff_change )
2023-09-30 21:06:36 +02:00
# Ensure long lookups get truncated to chain length
hashes_per_second = self . nodes [ 0 ] . getnetworkhashps ( self . nodes [ 0 ] . getblockcount ( ) + 1000 )
assert hashes_per_second > 0.003
2023-06-10 22:54:13 +02:00
2017-06-19 21:40:38 +02:00
def _test_stopatheight ( self ) :
2021-07-21 18:11:19 +02:00
self . log . info ( " Test stopping at height " )
2021-07-21 17:15:06 +02:00
assert_equal ( self . nodes [ 0 ] . getblockcount ( ) , HEIGHT )
2021-10-27 13:25:28 +02:00
self . generate ( self . wallet , 6 )
2021-07-21 17:15:06 +02:00
assert_equal ( self . nodes [ 0 ] . getblockcount ( ) , HEIGHT + 6 )
2017-06-19 21:40:38 +02:00
self . log . debug ( ' Node should not stop at this height ' )
2017-06-02 20:30:36 +02:00
assert_raises ( subprocess . TimeoutExpired , lambda : self . nodes [ 0 ] . process . wait ( timeout = 3 ) )
2017-06-23 09:17:18 +02:00
try :
2021-10-27 13:25:28 +02:00
self . generatetoaddress ( self . nodes [ 0 ] , 1 , self . wallet . get_address ( ) , sync_fun = self . no_op )
2017-06-23 09:17:18 +02:00
except ( ConnectionError , http . client . BadStatusLine ) :
pass # The node already shut down before response
2017-06-19 21:40:38 +02:00
self . log . debug ( ' Node should stop at this height... ' )
2017-08-16 17:52:24 +02:00
self . nodes [ 0 ] . wait_until_stopped ( )
2017-06-09 22:35:17 +02:00
self . start_node ( 0 )
2021-07-21 17:15:06 +02:00
assert_equal ( self . nodes [ 0 ] . getblockcount ( ) , HEIGHT + 7 )
2017-06-19 21:40:38 +02:00
2018-02-14 04:37:36 +01:00
def _test_waitforblockheight ( self ) :
self . log . info ( " Test waitforblockheight " )
node = self . nodes [ 0 ]
2020-09-04 03:05:26 +02:00
peer = node . add_p2p_connection ( P2PInterface ( ) )
2018-02-14 04:37:36 +01:00
current_height = node . getblock ( node . getbestblockhash ( ) ) [ ' height ' ]
# Create a fork somewhere below our current height, invalidate the tip
# of that fork, and then ensure that waitforblockheight still
# works as expected.
#
# (Previously this was broken based on setting
# `rpc/blockchain.cpp:latestblock` incorrectly.)
#
2022-08-02 22:48:57 +02:00
fork_height = current_height - 100 # choose something vaguely near our tip
fork_hash = node . getblockhash ( fork_height )
fork_block = node . getblock ( fork_hash )
2018-02-14 04:37:36 +01:00
def solve_and_send_block ( prevhash , height , time ) :
b = create_block ( prevhash , create_coinbase ( height ) , time )
b . solve ( )
2020-09-04 03:05:26 +02:00
peer . send_and_ping ( msg_block ( b ) )
2018-02-14 04:37:36 +01:00
return b
2022-08-02 22:48:57 +02:00
b1 = solve_and_send_block ( int ( fork_hash , 16 ) , fork_height + 1 , fork_block [ ' time ' ] + 1 )
b2 = solve_and_send_block ( b1 . sha256 , fork_height + 2 , b1 . nTime + 1 )
2018-02-14 04:37:36 +01:00
2022-08-02 22:48:57 +02:00
node . invalidateblock ( b2 . hash )
2018-02-14 04:37:36 +01:00
def assert_waitforheight ( height , timeout = 2 ) :
assert_equal (
2018-11-13 19:42:36 +01:00
node . waitforblockheight ( height = height , timeout = timeout ) [ ' height ' ] ,
2018-02-14 04:37:36 +01:00
current_height )
assert_waitforheight ( 0 )
assert_waitforheight ( current_height - 1 )
assert_waitforheight ( current_height )
assert_waitforheight ( current_height + 1 )
2020-10-09 17:50:49 +02:00
def _test_getblock ( self ) :
node = self . nodes [ 0 ]
fee_per_byte = Decimal ( ' 0.00000010 ' )
fee_per_kb = 1000 * fee_per_byte
2021-10-27 13:25:28 +02:00
self . wallet . send_self_transfer ( fee_rate = fee_per_kb , from_node = node )
2021-08-19 17:10:24 +02:00
blockhash = self . generate ( node , 1 ) [ 0 ]
2020-10-09 17:50:49 +02:00
2022-09-01 20:47:36 +02:00
def assert_hexblock_hashes ( verbosity ) :
block = node . getblock ( blockhash , verbosity )
assert_equal ( blockhash , hash256 ( bytes . fromhex ( block [ : 160 ] ) ) [ : : - 1 ] . hex ( ) )
2021-02-27 15:19:32 +01:00
def assert_fee_not_in_block ( verbosity ) :
block = node . getblock ( blockhash , verbosity )
assert ' fee ' not in block [ ' tx ' ] [ 1 ]
def assert_fee_in_block ( verbosity ) :
block = node . getblock ( blockhash , verbosity )
tx = block [ ' tx ' ] [ 1 ]
assert ' fee ' in tx
assert_equal ( tx [ ' fee ' ] , tx [ ' vsize ' ] * fee_per_byte )
def assert_vin_contains_prevout ( verbosity ) :
block = node . getblock ( blockhash , verbosity )
tx = block [ " tx " ] [ 1 ]
total_vin = Decimal ( " 0.00000000 " )
total_vout = Decimal ( " 0.00000000 " )
for vin in tx [ " vin " ] :
assert " prevout " in vin
assert_equal ( set ( vin [ " prevout " ] . keys ( ) ) , set ( ( " value " , " height " , " generated " , " scriptPubKey " ) ) )
assert_equal ( vin [ " prevout " ] [ " generated " ] , True )
total_vin + = vin [ " prevout " ] [ " value " ]
for vout in tx [ " vout " ] :
total_vout + = vout [ " value " ]
assert_equal ( total_vin , total_vout + tx [ " fee " ] )
def assert_vin_does_not_contain_prevout ( verbosity ) :
block = node . getblock ( blockhash , verbosity )
tx = block [ " tx " ] [ 1 ]
if isinstance ( tx , str ) :
# In verbosity level 1, only the transaction hashes are written
pass
else :
for vin in tx [ " vin " ] :
assert " prevout " not in vin
2022-09-01 20:47:36 +02:00
self . log . info ( " Test that getblock with verbosity 0 hashes to expected value " )
assert_hexblock_hashes ( 0 )
assert_hexblock_hashes ( False )
2021-02-27 15:19:32 +01:00
self . log . info ( " Test that getblock with verbosity 1 doesn ' t include fee " )
assert_fee_not_in_block ( 1 )
2022-09-01 20:47:36 +02:00
assert_fee_not_in_block ( True )
2021-02-27 15:19:32 +01:00
self . log . info ( ' Test that getblock with verbosity 2 and 3 includes expected fee ' )
assert_fee_in_block ( 2 )
assert_fee_in_block ( 3 )
self . log . info ( " Test that getblock with verbosity 1 and 2 does not include prevout " )
assert_vin_does_not_contain_prevout ( 1 )
assert_vin_does_not_contain_prevout ( 2 )
self . log . info ( " Test that getblock with verbosity 3 includes prevout " )
assert_vin_contains_prevout ( 3 )
2021-07-21 18:11:19 +02:00
self . log . info ( " Test getblock with invalid verbosity type returns proper error message " )
2022-09-14 17:13:58 +02:00
assert_raises_rpc_error ( - 3 , " JSON value of type string is not of expected type number " , node . getblock , blockhash , " 2 " )
2021-04-17 13:08:47 +02:00
2023-06-14 13:00:11 +02:00
self . log . info ( " Test that getblock with verbosity 2 and 3 still works with pruned Undo data " )
2020-10-09 17:50:49 +02:00
def move_block_file ( old , new ) :
2023-07-12 15:03:38 +02:00
old_path = self . nodes [ 0 ] . blocks_path / old
new_path = self . nodes [ 0 ] . blocks_path / new
2023-06-14 13:00:11 +02:00
old_path . rename ( new_path )
2020-10-09 17:50:49 +02:00
# Move instead of deleting so we can restore chain state afterwards
move_block_file ( ' rev00000.dat ' , ' rev_wrong ' )
2021-02-27 15:19:32 +01:00
assert_fee_not_in_block ( 2 )
assert_fee_not_in_block ( 3 )
assert_vin_does_not_contain_prevout ( 2 )
assert_vin_does_not_contain_prevout ( 3 )
2020-10-09 17:50:49 +02:00
# Restore chain state
move_block_file ( ' rev_wrong ' , ' rev00000.dat ' )
2021-01-29 01:07:18 +01:00
assert ' previousblockhash ' not in node . getblock ( node . getblockhash ( 0 ) )
assert ' nextblockhash ' not in node . getblock ( node . getbestblockhash ( ) )
2017-06-19 21:40:38 +02:00
2015-10-08 10:22:50 +02:00
if __name__ == ' __main__ ' :
2024-07-16 23:05:14 +02:00
BlockchainTest ( __file__ ) . main ( )