@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
@@ -19,13 +19,17 @@ export type TryAddResult = {
19
19
  added: boolean;
20
20
  /** Whether the exact item already existed */
21
21
  alreadyExists: boolean;
22
- /** Total items for this position - used for duplicate detection */
23
- totalForPosition: number;
22
+ /** Count of items for the position. Meaning varies by method:
23
+ * - tryAddBlockProposal: proposals at (slot, indexWithinCheckpoint)
24
+ * - tryAddCheckpointProposal: proposals at slot
25
+ * - tryAddCheckpointAttestation: attestations by this signer for this slot */
26
+ count: number;
24
27
  };
25
28
 
26
- export const MAX_PROPOSALS_PER_SLOT = 5;
27
- export const MAX_PROPOSALS_PER_POSITION = 3;
28
- export const ATTESTATION_CAP_BUFFER = 10;
29
+ export const MAX_CHECKPOINT_PROPOSALS_PER_SLOT = 5;
30
+ export const MAX_BLOCK_PROPOSALS_PER_POSITION = 3;
31
+ /** Maximum attestations a single signer can make per slot before being rejected. */
32
+ export const MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER = 3;
29
33
 
30
34
  /** Public API interface for attestation pools. Used for typing mocks and test implementations. */
31
35
  export type AttestationPoolApi = Pick<
