|
9 | 9 | - submitblock"""
|
10 | 10 |
|
11 | 11 | import copy
|
12 |
| -from binascii import b2a_hex |
13 | 12 | from decimal import Decimal
|
14 | 13 |
|
15 | 14 | from test_framework.blocktools import create_coinbase
|
16 |
| -from test_framework.messages import CBlock |
| 15 | +from test_framework.messages import ( |
| 16 | + CBlock, |
| 17 | + CBlockHeader, |
| 18 | +) |
| 19 | +from test_framework.mininode import ( |
| 20 | + P2PDataStore, |
| 21 | +) |
17 | 22 | from test_framework.test_framework import BitcoinTestFramework
|
18 |
| -from test_framework.util import assert_equal, assert_raises_rpc_error |
| 23 | +from test_framework.util import ( |
| 24 | + assert_equal, |
| 25 | + assert_raises_rpc_error, |
| 26 | + bytes_to_hex_str as b2x, |
| 27 | +) |
19 | 28 |
|
20 |
| -def b2x(b): |
21 |
| - return b2a_hex(b).decode('ascii') |
22 | 29 |
|
23 | 30 | def assert_template(node, block, expect, rehash=True):
|
24 | 31 | if rehash:
|
@@ -131,5 +138,67 @@ def run_test(self):
|
131 | 138 | bad_block.hashPrevBlock = 123
|
132 | 139 | assert_template(node, bad_block, 'inconclusive-not-best-prevblk')
|
133 | 140 |
|
| 141 | + self.log.info('submitheader tests') |
| 142 | + assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * 80)) |
| 143 | + assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * 78)) |
| 144 | + assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata='ff' * 80)) |
| 145 | + |
| 146 | + block.solve() |
| 147 | + |
| 148 | + def chain_tip(b_hash, *, status='headers-only', branchlen=1): |
| 149 | + return {'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status} |
| 150 | + |
| 151 | + assert chain_tip(block.hash) not in node.getchaintips() |
| 152 | + node.submitheader(hexdata=b2x(block.serialize())) |
| 153 | + assert chain_tip(block.hash) in node.getchaintips() |
| 154 | + node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) # Noop |
| 155 | + assert chain_tip(block.hash) in node.getchaintips() |
| 156 | + |
| 157 | + bad_block_root = copy.deepcopy(block) |
| 158 | + bad_block_root.hashMerkleRoot += 2 |
| 159 | + bad_block_root.solve() |
| 160 | + assert chain_tip(bad_block_root.hash) not in node.getchaintips() |
| 161 | + node.submitheader(hexdata=b2x(CBlockHeader(bad_block_root).serialize())) |
| 162 | + assert chain_tip(bad_block_root.hash) in node.getchaintips() |
| 163 | + # Should still reject invalid blocks, even if we have the header: |
| 164 | + assert_equal(node.submitblock(hexdata=b2x(bad_block_root.serialize())), 'invalid') |
| 165 | + assert chain_tip(bad_block_root.hash) in node.getchaintips() |
| 166 | + # We know the header for this invalid block, so should just return early without error: |
| 167 | + node.submitheader(hexdata=b2x(CBlockHeader(bad_block_root).serialize())) |
| 168 | + assert chain_tip(bad_block_root.hash) in node.getchaintips() |
| 169 | + |
| 170 | + bad_block_lock = copy.deepcopy(block) |
| 171 | + bad_block_lock.vtx[0].nLockTime = 2**32 - 1 |
| 172 | + bad_block_lock.vtx[0].rehash() |
| 173 | + bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root() |
| 174 | + bad_block_lock.solve() |
| 175 | + assert_equal(node.submitblock(hexdata=b2x(bad_block_lock.serialize())), 'invalid') |
| 176 | + # Build a "good" block on top of the submitted bad block |
| 177 | + bad_block2 = copy.deepcopy(block) |
| 178 | + bad_block2.hashPrevBlock = bad_block_lock.sha256 |
| 179 | + bad_block2.solve() |
| 180 | + assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block2).serialize()))) |
| 181 | + |
| 182 | + # Should reject invalid header right away |
| 183 | + bad_block_time = copy.deepcopy(block) |
| 184 | + bad_block_time.nTime = 1 |
| 185 | + bad_block_time.solve() |
| 186 | + assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block_time).serialize()))) |
| 187 | + |
| 188 | + # Should ask for the block from a p2p node, if they announce the header as well: |
| 189 | + node.add_p2p_connection(P2PDataStore()) |
| 190 | + node.p2p.wait_for_getheaders(timeout=5) # Drop the first getheaders |
| 191 | + node.p2p.send_blocks_and_test(blocks=[block], rpc=node) |
| 192 | + # Must be active now: |
| 193 | + assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips() |
| 194 | + |
| 195 | + # Building a few blocks should give the same results |
| 196 | + node.generate(10) |
| 197 | + assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block_time).serialize()))) |
| 198 | + assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block2).serialize()))) |
| 199 | + node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) |
| 200 | + node.submitheader(hexdata=b2x(CBlockHeader(bad_block_root).serialize())) |
| 201 | + |
| 202 | + |
134 | 203 | if __name__ == '__main__':
|
135 | 204 | MiningTest().main()
|
0 commit comments