Skip to content
Bitcoin Roots

v31.1-roots.1 · Commit 5: [PATCH 05/17] feat(net): preserve Roots peer controls and health view on v31

test/functional/p2p_compactblocks_extratxs.py

Before: /dev/null · After: test/functional/p2p_compactblocks_extratxs.py

File metadata
new file mode 100644
index 0000000000000000000000000000000000000000..611ce4c678f032ca2390960ab122176132b871e7

Download original patch · Release notes

Diff rows 401–521 of 521. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.

test/functional/p2p_compactblocks_extratxs.py ADDED
@@ -0,0 +1,521 @@ (continued)
401
+
402
+ def test_extratxnsize_boundary(self):
403
+ """Test extra transaction pool at exact size limit boundary."""
404
+ self.log.info("Testing extra transaction pool exact size limit boundary...")
405
+
406
+ limit_mb = 1
407
+ self.restart_node_with_limit(memory_mb=limit_mb)
408
+
409
+ test_count = 100
410
+ rejected_txs = self.populate_extra_pool(test_count, target_size=20000)
411
+
412
+ indices = list(range(test_count))
413
+ result = self.send_compact_block(rejected_txs, indices)
414
+
415
+ # Find the boundary - how many fit vs how many were evicted
416
+ num_evicted = len(result["missing_indices"])
417
+ num_fit = test_count - num_evicted
418
+
419
+ # Now restart and add exactly the number that fit
420
+ self.restart_node_with_limit(memory_mb=limit_mb)
421
+ rejected_txs = self.populate_extra_pool(num_fit, target_size=20000)
422
+
423
+ # Verify all fit
424
+ indices = list(range(num_fit))
425
+ result = self.send_compact_block(rejected_txs, indices)
426
+ assert result["missing_indices"] == [], f"Expected all {num_fit} transactions to fit at boundary"
427
+
428
+ # Add one more transaction - should evict exactly one
429
+ self.log.info("Adding one more transaction at the boundary...")
430
+ self.populate_extra_pool(1, target_size=20000)
431
+
432
+ # Check original transactions again
433
+ result2 = self.send_compact_block(rejected_txs, indices)
434
+ assert len(result2["missing_indices"]) == 1, "Expected exactly 1 eviction at boundary"
435
+ assert result2["missing_indices"] == [0], "Expected oldest transaction (0) to be evicted"
436
+
437
+ self.log.info("Size limit boundary behavior verified - one transaction evicted when limit exceeded")
438
+
439
+ def test_extratxnsize_small_limit(self):
440
+ """Test extra transaction pool with very small size limit (0.1 MB)."""
441
+ self.log.info("Testing extra transaction pool with 0.1 MB size limit...")
442
+
443
+ limit_mb = 0.1
444
+ self.restart_node_with_limit(memory_mb=limit_mb)
445
+
446
+ test_count = 100
447
+ rejected_txs = self.populate_extra_pool(test_count, target_size=2000)
448
+ indices = list(range(test_count))
449
+ result = self.send_compact_block(rejected_txs, indices)
450
+
451
+ # Find the boundary - how many fit vs how many were evicted
452
+ num_evicted = len(result["missing_indices"])
453
+ num_fit = test_count - num_evicted
454
+
455
+ # fill exact capacity
456
+ self.restart_node_with_limit(memory_mb=limit_mb)
457
+ rejected_txs = self.populate_extra_pool(num_fit, target_size=2000)
458
+
459
+ indices = list(range(num_fit))
460
+ result = self.send_compact_block(rejected_txs, indices)
461
+ assert result["missing_indices"] == [], f"Expected all {num_fit} transactions to fit at boundary"
462
+
463
+ # Add one more transaction - should evict exactly one
464
+ self.log.info("Adding one more transaction at the boundary...")
465
+ self.populate_extra_pool(1, target_size=2000)
466
+
467
+ # Check original transactions again
468
+ result = self.send_compact_block(rejected_txs, indices)
469
+ assert len(result["missing_indices"]) == 1, "Expected exactly 1 eviction at boundary"
470
+ assert result["missing_indices"] == [0], "Expected oldest transaction (0) to be evicted"
471
+
472
+ self.log.info("Small size limit boundary behavior verified - one transaction evicted when limit exceeded")
473
+
474
+ def test_extratxnsize_large_transaction_exceeds_limit(self):
475
+ """Test that a transaction larger than the entire size limit is rejected."""
476
+ self.log.info("Testing large transaction that exceeds size limit...")
477
+
478
+ limit_mb = 0.5 # 0.5 MB/500KB
479
+ self.restart_node_with_limit(memory_mb=limit_mb)
480
+
481
+ # Create a 600KB transaction (larger than 500KB limit)
482
+ # This should be rejected from mempool AND not stored in extra pool
483
+ rejected_txs = self.populate_extra_pool(1, target_size=600000) # 600 KB
484
+
485
+ # try block reconstruction
486
+ result = self.send_compact_block(rejected_txs, [0])
487
+
488
+ # Verify the transaction is NOT available (was not stored in extra pool)
489
+ assert len(result["missing_indices"]) == 1, "Large transaction should not be stored in extra pool"
490
+
491
+ def run_test(self):
492
+ self.wallet = MiniWallet(self.nodes[0])
493
+
494
+ # Setup the p2p connection
495
+ self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn())
496
+
497
+ # Create UTXOs for testing
498
+ self.make_utxos()
499
+
500
+ # Ensure segwit is active
501
+ assert softfork_active(self.nodes[0], "segwit")
502
+
503
+ # Test policy rejection types first
504
+ self.test_policy_rejection_types()
505
+
506
+ # Extra Txn capacity tests
507
+ self.test_extratxnpool_disabled()
508
+ self.test_extratxnpool_capacity_and_wraparound()
509
+ self.test_single_extratxnpool_capacity()
510
+ self.test_extratxn_invalid_parameters()
511
+
512
+ # Extra Txn size tests
513
+ self.test_extratxnsize_zero_limit()
514
+ self.test_extratxnsize_eviction()
515
+ self.test_extratxnsize_boundary()
516
+ self.test_extratxnsize_small_limit()
517
+ self.test_extratxnsize_large_transaction_exceeds_limit()
518
+
519
+
520
+ if __name__ == '__main__':
521
+ CompactBlocksBlockReconstructionLimitTest(__file__).main()