@@ -69,6 +73,10 @@ export class AttestationPool {
69
73
  // Key: (slot << 10) | indexWithinCheckpoint, Value: archive string
70
74
  private blockProposalsForSlotAndIndex: AztecAsyncMultiMap<number, string>;
71
75
 
76
+ // Checkpoint attestations indexed by (slot, signer) for tracking attestations per (slot, signer) for duplicate detection
77
+ // Key: `${Fr(slot).toString()}-${signerAddress}` string (padded for lexicographic ordering), Value: `proposalId` strings
78
+ private checkpointAttestationsPerSlotAndSigner: AztecAsyncMultiMap<string, string>;
79
+
72
80
  constructor(
73
81
  private store: AztecAsyncKVStore,
74
82
  telemetry: TelemetryClient = getTelemetryClient(),
@@ -80,6 +88,7 @@ export class AttestationPool {
80
88
 
81
89
  // Initialize checkpoint attestations storage
82
90
  this.checkpointAttestations = store.openMap('checkpoint_attestations');
91
+ this.checkpointAttestationsPerSlotAndSigner = store.openMultiMap('checkpoint_attestations_per_slot_and_signer');
83
92
 
84
93
  // Initialize checkpoint proposal storage
85
94
  this.checkpointProposals = store.openMap('checkpoint_proposals');
@@ -133,6 +142,12 @@ export class AttestationPool {
133
142
  return { start: `${proposalKey}-`, end: `${proposalKey}-Z` };
134
143
  }
135
144
 
145
+ /** Creates a key for the per-signer-per-slot attestation index. Uses padded slot for lexicographic ordering. */
146
+ private getSlotSignerKey(slot: SlotNumber, signerAddress: string): string {
147
+ const slotStr = new Fr(slot).toString();
148
+ return `${slotStr}-${signerAddress}`;
149
+ }
150
+
136
151
  /** Number of bits reserved for indexWithinCheckpoint in position keys. */
137
152
  private static readonly INDEX_BITS = 10;
138
153
  /** Maximum indexWithinCheckpoint value (2^10 - 1 = 1023). */
@@ -166,21 +181,21 @@ export class AttestationPool {
166
181
  // Check if already exists
167
182
  const alreadyExists = await this.blockProposals.hasAsync(proposalId);
168
183
  if (alreadyExists) {
169
- const totalForPosition = await this.getBlockProposalCountForPosition(
184
+ const count = await this.getBlockProposalCountForPosition(
170
185
  blockProposal.slotNumber,
171
186
  blockProposal.indexWithinCheckpoint,
172
187
  );
173
- return { added: false, alreadyExists: true, totalForPosition };
188
+ return { added: false, alreadyExists: true, count };
174
189
  }
175
190
 
176
191
  // Get current count for position and check cap, do not add if exceeded
177
- const totalForPosition = await this.getBlockProposalCountForPosition(
192
+ const count = await this.getBlockProposalCountForPosition(
178
193
  blockProposal.slotNumber,
179
194
  blockProposal.indexWithinCheckpoint,
180
195
  );
181
196
 
182
- if (totalForPosition >= MAX_PROPOSALS_PER_POSITION) {
183
- return { added: false, alreadyExists: false, totalForPosition };
197
+ if (count >= MAX_BLOCK_PROPOSALS_PER_POSITION) {
198
+ return { added: false, alreadyExists: false, count };
184
199
  }
185
200
 
186
201
  // Add the proposal
@@ -195,7 +210,7 @@ export class AttestationPool {
195
210
  },
196
211
  );
197
212
 
198
- return { added: true, alreadyExists: false, totalForPosition: totalForPosition + 1 };
213
+ return { added: true, alreadyExists: false, count: count + 1 };
199
214
  });
200
215
  }
201
216
 
@@ -261,14 +276,14 @@ export class AttestationPool {
261
276
  // Check if already exists
262
277
  const alreadyExists = await this.checkpointProposals.hasAsync(proposalId);
263
278
  if (alreadyExists) {
264
- const totalForPosition = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
265
- return { added: false, alreadyExists: true, totalForPosition };
279
+ const count = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
280
+ return { added: false, alreadyExists: true, count };
266
281
  }
267
282
 
268
283
  // Get current count for slot and check cap
269
- const totalForPosition = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
270
- if (totalForPosition >= MAX_PROPOSALS_PER_SLOT) {
271
- return { added: false, alreadyExists: false, totalForPosition };
284
+ const count = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
285
+ if (count >= MAX_CHECKPOINT_PROPOSALS_PER_SLOT) {
286
+ return { added: false, alreadyExists: false, count };
272
287
  }
273
288
 
274
289
  // Add the proposal if cap not exceeded
@@ -279,7 +294,7 @@ export class AttestationPool {
279
294
  slotNumber: proposal.slotNumber,
280
295
  });
281
296
 
282
- return { added: true, alreadyExists: false, totalForPosition: totalForPosition + 1 };
297
+ return { added: true, alreadyExists: false, count: count + 1 };
283
298
  });
284
299
  }
285
300
 
@@ -409,6 +424,14 @@ export class AttestationPool {
409
424
  numberOfAttestations++;
410
425
  }
411
426
 
427
+ // Clean up per-signer-per-slot index. Keys are formatted as `${Fr(slot).toString()}-${signerAddress}`.
428
+ // Since Fr pads to fixed-width hex, Fr(oldestSlot) is lexicographically greater than any key with
429
+ // a smaller slot (even with the signer suffix), so using it as the exclusive end bound is correct.
430
+ const slotSignerEndKey = new Fr(oldestSlot).toString();
431
+ for await (const key of this.checkpointAttestationsPerSlotAndSigner.keysAsync({ end: slotSignerEndKey })) {
432
+ await this.checkpointAttestationsPerSlotAndSigner.delete(key);
433
+ }
434
+
412
435
  // Delete checkpoint proposals for slots < oldestSlot, using checkpointProposalsForSlot as index
413
436
  for await (const slot of this.checkpointProposalsForSlot.keysAsync({ end: oldestSlot })) {
414
437
  const proposalIds = await toArray(this.checkpointProposalsForSlot.getValuesAsync(slot));
@@ -445,61 +468,81 @@ export class AttestationPool {
445
468
  *
446
469
  * This method performs validation and addition in a single call:
447
470
  * - Checks if the attestation already exists (returns alreadyExists: true if so)
448
- * - Checks if the (slot, proposalId) has reached the attestation cap (returns added: false if so)
471
+ * - Checks if this signer has reached the per-signer attestation cap for this slot
449
472
  * - Adds the attestation if validation passes
450
473
  *
451
474
  * @param attestation - The checkpoint attestation to add
452
- * @param committeeSize - Committee size for the attestation's slot
453
- * @returns Result indicating whether the attestation was added and existence info
475
+ * @returns Result indicating whether the attestation was added, existence info, and count of
476
+ * attestations by this signer for this slot (for equivocation detection)
454
477
  */
455
- public async tryAddCheckpointAttestation(
456
- attestation: CheckpointAttestation,
457
- committeeSize: number,
458
- ): Promise<TryAddResult> {
478
+ public async tryAddCheckpointAttestation(attestation: CheckpointAttestation): Promise<TryAddResult> {
459
479
  const slotNumber = attestation.payload.header.slotNumber;
460
480
  const proposalId = attestation.archive.toString();
461
481
  const sender = attestation.getSender();
462
482
 
463
483
  if (!sender) {
464
- return { added: false, alreadyExists: false, totalForPosition: 0 };
484
+ return { added: false, alreadyExists: false, count: 0 };
465
485
  }
466
486
 
487
+ const signerAddress = sender.toString();
488
+
467
489
  return await this.store.transactionAsync(async () => {
468
- const key = this.getAttestationKey(slotNumber, proposalId, sender.toString());
490
+ const key = this.getAttestationKey(slotNumber, proposalId, signerAddress);
469
491
  const alreadyExists = await this.checkpointAttestations.hasAsync(key);
470
492
 
493
+ // Get count of attestations by this signer for this slot (for duplicate detection)
494
+ const signerAttestationCount = await this.getSignerAttestationCountForSlot(slotNumber, signerAddress);
495
+
471
496
  if (alreadyExists) {
472
- const total = await this.getAttestationCount(slotNumber, proposalId);
473
- return { added: false, alreadyExists: true, totalForPosition: total };
497
+ return {
498
+ added: false,
499
+ alreadyExists: true,
500
+ count: signerAttestationCount,
501
+ };
474
502
  }
475
503
 
476
- const limit = committeeSize + ATTESTATION_CAP_BUFFER;
477
- const currentCount = await this.getAttestationCount(slotNumber, proposalId);
478
-
479
- if (currentCount >= limit) {
480
- return { added: false, alreadyExists: false, totalForPosition: currentCount };
504
+ // Check if this signer has exceeded the per-signer cap for this slot
505
+ if (signerAttestationCount >= MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER) {
506
+ this.log.debug(`Rejecting attestation: signer ${signerAddress} exceeded per-slot cap for slot ${slotNumber}`, {
507
+ slotNumber,
508
+ signerAddress,
509
+ proposalId,
510
+ signerAttestationCount,
511
+ });
512
+ return {
513
+ added: false,
514
+ alreadyExists: false,
515
+ count: signerAttestationCount,
516
+ };
481
517
  }
482
518
 
519
+ // Add the attestation
483
520
  await this.checkpointAttestations.set(key, attestation.toBuffer());
484
521
 
485
- this.log.debug(`Added checkpoint attestation for slot ${slotNumber} from ${sender.toString()}`, {
522
+ // Track this attestation in the per-signer-per-slot index for duplicate detection
523
+ const slotSignerKey = this.getSlotSignerKey(slotNumber, signerAddress);
524
+ await this.checkpointAttestationsPerSlotAndSigner.set(slotSignerKey, proposalId);
525
+
526
+ this.log.debug(`Added checkpoint attestation for slot ${slotNumber} from ${signerAddress}`, {
486
527
  signature: attestation.signature.toString(),
487
528
  slotNumber,
488
- address: sender.toString(),
529
+ address: signerAddress,
489
530
  proposalId,
490
531
  });
491
- return { added: true, alreadyExists: false, totalForPosition: currentCount + 1 };
532
+
533
+ // Return the new count
534
+ return {
535
+ added: true,
536
+ alreadyExists: false,
537
+ count: signerAttestationCount + 1,
538
+ };
492
539
  });
493
540
  }
494
541
 
495
- /** Gets the count of attestations for a given (slot, proposalId). */
496
- private async getAttestationCount(slot: SlotNumber, proposalId: string): Promise<number> {
497
- const range = this.getAttestationKeyRangeForProposal(slot, proposalId);
498
- let count = 0;
499
- for await (const _ of this.checkpointAttestations.keysAsync(range)) {
500
- count++;
501
- }
502
- return count;
542
+ /** Gets the count of attestations by a specific signer for a given slot. */
543
+ private async getSignerAttestationCountForSlot(slot: SlotNumber, signerAddress: string): Promise<number> {
544
+ const slotSignerKey = this.getSlotSignerKey(slot, signerAddress);
545
+ return await this.checkpointAttestationsPerSlotAndSigner.getValueCountAsync(slotSignerKey);
503
546
  }
504
547
  }
505
548
 
@@ -10,7 +10,11 @@ import {
10
10
  makeCheckpointProposal,
11
11
  } from '@aztec/stdlib/testing';
12
12
 
13
- import { type AttestationPool, MAX_PROPOSALS_PER_POSITION, MAX_PROPOSALS_PER_SLOT } from './attestation_pool.js';
13
+ import {
14
+ type AttestationPool,
15
+ MAX_BLOCK_PROPOSALS_PER_POSITION,
16
+ MAX_CHECKPOINT_PROPOSALS_PER_SLOT,
17
+ } from './attestation_pool.js';
14
18
  import { mockCheckpointAttestation } from './mocks.js';
15
19
 
16
20
  const NUMBER_OF_SIGNERS_PER_TEST = 4;
@@ -191,7 +195,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
191
195
 
192
196
  expect(result.added).toBe(true);
193
197
  expect(result.alreadyExists).toBe(false);
194
- expect(result.totalForPosition).toBe(1);
198
+ expect(result.count).toBe(1);
195
199
 
196
200
  const retrievedProposal = await ap.getBlockProposal(proposalId);
197
201
 
@@ -258,7 +262,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
258
262
 
259
263
  expect(result.added).toBe(true);
260
264
  expect(result.alreadyExists).toBe(false);
261
- expect(result.totalForPosition).toBe(1);
265
+ expect(result.count).toBe(1);
262
266
 
263
267
  const retrievedProposal = await ap.getCheckpointProposal(proposalId);
264
268
 
@@ -324,12 +328,12 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
324
328
  it('should return added=false when exceeding capacity', async () => {
325
329
  const slotNumber = 420;
326
330
 
327
- // Add MAX_PROPOSALS_PER_SLOT proposals
328
- for (let i = 0; i < MAX_PROPOSALS_PER_SLOT; i++) {
331
+ // Add MAX_CHECKPOINT_PROPOSALS_PER_SLOT proposals
332
+ for (let i = 0; i < MAX_CHECKPOINT_PROPOSALS_PER_SLOT; i++) {
329
333
  const proposal = await mockCheckpointProposalForPool(signers[i % NUMBER_OF_SIGNERS_PER_TEST], slotNumber);
330
334
  const result = await ap.tryAddCheckpointProposal(proposal);
331
335
  expect(result.added).toBe(true);
332
- expect(result.totalForPosition).toBe(i + 1);
336
+ expect(result.count).toBe(i + 1);
333
337
  }
334
338
 
335
339
  // The next proposal should not be added
@@ -337,7 +341,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
337
341
  const result = await ap.tryAddCheckpointProposal(extraProposal);
338
342
  expect(result.added).toBe(false);
339
343
  expect(result.alreadyExists).toBe(false);
340
- expect(result.totalForPosition).toBe(MAX_PROPOSALS_PER_SLOT);
344
+ expect(result.count).toBe(MAX_CHECKPOINT_PROPOSALS_PER_SLOT);
341
345
  });
342
346
  });
343
347
 
@@ -358,13 +362,13 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
358
362
  };
359
363
 
360
364
  describe('tryAddBlockProposal duplicate detection', () => {
361
- it('should return totalForPosition=1 when pool is empty', async () => {
365
+ it('should return count=1 when pool is empty', async () => {
362
366
  const proposal = await mockBlockProposalWithIndex(signers[0], 100, 0);
363
367
  const result = await ap.tryAddBlockProposal(proposal);
364
368
 
365
369
  expect(result.added).toBe(true);
366
370
  expect(result.alreadyExists).toBe(false);
367
- expect(result.totalForPosition).toBe(1);
371
+ expect(result.count).toBe(1);
368
372
  });
369
373
 
370
374
  it('should return alreadyExists when same proposal exists', async () => {
@@ -375,17 +379,17 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
375
379
 
376
380
  expect(result.added).toBe(false);
377
381
  expect(result.alreadyExists).toBe(true);
378
- expect(result.totalForPosition).toBe(1);
382
+ expect(result.count).toBe(1);
379
383
  });
380
384
 
381
- it('should detect duplicate via totalForPosition when different proposal exists at same position', async () => {
385
+ it('should detect duplicate via count when different proposal exists at same position', async () => {
382
386
  const slotNumber = 100;
383
387
  const indexWithinCheckpoint = 2;
384
388
 
385
389
  // Add first proposal
386
390
  const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
387
391
  const result1 = await ap.tryAddBlockProposal(proposal1);
388
- expect(result1.totalForPosition).toBe(1);
392
+ expect(result1.count).toBe(1);
389
393
 
390
394
  // Add a different proposal at same position - this is a duplicate (equivocation)
391
395
  const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, indexWithinCheckpoint);
@@ -393,8 +397,8 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
393
397
 
394
398
  expect(result2.added).toBe(true);
395
399
  expect(result2.alreadyExists).toBe(false);
396
- // totalForPosition >= 2 indicates duplicate detection
397
- expect(result2.totalForPosition).toBe(2);
400
+ // count >= 2 indicates duplicate detection
401
+ expect(result2.count).toBe(2);
398
402
  });
399
403
 
400
404
  it('should not detect duplicate for different positions in same slot', async () => {
@@ -409,8 +413,8 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
409
413
  const result = await ap.tryAddBlockProposal(proposal2);
410
414
 
411
415
  expect(result.added).toBe(true);
412
- // totalForPosition = 1 means no duplicate for this position
413
- expect(result.totalForPosition).toBe(1);
416
+ // count = 1 means no duplicate for this position
417
+ expect(result.count).toBe(1);
414
418
  });
415
419
 
416
420
  it('should not detect duplicate for same position in different slots', async () => {
@@ -425,37 +429,37 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
425
429
  const result = await ap.tryAddBlockProposal(proposal2);
426
430
 
427
431
  expect(result.added).toBe(true);
428
- // totalForPosition = 1 means no duplicate for this position
429
- expect(result.totalForPosition).toBe(1);
432
+ // count = 1 means no duplicate for this position
433
+ expect(result.count).toBe(1);
430
434
  });
431
435
 
432
- it('should track multiple duplicates correctly via totalForPosition', async () => {
436
+ it('should track multiple duplicates correctly via count', async () => {
433
437
  const slotNumber = 100;
434
438
  const indexWithinCheckpoint = 0;
435
439
 
436
440
  // Add multiple proposals for same position
437
441
  const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
438
442
  const result1 = await ap.tryAddBlockProposal(proposal1);
439
- expect(result1.totalForPosition).toBe(1);
443
+ expect(result1.count).toBe(1);
440
444
 
441
445
  const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, indexWithinCheckpoint);
442
446
  const result2 = await ap.tryAddBlockProposal(proposal2);
443
- expect(result2.totalForPosition).toBe(2);
447
+ expect(result2.count).toBe(2);
444
448
 
445
449
  // Add a third proposal for same position
446
450
  const proposal3 = await mockBlockProposalWithIndex(signers[2], slotNumber, indexWithinCheckpoint);
447
451
  const result3 = await ap.tryAddBlockProposal(proposal3);
448
452
 
449
453
  expect(result3.added).toBe(true);
450
- expect(result3.totalForPosition).toBe(3);
454
+ expect(result3.count).toBe(3);
451
455
  });
452
456
 
453
457
  it('should return added=false when exceeding capacity', async () => {
454
458
  const slotNumber = 100;
455
459
  const indexWithinCheckpoint = 0;
456
460
 
457
- // Add MAX_PROPOSALS_PER_POSITION proposals
458
- for (let i = 0; i < MAX_PROPOSALS_PER_POSITION; i++) {
461
+ // Add MAX_BLOCK_PROPOSALS_PER_POSITION proposals
462
+ for (let i = 0; i < MAX_BLOCK_PROPOSALS_PER_POSITION; i++) {
459
463
  const proposal = await mockBlockProposalWithIndex(
460
464
  signers[i % NUMBER_OF_SIGNERS_PER_TEST],
461
465
  slotNumber,
@@ -463,7 +467,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
463
467
  );
464
468
  const result = await ap.tryAddBlockProposal(proposal);
465
469
  expect(result.added).toBe(true);
466
- expect(result.totalForPosition).toBe(i + 1);
470
+ expect(result.count).toBe(i + 1);
467
471
  }
468
472
 
469
473
  // The next proposal should not be added
@@ -471,7 +475,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
471
475
  const result = await ap.tryAddBlockProposal(extraProposal);
472
476
  expect(result.added).toBe(false);
473
477
  expect(result.alreadyExists).toBe(false);
474
- expect(result.totalForPosition).toBe(MAX_PROPOSALS_PER_POSITION);
478
+ expect(result.count).toBe(MAX_BLOCK_PROPOSALS_PER_POSITION);
475
479
  });
476
480
 
477
481
  it('should clean up block position index when deleting old data', async () => {
@@ -482,18 +486,18 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
482
486
  const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
483
487
  await ap.tryAddBlockProposal(proposal1);
484
488
 
485
- // Verify it's tracked (adding another should show totalForPosition = 2)
489
+ // Verify it's tracked (adding another should show count = 2)
486
490
  const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, indexWithinCheckpoint);
487
491
  let result = await ap.tryAddBlockProposal(proposal2);
488
- expect(result.totalForPosition).toBe(2);
492
+ expect(result.count).toBe(2);
489
493
 
490
494
  // Delete old data
491
495
  await ap.deleteOlderThan(SlotNumber(slotNumber + 1));
492
496
 
493
- // Verify position index is cleaned up (totalForPosition should be 1 now)
497
+ // Verify position index is cleaned up (count should be 1 now)
494
498
  const proposal3 = await mockBlockProposalWithIndex(signers[2], slotNumber, indexWithinCheckpoint);
495
499
  result = await ap.tryAddBlockProposal(proposal3);
496
- expect(result.totalForPosition).toBe(1);
500
+ expect(result.count).toBe(1);
497
501
  });
498
502
 
499
503
  it('should correctly delete block proposals at slot boundary', async () => {
@@ -514,16 +518,16 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
514
518
  // Slot 99 proposals should have their index cleaned up
515
519
  const newProposal99 = await mockBlockProposalWithIndex(signers[0], 99, 0);
516
520
  const result99 = await ap.tryAddBlockProposal(newProposal99);
517
- expect(result99.totalForPosition).toBe(1); // Index was cleaned up
521
+ expect(result99.count).toBe(1); // Index was cleaned up
518
522
 
519
523
  // Slot 100 and 101 should still be tracked
520
524
  const newProposal100 = await mockBlockProposalWithIndex(signers[1], 100, 0);
521
525
  const result100 = await ap.tryAddBlockProposal(newProposal100);
522
- expect(result100.totalForPosition).toBe(2); // Still has the original
526
+ expect(result100.count).toBe(2); // Still has the original
523
527
 
524
528
  const newProposal101 = await mockBlockProposalWithIndex(signers[2], 101, 0);
525
529
  const result101 = await ap.tryAddBlockProposal(newProposal101);
526
- expect(result101.totalForPosition).toBe(2); // Still has the original
530
+ expect(result101.count).toBe(2); // Still has the original
527
531
  });
528
532
 
529
533
  it('should delete all indices for a given slot', async () => {
@@ -544,15 +548,15 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
544
548
  // All indices should be cleaned up
545
549
  const newProposal0 = await mockBlockProposalWithIndex(signers[0], slotNumber, 0);
546
550
  const result0 = await ap.tryAddBlockProposal(newProposal0);
547
- expect(result0.totalForPosition).toBe(1);
551
+ expect(result0.count).toBe(1);
548
552
 
549
553
  const newProposal1 = await mockBlockProposalWithIndex(signers[1], slotNumber, 1);
550
554
  const result1 = await ap.tryAddBlockProposal(newProposal1);
551
- expect(result1.totalForPosition).toBe(1);
555
+ expect(result1.count).toBe(1);
552
556
 
553
557
  const newProposal2 = await mockBlockProposalWithIndex(signers[2], slotNumber, 2);
554
558
  const result2 = await ap.tryAddBlockProposal(newProposal2);
555
- expect(result2.totalForPosition).toBe(1);
559
+ expect(result2.count).toBe(1);
556
560
  });
557
561
 
558
562
  it('should delete block proposals from storage when deleting old data', async () => {
@@ -598,13 +602,13 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
598
602
  return proposal.toCore();
599
603
  };
600
604
 
601
- it('should return totalForPosition=1 when pool is empty', async () => {
605
+ it('should return count=1 when pool is empty', async () => {
602
606
  const proposal = await mockCheckpointProposalCoreForPool(signers[0], 100);
603
607
  const result = await ap.tryAddCheckpointProposal(proposal);
604
608
 
605
609
  expect(result.added).toBe(true);
606
610
  expect(result.alreadyExists).toBe(false);
607
- expect(result.totalForPosition).toBe(1);
611
+ expect(result.count).toBe(1);
608
612
  });
609
613
 
610
614
  it('should return alreadyExists when same proposal exists', async () => {
@@ -615,16 +619,16 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
615
619
 
616
620
  expect(result.added).toBe(false);
617
621
  expect(result.alreadyExists).toBe(true);
618
- expect(result.totalForPosition).toBe(1);
622
+ expect(result.count).toBe(1);
619
623
  });
620
624
 
621
- it('should detect duplicate via totalForPosition when different proposal exists for same slot', async () => {
625
+ it('should detect duplicate via count when different proposal exists for same slot', async () => {
622
626
  const slotNumber = 100;
623
627
 
624
628
  // Add first proposal
625
629
  const proposal1 = await mockCheckpointProposalCoreForPool(signers[0], slotNumber);
626
630
  const result1 = await ap.tryAddCheckpointProposal(proposal1);
627
- expect(result1.totalForPosition).toBe(1);
631
+ expect(result1.count).toBe(1);
628
632
 
629
633
  // Add a different proposal for same slot - this is a duplicate (equivocation)
630
634
  const proposal2 = await mockCheckpointProposalCoreForPool(signers[1], slotNumber);
@@ -632,8 +636,8 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
632
636
 
633
637
  expect(result2.added).toBe(true);
634
638
  expect(result2.alreadyExists).toBe(false);
635
- // totalForPosition >= 2 indicates duplicate detection
636
- expect(result2.totalForPosition).toBe(2);
639
+ // count >= 2 indicates duplicate detection
640
+ expect(result2.count).toBe(2);
637
641
  });
638
642
 
639
643
  it('should not detect duplicate for different slots', async () => {
@@ -646,28 +650,28 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
646
650
  const result = await ap.tryAddCheckpointProposal(proposal2);
647
651
 
648
652
  expect(result.added).toBe(true);
649
- // totalForPosition = 1 means no duplicate for this slot
650
- expect(result.totalForPosition).toBe(1);
653
+ // count = 1 means no duplicate for this slot
654
+ expect(result.count).toBe(1);
651
655
  });
652
656
 
653
- it('should track multiple duplicates correctly via totalForPosition', async () => {
657
+ it('should track multiple duplicates correctly via count', async () => {
654
658
  const slotNumber = 100;
655
659
 
656
660
  // Add multiple proposals for same slot
657
661
  const proposal1 = await mockCheckpointProposalCoreForPool(signers[0], slotNumber);
658
662
  const result1 = await ap.tryAddCheckpointProposal(proposal1);
659
- expect(result1.totalForPosition).toBe(1);
663
+ expect(result1.count).toBe(1);
660
664
 
661
665
  const proposal2 = await mockCheckpointProposalCoreForPool(signers[1], slotNumber);
662
666
  const result2 = await ap.tryAddCheckpointProposal(proposal2);
663
- expect(result2.totalForPosition).toBe(2);
667
+ expect(result2.count).toBe(2);
664
668
 
665
669
  // Add a third proposal for same slot
666
670
  const proposal3 = await mockCheckpointProposalCoreForPool(signers[2], slotNumber);
667
671
  const result3 = await ap.tryAddCheckpointProposal(proposal3);
668
672
 
669
673
  expect(result3.added).toBe(true);
670
- expect(result3.totalForPosition).toBe(3);
674
+ expect(result3.count).toBe(3);
671
675
  });
672
676
 
673
677
  it('should not count attestations as proposals for duplicate detection', async () => {
@@ -684,8 +688,8 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
684
688
 
685
689
  expect(result.added).toBe(true);
686
690
  expect(result.alreadyExists).toBe(false);
687
- // totalForPosition should be 1, NOT 2 - attestations should not count as proposals
688
- expect(result.totalForPosition).toBe(1);
691
+ // count should be 1, NOT 2 - attestations should not count as proposals
692
+ expect(result.count).toBe(1);
689
693
  });
690
694
 
691
695
  it('should not count attestations for different proposals as duplicates', async () => {
@@ -703,14 +707,14 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
703
707
  const result1 = await ap.tryAddCheckpointProposal(proposal1);
704
708
 
705
709
  expect(result1.added).toBe(true);
706
- expect(result1.totalForPosition).toBe(1);
710
+ expect(result1.count).toBe(1);
707
711
 
708
712
  // Add the second checkpoint proposal - this IS a duplicate (different archive, same slot)
709
713
  const proposal2 = await mockCheckpointProposalCoreForPool(signers[3], slotNumber, archive2);
710
714
  const result2 = await ap.tryAddCheckpointProposal(proposal2);
711
715
 
712
716
  expect(result2.added).toBe(true);
713
- expect(result2.totalForPosition).toBe(2);
717
+ expect(result2.count).toBe(2);
714
718
  });
715
719
  });
716
720
  });
@@ -3,7 +3,7 @@ export {
3
3
  type AttestationPoolApi,
4
4
  type TryAddResult,
5
5
  createTestAttestationPool,
6
- MAX_PROPOSALS_PER_SLOT,
7
- MAX_PROPOSALS_PER_POSITION,
8
- ATTESTATION_CAP_BUFFER,
6
+ MAX_CHECKPOINT_PROPOSALS_PER_SLOT,
7
+ MAX_BLOCK_PROPOSALS_PER_POSITION,
8
+ MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER,
9
9
  } from './attestation_pool.js';
@@ -1,3 +1,6 @@
1
1
  export { AttestationPool, type AttestationPoolApi } from './attestation_pool/attestation_pool.js';
2
2
  export { type MemPools } from './interface.js';
3
+ // Old TxPool exports - kept temporarily for external consumers
3
4
  export { type TxPool } from './tx_pool/tx_pool.js';
5
+ // New TxPoolV2 exports
6
+ export { type TxPoolV2, type TxPoolV2Config, type TxPoolV2Events, type AddTxsResult } from './tx_pool_v2/index.js';
@@ -1,10 +1,10 @@
1
1
  import type { AttestationPoolApi } from './attestation_pool/attestation_pool.js';
2
- import type { TxPool } from './tx_pool/tx_pool.js';
2
+ import type { TxPoolV2 } from './tx_pool_v2/interfaces.js';
3
3
 
4
4
  /**
5
5
  * A interface the combines all mempools
6
6
  */
7
7
  export type MemPools = {
8
- txPool: TxPool;
8
+ txPool: TxPoolV2;
9
9
  attestationPool: AttestationPoolApi;
10
10
  };