@aztec/p2p 0.0.1-commit.f2ce05ee → 0.0.1-commit.f8ca9b2f3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dest/client/factory.d.ts +3 -3
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +38 -7
  4. package/dest/client/interface.d.ts +26 -15
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +31 -35
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +97 -138
  9. package/dest/config.d.ts +10 -2
  10. package/dest/config.d.ts.map +1 -1
  11. package/dest/config.js +9 -0
  12. package/dest/index.d.ts +2 -1
  13. package/dest/index.d.ts.map +1 -1
  14. package/dest/index.js +1 -0
  15. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +18 -11
  16. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  17. package/dest/mem_pools/attestation_pool/attestation_pool.js +62 -37
  18. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  19. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  20. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +53 -53
  21. package/dest/mem_pools/attestation_pool/index.d.ts +2 -2
  22. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
  23. package/dest/mem_pools/attestation_pool/index.js +1 -1
  24. package/dest/mem_pools/index.d.ts +2 -1
  25. package/dest/mem_pools/index.d.ts.map +1 -1
  26. package/dest/mem_pools/interface.d.ts +3 -3
  27. package/dest/mem_pools/interface.d.ts.map +1 -1
  28. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +87 -0
  29. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
  30. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +180 -0
  31. package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -1
  32. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
  33. package/dest/mem_pools/tx_pool_v2/index.js +1 -0
  34. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +3 -3
  35. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  36. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +25 -3
  37. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  38. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +36 -4
  39. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +1 -1
  40. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  41. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +77 -74
  42. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +3 -3
  43. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  44. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +16 -3
  45. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  46. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  47. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
  48. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  49. package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
  50. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +20 -4
  51. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  52. package/dest/msg_validators/tx_validator/timestamp_validator.js +2 -2
  53. package/dest/services/dummy_service.d.ts +6 -2
  54. package/dest/services/dummy_service.d.ts.map +1 -1
  55. package/dest/services/dummy_service.js +3 -0
  56. package/dest/services/encoding.d.ts +2 -2
  57. package/dest/services/encoding.d.ts.map +1 -1
  58. package/dest/services/encoding.js +2 -2
  59. package/dest/services/libp2p/libp2p_service.d.ts +11 -3
  60. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  61. package/dest/services/libp2p/libp2p_service.js +71 -35
  62. package/dest/services/reqresp/interface.d.ts +10 -1
  63. package/dest/services/reqresp/interface.d.ts.map +1 -1
  64. package/dest/services/reqresp/interface.js +15 -1
  65. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +3 -3
  66. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  67. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +7 -1
  68. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  69. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +15 -0
  70. package/dest/services/reqresp/protocols/tx.d.ts +7 -1
  71. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  72. package/dest/services/reqresp/protocols/tx.js +20 -0
  73. package/dest/services/reqresp/reqresp.d.ts +1 -1
  74. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  75. package/dest/services/reqresp/reqresp.js +11 -4
  76. package/dest/services/service.d.ts +18 -1
  77. package/dest/services/service.d.ts.map +1 -1
  78. package/dest/services/tx_collection/config.d.ts +7 -1
  79. package/dest/services/tx_collection/config.d.ts.map +1 -1
  80. package/dest/services/tx_collection/config.js +16 -0
  81. package/dest/services/tx_collection/fast_tx_collection.d.ts +3 -1
  82. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  83. package/dest/services/tx_collection/fast_tx_collection.js +17 -3
  84. package/dest/services/tx_collection/file_store_tx_collection.d.ts +44 -0
  85. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  86. package/dest/services/tx_collection/file_store_tx_collection.js +118 -0
  87. package/dest/services/tx_collection/file_store_tx_source.d.ts +27 -0
  88. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  89. package/dest/services/tx_collection/file_store_tx_source.js +57 -0
  90. package/dest/services/tx_collection/index.d.ts +2 -1
  91. package/dest/services/tx_collection/index.d.ts.map +1 -1
  92. package/dest/services/tx_collection/index.js +1 -0
  93. package/dest/services/tx_collection/slow_tx_collection.d.ts +3 -1
  94. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  95. package/dest/services/tx_collection/slow_tx_collection.js +48 -19
  96. package/dest/services/tx_collection/tx_collection.d.ts +17 -7
  97. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  98. package/dest/services/tx_collection/tx_collection.js +58 -2
  99. package/dest/services/tx_collection/tx_collection_sink.d.ts +15 -6
  100. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  101. package/dest/services/tx_collection/tx_collection_sink.js +13 -7
  102. package/dest/services/tx_file_store/config.d.ts +1 -3
  103. package/dest/services/tx_file_store/config.d.ts.map +1 -1
  104. package/dest/services/tx_file_store/config.js +0 -4
  105. package/dest/services/tx_file_store/tx_file_store.d.ts +3 -3
  106. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  107. package/dest/services/tx_provider.d.ts +3 -3
  108. package/dest/services/tx_provider.d.ts.map +1 -1
  109. package/dest/services/tx_provider.js +5 -4
  110. package/dest/test-helpers/make-test-p2p-clients.d.ts +3 -3
  111. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  112. package/dest/test-helpers/mock-pubsub.d.ts +27 -1
  113. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  114. package/dest/test-helpers/mock-pubsub.js +97 -2
  115. package/dest/test-helpers/testbench-utils.d.ts +30 -24
  116. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  117. package/dest/test-helpers/testbench-utils.js +87 -35
  118. package/dest/testbench/p2p_client_testbench_worker.js +1 -1
  119. package/package.json +14 -14
  120. package/src/client/factory.ts +65 -8
  121. package/src/client/interface.ts +26 -13
  122. package/src/client/p2p_client.ts +123 -158
  123. package/src/config.ts +16 -0
  124. package/src/index.ts +1 -0
  125. package/src/mem_pools/attestation_pool/attestation_pool.ts +87 -44
  126. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +57 -53
  127. package/src/mem_pools/attestation_pool/index.ts +3 -3
  128. package/src/mem_pools/index.ts +3 -0
  129. package/src/mem_pools/interface.ts +2 -2
  130. package/src/mem_pools/tx_pool_v2/README.md +59 -9
  131. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +234 -0
  132. package/src/mem_pools/tx_pool_v2/index.ts +1 -0
  133. package/src/mem_pools/tx_pool_v2/interfaces.ts +2 -2
  134. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +51 -5
  135. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +90 -77
  136. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +2 -2
  137. package/src/msg_validators/tx_validator/block_header_validator.ts +15 -3
  138. package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
  139. package/src/msg_validators/tx_validator/timestamp_validator.ts +19 -14
  140. package/src/services/dummy_service.ts +6 -0
  141. package/src/services/encoding.ts +2 -2
  142. package/src/services/libp2p/libp2p_service.ts +70 -37
  143. package/src/services/reqresp/interface.ts +26 -1
  144. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +2 -2
  145. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +17 -0
  146. package/src/services/reqresp/protocols/tx.ts +22 -0
  147. package/src/services/reqresp/reqresp.ts +13 -3
  148. package/src/services/service.ts +20 -0
  149. package/src/services/tx_collection/config.ts +26 -0
  150. package/src/services/tx_collection/fast_tx_collection.ts +14 -2
  151. package/src/services/tx_collection/file_store_tx_collection.ts +152 -0
  152. package/src/services/tx_collection/file_store_tx_source.ts +70 -0
  153. package/src/services/tx_collection/index.ts +1 -0
  154. package/src/services/tx_collection/slow_tx_collection.ts +55 -26
  155. package/src/services/tx_collection/tx_collection.ts +78 -12
  156. package/src/services/tx_collection/tx_collection_sink.ts +17 -7
  157. package/src/services/tx_file_store/config.ts +0 -6
  158. package/src/services/tx_file_store/tx_file_store.ts +4 -4
  159. package/src/services/tx_provider.ts +8 -7
  160. package/src/test-helpers/make-test-p2p-clients.ts +3 -3
  161. package/src/test-helpers/mock-pubsub.ts +133 -3
  162. package/src/test-helpers/testbench-utils.ts +100 -40
  163. package/src/testbench/p2p_client_testbench_worker.ts +1 -1
