mining_basic.py raw
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 mining RPCs
6
7 - getmininginfo
8 - getblocktemplate
9 - submitblock
10
11 mining_template_verification.py tests getblocktemplate in proposal mode"""
12
13 import copy
14 from decimal import Decimal
15
16 from test_framework.blocktools import (
17 create_coinbase,
18 get_witness_script,
19 NORMAL_GBT_REQUEST_PARAMS,
20 TIME_GENESIS_BLOCK,
21 REGTEST_N_BITS,
22 REGTEST_TARGET,
23 nbits_str,
24 target_str,
25 )
26 from test_framework.messages import (
27 BLOCK_HEADER_SIZE,
28 CBlock,
29 CBlockHeader,
30 COIN,
31 DEFAULT_BLOCK_RESERVED_WEIGHT,
32 MAX_BLOCK_WEIGHT,
33 MAX_SEQUENCE_NONFINAL,
34 MINIMUM_BLOCK_RESERVED_WEIGHT,
35 ser_uint256,
36 WITNESS_SCALE_FACTOR,
37 )
38 from test_framework.p2p import P2PDataStore
39 from test_framework.test_framework import BitcoinTestFramework
40 from test_framework.util import (
41 assert_equal,
42 assert_greater_than,
43 assert_greater_than_or_equal,
44 assert_raises_rpc_error,
45 get_fee,
46 )
47 from test_framework.wallet import (
48 MiniWallet,
49 MiniWalletMode,
50 )
51
52
53 DIFFICULTY_ADJUSTMENT_INTERVAL = 144
54 MAX_FUTURE_BLOCK_TIME = 2 * 3600
55 MAX_TIMEWARP = 600
56 VERSIONBITS_TOP_BITS = 0x20000000
57 VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
58 DEFAULT_BLOCK_MIN_TX_FEE = 1 # default `-blockmintxfee` setting [sat/kvB]
59
60 class MiningTest(BitcoinTestFramework):
61 def set_test_params(self):
62 self.num_nodes = 3
63 self.extra_args = [
64 [],
65 [],
66 ["-fastprune", "-prune=1"]
67 ]
68 self.setup_clean_chain = True
69
70 def mine_chain(self):
71 self.log.info('Create some old blocks')
72 for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
73 self.nodes[0].setmocktime(t)
74 self.generate(self.wallet, 1, sync_fun=self.no_op)
75 mining_info = self.nodes[0].getmininginfo()
76 assert_equal(mining_info['blocks'], 200)
77 assert_equal(mining_info['currentblocktx'], 0)
78 assert_equal(mining_info['currentblockweight'], DEFAULT_BLOCK_RESERVED_WEIGHT)
79
80 self.log.info('test blockversion')
81 self.restart_node(0, extra_args=[f'-mocktime={t}', '-blockversion=1337'])
82 self.connect_nodes(0, 1)
83 assert_equal(1337, self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)['version'])
84 self.restart_node(0, extra_args=[f'-mocktime={t}'])
85 self.connect_nodes(0, 1)
86 assert_equal(VERSIONBITS_TOP_BITS + (1 << VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT), self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)['version'])
87 self.restart_node(0)
88 self.connect_nodes(0, 1)
89
90 def test_fees_and_sigops(self):
91 self.log.info("Test fees and sigops in getblocktemplate result")
92 node = self.nodes[0]
93
94 # Generate a coinbases with p2pk transactions for its sigops.
95 wallet_sigops = MiniWallet(node, mode=MiniWalletMode.RAW_P2PK)
96 self.generate(wallet_sigops, 1, sync_fun=self.no_op)
97
98 # Mature with regular coinbases to prevent interference with other tests
99 self.generate(self.wallet, 100, sync_fun=self.no_op)
100
101 # Generate three transactions that must be mined in sequence
102 #
103 # tx_a (1 sat/vbyte)
104 # |
105 # |
106 # tx_b (2 sat/vbyte)
107 # |
108 # |
109 # tx_c (3 sat/vbyte)
110 #
111 tx_a = wallet_sigops.send_self_transfer(from_node=node,
112 fee_rate=Decimal("0.00001"))
113 tx_b = wallet_sigops.send_self_transfer(from_node=node,
114 fee_rate=Decimal("0.00002"),
115 utxo_to_spend=tx_a["new_utxo"])
116 tx_c = wallet_sigops.send_self_transfer(from_node=node,
117 fee_rate=Decimal("0.00003"),
118 utxo_to_spend=tx_b["new_utxo"])
119
120 # Generate transaction without sigops. It will go first because it pays
121 # higher fees (100 sat/vbyte) and descends from a different coinbase.
122 tx_d = self.wallet.send_self_transfer(from_node=node,
123 fee_rate=Decimal("0.00100"))
124
125 block_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
126 block_template_txs = block_template['transactions']
127
128 block_template_fees = [tx['fee'] for tx in block_template_txs]
129 assert_equal(block_template_fees, [
130 tx_d["fee"] * COIN,
131 tx_a["fee"] * COIN,
132 tx_b["fee"] * COIN,
133 tx_c["fee"] * COIN
134 ])
135 # verify that coinbasevalue field is set to claim full block reward (subsidy + fees)
136 expected_block_reward = create_coinbase(
137 height=int(block_template["height"]), fees=sum(block_template_fees)).vout[0].nValue
138 assert_equal(block_template["coinbasevalue"], expected_block_reward)
139
140 block_template_sigops = [tx['sigops'] for tx in block_template_txs]
141 assert_equal(block_template_sigops, [0, 4, 4, 4])
142
143 # Clear mempool
144 self.generate(self.wallet, 1, sync_fun=self.no_op)
145
146 def test_blockmintxfee_parameter(self):
147 self.log.info("Test -blockmintxfee setting")
148 self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
149 node = self.nodes[0]
150
151 # test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB]
152 for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 5, 10, 50, 100, 500, 1000, 2500, 5000, 21000, 333333, 2500000):
153 blockmintxfee_btc_kvb = blockmintxfee_sat_kvb / Decimal(COIN)
154 if blockmintxfee_sat_kvb == DEFAULT_BLOCK_MIN_TX_FEE:
155 self.log.info(f"-> Default -blockmintxfee setting ({blockmintxfee_sat_kvb} sat/kvB)...")
156 else:
157 blockmintxfee_parameter = f"-blockmintxfee={blockmintxfee_btc_kvb:.8f}"
158 self.log.info(f"-> Test {blockmintxfee_parameter} ({blockmintxfee_sat_kvb} sat/kvB)...")
159 self.restart_node(0, extra_args=[blockmintxfee_parameter, '-minrelaytxfee=0', '-persistmempool=0'])
160 assert_equal(node.getmininginfo()['blockmintxfee'], blockmintxfee_btc_kvb)
161
162 # submit one tx with exactly the blockmintxfee rate, and one slightly below
163 tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
164 assert_equal(tx_with_min_feerate["fee"], get_fee(tx_with_min_feerate["tx"].get_vsize(), blockmintxfee_btc_kvb))
165 if blockmintxfee_sat_kvb >= 10:
166 lowerfee_btc_kvb = blockmintxfee_btc_kvb - Decimal(10)/COIN # 0.01 sat/vbyte lower
167 assert_greater_than(blockmintxfee_btc_kvb, lowerfee_btc_kvb)
168 assert_greater_than_or_equal(lowerfee_btc_kvb, 0)
169 tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb, confirmed_only=True)
170 assert_equal(tx_below_min_feerate["fee"], get_fee(tx_below_min_feerate["tx"].get_vsize(), lowerfee_btc_kvb))
171 else: # go below zero fee by using modified fees
172 tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
173 node.prioritisetransaction(tx_below_min_feerate["txid"], 0, -11)
174
175 # check that tx below specified fee-rate is neither in template nor in the actual block
176 block_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
177 block_template_txids = [tx['txid'] for tx in block_template['transactions']]
178
179 # Unless blockmintxfee is 0, the template shouldn't contain free transactions.
180 # Note that the real block assembler uses package feerates, but we didn't create dependent transactions so it's ok to use base feerate.
181 if blockmintxfee_btc_kvb > 0:
182 for txid in block_template_txids:
183 tx = node.getmempoolentry(txid)
184 assert_greater_than(tx['fees']['base'], 0)
185
186 self.generate(self.wallet, 1, sync_fun=self.no_op)
187 block = node.getblock(node.getbestblockhash(), verbosity=2)
188 block_txids = [tx['txid'] for tx in block['tx']]
189
190 assert tx_with_min_feerate['txid'] in block_template_txids
191 assert tx_with_min_feerate['txid'] in block_txids
192 assert tx_below_min_feerate['txid'] not in block_template_txids
193 assert tx_below_min_feerate['txid'] not in block_txids
194
195 # Restart node to clear mempool for the next test
196 self.restart_node(0)
197
198 def test_timewarp(self):
199 self.log.info("Test timewarp attack mitigation (BIP94)")
200 node = self.nodes[0]
201 self.restart_node(0, extra_args=['-test=bip94'])
202
203 self.log.info("Mine until the last block of the retarget period")
204 blockchain_info = self.nodes[0].getblockchaininfo()
205 n = DIFFICULTY_ADJUSTMENT_INTERVAL - blockchain_info['blocks'] % DIFFICULTY_ADJUSTMENT_INTERVAL - 2
206 t = blockchain_info['time']
207
208 for _ in range(n):
209 t += 600
210 self.nodes[0].setmocktime(t)
211 self.generate(self.wallet, 1, sync_fun=self.no_op)
212
213 self.log.info("Create block two hours in the future")
214 self.nodes[0].setmocktime(t + MAX_FUTURE_BLOCK_TIME)
215 self.generate(self.wallet, 1, sync_fun=self.no_op)
216 assert_equal(node.getblock(node.getbestblockhash())['time'], t + MAX_FUTURE_BLOCK_TIME)
217
218 self.log.info("First block template of retarget period can't use wall clock time")
219 self.nodes[0].setmocktime(t)
220 # The template will have an adjusted timestamp, which we then modify
221 tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
222 assert_greater_than_or_equal(tmpl['curtime'], t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP)
223 # mintime and curtime should match
224 assert_equal(tmpl['mintime'], tmpl['curtime'])
225
226 block = CBlock()
227 block.nVersion = tmpl["version"]
228 block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
229 block.nTime = tmpl["curtime"]
230 block.nBits = int(tmpl["bits"], 16)
231 block.nNonce = 0
232 block.vtx = [create_coinbase(height=int(tmpl["height"]))]
233 block.hashMerkleRoot = block.calc_merkle_root()
234 block.solve()
235 assert_equal(node.getblocktemplate(template_request={
236 'data': block.serialize().hex(),
237 'mode': 'proposal',
238 **NORMAL_GBT_REQUEST_PARAMS,
239 }), None)
240
241 bad_block = copy.deepcopy(block)
242 bad_block.nTime = t
243 bad_block.solve()
244 assert_raises_rpc_error(-25, 'time-timewarp-attack', lambda: node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex()))
245
246 self.log.info("Test timewarp protection boundary")
247 bad_block.nTime = t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP - 1
248 bad_block.solve()
249 assert_raises_rpc_error(-25, 'time-timewarp-attack', lambda: node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex()))
250
251 bad_block.nTime = t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP
252 bad_block.solve()
253 node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex())
254
255 def test_pruning(self):
256 self.log.info("Test that submitblock stores previously pruned block")
257 prune_node = self.nodes[2]
258 self.generate(prune_node, 400, sync_fun=self.no_op)
259 pruned_block = prune_node.getblock(prune_node.getblockhash(2), verbosity=0)
260 pruned_height = prune_node.pruneblockchain(400)
261 assert_greater_than_or_equal(pruned_height, 2)
262 pruned_blockhash = prune_node.getblockhash(2)
263
264 assert_raises_rpc_error(-1, 'Block not available (pruned data)', prune_node.getblock, pruned_blockhash)
265
266 result = prune_node.submitblock(pruned_block)
267 assert_equal(result, "inconclusive")
268 assert_equal(prune_node.getblock(pruned_blockhash, verbosity=0), pruned_block)
269
270
271 def send_transactions(self, utxos, fee_rate, target_vsize):
272 """
273 Helper to create and send transactions with the specified target virtual size and fee rate.
274 """
275 for utxo in utxos:
276 self.wallet.send_self_transfer(
277 from_node=self.nodes[0],
278 utxo_to_spend=utxo,
279 target_vsize=target_vsize,
280 fee_rate=fee_rate,
281 )
282
283 def verify_block_template(self, expected_tx_count, expected_weight):
284 """
285 Create a block template and check that it satisfies the expected transaction count and total weight.
286 """
287 response = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
288 self.log.info(f"Testing block template: contains {expected_tx_count} transactions, and total weight <= {expected_weight}")
289 assert_equal(len(response["transactions"]), expected_tx_count)
290 total_weight = sum(transaction["weight"] for transaction in response["transactions"])
291 assert_greater_than_or_equal(expected_weight, total_weight)
292
293 def test_block_max_weight(self):
294 self.log.info("Testing default and custom -blockmaxweight startup options.")
295
296 LARGE_TXS_COUNT = 10
297 LARGE_VSIZE = int(((MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT) / WITNESS_SCALE_FACTOR) / LARGE_TXS_COUNT)
298 HIGH_FEERATE = Decimal("0.0003")
299
300 # Ensure the mempool is empty
301 assert_equal(len(self.nodes[0].getrawmempool()), 0)
302
303 # Generate UTXOs and send 10 large transactions with a high fee rate
304 utxos = [self.wallet.get_utxo(confirmed_only=True) for _ in range(LARGE_TXS_COUNT + 4)] # Add 4 more utxos that will be used in the test later
305 self.send_transactions(utxos[:LARGE_TXS_COUNT], HIGH_FEERATE, LARGE_VSIZE)
306
307 # Send 2 normal transactions with a lower fee rate
308 NORMAL_VSIZE = int(2000 / WITNESS_SCALE_FACTOR)
309 NORMAL_FEERATE = Decimal("0.0001")
310 self.send_transactions(utxos[LARGE_TXS_COUNT:LARGE_TXS_COUNT + 2], NORMAL_FEERATE, NORMAL_VSIZE)
311
312 # Check that the mempool contains all transactions
313 self.log.info(f"Testing that the mempool contains {LARGE_TXS_COUNT + 2} transactions.")
314 assert_equal(len(self.nodes[0].getrawmempool()), LARGE_TXS_COUNT + 2)
315
316 # Verify the block template includes only the 10 high-fee transactions
317 self.log.info("Testing that the block template includes only the 10 large transactions.")
318 self.verify_block_template(
319 expected_tx_count=LARGE_TXS_COUNT,
320 expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT,
321 )
322
323 # Test block template creation with custom -blockmaxweight
324 custom_block_weight = MAX_BLOCK_WEIGHT - 2000
325 # Reducing the weight by 2000 units will prevent 1 large transaction from fitting into the block.
326 self.restart_node(0, extra_args=[f"-blockmaxweight={custom_block_weight}"])
327
328 self.log.info("Testing the block template with custom -blockmaxweight to include 9 large and 2 normal transactions.")
329 self.verify_block_template(
330 expected_tx_count=11,
331 expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT - 2000,
332 )
333
334 # Ensure the block weight does not exceed the maximum
335 self.log.info(f"Testing that the block weight will never exceed {MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT}.")
336 self.restart_node(0, extra_args=[f"-blockmaxweight={MAX_BLOCK_WEIGHT}"])
337 self.log.info("Sending 2 additional normal transactions to fill the mempool to the maximum block weight.")
338 self.send_transactions(utxos[LARGE_TXS_COUNT + 2:], NORMAL_FEERATE, NORMAL_VSIZE)
339 self.log.info(f"Testing that the mempool's weight matches the maximum block weight: {MAX_BLOCK_WEIGHT}.")
340 assert_equal(self.nodes[0].getmempoolinfo()['bytes'] * WITNESS_SCALE_FACTOR, MAX_BLOCK_WEIGHT)
341
342 self.log.info("Testing that the block template includes only 10 transactions and cannot reach full block weight.")
343 self.verify_block_template(
344 expected_tx_count=LARGE_TXS_COUNT,
345 expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT,
346 )
347
348 self.log.info("Test -blockreservedweight startup option.")
349 # Lowering the -blockreservedweight by 4000 will allow for two more transactions.
350 self.restart_node(0, extra_args=["-blockreservedweight=4000"])
351 self.verify_block_template(
352 expected_tx_count=12,
353 expected_weight=MAX_BLOCK_WEIGHT - 4000,
354 )
355
356 self.log.info("Test that node will fail to start when user provide invalid -blockreservedweight")
357 self.stop_node(0)
358 self.nodes[0].assert_start_raises_init_error(
359 extra_args=[f"-blockreservedweight={MAX_BLOCK_WEIGHT + 1}"],
360 expected_msg=f"Error: -blockreservedweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
361 )
362
363 self.log.info(f"Test that node will fail to start when user provide -blockreservedweight below {MINIMUM_BLOCK_RESERVED_WEIGHT}")
364 self.stop_node(0)
365 self.nodes[0].assert_start_raises_init_error(
366 extra_args=[f"-blockreservedweight={MINIMUM_BLOCK_RESERVED_WEIGHT - 1}"],
367 expected_msg=f"Error: -blockreservedweight ({MINIMUM_BLOCK_RESERVED_WEIGHT - 1}) is lower than minimum safety value of ({MINIMUM_BLOCK_RESERVED_WEIGHT})",
368 )
369
370 self.log.info("Test that node will fail to start when user provide invalid -blockmaxweight")
371 self.stop_node(0)
372 self.nodes[0].assert_start_raises_init_error(
373 extra_args=[f"-blockmaxweight={MAX_BLOCK_WEIGHT + 1}"],
374 expected_msg=f"Error: -blockmaxweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
375 )
376
377 self.log.info("Test that node will fail to start when -blockmaxweight is lower than -blockreservedweight")
378 self.stop_node(0)
379 self.nodes[0].assert_start_raises_init_error(
380 extra_args=[f"-blockmaxweight={DEFAULT_BLOCK_RESERVED_WEIGHT - 1}"],
381 expected_msg=f"Error: -blockreservedweight ({DEFAULT_BLOCK_RESERVED_WEIGHT}) exceeds -blockmaxweight ({DEFAULT_BLOCK_RESERVED_WEIGHT - 1})",
382 )
383
384 def test_height_in_locktime(self):
385 self.log.info("Sanity check generated blocks have their coinbase timelocked to their height.")
386 self.generate(self.nodes[0], 1, sync_fun=self.no_op)
387 block = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 2)
388 assert_equal(block["tx"][0]["locktime"], block["height"] - 1)
389 assert_equal(block["tx"][0]["vin"][0]["sequence"], MAX_SEQUENCE_NONFINAL)
390
391 def run_test(self):
392 node = self.nodes[0]
393 self.wallet = MiniWallet(node)
394 self.mine_chain()
395
396 self.log.info('getmininginfo')
397 mining_info = node.getmininginfo()
398 assert_equal(mining_info['blocks'], 200)
399 assert_equal(mining_info['chain'], self.chain)
400 assert 'currentblocktx' not in mining_info
401 assert 'currentblockweight' not in mining_info
402 assert_equal(mining_info['bits'], nbits_str(REGTEST_N_BITS))
403 assert_equal(mining_info['target'], target_str(REGTEST_TARGET))
404 # We don't care about precision, round to avoid mismatch under Valgrind:
405 assert_equal(round(mining_info['difficulty'], 10), Decimal('0.0000000005'))
406 assert_equal(mining_info['next']['height'], 201)
407 assert_equal(mining_info['next']['target'], target_str(REGTEST_TARGET))
408 assert_equal(mining_info['next']['bits'], nbits_str(REGTEST_N_BITS))
409 assert_equal(round(mining_info['next']['difficulty'], 10), Decimal('0.0000000005'))
410 assert_equal(round(mining_info['networkhashps'], 5), Decimal('0.00333'))
411 assert_equal(mining_info['pooledtx'], 0)
412
413 self.log.info("getblocktemplate: Test default witness commitment")
414 txid = int(self.wallet.send_self_transfer(from_node=node)['wtxid'], 16)
415 tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
416
417 # Check that default_witness_commitment is present.
418 assert 'default_witness_commitment' in tmpl
419 witness_commitment = tmpl['default_witness_commitment']
420
421 # Check that default_witness_commitment is correct.
422 witness_root = CBlock.get_merkle_root([ser_uint256(0),
423 ser_uint256(txid)])
424 script = get_witness_script(witness_root, 0)
425 assert_equal(witness_commitment, script.hex())
426
427 # Mine a block to leave initial block download and clear the mempool
428 self.generatetoaddress(node, 1, node.get_deterministic_priv_key().address)
429 tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
430 self.log.info("getblocktemplate: Test capability advertised")
431 assert 'proposal' in tmpl['capabilities']
432 assert 'coinbasetxn' not in tmpl
433
434 next_height = int(tmpl["height"])
435 coinbase_tx = create_coinbase(height=next_height)
436 # sequence numbers must not be max for nLockTime to have effect
437 coinbase_tx.vin[0].nSequence = 2**32 - 2
438
439 block = CBlock()
440 block.nVersion = tmpl["version"]
441 block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
442 block.nTime = tmpl["curtime"]
443 block.nBits = int(tmpl["bits"], 16)
444 block.nNonce = 0
445 block.vtx = [coinbase_tx]
446 block.hashMerkleRoot = block.calc_merkle_root()
447
448 self.log.info("getblocktemplate: segwit rule must be set")
449 assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate, {})
450
451 self.log.info("getblocktemplate: result should set the right rules")
452 assert_equal(['csv', '!segwit', 'taproot'], self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)['rules'])
453
454 self.log.info("submitblock: Test block decode failure")
455 assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex())
456
457 self.log.info("submitblock: Test empty block")
458 assert_equal('high-hash', node.submitblock(hexdata=CBlock().serialize().hex()))
459
460 self.log.info('submitheader tests')
461 assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE))
462 assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE-2)))
463
464 missing_ancestor_block = copy.deepcopy(block)
465 missing_ancestor_block.hashPrevBlock = 123
466 assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata=super(CBlock, missing_ancestor_block).serialize().hex()))
467
468 block.nTime += 1
469 block.solve()
470
471 def chain_tip(b_hash, *, status='headers-only', branchlen=1):
472 return {'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status}
473
474 assert chain_tip(block.hash_hex) not in node.getchaintips()
475 node.submitheader(hexdata=block.serialize().hex())
476 assert chain_tip(block.hash_hex) in node.getchaintips()
477 node.submitheader(hexdata=CBlockHeader(block).serialize().hex()) # Noop
478 assert chain_tip(block.hash_hex) in node.getchaintips()
479
480 bad_block_root = copy.deepcopy(block)
481 bad_block_root.hashMerkleRoot += 2
482 bad_block_root.solve()
483 assert chain_tip(bad_block_root.hash_hex) not in node.getchaintips()
484 node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
485 assert chain_tip(bad_block_root.hash_hex) in node.getchaintips()
486 # Should still reject invalid blocks, even if we have the header:
487 assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot')
488 assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot')
489 assert chain_tip(bad_block_root.hash_hex) in node.getchaintips()
490 # We know the header for this invalid block, so should just return early without error:
491 node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
492 assert chain_tip(bad_block_root.hash_hex) in node.getchaintips()
493
494 bad_block_lock = copy.deepcopy(block)
495 bad_block_lock.vtx[0].nLockTime = 2**32 - 1
496 bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root()
497 bad_block_lock.solve()
498 assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'bad-txns-nonfinal')
499 assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'duplicate-invalid')
500 # Build a "good" block on top of the submitted bad block
501 bad_block2 = copy.deepcopy(block)
502 bad_block2.hashPrevBlock = bad_block_lock.hash_int
503 bad_block2.solve()
504 assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex()))
505
506 # Should reject invalid header right away
507 bad_block_time = copy.deepcopy(block)
508 bad_block_time.nTime = 1
509 bad_block_time.solve()
510 assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
511
512 # Should ask for the block from a p2p node, if they announce the header as well:
513 peer = node.add_p2p_connection(P2PDataStore())
514 peer.wait_for_getheaders(timeout=5, block_hash=block.hashPrevBlock)
515 peer.send_blocks_and_test(blocks=[block], node=node)
516 # Must be active now:
517 assert chain_tip(block.hash_hex, status='active', branchlen=0) in node.getchaintips()
518
519 # Building a few blocks should give the same results
520 self.generatetoaddress(node, 10, node.get_deterministic_priv_key().address)
521 assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
522 assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex()))
523 node.submitheader(hexdata=CBlockHeader(block).serialize().hex())
524 node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
525 assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
526
527 self.test_fees_and_sigops()
528 self.test_blockmintxfee_parameter()
529 self.test_block_max_weight()
530 self.test_timewarp()
531 self.test_pruning()
532 self.test_height_in_locktime()
533
534
535 if __name__ == '__main__':
536 MiningTest(__file__).main()
537