test: add test for chains building on invalid blocks

This commit is contained in:
Martin Zumsande 2024-05-30 14:02:32 -04:00
parent 155002ae12
commit ee61607a6a
2 changed files with 106 additions and 0 deletions

View file

@ -0,0 +1,105 @@
#!/usr/bin/env python3
# Copyright (c) 2024-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test how invalid blocks will influence chain behavior, in particular
the acceptance of future blocks and / or headers building on an invalid chain
For simplicity, submitheader / submitblock rpcs will be used in this test. The
behavior will be the same if blocks / headers are received through the p2p network.
"""
from test_framework.blocktools import (
create_block,
create_coinbase,
)
from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
class InvalidChainTest (BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
def run_test(self):
node = self.nodes[0]
tip = int(node.getbestblockhash(), 16)
start_height = self.nodes[0].getblockcount()
self.log.info("Test chain with an invalid block (found invalid during connect)")
# Create invalid block (too high coinbase)
block_time = node.getblock(node.getbestblockhash())['time'] + 1
invalid_block = create_block(tip, create_coinbase(start_height + 1, nValue=100), block_time)
invalid_block.solve()
# Submit the valid header of the invalid block and a header that builds on it
node.submitheader(invalid_block.serialize().hex())
block_time += 1
block2a = create_block(invalid_block.sha256, create_coinbase(start_height + 2), block_time)
block2a.solve()
node.submitheader(block2a.serialize().hex())
# Submit the invalid block
assert_equal(node.submitblock(invalid_block.serialize().hex()), "bad-cb-amount")
# Submit a block building directly on the invalid block
block_time += 1
block2b = create_block(invalid_block.sha256, create_coinbase(start_height + 2), block_time)
block2b.solve()
assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(block2b.serialize().hex()))
# Submit a block building indirectly on the invalid block
block_time += 1
block3 = create_block(block2a.sha256, create_coinbase(start_height + 3), block_time)
block3.solve()
assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(block3.serialize().hex()))
self.log.info("Test chain with an invalid block (found invalid during acceptance)")
# Create invalid block (bad coinbase height)
block_time += 1
invalid_block = create_block(tip, create_coinbase(start_height + 100), block_time)
invalid_block.solve()
# Submit the valid header of the invalid block and a header that builds on it
node.submitheader(invalid_block.serialize().hex())
block_time += 1
block2a = create_block(invalid_block.sha256, create_coinbase(start_height + 2), block_time)
block2a.solve()
node.submitheader(block2a.serialize().hex())
# Submit the invalid block
assert_equal(node.submitblock(invalid_block.serialize().hex()), "bad-cb-height")
# Submit a block building directly on the invalid block
block_time += 1
block2b = create_block(invalid_block.sha256, create_coinbase(start_height + 2), block_time)
block2b.solve()
assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(block2b.serialize().hex()))
# Submit a block building indirectly on the invalid block
block_time += 1
block3 = create_block(block2a.sha256, create_coinbase(start_height + 3), block_time)
block3.solve()
node.submitheader(block3.serialize().hex()) # No error, we accept more headers building on a block we have marked as invalid.
self.log.info("Test chain with an invalid block (found invalid before acceptance)")
# Create invalid block (too large)
block_time += 1
invalid_block = create_block(tip, create_coinbase(start_height + 1, extra_output_script=CScript([b'\x00' * 1000000])), block_time)
invalid_block.solve()
# Submit the valid header of the invalid block and a header that builds on it
node.submitheader(invalid_block.serialize().hex())
block_time += 1
block2a = create_block(invalid_block.sha256, create_coinbase(start_height + 2), block_time)
block2a.solve()
node.submitheader(block2a.serialize().hex())
# Submit a block building directly on the invalid block
block_time += 1
block2b = create_block(invalid_block.sha256, create_coinbase(start_height + 2), block_time)
block2b.solve()
node.submitheader(block2b.serialize().hex()) # No error, we don't mark the block permanently as invalid.
# Submit a block building indirectly on the invalid block
assert_equal(node.submitblock(invalid_block.serialize().hex()), "bad-blk-length")
block_time += 1
block3 = create_block(block2a.sha256, create_coinbase(start_height + 3), block_time)
block3.solve()
node.submitheader(block3.serialize().hex()) # No error, we don't mark the block permanently as invalid.
if __name__ == '__main__':
InvalidChainTest(__file__).main()

View file

@ -262,6 +262,7 @@ BASE_SCRIPTS = [
'p2p_invalid_block.py --v2transport',
'p2p_invalid_tx.py --v1transport',
'p2p_invalid_tx.py --v2transport',
'p2p_invalid_chain.py',
'p2p_v2_transport.py',
'p2p_v2_encrypted.py',
'p2p_v2_misbehaving.py',