@@ -1,4 +1,4 @@
1
- import { SlotNumber } from '@aztec/foundation/branded-types';
1
+ import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import type { Logger } from '@aztec/foundation/log';
3
3
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
4
4
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
@@ -10,6 +10,7 @@ import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
10
10
  import { BlockHeader, Tx, TxHash, type TxValidator } from '@aztec/stdlib/tx';
11
11
 
12
12
  import { TxArchive } from './archive/index.js';
13
+ import { DeletedPool } from './deleted_pool.js';
13
14
  import {
14
15
  EvictionManager,
15
16
  FeePayerBalanceEvictionRule,
@@ -53,7 +54,7 @@ export class TxPoolV2Impl {
53
54
  // === Dependencies ===
54
55
  #l2BlockSource: L2BlockSource;
55
56
  #worldStateSynchronizer: WorldStateSynchronizer;
56
- #pendingTxValidator: TxValidator<Tx>;
57
+ #createTxValidator: TxPoolV2Dependencies['createTxValidator'];
57
58
 
58
59
  // === In-Memory Indices ===
59
60
  #indices: TxPoolIndices = new TxPoolIndices();
@@ -61,6 +62,7 @@ export class TxPoolV2Impl {
61
62
  // === Config & Services ===
62
63
  #config: TxPoolV2Config;
63
64
  #archive: TxArchive;
65
+ #deletedPool: DeletedPool;
64
66
  #evictionManager: EvictionManager;
65
67
  #log: Logger;
66
68
  #callbacks: TxPoolV2Callbacks;
@@ -78,10 +80,11 @@ export class TxPoolV2Impl {
78
80
 
79
81
  this.#l2BlockSource = deps.l2BlockSource;
80
82
  this.#worldStateSynchronizer = deps.worldStateSynchronizer;
81
- this.#pendingTxValidator = deps.pendingTxValidator;
83
+ this.#createTxValidator = deps.createTxValidator;
82
84
 
83
85
  this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
84
86
  this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
87
+ this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
85
88
  this.#log = log;
86
89
  this.#callbacks = callbacks;
87
90
 
@@ -116,7 +119,10 @@ export class TxPoolV2Impl {
116
119
  * by running pre-add rules to resolve nullifier conflicts, balance checks, and pool size limits.
117
120
  */
118
121
  async hydrateFromDatabase(): Promise<void> {
119
- // Step 1: Load all transactions from DB
122
+ // Step 0: Hydrate deleted pool state
123
+ await this.#deletedPool.hydrateFromDatabase();
124
+
125
+ // Step 1: Load all transactions from DB (excluding soft-deleted)
120
126
  const { loaded, errors: deserializationErrors } = await this.#loadAllTxsFromDb();
121
127
 
122
128
  // Step 2: Check mined status for each tx
@@ -134,7 +140,10 @@ export class TxPoolV2Impl {
134
140
  }
135
141
 
136
142
  // Step 4: Validate non-mined transactions
137
- const { valid, invalid } = await this.#validateTxBatch(nonMined, 'on startup');
143
+ const { valid, invalid } = await this.#revalidateMetadata(
144
+ nonMined.map(e => e.meta),
145
+ 'on startup',
146
+ );
138
147
 
139
148
  // Step 5: Populate mined indices (these don't need conflict resolution)
140
149
  for (const meta of mined) {
@@ -229,13 +238,13 @@ export class TxPoolV2Impl {
229
238
  const txHash = tx.getTxHash();
230
239
  const txHashStr = txHash.toString();
231
240
 
232
- // Validate transaction
233
- if (!(await this.#validateTx(tx))) {
241
+ // Build metadata and validate using metadata
242
+ const meta = await buildTxMetaData(tx);
243
+ if (!(await this.#validateMeta(meta))) {
234
244
  return { status: 'rejected' };
235
245
  }
236
246
 
237
- // Build metadata and run pre-add rules
238
- const meta = await buildTxMetaData(tx);
247
+ // Run pre-add rules
239
248
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
240
249
 
241
250
  if (preAddResult.shouldIgnore) {
@@ -267,14 +276,14 @@ export class TxPoolV2Impl {
267
276
  return 'ignored';
268
277
  }
269
278
 
270
- // Validate transaction (no logging for dry-run check)
271
- const validationResult = await this.#pendingTxValidator.validateTx(tx);
272
- if (validationResult.result !== 'valid') {
279
+ // Build metadata and validate using metadata
280
+ const meta = await buildTxMetaData(tx);
281
+ const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
282
+ if (validationResult !== true) {
273
283
  return 'rejected';
274
284
  }
275
285
 
276
- // Build metadata and use pre-add rules
277
- const meta = await buildTxMetaData(tx);
286
+ // Use pre-add rules
278
287
  const poolAccess = this.#createPreAddPoolAccess();
279
288
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
280
289
 
@@ -347,6 +356,7 @@ export class TxPoolV2Impl {
347
356
  // Add new mined tx (callback emitted by #addTx)
348
357
  await this.#addTx(tx, { mined: blockId }, opts);
349
358
  }
359
+ await this.#deletedPool.clearIfMinedHigher(txHashStr, blockId.number);
350
360
  }
351
361
  });
352
362
  }
@@ -373,6 +383,7 @@ export class TxPoolV2Impl {
373
383
  // Step 4: Mark txs as mined (only those we have in the pool)
374
384
  for (const meta of found) {
375
385
  this.#indices.markAsMined(meta, blockId);
386
+ await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
376
387
  }
377
388
 
378
389
  // Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
@@ -397,7 +408,7 @@ export class TxPoolV2Impl {
397
408
  this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
398
409
 
399
410
  // Step 4: Validate for pending pool
400
- const { valid, invalid } = await this.#loadAndValidateTxs(txsToRestore, 'during prepareForSlot');
411
+ const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
401
412
 
402
413
  // Step 5: Resolve nullifier conflicts and add winners to pending indices
403
414
  const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
@@ -426,24 +437,34 @@ export class TxPoolV2Impl {
426
437
 
427
438
  this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
428
439
 
429
- // Step 2: Unmine - clear mined status from metadata
440
+ // Step 2: Mark ALL un-mined txs with their original mined block number
441
+ // This ensures they get soft-deleted if removed later, and only hard-deleted
442
+ // when their original mined block is finalized
443
+ await this.#deletedPool.markFromPrunedBlock(
444
+ txsToUnmine.map(m => ({
445
+ txHash: m.txHash,
446
+ minedAtBlock: BlockNumber(m.minedL2BlockId!.number),
447
+ })),
448
+ );
449
+
450
+ // Step 3: Unmine - clear mined status from metadata
430
451
  for (const meta of txsToUnmine) {
431
452
  this.#indices.markAsUnmined(meta);
432
453
  }
433
454
 
434
- // Step 3: Filter out protected txs (they'll be handled by prepareForSlot)
455
+ // Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
435
456
  const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
436
457
 
437
458
  // Step 4: Validate for pending pool
438
- const { valid, invalid } = await this.#loadAndValidateTxs(unprotectedTxs, 'during handlePrunedBlocks');
459
+ const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
439
460
 
440
- // Step 5: Resolve nullifier conflicts and add winners to pending indices
461
+ // Step 6: Resolve nullifier conflicts and add winners to pending indices
441
462
  const { toEvict } = this.#applyNullifierConflictResolution(valid);
442
463
 
443
- // Step 6: Delete invalid and evicted txs
464
+ // Step 7: Delete invalid and evicted txs
444
465
  await this.#deleteTxsBatch([...invalid, ...toEvict]);
445
466
 
446
- // Step 7: Run eviction rules for ALL pending txs (not just restored ones)
467
+ // Step 8: Run eviction rules for ALL pending txs (not just restored ones)
447
468
  // This handles cases like existing pending txs with invalid fee payer balances
448
469
  await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
449
470
  }
@@ -458,16 +479,13 @@ export class TxPoolV2Impl {
458
479
  async handleFinalizedBlock(block: BlockHeader): Promise<void> {
459
480
  const blockNumber = block.globalVariables.blockNumber;
460
481
 
461
- // Step 1: Find txs mined at or before finalized block
462
- const txsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
463
- if (txsToFinalize.length === 0) {
464
- return;
465
- }
482
+ // Step 1: Find mined txs at or before finalized block
483
+ const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
466
484
 
467
- // Step 2: Collect txs for archiving (before deletion)
485
+ // Step 2: Collect mined txs for archiving (before deletion)
468
486
  const txsToArchive: Tx[] = [];
469
487
  if (this.#archive.isEnabled()) {
470
- for (const txHashStr of txsToFinalize) {
488
+ for (const txHashStr of minedTxsToFinalize) {
471
489
  const buffer = await this.#txsDB.getAsync(txHashStr);
472
490
  if (buffer) {
473
491
  txsToArchive.push(Tx.fromBuffer(buffer));
@@ -475,15 +493,20 @@ export class TxPoolV2Impl {
475
493
  }
476
494
  }
477
495
 
478
- // Step 3: Delete from active pool
479
- await this.#deleteTxsBatch(txsToFinalize);
496
+ // Step 3: Delete mined txs from active pool
497
+ await this.#deleteTxsBatch(minedTxsToFinalize);
498
+
499
+ // Step 4: Finalize soft-deleted txs
500
+ await this.#deletedPool.finalizeBlock(blockNumber);
480
501
 
481
- // Step 4: Archive
502
+ // Step 5: Archive mined txs
482
503
  if (txsToArchive.length > 0) {
483
504
  await this.#archive.archiveTxs(txsToArchive);
484
505
  }
485
506
 
486
- this.#log.info(`Finalized ${txsToFinalize.length} txs from blocks up to ${blockNumber}`);
507
+ if (minedTxsToFinalize.length > 0) {
508
+ this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`);
509
+ }
487
510
  }
488
511
 
489
512
  // === Query Methods ===
@@ -503,15 +526,23 @@ export class TxPoolV2Impl {
503
526
  }
504
527
 
505
528
  hasTxs(txHashes: TxHash[]): boolean[] {
506
- return txHashes.map(h => this.#indices.has(h.toString()));
529
+ return txHashes.map(h => {
530
+ const hashStr = h.toString();
531
+ return this.#indices.has(hashStr) || this.#deletedPool.isSoftDeleted(hashStr);
532
+ });
507
533
  }
508
534
 
509
535
  getTxStatus(txHash: TxHash): TxState | undefined {
510
- const meta = this.#indices.getMetadata(txHash.toString());
511
- if (!meta) {
512
- return undefined;
536
+ const txHashStr = txHash.toString();
537
+ const meta = this.#indices.getMetadata(txHashStr);
538
+ if (meta) {
539
+ return this.#indices.getTxState(meta);
540
+ }
541
+ // Check if soft-deleted
542
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
543
+ return 'deleted';
513
544
  }
514
- return this.#indices.getTxState(meta);
545
+ return undefined;
515
546
  }
516
547
 
517
548
  getPendingTxHashes(): TxHash[] {
@@ -624,10 +655,15 @@ export class TxPoolV2Impl {
624
655
  * Deletes a transaction from both indices and DB.
625
656
  * Emits onTxsRemoved callback immediately after DB delete.
626
657
  */
658
+ /**
659
+ * Deletes a transaction from the pool.
660
+ * Delegates to DeletedPool which decides soft vs hard delete based on whether
661
+ * the tx is from a pruned block.
662
+ */
627
663
  async #deleteTx(txHashStr: string): Promise<void> {
628
664
  this.#indices.remove(txHashStr);
629
- await this.#txsDB.delete(txHashStr);
630
665
  this.#callbacks.onTxsRemoved([txHashStr]);
666
+ await this.#deletedPool.deleteTx(txHashStr);
631
667
  }
632
668
 
633
669
  /** Deletes a batch of transactions, emitting callbacks individually for each. */
@@ -641,64 +677,36 @@ export class TxPoolV2Impl {
641
677
  // PRIVATE HELPERS - Validation & Conflict Resolution
642
678
  // ============================================================================
643
679
 
644
- /** Validates a single transaction, returning true if valid */
645
- async #validateTx(tx: Tx, context?: string): Promise<boolean> {
646
- const result = await this.#pendingTxValidator.validateTx(tx);
680
+ /** Validates transaction metadata, returning true if valid */
681
+ async #validateMeta(meta: TxMetaData, validator?: TxValidator<TxMetaData>, context?: string): Promise<boolean> {
682
+ const txValidator = validator ?? (await this.#createTxValidator());
683
+ const result = await txValidator.validateTx(meta);
647
684
  if (result.result !== 'valid') {
648
685
  const contextStr = context ? ` ${context}` : '';
649
- this.#log.info(`Tx ${tx.getTxHash()}${contextStr} failed validation: ${result.reason?.join(', ')}`);
686
+ this.#log.info(`Tx ${meta.txHash}${contextStr} failed validation: ${result.reason?.join(', ')}`);
650
687
  return false;
651
688
  }
652
689
  return true;
653
690
  }
654
691
 
655
- /** Loads transactions from DB, returning loaded txs and missing hashes */
656
- async #loadTxsFromDb(metas: TxMetaData[]): Promise<{ loaded: { tx: Tx; meta: TxMetaData }[]; missing: string[] }> {
657
- const loaded: { tx: Tx; meta: TxMetaData }[] = [];
658
- const missing: string[] = [];
659
-
660
- for (const meta of metas) {
661
- const buffer = await this.#txsDB.getAsync(meta.txHash);
662
- if (!buffer) {
663
- this.#log.warn(`Tx ${meta.txHash} not found in DB`);
664
- missing.push(meta.txHash);
665
- continue;
666
- }
667
- loaded.push({ tx: Tx.fromBuffer(buffer), meta });
668
- }
669
-
670
- return { loaded, missing };
671
- }
672
-
673
- /** Validates a batch of transactions, returning valid and invalid groups */
674
- async #validateTxBatch(
675
- txs: { tx: Tx; meta: TxMetaData }[],
692
+ /** Validates metadata directly */
693
+ async #revalidateMetadata(
694
+ metas: TxMetaData[],
676
695
  context?: string,
677
696
  ): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
678
697
  const valid: TxMetaData[] = [];
679
698
  const invalid: string[] = [];
680
-
681
- for (const { tx, meta } of txs) {
682
- if (await this.#validateTx(tx, context)) {
699
+ const validator = await this.#createTxValidator();
700
+ for (const meta of metas) {
701
+ if (await this.#validateMeta(meta, validator, context)) {
683
702
  valid.push(meta);
684
703
  } else {
685
704
  invalid.push(meta.txHash);
686
705
  }
687
706
  }
688
-
689
707
  return { valid, invalid };
690
708
  }
691
709
 
692
- /** Loads transactions from DB and validates them */
693
- async #loadAndValidateTxs(
694
- metas: TxMetaData[],
695
- context?: string,
696
- ): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
697
- const { loaded, missing } = await this.#loadTxsFromDb(metas);
698
- const { valid, invalid } = await this.#validateTxBatch(loaded, context);
699
- return { valid, invalid: [...missing, ...invalid] };
700
- }
701
-
702
710
  /**
703
711
  * Resolves nullifier conflicts between incoming txs and existing pending txs.
704
712
  * Modifies the pending indices during iteration to maintain consistent state
@@ -768,6 +776,11 @@ export class TxPoolV2Impl {
768
776
  const errors: string[] = [];
769
777
 
770
778
  for await (const [txHashStr, buffer] of this.#txsDB.entriesAsync()) {
779
+ // Skip soft-deleted transactions - they stay in DB but not in indices
780
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
781
+ continue;
782
+ }
783
+
771
784
  try {
772
785
  const tx = Tx.fromBuffer(buffer);
773
786
  const meta = await buildTxMetaData(tx);
@@ -1,6 +1,6 @@
1
- import type { ProcessedTx, Tx, TxValidationResult, TxValidator } from '@aztec/stdlib/tx';
1
+ import type { TxValidationResult, TxValidator } from '@aztec/stdlib/tx';
2
2
 
3
- export class AggregateTxValidator<T extends Tx | ProcessedTx> implements TxValidator<T> {
3
+ export class AggregateTxValidator<T> implements TxValidator<T> {
4
4
  #validators: TxValidator<T>[];
5
5
  constructor(...validators: TxValidator<T>[]) {
6
6
  if (validators.length === 0) {
@@ -1,12 +1,24 @@
1
1
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
2
2
  import type { BlockHash } from '@aztec/stdlib/block';
3
- import { type AnyTx, TX_ERROR_BLOCK_HEADER, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
3
+ import { TX_ERROR_BLOCK_HEADER, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
4
4
 
5
5
  export interface ArchiveSource {
6
6
  getArchiveIndices: (archives: BlockHash[]) => Promise<(bigint | undefined)[]>;
7
7
  }
8
8
 
9
- export class BlockHeaderTxValidator<T extends AnyTx> implements TxValidator<T> {
9
+ /** Structural interface for block header validation. */
10
+ export interface HasBlockHeaderData {
11
+ txHash: { toString(): string };
12
+ data: {
13
+ constants: {
14
+ anchorBlockHeader: {
15
+ hash(): Promise<BlockHash>;
16
+ };
17
+ };
18
+ };
19
+ }
20
+
21
+ export class BlockHeaderTxValidator<T extends HasBlockHeaderData> implements TxValidator<T> {
10
22
  #log: Logger;
11
23
  #archiveSource: ArchiveSource;
12
24
 
@@ -18,7 +30,7 @@ export class BlockHeaderTxValidator<T extends AnyTx> implements TxValidator<T> {
18
30
  async validateTx(tx: T): Promise<TxValidationResult> {
19
31
  const [index] = await this.#archiveSource.getArchiveIndices([await tx.data.constants.anchorBlockHeader.hash()]);
20
32
  if (index === undefined) {
21
- this.#log.verbose(`Rejecting tx ${'txHash' in tx ? tx.txHash : tx.hash} for referencing an unknown block header`);
33
+ this.#log.verbose(`Rejecting tx ${tx.txHash} for referencing an unknown block header`);
22
34
  return { result: 'invalid', reason: [TX_ERROR_BLOCK_HEADER] };
23
35
  }
24
36
  return { result: 'valid' };
@@ -1,9 +1,8 @@
1
+ import type { Fr } from '@aztec/foundation/curves/bn254';
1
2
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
2
3
  import {
3
- type AnyTx,
4
4
  TX_ERROR_DUPLICATE_NULLIFIER_IN_TX,
5
5
  TX_ERROR_EXISTING_NULLIFIER,
6
- Tx,
7
6
  type TxValidationResult,
8
7
  type TxValidator,
9
8
  } from '@aztec/stdlib/tx';
@@ -12,7 +11,13 @@ export interface NullifierSource {
12
11
  nullifiersExist: (nullifiers: Buffer[]) => Promise<boolean[]>;
13
12
  }
14
13
 
15
- export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
14
+ /** Structural interface for double-spend validation. */
15
+ export interface HasNullifierData {
16
+ txHash: { toString(): string };
17
+ data: { getNonEmptyNullifiers(): Fr[] };
18
+ }
19
+
20
+ export class DoubleSpendTxValidator<T extends HasNullifierData> implements TxValidator<T> {
16
21
  #log: Logger;
17
22
  #nullifierSource: NullifierSource;
18
23
 
@@ -22,17 +27,17 @@ export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
22
27
  }
23
28
 
24
29
  async validateTx(tx: T): Promise<TxValidationResult> {
25
- const nullifiers = tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers;
30
+ const nullifiers = tx.data.getNonEmptyNullifiers();
26
31
 
27
32
  // Ditch this tx if it has repeated nullifiers
28
33
  const uniqueNullifiers = new Set(nullifiers.map(n => n.toBigInt()));
29
34
  if (uniqueNullifiers.size !== nullifiers.length) {
30
- this.#log.verbose(`Rejecting tx ${'txHash' in tx ? tx.txHash : tx.hash} for emitting duplicate nullifiers`);
35
+ this.#log.verbose(`Rejecting tx ${tx.txHash} for emitting duplicate nullifiers`);
31
36
  return { result: 'invalid', reason: [TX_ERROR_DUPLICATE_NULLIFIER_IN_TX] };
32
37
  }
33
38
 
34
39
  if ((await this.#nullifierSource.nullifiersExist(nullifiers.map(n => n.toBuffer()))).some(Boolean)) {
35
- this.#log.verbose(`Rejecting tx ${'txHash' in tx ? tx.txHash : tx.hash} for repeating a nullifier`);
40
+ this.#log.verbose(`Rejecting tx ${tx.txHash} for repeating a nullifier`);
36
41
  return { result: 'invalid', reason: [TX_ERROR_EXISTING_NULLIFIER] };
37
42
  }
38
43
 
@@ -1,15 +1,24 @@
1
- import { BlockNumber } from '@aztec/foundation/branded-types';
1
+ import type { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
3
- import {
4
- type AnyTx,
5
- TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP,
6
- type TxValidationResult,
7
- type TxValidator,
8
- getTxHash,
9
- } from '@aztec/stdlib/tx';
3
+ import { TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
10
4
  import type { UInt64 } from '@aztec/stdlib/types';
11
5
 
12
- export class TimestampTxValidator<T extends AnyTx> implements TxValidator<T> {
6
+ /** Structural interface for timestamp validation. */
7
+ export interface HasTimestampData {
8
+ txHash: { toString(): string };
9
+ data: {
10
+ includeByTimestamp: bigint;
11
+ constants: {
12
+ anchorBlockHeader: {
13
+ globalVariables: {
14
+ blockNumber: BlockNumber;
15
+ };
16
+ };
17
+ };
18
+ };
19
+ }
20
+
21
+ export class TimestampTxValidator<T extends HasTimestampData> implements TxValidator<T> {
13
22
  #log: Logger;
14
23
 
15
24
  constructor(
@@ -38,11 +47,7 @@ export class TimestampTxValidator<T extends AnyTx> implements TxValidator<T> {
38
47
  );
39
48
  }
40
49
  this.#log.verbose(
41
- `Rejecting tx ${getTxHash(
42
- tx,
43
- )} for low expiration timestamp. Tx expiration timestamp: ${includeByTimestamp}, timestamp: ${
44
- this.values.timestamp
45
- }.`,
50
+ `Rejecting tx ${tx.txHash} for low expiration timestamp. Tx expiration timestamp: ${includeByTimestamp}, timestamp: ${this.values.timestamp}.`,
46
51
  );
47
52
  return Promise.resolve({ result: 'invalid', reason: [TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP] });
48
53
  } else {
@@ -26,6 +26,7 @@ import { ReqRespStatus } from './reqresp/status.js';
26
26
  import {
27
27
  type P2PBlockReceivedCallback,
28
28
  type P2PCheckpointReceivedCallback,
29
+ type P2PDuplicateAttestationCallback,
29
30
  type P2PDuplicateProposalCallback,
30
31
  type P2PService,
31
32
  type PeerDiscoveryService,
@@ -88,6 +89,11 @@ export class DummyP2PService implements P2PService {
88
89
  */
89
90
  public registerDuplicateProposalCallback(_callback: P2PDuplicateProposalCallback): void {}
90
91
 
92
+ /**
93
+ * Register a callback for when a duplicate attestation is detected
94
+ */
95
+ public registerDuplicateAttestationCallback(_callback: P2PDuplicateAttestationCallback): void {}
96
+
91
97
  /**
92
98
  * Sends a request to a peer.
93
99
  * @param _protocol - The protocol to send the request on.
@@ -78,11 +78,11 @@ export class SnappyTransform implements DataTransform {
78
78
  return this.inboundTransformData(Buffer.from(data), topic);
79
79
  }
80
80
 
81
- public inboundTransformData(data: Buffer, topic?: TopicType): Buffer {
81
+ public inboundTransformData(data: Buffer, topic?: TopicType, maxSizeKbOverride?: number): Buffer {
82
82
  if (data.length === 0) {
83
83
  return data;
84
84
  }
85
- const maxSizeKb = this.maxSizesKb[topic!] ?? this.defaultMaxSizeKb;
85
+ const maxSizeKb = maxSizeKbOverride ?? this.maxSizesKb[topic!] ?? this.defaultMaxSizeKb;
86
86
  const { decompressedSize } = readSnappyPreamble(data);
87
87
  if (decompressedSize > maxSizeKb * 1024) {
88
88
  this.logger.warn(`Decompressed size ${decompressedSize} exceeds maximum allowed size of ${maxSizeKb}kb`);