|
27 | 27 | CBlockHeader,
|
28 | 28 | COIN,
|
29 | 29 | DEFAULT_BLOCK_RESERVED_WEIGHT,
|
| 30 | + MAX_BLOCK_WEIGHT, |
30 | 31 | ser_uint256,
|
| 32 | + WITNESS_SCALE_FACTOR |
31 | 33 | )
|
32 | 34 | from test_framework.p2p import P2PDataStore
|
33 | 35 | from test_framework.test_framework import BitcoinTestFramework
|
@@ -194,6 +196,87 @@ def test_pruning(self):
|
194 | 196 | assert_equal(result, "inconclusive")
|
195 | 197 | assert_equal(prune_node.getblock(pruned_blockhash, verbosity=0), pruned_block)
|
196 | 198 |
|
| 199 | + |
| 200 | + def send_transactions(self, utxos, fee_rate, target_vsize): |
| 201 | + """ |
| 202 | + Helper to create and send transactions with the specified target virtual size and fee rate. |
| 203 | + """ |
| 204 | + for utxo in utxos: |
| 205 | + self.wallet.send_self_transfer( |
| 206 | + from_node=self.nodes[0], |
| 207 | + utxo_to_spend=utxo, |
| 208 | + target_vsize=target_vsize, |
| 209 | + fee_rate=fee_rate, |
| 210 | + ) |
| 211 | + |
| 212 | + def verify_block_template(self, expected_tx_count, expected_weight): |
| 213 | + """ |
| 214 | + Create a block template and check that it satisfies the expected transaction count and total weight. |
| 215 | + """ |
| 216 | + response = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS) |
| 217 | + self.log.info(f"Testing block template: contains {expected_tx_count} transactions, and total weight <= {expected_weight}") |
| 218 | + assert_equal(len(response["transactions"]), expected_tx_count) |
| 219 | + total_weight = sum(transaction["weight"] for transaction in response["transactions"]) |
| 220 | + assert_greater_than_or_equal(expected_weight, total_weight) |
| 221 | + |
| 222 | + def test_block_max_weight(self): |
| 223 | + self.log.info("Testing default and custom -blockmaxweight startup options.") |
| 224 | + |
| 225 | + # Restart the node to allow large transactions |
| 226 | + LARGE_TXS_COUNT = 10 |
| 227 | + LARGE_VSIZE = int(((MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT) / WITNESS_SCALE_FACTOR) / LARGE_TXS_COUNT) |
| 228 | + HIGH_FEERATE = Decimal("0.0003") |
| 229 | + self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}"]) |
| 230 | + |
| 231 | + # Ensure the mempool is empty |
| 232 | + assert_equal(len(self.nodes[0].getrawmempool()), 0) |
| 233 | + |
| 234 | + # Generate UTXOs and send 10 large transactions with a high fee rate |
| 235 | + 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 |
| 236 | + self.send_transactions(utxos[:LARGE_TXS_COUNT], HIGH_FEERATE, LARGE_VSIZE) |
| 237 | + |
| 238 | + # Send 2 normal transactions with a lower fee rate |
| 239 | + NORMAL_VSIZE = int(2000 / WITNESS_SCALE_FACTOR) |
| 240 | + NORMAL_FEERATE = Decimal("0.0001") |
| 241 | + self.send_transactions(utxos[LARGE_TXS_COUNT:LARGE_TXS_COUNT + 2], NORMAL_FEERATE, NORMAL_VSIZE) |
| 242 | + |
| 243 | + # Check that the mempool contains all transactions |
| 244 | + self.log.info(f"Testing that the mempool contains {LARGE_TXS_COUNT + 2} transactions.") |
| 245 | + assert_equal(len(self.nodes[0].getrawmempool()), LARGE_TXS_COUNT + 2) |
| 246 | + |
| 247 | + # Verify the block template includes only the 10 high-fee transactions |
| 248 | + self.log.info("Testing that the block template includes only the 10 large transactions.") |
| 249 | + self.verify_block_template( |
| 250 | + expected_tx_count=LARGE_TXS_COUNT, |
| 251 | + expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT, |
| 252 | + ) |
| 253 | + |
| 254 | + # Test block template creation with custom -blockmaxweight |
| 255 | + custom_block_weight = MAX_BLOCK_WEIGHT - 2000 |
| 256 | + # Reducing the weight by 2000 units will prevent 1 large transaction from fitting into the block. |
| 257 | + self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", f"-blockmaxweight={custom_block_weight}"]) |
| 258 | + |
| 259 | + self.log.info("Testing the block template with custom -blockmaxweight to include 9 large and 2 normal transactions.") |
| 260 | + self.verify_block_template( |
| 261 | + expected_tx_count=11, |
| 262 | + expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT - 2000, |
| 263 | + ) |
| 264 | + |
| 265 | + # Ensure the block weight does not exceed the maximum |
| 266 | + self.log.info(f"Testing that the block weight will never exceed {MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT}.") |
| 267 | + self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", f"-blockmaxweight={MAX_BLOCK_WEIGHT}"]) |
| 268 | + self.log.info("Sending 2 additional normal transactions to fill the mempool to the maximum block weight.") |
| 269 | + self.send_transactions(utxos[LARGE_TXS_COUNT + 2:], NORMAL_FEERATE, NORMAL_VSIZE) |
| 270 | + self.log.info(f"Testing that the mempool's weight matches the maximum block weight: {MAX_BLOCK_WEIGHT}.") |
| 271 | + assert_equal(self.nodes[0].getmempoolinfo()['bytes'] * WITNESS_SCALE_FACTOR, MAX_BLOCK_WEIGHT) |
| 272 | + |
| 273 | + self.log.info("Testing that the block template includes only 10 transactions and cannot reach full block weight.") |
| 274 | + self.verify_block_template( |
| 275 | + expected_tx_count=LARGE_TXS_COUNT, |
| 276 | + expected_weight=MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT, |
| 277 | + ) |
| 278 | + |
| 279 | + |
197 | 280 | def run_test(self):
|
198 | 281 | node = self.nodes[0]
|
199 | 282 | self.wallet = MiniWallet(node)
|
@@ -419,6 +502,7 @@ def chain_tip(b_hash, *, status='headers-only', branchlen=1):
|
419 | 502 | assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
|
420 | 503 |
|
421 | 504 | self.test_blockmintxfee_parameter()
|
| 505 | + self.test_block_max_weight() |
422 | 506 | self.test_timewarp()
|
423 | 507 | self.test_pruning()
|
424 | 508 |
|
|
0 commit comments