mining_basic.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2014-2022 The Limenka 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 proposal mode
9 - submitblock"""
10
11 import copy
12 from decimal import Decimal
13
14 from test_framework.blocktools import (
15 create_coinbase,
16 get_witness_script,
17 NORMAL_GBT_REQUEST_PARAMS,
18 TIME_GENESIS_BLOCK,
19 REGTEST_N_BITS,
20 REGTEST_TARGET,
21 nbits_str,
22 target_str,
23 )
24 from test_framework.messages import (
25 BLOCK_HEADER_SIZE,
26 CBlock,
27 CBlockHeader,
28 COIN,
29 DEFAULT_BLOCK_RESERVED_WEIGHT,
30 MAX_BLOCK_WEIGHT,
31 MINIMUM_BLOCK_RESERVED_WEIGHT,
32 ser_uint256,
33 WITNESS_SCALE_FACTOR
34 )
35 from test_framework.p2p import P2PDataStore
36 from test_framework.test_framework import LimenkaTestFramework
37 from test_framework.util import (
38 assert_equal,
39 assert_greater_than,
40 assert_greater_than_or_equal,
41 assert_raises_rpc_error,
42 get_fee,
43 )
44 from test_framework.wallet import MiniWallet, MiniWalletMode
45
46
47 DIFFICULTY_ADJUSTMENT_INTERVAL = 144
48 MAX_FUTURE_BLOCK_TIME = 2 * 3600
49 MAX_TIMEWARP = 600
50 ASSUMED_BLOCK_OVERHEAD_SIZE = 1000
51 ASSUMED_BLOCK_OVERHEAD_WEIGHT = ASSUMED_BLOCK_OVERHEAD_SIZE * WITNESS_SCALE_FACTOR
52 VERSIONBITS_TOP_BITS = 0x20000000
53 VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
54 DEFAULT_BLOCK_MIN_TX_FEE = 1 # default `-blockmintxfee` setting [sat/kvB]
55 MAX_SIGOP_COST = 80000
56
57
58 def assert_template(node, block, expect, rehash=True):
59 if rehash:
60 block.hashMerkleRoot = block.calc_merkle_root()
61 rsp = node.getblocktemplate(template_request={
62 'data': block.serialize().hex(),
63 'mode': 'proposal',
64 'rules': ['segwit'],
65 })
66 assert_equal(rsp, expect)
67
68
69 class MiningTest(LimenkaTestFramework):
70 def set_test_params(self):
71 self.num_nodes = 3
72 self.extra_args = [
73 [],
74 [],
75 ["-fastprune", "-prune=1"]
76 ]
77 self.setup_clean_chain = True
78 self.supports_cli = False
79
80 def mine_chain(self):
81 self.log.info('Create some old blocks')
82 for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
83 self.nodes[0].setmocktime(t)
84 self.generate(self.wallet, 1, sync_fun=self.no_op)
85 mining_info = self.nodes[0].getmininginfo()
86 assert_equal(mining_info['blocks'], 200)
87 assert_equal(mining_info['currentblocktx'], 0)
88 assert_equal(mining_info['currentblockweight'], DEFAULT_BLOCK_RESERVED_WEIGHT)
89 assert 'currentblocksize' not in mining_info
90
91 self.log.info('test blockversion')
92 self.restart_node(0, extra_args=[f'-mocktime={t}', '-blockversion=1337'])
93 self.connect_nodes(0, 1)
94 assert_equal(1337, self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)['version'])
95 self.restart_node(0, extra_args=[f'-mocktime={t}'])
96 self.connect_nodes(0, 1)
97 assert_equal(VERSIONBITS_TOP_BITS + (1 << VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT), self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)['version'])
98 self.restart_node(0)
99 self.connect_nodes(0, 1)
100
101 def test_blockmintxfee_parameter(self, *, use_rpc=False):
102 if not use_rpc:
103 self.log.info("Test -blockmintxfee setting")
104 self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
105 node = self.nodes[0]
106
107 # test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB]
108 for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 5, 10, 50, 100, 500, 1000, 2500, 5000, 21000, 333333, 2500000):
109 blockmintxfee_btc_kvb = blockmintxfee_sat_kvb / Decimal(COIN)
110 if use_rpc:
111 blockmintxfee_sat_vb = blockmintxfee_sat_kvb / 1000
112 self.log.info(f"-> Test RPC param minfeerate={blockmintxfee_sat_vb} ({blockmintxfee_sat_kvb} sat/kvB)...")
113 self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
114 self.wallet.rescan_utxos() # to avoid spending outputs of txs that are not in mempool anymore after restart
115 elif blockmintxfee_sat_kvb == DEFAULT_BLOCK_MIN_TX_FEE:
116 self.log.info(f"-> Default -blockmintxfee setting ({blockmintxfee_sat_kvb} sat/kvB)...")
117 else:
118 blockmintxfee_parameter = f"-blockmintxfee={blockmintxfee_btc_kvb:.8f}"
119 self.log.info(f"-> Test {blockmintxfee_parameter} ({blockmintxfee_sat_kvb} sat/kvB)...")
120 self.restart_node(0, extra_args=[blockmintxfee_parameter, '-minrelaytxfee=0', '-persistmempool=0'])
121 self.wallet.rescan_utxos() # to avoid spending outputs of txs that are not in mempool anymore after restart
122
123 # submit one tx with exactly the blockmintxfee rate, and one slightly below
124 tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
125 assert_equal(tx_with_min_feerate["fee"], get_fee(tx_with_min_feerate["tx"].get_vsize(), blockmintxfee_btc_kvb))
126 if blockmintxfee_sat_kvb > 5:
127 lowerfee_btc_kvb = blockmintxfee_btc_kvb - Decimal(10)/COIN # 0.01 sat/vbyte lower
128 tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb, confirmed_only=True)
129 assert_equal(tx_below_min_feerate["fee"], get_fee(tx_below_min_feerate["tx"].get_vsize(), lowerfee_btc_kvb))
130 else: # go below zero fee by using modified fees
131 tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
132 node.prioritisetransaction(tx_below_min_feerate["txid"], 0, -1)
133
134 # check that tx below specified fee-rate is neither in template nor in the actual block
135 req = NORMAL_GBT_REQUEST_PARAMS
136 if use_rpc:
137 req = copy.deepcopy(req)
138 req['minfeerate'] = blockmintxfee_sat_vb
139 block_template = node.getblocktemplate(req)
140 block_template_txids = [tx['txid'] for tx in block_template['transactions']]
141
142 # Unless blockmintxfee is 0, the template shouldn't contain free transactions.
143 # Note that the real block assembler uses package feerates, but we didn't create dependent transactions so it's ok to use base feerate.
144 if blockmintxfee_btc_kvb > 0:
145 for txid in block_template_txids:
146 tx = node.getmempoolentry(txid)
147 assert_greater_than(tx['fees']['base'], 0)
148
149 self.generate(self.wallet, 1, sync_fun=self.no_op)
150 block = node.getblock(node.getbestblockhash(), verbosity=2)
151 block_txids = [tx['txid'] for tx in block['tx']]
152
153 assert tx_with_min_feerate['txid'] in block_template_txids
154 assert tx_below_min_feerate['txid'] not in block_template_txids
155
156 if not use_rpc:
157 assert tx_with_min_feerate['txid'] in block_txids
158 assert tx_below_min_feerate['txid'] not in block_txids
159
160 def test_rpc_params(self):
161 self.log.info("Test minfeerate RPC param")
162 self.test_blockmintxfee_parameter(use_rpc=True)
163
164 node = self.nodes[0]
165 wallet = MiniWallet(node, mode=MiniWalletMode.RAW_P2PK)
166 self.wallet.send_to(from_node=node, scriptPubKey=wallet.get_output_script(), amount=40 * COIN)
167 self.wallet.send_to(from_node=node, scriptPubKey=wallet.get_output_script(), amount=40 * COIN)
168 self.generate(wallet, 1, sync_fun=self.no_op)
169
170 self.log.info("Preparing mempool")
171 self.restart_node(0, extra_args=['-limitancestorcount=1000', '-limitancestorsize=7000', '-limitdescendantcount=1000', '-limitdescendantsize=7000'])
172
173 # Fill the mempool
174 target_mempool_size = 200000
175 last_tx_size = 0
176 utxo = wallet.get_utxo() # save for small coins
177 while node.getmempoolinfo()['bytes'] < target_mempool_size - last_tx_size:
178 tx = wallet.send_self_transfer_multi(
179 from_node=self.nodes[0],
180 num_outputs=1000,
181 )
182 last_tx_size = len(tx['hex']) / 2
183 while node.getmempoolinfo()['bytes'] < 200000:
184 tx = wallet.send_self_transfer_multi(
185 utxos_to_spend=[utxo],
186 from_node=node,
187 num_outputs=1,
188 )
189 utxo = tx['new_utxos'][0]
190
191 self.log.info("Test blockmaxsize RPC param")
192 req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
193 normal_size = ASSUMED_BLOCK_OVERHEAD_SIZE + (sum(len(tx['data']) for tx in self.nodes[0].getblocktemplate(req)['transactions']) // 2)
194 last_size = ASSUMED_BLOCK_OVERHEAD_SIZE
195 for target_size in (50000, 100000, 150000):
196 self.log.info(f"-> Test RPC param blockmaxsize={target_size}...")
197 req['blockmaxsize'] = target_size
198 tmpl = self.nodes[0].getblocktemplate(req)
199 blk_size = ASSUMED_BLOCK_OVERHEAD_SIZE + (sum(len(tx['data']) for tx in tmpl['transactions']) // 2)
200 assert blk_size < normal_size
201 assert blk_size < target_size
202 assert blk_size > last_size
203 last_size = blk_size
204
205 self.log.info("Test blockreservedsize RPC param")
206 req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
207 req['blockmaxsize'] = 150000
208 normal_size = (sum(len(tx['data']) for tx in self.nodes[0].getblocktemplate(req)['transactions']) // 2)
209 last_size = 0
210 for reserved_size in (100000, 10000, 100):
211 self.log.info(f"-> Test RPC param blockreservedsize={reserved_size}...")
212 req['blockreservedsize'] = reserved_size
213 tmpl = self.nodes[0].getblocktemplate(req)
214 blk_size = (sum(len(tx['data']) for tx in tmpl['transactions']) // 2)
215 assert blk_size < normal_size if reserved_size > 1000 else blk_size > normal_size
216 assert blk_size + reserved_size <= req['blockmaxsize']
217 assert blk_size > last_size
218 last_size = blk_size
219
220 self.log.info("Test blockmaxweight RPC param")
221 req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
222 normal_weight = ASSUMED_BLOCK_OVERHEAD_WEIGHT + sum(tx['weight'] for tx in self.nodes[0].getblocktemplate(req)['transactions'])
223 last_weight = ASSUMED_BLOCK_OVERHEAD_WEIGHT
224 for target_weight in (200000, 400000, 600000):
225 self.log.info(f"-> Test RPC param blockmaxweight={target_weight}...")
226 req['blockmaxweight'] = target_weight
227 tmpl = self.nodes[0].getblocktemplate(req)
228 blk_weight = ASSUMED_BLOCK_OVERHEAD_WEIGHT + sum(tx['weight'] for tx in tmpl['transactions'])
229 assert blk_weight < normal_weight
230 assert blk_weight < target_weight
231 assert blk_weight > last_weight
232 last_weight = blk_weight
233
234 self.log.info("Test blockreservedweight RPC param")
235 req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
236 req['blockmaxweight'] = 600000
237 normal_weight = sum(tx['weight'] for tx in self.nodes[0].getblocktemplate(req)['transactions'])
238 last_weight = 0
239 for reserved_weight in (400000, 40000, MINIMUM_BLOCK_RESERVED_WEIGHT):
240 self.log.info(f"-> Test RPC param blockreservedweight={reserved_weight}...")
241 req['blockreservedweight'] = reserved_weight
242 tmpl = self.nodes[0].getblocktemplate(req)
243 blk_weight = sum(tx['weight'] for tx in tmpl['transactions'])
244 assert blk_weight < normal_weight if reserved_weight > 4000 else blk_weight > normal_weight
245 assert blk_weight + reserved_weight <= req['blockmaxweight']
246 assert blk_weight > last_weight
247 last_weight = blk_weight
248
249 self.log.info("Test blockreservedsigops RPC param")
250 req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
251 normal_sigops = sum(tx['sigops'] for tx in self.nodes[0].getblocktemplate(req)['transactions'])
252 assert normal_sigops
253 last_sigops = 0
254 baseline_sigops = MAX_SIGOP_COST - normal_sigops
255 for reserved_sigops in (800, 400, 100):
256 reserved_sigops += baseline_sigops
257 self.log.info(f"-> Test RPC param blockreservedsigops={reserved_sigops}...")
258 req['blockreservedsigops'] = reserved_sigops
259 tmpl = self.nodes[0].getblocktemplate(req)
260 blk_sigops = sum(tx['sigops'] for tx in tmpl['transactions'])
261 assert blk_sigops < normal_sigops if reserved_sigops > 400 else blk_sigops > normal_sigops
262 assert blk_sigops + reserved_sigops <= MAX_SIGOP_COST
263 assert blk_sigops > last_sigops
264 last_sigops = blk_sigops
265
266 def test_timewarp(self):
267 self.log.info("Test timewarp attack mitigation (BIP94)")
268 node = self.nodes[0]
269 self.restart_node(0, extra_args=['-test=bip94'])
270
271 self.log.info("Mine until the last block of the retarget period")
272 blockchain_info = self.nodes[0].getblockchaininfo()
273 n = DIFFICULTY_ADJUSTMENT_INTERVAL - blockchain_info['blocks'] % DIFFICULTY_ADJUSTMENT_INTERVAL - 2
274 t = blockchain_info['time']
275
276 for _ in range(n):
277 t += 600
278 self.nodes[0].setmocktime(t)
279 self.generate(self.wallet, 1, sync_fun=self.no_op)
280
281 self.log.info("Create block two hours in the future")
282 self.nodes[0].setmocktime(t + MAX_FUTURE_BLOCK_TIME)
283 self.generate(self.wallet, 1, sync_fun=self.no_op)
284 assert_equal(node.getblock(node.getbestblockhash())['time'], t + MAX_FUTURE_BLOCK_TIME)
285
286 self.log.info("First block template of retarget period can't use wall clock time")
287 self.nodes[0].setmocktime(t)
288 # The template will have an adjusted timestamp, which we then modify
289 tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
290 assert_greater_than_or_equal(tmpl['curtime'], t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP)
291 # mintime and curtime should match
292 assert_equal(tmpl['mintime'], tmpl['curtime'])
293
294 block = CBlock()
295 block.nVersion = tmpl["version"]
296 block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
297 block.nTime = tmpl["curtime"]
298 block.nBits = int(tmpl["bits"], 16)
299 block.nNonce = 0
300 block.vtx = [create_coinbase(height=int(tmpl["height"]))]
301 block.solve()
302 assert_template(node, block, None)
303
304 bad_block = copy.deepcopy(block)
305 bad_block.nTime = t
306 bad_block.solve()
307 assert_raises_rpc_error(-25, 'time-timewarp-attack', lambda: node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex()))
308
309 self.log.info("Test timewarp protection boundary")
310 bad_block.nTime = t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP - 1
311 bad_block.solve()
312 assert_raises_rpc_error(-25, 'time-timewarp-attack', lambda: node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex()))
313
314 bad_block.nTime = t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP
315 bad_block.solve()
316 node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex())
317
318 def test_pruning(self):
319 self.log.info("Test that submitblock stores previously pruned block")
320 prune_node = self.nodes[2]
321 self.generate(prune_node, 400, sync_fun=self.no_op)
322 pruned_block = prune_node.getblock(prune_node.getblockhash(2), verbosity=0)
323 pruned_height = prune_node.pruneblockchain(400)
324 assert_greater_than_or_equal(pruned_height, 2)
325 pruned_blockhash = prune_node.getblockhash(2)
326
327 assert_raises_rpc_error(-1, 'Block not available (pruned data)', prune_node.getblock, pruned_blockhash)
328
329 result = prune_node.submitblock(pruned_block)
330 assert_equal(result, "inconclusive")
331 assert_equal(prune_node.getblock(pruned_blockhash, verbosity=0), pruned_block)
332
333
334 def send_transactions(self, utxos, fee_rate, target_vsize):
335 """
336 Helper to create and send transactions with the specified target virtual size and fee rate.
337 """
338 for utxo in utxos:
339 self.wallet.send_self_transfer(
340 from_node=self.nodes[0],
341 utxo_to_spend=utxo,
342 target_vsize=target_vsize,
343 fee_rate=fee_rate,
344 )
345
346 def verify_block_template(self, expected_tx_count, expected_weight):
347 """
348 Create a block template and check that it satisfies the expected transaction count and total weight.
349 """
350 response = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
351 self.log.info(f"Testing block template: contains {expected_tx_count} transactions, and total weight <= {expected_weight}")
352 assert_equal(len(response["transactions"]), expected_tx_count)
353 total_weight = sum(transaction["weight"] for transaction in response["transactions"])
354 assert_greater_than_or_equal(expected_weight, total_weight)
355
356 def test_block_max_weight(self):
357 self.log.info("Testing default and custom -blockmaxweight startup options.")
358
359 # Restart the node to allow large transactions
360 LARGE_TXS_COUNT = 10
361 LARGE_VSIZE = int(((MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT) / WITNESS_SCALE_FACTOR) / LARGE_TXS_COUNT)
362 HIGH_FEERATE = Decimal("0.0003")
363 self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}"])
364
365 # Ensure the mempool is empty
366 assert_equal(len(self.nodes[0].getrawmempool()), 0)
367
368 # Generate UTXOs and send 10 large transactions with a high fee rate
369 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
370 self.send_transactions(utxos[:LARGE_TXS_COUNT], HIGH_FEERATE, LARGE_VSIZE)
371
372 # Send 2 normal transactions with a lower fee rate
373 NORMAL_VSIZE = int(2000 / WITNESS_SCALE_FACTOR)
374 NORMAL_FEERATE = Decimal("0.0001")
375 self.send_transactions(utxos[LARGE_TXS_COUNT:LARGE_TXS_COUNT + 2], NORMAL_FEERATE, NORMAL_VSIZE)
376
377 # Check that the mempool contains all transactions
378 self.log.info(f"Testing that the mempool contains {LARGE_TXS_COUNT + 2} transactions.")
379 assert_equal(len(self.nodes[0].getrawmempool()), LARGE_TXS_COUNT + 2)
380
381 # Verify the block template includes only the 10 high-fee transactions
382 self.log.info("Testing that the block template includes only the 10 large transactions.")
383 self.verify_block_template(
384 expected_tx_count=LARGE_TXS_COUNT,
385 expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT,
386 )
387
388 # Test block template creation with custom -blockmaxweight
389 custom_block_weight = MAX_BLOCK_WEIGHT - 2000
390 # Reducing the weight by 2000 units will prevent 1 large transaction from fitting into the block.
391 self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", f"-blockmaxweight={custom_block_weight}"])
392
393 self.log.info("Testing the block template with custom -blockmaxweight to include 9 large and 2 normal transactions.")
394 self.verify_block_template(
395 expected_tx_count=11,
396 expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT - 2000,
397 )
398
399 # Ensure the block weight does not exceed the maximum
400 self.log.info(f"Testing that the block weight will never exceed {MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT}.")
401 self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", f"-blockmaxweight={MAX_BLOCK_WEIGHT}"])
402 self.log.info("Sending 2 additional normal transactions to fill the mempool to the maximum block weight.")
403 self.send_transactions(utxos[LARGE_TXS_COUNT + 2:], NORMAL_FEERATE, NORMAL_VSIZE)
404 self.log.info(f"Testing that the mempool's weight matches the maximum block weight: {MAX_BLOCK_WEIGHT}.")
405 assert_equal(self.nodes[0].getmempoolinfo()['bytes'] * WITNESS_SCALE_FACTOR, MAX_BLOCK_WEIGHT)
406
407 self.log.info("Testing that the block template includes only 10 transactions and cannot reach full block weight.")
408 self.verify_block_template(
409 expected_tx_count=LARGE_TXS_COUNT,
410 expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT,
411 )
412
413 self.log.info("Test -blockreservedweight startup option.")
414 # Lowering the -blockreservedweight by 4000 will allow for two more transactions.
415 self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", "-blockreservedweight=4000"])
416 self.verify_block_template(
417 expected_tx_count=12,
418 expected_weight=MAX_BLOCK_WEIGHT - 4000,
419 )
420
421 self.log.info("Test that node will fail to start when user provide invalid -blockreservedweight")
422 self.stop_node(0)
423 self.nodes[0].assert_start_raises_init_error(
424 extra_args=[f"-blockreservedweight={MAX_BLOCK_WEIGHT + 1}"],
425 expected_msg=f"Error: Specified -blockreservedweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
426 )
427
428 self.log.info(f"Test that node will fail to start when user provide -blockreservedweight below {MINIMUM_BLOCK_RESERVED_WEIGHT}")
429 self.stop_node(0)
430 self.nodes[0].assert_start_raises_init_error(
431 extra_args=[f"-blockreservedweight={MINIMUM_BLOCK_RESERVED_WEIGHT - 1}"],
432 expected_msg=f"Error: Specified -blockreservedweight ({MINIMUM_BLOCK_RESERVED_WEIGHT - 1}) is lower than minimum safety value of ({MINIMUM_BLOCK_RESERVED_WEIGHT})",
433 )
434
435 self.log.info("Test that node will fail to start when user provide invalid -blockmaxweight")
436 self.stop_node(0)
437 self.nodes[0].assert_start_raises_init_error(
438 extra_args=[f"-blockmaxweight={MAX_BLOCK_WEIGHT + 1}"],
439 expected_msg=f"Error: Specified -blockmaxweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
440 )
441
442
443 def run_test(self):
444 node = self.nodes[0]
445 self.wallet = MiniWallet(node)
446 self.mine_chain()
447
448 def assert_submitblock(block, result_str_1, result_str_2=None):
449 block.solve()
450 result_str_2 = result_str_2 or 'duplicate-invalid'
451 assert_equal(result_str_1, node.submitblock(hexdata=block.serialize().hex()))
452 assert_equal(result_str_2, node.submitblock(hexdata=block.serialize().hex()))
453
454 self.log.info('getmininginfo')
455 mining_info = node.getmininginfo()
456 assert_equal(mining_info['blocks'], 200)
457 assert_equal(mining_info['chain'], self.chain)
458 assert 'currentblocktx' not in mining_info
459 assert 'currentblockweight' not in mining_info
460 assert 'currentblocksize' not in mining_info
461 assert_equal(mining_info['bits'], nbits_str(REGTEST_N_BITS))
462 assert_equal(mining_info['target'], target_str(REGTEST_TARGET))
463 assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10'))
464 assert_equal(mining_info['next'], {
465 'height': 201,
466 'target': target_str(REGTEST_TARGET),
467 'bits': nbits_str(REGTEST_N_BITS),
468 'difficulty': Decimal('4.656542373906925E-10')
469 })
470 assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334'))
471 assert_equal(mining_info['pooledtx'], 0)
472
473 self.log.info("getblocktemplate: Test default witness commitment")
474 txid = int(self.wallet.send_self_transfer(from_node=node)['wtxid'], 16)
475 tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
476
477 # Check that default_witness_commitment is present.
478 assert 'default_witness_commitment' in tmpl
479 witness_commitment = tmpl['default_witness_commitment']
480
481 # Check that default_witness_commitment is correct.
482 witness_root = CBlock.get_merkle_root([ser_uint256(0),
483 ser_uint256(txid)])
484 script = get_witness_script(witness_root, 0)
485 assert_equal(witness_commitment, script.hex())
486
487 # Mine a block to leave initial block download and clear the mempool
488 self.generatetoaddress(node, 1, node.get_deterministic_priv_key().address)
489 tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
490 self.log.info("getblocktemplate: Test capability advertised")
491 assert 'proposal' in tmpl['capabilities']
492 assert 'coinbasetxn' not in tmpl
493
494 next_height = int(tmpl["height"])
495 coinbase_tx = create_coinbase(height=next_height)
496 # sequence numbers must not be max for nLockTime to have effect
497 coinbase_tx.vin[0].nSequence = 2**32 - 2
498 coinbase_tx.rehash()
499
500 block = CBlock()
501 block.nVersion = tmpl["version"]
502 block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
503 block.nTime = tmpl["curtime"]
504 block.nBits = int(tmpl["bits"], 16)
505 block.nNonce = 0
506 block.vtx = [coinbase_tx]
507
508 self.log.info("getblocktemplate: segwit rule must be set")
509 assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate, {})
510
511 self.log.info("getblocktemplate: Test valid block")
512 assert_template(node, block, None)
513
514 self.log.info("submitblock: Test block decode failure")
515 assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex())
516
517 self.log.info("getblocktemplate: Test bad input hash for coinbase transaction")
518 bad_block = copy.deepcopy(block)
519 bad_block.vtx[0].vin[0].prevout.hash += 1
520 bad_block.vtx[0].rehash()
521 assert_template(node, bad_block, 'bad-cb-missing')
522
523 self.log.info("submitblock: Test bad input hash for coinbase transaction")
524 bad_block.solve()
525 assert_equal("bad-cb-missing", node.submitblock(hexdata=bad_block.serialize().hex()))
526
527 self.log.info("submitblock: Test block with no transactions")
528 no_tx_block = copy.deepcopy(block)
529 no_tx_block.vtx.clear()
530 no_tx_block.hashMerkleRoot = 0
531 no_tx_block.solve()
532 assert_equal("bad-blk-length", node.submitblock(hexdata=no_tx_block.serialize().hex()))
533
534 self.log.info("submitblock: Test empty block")
535 assert_equal('high-hash', node.submitblock(hexdata=CBlock().serialize().hex()))
536
537 self.log.info("getblocktemplate: Test truncated final transaction")
538 assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
539 'data': block.serialize()[:-1].hex(),
540 'mode': 'proposal',
541 'rules': ['segwit'],
542 })
543
544 self.log.info("getblocktemplate: Test duplicate transaction")
545 bad_block = copy.deepcopy(block)
546 bad_block.vtx.append(bad_block.vtx[0])
547 assert_template(node, bad_block, 'bad-txns-duplicate')
548 assert_submitblock(bad_block, 'bad-txns-duplicate', 'bad-txns-duplicate')
549
550 self.log.info("getblocktemplate: Test invalid transaction")
551 bad_block = copy.deepcopy(block)
552 bad_tx = copy.deepcopy(bad_block.vtx[0])
553 bad_tx.vin[0].prevout.hash = 255
554 bad_tx.rehash()
555 bad_block.vtx.append(bad_tx)
556 assert_template(node, bad_block, 'bad-txns-inputs-missingorspent')
557 assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent')
558
559 self.log.info("getblocktemplate: Test nonfinal transaction")
560 bad_block = copy.deepcopy(block)
561 bad_block.vtx[0].nLockTime = 2**32 - 1
562 bad_block.vtx[0].rehash()
563 assert_template(node, bad_block, 'bad-txns-nonfinal')
564 assert_submitblock(bad_block, 'bad-txns-nonfinal')
565
566 self.log.info("getblocktemplate: Test bad tx count")
567 # The tx count is immediately after the block header
568 bad_block_sn = bytearray(block.serialize())
569 assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
570 bad_block_sn[BLOCK_HEADER_SIZE] += 1
571 assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
572 'data': bad_block_sn.hex(),
573 'mode': 'proposal',
574 'rules': ['segwit'],
575 })
576
577 self.log.info("getblocktemplate: Test bad bits")
578 bad_block = copy.deepcopy(block)
579 bad_block.nBits = 469762303 # impossible in the real world
580 assert_template(node, bad_block, 'bad-diffbits')
581
582 self.log.info("getblocktemplate: Test bad merkle root")
583 bad_block = copy.deepcopy(block)
584 bad_block.hashMerkleRoot += 1
585 assert_template(node, bad_block, 'bad-txnmrklroot', False)
586 assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot')
587
588 self.log.info("getblocktemplate: Test bad timestamps")
589 bad_block = copy.deepcopy(block)
590 bad_block.nTime = 2**32 - 1
591 assert_template(node, bad_block, 'time-too-new')
592 assert_submitblock(bad_block, 'time-too-new', 'time-too-new')
593 bad_block.nTime = 0
594 assert_template(node, bad_block, 'time-too-old')
595 assert_submitblock(bad_block, 'time-too-old', 'time-too-old')
596
597 self.log.info("getblocktemplate: Test not best block")
598 bad_block = copy.deepcopy(block)
599 bad_block.hashPrevBlock = 123
600 assert_template(node, bad_block, 'inconclusive-not-best-prevblk')
601 assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found')
602
603 self.log.info('submitheader tests')
604 assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE))
605 assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE-2)))
606 assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata=super(CBlock, bad_block).serialize().hex()))
607
608 block.nTime += 1
609 block.solve()
610
611 def chain_tip(b_hash, *, status='headers-only', branchlen=1):
612 return {'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status}
613
614 assert chain_tip(block.hash) not in node.getchaintips()
615 node.submitheader(hexdata=block.serialize().hex())
616 assert chain_tip(block.hash) in node.getchaintips()
617 node.submitheader(hexdata=CBlockHeader(block).serialize().hex()) # Noop
618 assert chain_tip(block.hash) in node.getchaintips()
619
620 bad_block_root = copy.deepcopy(block)
621 bad_block_root.hashMerkleRoot += 2
622 bad_block_root.solve()
623 assert chain_tip(bad_block_root.hash) not in node.getchaintips()
624 node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
625 assert chain_tip(bad_block_root.hash) in node.getchaintips()
626 # Should still reject invalid blocks, even if we have the header:
627 assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot')
628 assert_equal(node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot')
629 assert chain_tip(bad_block_root.hash) in node.getchaintips()
630 # We know the header for this invalid block, so should just return early without error:
631 node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
632 assert chain_tip(bad_block_root.hash) in node.getchaintips()
633
634 bad_block_lock = copy.deepcopy(block)
635 bad_block_lock.vtx[0].nLockTime = 2**32 - 1
636 bad_block_lock.vtx[0].rehash()
637 bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root()
638 bad_block_lock.solve()
639 assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'bad-txns-nonfinal')
640 assert_equal(node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'duplicate-invalid')
641 # Build a "good" block on top of the submitted bad block
642 bad_block2 = copy.deepcopy(block)
643 bad_block2.hashPrevBlock = bad_block_lock.sha256
644 bad_block2.solve()
645 assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex()))
646
647 # Should reject invalid header right away
648 bad_block_time = copy.deepcopy(block)
649 bad_block_time.nTime = 1
650 bad_block_time.solve()
651 assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
652
653 # Should ask for the block from a p2p node, if they announce the header as well:
654 peer = node.add_p2p_connection(P2PDataStore())
655 peer.wait_for_getheaders(timeout=5, block_hash=block.hashPrevBlock)
656 peer.send_blocks_and_test(blocks=[block], node=node)
657 # Must be active now:
658 assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips()
659
660 # Building a few blocks should give the same results
661 self.generatetoaddress(node, 10, node.get_deterministic_priv_key().address)
662 assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
663 assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex()))
664 node.submitheader(hexdata=CBlockHeader(block).serialize().hex())
665 node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
666 assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
667
668 self.test_blockmintxfee_parameter()
669 self.test_block_max_weight()
670 self.test_rpc_params()
671 self.test_timewarp()
672 self.test_pruning()
673
674
675 if __name__ == '__main__':
676 MiningTest(__file__).main()
677