@aztec/p2p 5.0.0-rc.1 → 5.0.0

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 (103) hide show
  1. package/README.md +3 -5
  2. package/dest/bootstrap/bootstrap.d.ts +1 -1
  3. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  4. package/dest/bootstrap/bootstrap.js +20 -12
  5. package/dest/client/factory.d.ts +1 -1
  6. package/dest/client/factory.d.ts.map +1 -1
  7. package/dest/client/factory.js +2 -1
  8. package/dest/client/interface.d.ts +14 -2
  9. package/dest/client/interface.d.ts.map +1 -1
  10. package/dest/client/p2p_client.d.ts +4 -2
  11. package/dest/client/p2p_client.d.ts.map +1 -1
  12. package/dest/client/p2p_client.js +10 -2
  13. package/dest/config.d.ts +7 -1
  14. package/dest/config.d.ts.map +1 -1
  15. package/dest/config.js +10 -5
  16. package/dest/errors/reqresp.error.d.ts +13 -1
  17. package/dest/errors/reqresp.error.d.ts.map +1 -1
  18. package/dest/errors/reqresp.error.js +14 -0
  19. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +1 -1
  20. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  21. package/dest/mem_pools/attestation_pool/attestation_pool.js +6 -2
  22. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  23. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  24. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +43 -0
  25. package/dest/mem_pools/instrumentation.d.ts +9 -4
  26. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  27. package/dest/mem_pools/instrumentation.js +11 -8
  28. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +1 -1
  29. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +12 -1
  30. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  31. package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
  32. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +9 -1
  33. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  34. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +21 -0
  35. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +2 -1
  36. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  37. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +12 -4
  38. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +10 -2
  39. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  40. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +56 -8
  41. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +10 -6
  42. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  43. package/dest/msg_validators/proposal_validator/proposal_validator.js +25 -12
  44. package/dest/msg_validators/tx_validator/phases_validator.js +2 -2
  45. package/dest/services/discv5/discV5_service.d.ts +12 -2
  46. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  47. package/dest/services/discv5/discV5_service.js +84 -15
  48. package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
  49. package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
  50. package/dest/services/discv5/persisted_enr_store.js +145 -0
  51. package/dest/services/dummy_service.d.ts +6 -2
  52. package/dest/services/dummy_service.d.ts.map +1 -1
  53. package/dest/services/dummy_service.js +3 -0
  54. package/dest/services/encoding.d.ts +19 -6
  55. package/dest/services/encoding.d.ts.map +1 -1
  56. package/dest/services/encoding.js +25 -15
  57. package/dest/services/libp2p/libp2p_service.d.ts +11 -3
  58. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  59. package/dest/services/libp2p/libp2p_service.js +98 -34
  60. package/dest/services/peer-manager/peer_manager.d.ts +1 -1
  61. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  62. package/dest/services/peer-manager/peer_manager.js +21 -12
  63. package/dest/services/reqresp/config.d.ts +13 -1
  64. package/dest/services/reqresp/config.d.ts.map +1 -1
  65. package/dest/services/reqresp/config.js +11 -0
  66. package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -3
  67. package/dest/services/reqresp/reqresp.d.ts +1 -1
  68. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  69. package/dest/services/reqresp/reqresp.js +34 -3
  70. package/dest/services/service.d.ts +17 -1
  71. package/dest/services/service.d.ts.map +1 -1
  72. package/dest/test-helpers/testbench-utils.d.ts +2 -1
  73. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  74. package/dest/test-helpers/testbench-utils.js +3 -0
  75. package/package.json +16 -17
  76. package/src/bootstrap/bootstrap.ts +15 -9
  77. package/src/client/factory.ts +1 -0
  78. package/src/client/interface.ts +15 -0
  79. package/src/client/p2p_client.ts +14 -3
  80. package/src/config.ts +17 -4
  81. package/src/errors/reqresp.error.ts +19 -0
  82. package/src/mem_pools/attestation_pool/attestation_pool.ts +6 -2
  83. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +40 -1
  84. package/src/mem_pools/instrumentation.ts +13 -11
  85. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +1 -1
  86. package/src/mem_pools/tx_pool_v2/interfaces.ts +13 -0
  87. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +23 -0
  88. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +14 -4
  89. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +71 -7
  90. package/src/msg_validators/proposal_validator/proposal_validator.ts +22 -15
  91. package/src/msg_validators/tx_validator/phases_validator.ts +2 -2
  92. package/src/services/discv5/discV5_service.ts +88 -9
  93. package/src/services/discv5/persisted_enr_store.ts +158 -0
  94. package/src/services/dummy_service.ts +6 -0
  95. package/src/services/encoding.ts +28 -20
  96. package/src/services/libp2p/libp2p_service.ts +107 -37
  97. package/src/services/peer-manager/peer_manager.ts +18 -11
  98. package/src/services/reqresp/README.md +2 -23
  99. package/src/services/reqresp/config.ts +17 -0
  100. package/src/services/reqresp/rate-limiter/rate_limits.ts +3 -3
  101. package/src/services/reqresp/reqresp.ts +42 -2
  102. package/src/services/service.ts +19 -0
  103. package/src/test-helpers/testbench-utils.ts +4 -0
@@ -1,4 +1,4 @@
1
- import { BlockNumber } from '@aztec/foundation/branded-types';
1
+ import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import { FifoSet } from '@aztec/foundation/fifo-set';
3
3
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
4
4
  import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
@@ -469,7 +469,13 @@ import { TxPoolIndices } from './tx_pool_indices.js';
469
469
  await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
470
470
  });
471
471
  if (found.length > 0) {
472
- this.#callbacks.onTxsMined(found.map((m)=>m.txHash));
472
+ // receivedAt is 0 for txs hydrated from the DB on restart (true receive time lost) — leave
473
+ // their delay undefined so the metric isn't polluted by epoch-sized values.
474
+ const now = this.#dateProvider.now();
475
+ this.#callbacks.onTxsMined(found.map((m)=>({
476
+ txHash: m.txHash,
477
+ minedDelayMs: m.receivedAt > 0 ? now - m.receivedAt : undefined
478
+ })));
473
479
  }
474
480
  this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
475
481
  }
@@ -588,8 +594,11 @@ import { TxPoolIndices } from './tx_pool_indices.js';
588
594
  }
589
595
  async handleFinalizedBlock(block) {
590
596
  const blockNumber = block.globalVariables.blockNumber;
591
- // Step 1: Find mined txs at or before finalized block
592
- const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
597
+ // Hold finalized txs for a configurable margin behind the finalized tip so a prover still
598
+ // proving an epoch with already-finalized blocks isn't starved of its txs. 0 deletes at the finalized tip.
599
+ const cutoffBlock = await this.#finalizationCutoffBlock(block);
600
+ // Step 1: Find mined txs at or before the cutoff block
601
+ const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(cutoffBlock);
593
602
  // Step 2: Archive in chunks if archiving is enabled. Hydrating an entire epoch's worth of
594
603
  // mined txs at once would OOM under load. When archiving is disabled there is no need to hydrate the txs at all.
595
604
  if (this.#archive.isEnabled()) {
@@ -611,14 +620,46 @@ import { TxPoolIndices } from './tx_pool_indices.js';
611
620
  // transaction. Only tx hashes are touched here, so memory is bounded and atomicity is preserved.
612
621
  await this.#store.transactionAsync(async ()=>{
613
622
  await this.#deleteTxsBatch(minedTxsToFinalize);
614
- await this.#deletedPool.finalizeBlock(blockNumber);
623
+ await this.#deletedPool.finalizeBlock(cutoffBlock);
615
624
  });
616
625
  if (minedTxsToFinalize.length > 0) {
617
- this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
618
- txHashes: minedTxsToFinalize
626
+ this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${cutoffBlock}`, {
627
+ txHashes: minedTxsToFinalize,
628
+ finalizedBlockNumber: blockNumber,
629
+ cutoffBlock
619
630
  });
620
631
  }
621
632
  }
633
+ /**
634
+ * Resolves the block up to which finalized txs may be deleted, given `keepFinalizedTxsForSlots`.
635
+ * With a margin of 0 this is just the finalized block. Otherwise we keep txs whose block is within
636
+ * the margin (in slots) of the finalized tip: take the target slot `finalizedSlot - margin`, find the
637
+ * latest checkpoint at or before it with a single reverse range query (`limit: 1`), and use that
638
+ * checkpoint's last block as the cutoff. This rounds to a checkpoint boundary, so it can retain slightly
639
+ * more than the configured margin but never less, and never deletes past the finalized block.
640
+ */ async #finalizationCutoffBlock(block) {
641
+ const keepSlots = this.#config.keepFinalizedTxsForSlots;
642
+ if (keepSlots <= 0) {
643
+ return block.globalVariables.blockNumber;
644
+ }
645
+ // The goal here really is to ensure we keep transactions long enough for them to be proven.
646
+ // So we could be smart and calculate the number of slots until the end of the proof submission window.
647
+ // Our approach here is a lot simpler however and we just keep transactions for the configured number
648
+ // of slots past L1 finalisation. This means we may keep transactions in the mined pool for longer
649
+ // than strictly necessary.
650
+ const targetSlot = block.getSlot() - keepSlots;
651
+ if (targetSlot < 0) {
652
+ // The margin reaches past genesis: keep everything.
653
+ return BlockNumber(0);
654
+ }
655
+ const [checkpoint] = await this.#l2BlockSource.getCheckpointsData({
656
+ fromSlot: SlotNumber(targetSlot),
657
+ limit: 1,
658
+ reverse: true
659
+ });
660
+ // No checkpoint at or before the target slot (e.g. early chain): keep everything.
661
+ return checkpoint ? BlockNumber(checkpoint.startBlock + checkpoint.blockCount - 1) : BlockNumber(0);
662
+ }
622
663
  // === Query Methods ===
623
664
  async getTxByHash(txHash, opts = {}) {
624
665
  const txHashStr = txHash.toString();
@@ -680,6 +721,10 @@ import { TxPoolIndices } from './tx_pool_indices.js';
680
721
  getPendingTxCount() {
681
722
  return this.#indices.getPendingTxCount();
682
723
  }
724
+ hasEligiblePendingTxs(minCount) {
725
+ const maxReceivedAt = this.#dateProvider.now() - this.#config.minTxPoolAgeMs;
726
+ return this.#indices.hasEligiblePendingTxs(maxReceivedAt, minCount);
727
+ }
683
728
  getMinedTxHashes() {
684
729
  return this.#indices.getMinedTxs().map(([hash, blockId])=>[
685
730
  TxHash.fromString(hash),
@@ -719,6 +764,9 @@ import { TxPoolIndices } from './tx_pool_indices.js';
719
764
  if (config.minTxPoolAgeMs !== undefined) {
720
765
  this.#config.minTxPoolAgeMs = config.minTxPoolAgeMs;
721
766
  }
767
+ if (config.keepFinalizedTxsForSlots !== undefined) {
768
+ this.#config.keepFinalizedTxsForSlots = config.keepFinalizedTxsForSlots;
769
+ }
722
770
  // Update eviction rules with new config
723
771
  this.#evictionManager.updateConfig(config);
724
772
  }
@@ -954,7 +1002,7 @@ import { TxPoolIndices } from './tx_pool_indices.js';
954
1002
  getFeePayerBalance: async (feePayer)=>{
955
1003
  const db = this.#worldStateSynchronizer.getCommitted();
956
1004
  const publicStateSource = new DatabasePublicStateSource(db);
957
- const balance = await publicStateSource.storageRead(ProtocolContractAddress.FeeJuice, await computeFeePayerBalanceStorageSlot(AztecAddress.fromString(feePayer)));
1005
+ const balance = await publicStateSource.storageRead(ProtocolContractAddress.FeeJuice, await computeFeePayerBalanceStorageSlot(AztecAddress.fromStringUnsafe(feePayer)));
958
1006
  return balance.toBigInt();
959
1007
  },
960
1008
  getFeePayerPendingTxs: (feePayer)=>this.#indices.getFeePayerPendingTxs(feePayer),
@@ -8,7 +8,6 @@ export declare class ProposalValidator {
8
8
  private logger;
9
9
  private txsPermitted;
10
10
  private maxTxsPerBlock?;
11
- private maxBlocksPerCheckpoint?;
12
11
  private skipSlotValidation;
13
12
  private signatureContext;
14
13
  private clockDisparityMs;
@@ -23,14 +22,19 @@ export declare class ProposalValidator {
23
22
  /** Validates header-level fields: slot, signature, and proposer. */
24
23
  validate(proposal: BlockProposal | CheckpointProposalCore): Promise<ValidationResult>;
25
24
  /**
26
- * Rejects a block proposal whose index within its checkpoint lands at or beyond the per-checkpoint
27
- * block limit. `MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT` is a hard ceiling applied even when
28
- * `maxBlocksPerCheckpoint` is unset; a lower configured value tightens it further.
29
- * `indexWithinCheckpoint` is 0-based, so a ceiling of 72 rejects the 73rd block. Applies to standalone
25
+ * Rejects a block proposal whose index within its checkpoint lands at or beyond the hard attestable
26
+ * ceiling `MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT` (a structurally impossible index that can never be
27
+ * part of any checkpoint we could attest to). `indexWithinCheckpoint` is 0-based, so a ceiling of 72
28
+ * rejects the 73rd block.
29
+ *
30
+ * Indices in `[maxBlocksPerCheckpoint, MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT)` are over the configured
31
+ * consensus limit but still structurally valid: they are proposer misbehavior, not relaying-peer
32
+ * fault, so they are *not* rejected here. The p2p service retains and re-broadcasts the first such
33
+ * proposal per (slot, proposer) as slashing evidence and skips processing it. Applies to standalone
30
34
  * block proposals and to the terminal block embedded in a checkpoint proposal.
31
35
  */
32
36
  validateBlockIndexWithinCheckpoint(proposal: BlockProposal): ValidationResult;
33
37
  /** Validates transaction-related fields of a block proposal. */
34
38
  validateTxs(proposal: BlockProposal): Promise<ValidationResult>;
35
39
  }
36
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvcG9zYWxfdmFsaWRhdG9yLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbXNnX3ZhbGlkYXRvcnMvcHJvcG9zYWxfdmFsaWRhdG9yL3Byb3Bvc2FsX3ZhbGlkYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBSTlELE9BQU8sRUFDTCxLQUFLLGFBQWEsRUFDbEIsS0FBSyxzQkFBc0IsRUFDM0IsS0FBSyw0QkFBNEIsRUFFakMsS0FBSyxnQkFBZ0IsRUFFdEIsTUFBTSxtQkFBbUIsQ0FBQztBQUMzQixPQUFPLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRWxFLG9GQUFvRjtBQUNwRixxQkFBYSxpQkFBaUI7SUFDNUIsT0FBTyxDQUFDLFVBQVUsQ0FBc0I7SUFDeEMsT0FBTyxDQUFDLFNBQVMsQ0FBcUI7SUFDdEMsT0FBTyxDQUFDLE1BQU0sQ0FBUztJQUN2QixPQUFPLENBQUMsWUFBWSxDQUFVO0lBQzlCLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBUztJQUNoQyxPQUFPLENBQUMsc0JBQXNCLENBQUMsQ0FBUztJQUN4QyxPQUFPLENBQUMsa0JBQWtCLENBQVU7SUFDcEMsT0FBTyxDQUFDLGdCQUFnQixDQUErQjtJQUN2RCxPQUFPLENBQUMsZ0JBQWdCLENBQVM7SUFFakMsWUFDRSxVQUFVLEVBQUUsbUJBQW1CLEVBQy9CLFNBQVMsRUFBRSxrQkFBa0IsRUFDN0IsSUFBSSxFQUFFO1FBQ0osWUFBWSxFQUFFLE9BQU8sQ0FBQztRQUN0QixjQUFjLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDeEIsc0JBQXNCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDaEMsa0JBQWtCLENBQUMsRUFBRSxPQUFPLENBQUM7UUFDN0IsZ0JBQWdCLEVBQUUsNEJBQTRCLENBQUM7UUFDL0MsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0tBQzFCLEVBQ0QsVUFBVSxFQUFFLE1BQU0sRUFXbkI7SUFFRCxvRUFBb0U7SUFDdkQsUUFBUSxDQUFDLFFBQVEsRUFBRSxhQUFhLEdBQUcsc0JBQXNCLEdBQUcsT0FBTyxDQUFDLGdCQUFnQixDQUFDLENBMEVqRztJQUVEOzs7Ozs7T0FNRztJQUNJLGtDQUFrQyxDQUFDLFFBQVEsRUFBRSxhQUFhLEdBQUcsZ0JBQWdCLENBWW5GO0lBRUQsZ0VBQWdFO0lBQ25ELFdBQVcsQ0FBQyxRQUFRLEVBQUUsYUFBYSxHQUFHLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxDQXdDM0U7Q0FDRiJ9
40
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvcG9zYWxfdmFsaWRhdG9yLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbXNnX3ZhbGlkYXRvcnMvcHJvcG9zYWxfdmFsaWRhdG9yL3Byb3Bvc2FsX3ZhbGlkYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBSTlELE9BQU8sRUFDTCxLQUFLLGFBQWEsRUFDbEIsS0FBSyxzQkFBc0IsRUFDM0IsS0FBSyw0QkFBNEIsRUFFakMsS0FBSyxnQkFBZ0IsRUFFdEIsTUFBTSxtQkFBbUIsQ0FBQztBQUMzQixPQUFPLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRWxFLG9GQUFvRjtBQUNwRixxQkFBYSxpQkFBaUI7SUFDNUIsT0FBTyxDQUFDLFVBQVUsQ0FBc0I7SUFDeEMsT0FBTyxDQUFDLFNBQVMsQ0FBcUI7SUFDdEMsT0FBTyxDQUFDLE1BQU0sQ0FBUztJQUN2QixPQUFPLENBQUMsWUFBWSxDQUFVO0lBQzlCLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBUztJQUNoQyxPQUFPLENBQUMsa0JBQWtCLENBQVU7SUFDcEMsT0FBTyxDQUFDLGdCQUFnQixDQUErQjtJQUN2RCxPQUFPLENBQUMsZ0JBQWdCLENBQVM7SUFFakMsWUFDRSxVQUFVLEVBQUUsbUJBQW1CLEVBQy9CLFNBQVMsRUFBRSxrQkFBa0IsRUFDN0IsSUFBSSxFQUFFO1FBQ0osWUFBWSxFQUFFLE9BQU8sQ0FBQztRQUN0QixjQUFjLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDeEIsc0JBQXNCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDaEMsa0JBQWtCLENBQUMsRUFBRSxPQUFPLENBQUM7UUFDN0IsZ0JBQWdCLEVBQUUsNEJBQTRCLENBQUM7UUFDL0MsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0tBQzFCLEVBQ0QsVUFBVSxFQUFFLE1BQU0sRUFVbkI7SUFFRCxvRUFBb0U7SUFDdkQsUUFBUSxDQUFDLFFBQVEsRUFBRSxhQUFhLEdBQUcsc0JBQXNCLEdBQUcsT0FBTyxDQUFDLGdCQUFnQixDQUFDLENBa0ZqRztJQUVEOzs7Ozs7Ozs7OztPQVdHO0lBQ0ksa0NBQWtDLENBQUMsUUFBUSxFQUFFLGFBQWEsR0FBRyxnQkFBZ0IsQ0FRbkY7SUFFRCxnRUFBZ0U7SUFDbkQsV0FBVyxDQUFDLFFBQVEsRUFBRSxhQUFhLEdBQUcsT0FBTyxDQUFDLGdCQUFnQixDQUFDLENBd0MzRTtDQUNGIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"proposal_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/proposal_validator/proposal_validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAI9D,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EAEjC,KAAK,gBAAgB,EAEtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,oFAAoF;AACpF,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,sBAAsB,CAAC,CAAS;IACxC,OAAO,CAAC,kBAAkB,CAAU;IACpC,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,gBAAgB,CAAS;IAEjC,YACE,UAAU,EAAE,mBAAmB,EAC/B,SAAS,EAAE,kBAAkB,EAC7B,IAAI,EAAE;QACJ,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,gBAAgB,EAAE,4BAA4B,CAAC;QAC/C,gBAAgB,EAAE,MAAM,CAAC;KAC1B,EACD,UAAU,EAAE,MAAM,EAWnB;IAED,oEAAoE;IACvD,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA0EjG;IAED;;;;;;OAMG;IACI,kCAAkC,CAAC,QAAQ,EAAE,aAAa,GAAG,gBAAgB,CAYnF;IAED,gEAAgE;IACnD,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwC3E;CACF"}
1
+ {"version":3,"file":"proposal_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/proposal_validator/proposal_validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAI9D,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EAEjC,KAAK,gBAAgB,EAEtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,oFAAoF;AACpF,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,kBAAkB,CAAU;IACpC,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,gBAAgB,CAAS;IAEjC,YACE,UAAU,EAAE,mBAAmB,EAC/B,SAAS,EAAE,kBAAkB,EAC7B,IAAI,EAAE;QACJ,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,gBAAgB,EAAE,4BAA4B,CAAC;QAC/C,gBAAgB,EAAE,MAAM,CAAC;KAC1B,EACD,UAAU,EAAE,MAAM,EAUnB;IAED,oEAAoE;IACvD,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAkFjG;IAED;;;;;;;;;;;OAWG;IACI,kCAAkC,CAAC,QAAQ,EAAE,aAAa,GAAG,gBAAgB,CAQnF;IAED,gEAAgE;IACnD,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwC3E;CACF"}
@@ -8,7 +8,6 @@ import { PeerErrorSeverity, hasValidSignatureContext } from '@aztec/stdlib/p2p';
8
8
  logger;
9
9
  txsPermitted;
10
10
  maxTxsPerBlock;
11
- maxBlocksPerCheckpoint;
12
11
  skipSlotValidation;
13
12
  signatureContext;
14
13
  clockDisparityMs;
@@ -17,7 +16,6 @@ import { PeerErrorSeverity, hasValidSignatureContext } from '@aztec/stdlib/p2p';
17
16
  this.timetable = timetable;
18
17
  this.txsPermitted = opts.txsPermitted;
19
18
  this.maxTxsPerBlock = opts.maxTxsPerBlock;
20
- this.maxBlocksPerCheckpoint = opts.maxBlocksPerCheckpoint;
21
19
  this.skipSlotValidation = opts.skipSlotValidation ?? false;
22
20
  this.signatureContext = opts.signatureContext;
23
21
  this.clockDisparityMs = opts.clockDisparityMs;
@@ -76,7 +74,14 @@ import { PeerErrorSeverity, hasValidSignatureContext } from '@aztec/stdlib/p2p';
76
74
  }
77
75
  // Proposer check
78
76
  const expectedProposer = await this.epochCache.getProposerAttesterAddressInSlot(slotNumber);
79
- if (expectedProposer !== undefined && !proposer.equals(expectedProposer)) {
77
+ if (expectedProposer === undefined) {
78
+ this.logger.warn(`Penalizing peer for proposal with no expected proposer for current slot ${slotNumber}`);
79
+ return {
80
+ result: 'reject',
81
+ severity: PeerErrorSeverity.HighToleranceError
82
+ };
83
+ }
84
+ if (!proposer.equals(expectedProposer)) {
80
85
  this.logger.warn(`Penalizing peer for invalid proposer for current slot ${slotNumber}`, {
81
86
  expectedProposer,
82
87
  proposer: proposer.toString()
@@ -86,8 +91,12 @@ import { PeerErrorSeverity, hasValidSignatureContext } from '@aztec/stdlib/p2p';
86
91
  severity: PeerErrorSeverity.MidToleranceError
87
92
  };
88
93
  }
89
- // A block proposal whose index lands beyond the checkpoint block limit can never be part of a
90
- // checkpoint we would attest to, so reject it immediately at ingress.
94
+ // A block proposal whose index lands at or beyond the hard attestable ceiling is structurally
95
+ // impossible garbage, so reject it immediately at ingress. Indices in
96
+ // `[maxBlocksPerCheckpoint, MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT)` are over the consensus limit
97
+ // but structurally valid proposer misbehavior; they pass gossip validation here so the offending
98
+ // proposal can be retained and re-broadcast as slashing evidence (handled downstream in the p2p
99
+ // service), rather than penalizing the relaying peer.
91
100
  if ('indexWithinCheckpoint' in proposal) {
92
101
  const indexResult = this.validateBlockIndexWithinCheckpoint(proposal);
93
102
  if (indexResult.result !== 'accept') {
@@ -108,15 +117,19 @@ import { PeerErrorSeverity, hasValidSignatureContext } from '@aztec/stdlib/p2p';
108
117
  }
109
118
  }
110
119
  /**
111
- * Rejects a block proposal whose index within its checkpoint lands at or beyond the per-checkpoint
112
- * block limit. `MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT` is a hard ceiling applied even when
113
- * `maxBlocksPerCheckpoint` is unset; a lower configured value tightens it further.
114
- * `indexWithinCheckpoint` is 0-based, so a ceiling of 72 rejects the 73rd block. Applies to standalone
120
+ * Rejects a block proposal whose index within its checkpoint lands at or beyond the hard attestable
121
+ * ceiling `MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT` (a structurally impossible index that can never be
122
+ * part of any checkpoint we could attest to). `indexWithinCheckpoint` is 0-based, so a ceiling of 72
123
+ * rejects the 73rd block.
124
+ *
125
+ * Indices in `[maxBlocksPerCheckpoint, MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT)` are over the configured
126
+ * consensus limit but still structurally valid: they are proposer misbehavior, not relaying-peer
127
+ * fault, so they are *not* rejected here. The p2p service retains and re-broadcasts the first such
128
+ * proposal per (slot, proposer) as slashing evidence and skips processing it. Applies to standalone
115
129
  * block proposals and to the terminal block embedded in a checkpoint proposal.
116
130
  */ validateBlockIndexWithinCheckpoint(proposal) {
117
- const maxBlocksPerCheckpoint = Math.min(this.maxBlocksPerCheckpoint ?? MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT, MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT);
118
- if (proposal.indexWithinCheckpoint >= maxBlocksPerCheckpoint) {
119
- this.logger.warn(`Penalizing peer for proposal with indexWithinCheckpoint ${proposal.indexWithinCheckpoint} >= max ${maxBlocksPerCheckpoint}`);
131
+ if (proposal.indexWithinCheckpoint >= MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT) {
132
+ this.logger.warn(`Penalizing peer for proposal with indexWithinCheckpoint ${proposal.indexWithinCheckpoint} >= attestable ceiling ${MAX_ATTESTABLE_BLOCKS_PER_CHECKPOINT}`);
120
133
  return {
121
134
  result: 'reject',
122
135
  severity: PeerErrorSeverity.MidToleranceError
@@ -74,7 +74,7 @@ export class PhasesTxValidator {
74
74
  if (entry.onlySelf && !publicCall.request.msgSender.equals(contractAddress)) {
75
75
  return TX_ERROR_SETUP_ONLY_SELF_WRONG_SENDER;
76
76
  }
77
- if (entry.rejectNullMsgSender && publicCall.request.msgSender.equals(AztecAddress.fromBigInt(NULL_MSG_SENDER_CONTRACT_ADDRESS))) {
77
+ if (entry.rejectNullMsgSender && publicCall.request.msgSender.equals(AztecAddress.fromBigIntUnsafe(NULL_MSG_SENDER_CONTRACT_ADDRESS))) {
78
78
  return TX_ERROR_SETUP_NULL_MSG_SENDER;
79
79
  }
80
80
  return undefined;
@@ -103,7 +103,7 @@ export class PhasesTxValidator {
103
103
  if (entry.onlySelf && !publicCall.request.msgSender.equals(contractAddress)) {
104
104
  return TX_ERROR_SETUP_ONLY_SELF_WRONG_SENDER;
105
105
  }
106
- if (entry.rejectNullMsgSender && publicCall.request.msgSender.equals(AztecAddress.fromBigInt(NULL_MSG_SENDER_CONTRACT_ADDRESS))) {
106
+ if (entry.rejectNullMsgSender && publicCall.request.msgSender.equals(AztecAddress.fromBigIntUnsafe(NULL_MSG_SENDER_CONTRACT_ADDRESS))) {
107
107
  return TX_ERROR_SETUP_NULL_MSG_SENDER;
108
108
  }
109
109
  return undefined;
@@ -1,3 +1,5 @@
1
+ import { DateProvider } from '@aztec/foundation/timer';
2
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
1
3
  import { type TelemetryClient } from '@aztec/telemetry-client';
2
4
  import type { PeerId } from '@libp2p/interface';
3
5
  import { type IDiscv5CreateOptions } from '@nethermindeth/discv5';
@@ -13,6 +15,7 @@ export declare class DiscV5Service extends EventEmitter implements PeerDiscovery
13
15
  private config;
14
16
  private readonly packageVersion;
15
17
  private logger;
18
+ private readonly dateProvider;
16
19
  /** The Discv5 instance */
17
20
  private discv5;
18
21
  /** This instance's ENR */
@@ -23,10 +26,14 @@ export declare class DiscV5Service extends EventEmitter implements PeerDiscovery
23
26
  private bootstrapNodePeerIds;
24
27
  bootstrapNodeEnrs: ENR[];
25
28
  private trustedPeerEnrs;
29
+ /** Node IDs of all config-provided peers (bootstrap, trusted, private); these are never persisted. */
30
+ private readonly configProvidedNodeIds;
26
31
  private startTime;
27
32
  private currentIp;
33
+ /** Persistent store of discovered peer ENRs, used to re-seed discovery after a restart. */
34
+ private readonly persistedEnrs?;
28
35
  private handlers;
29
- constructor(peerId: PeerId, config: P2PConfig, packageVersion: string, telemetry?: TelemetryClient, logger?: import("@aztec/foundation/log").Logger, configOverrides?: Partial<IDiscv5CreateOptions>);
36
+ constructor(peerId: PeerId, config: P2PConfig, packageVersion: string, telemetry?: TelemetryClient, logger?: import("@aztec/foundation/log").Logger, store?: AztecAsyncKVStore, configOverrides?: Partial<IDiscv5CreateOptions>, dateProvider?: DateProvider);
30
37
  private onMultiaddrUpdated;
31
38
  start(): Promise<void>;
32
39
  runRandomNodesQuery(): Promise<void>;
@@ -37,8 +44,11 @@ export declare class DiscV5Service extends EventEmitter implements PeerDiscovery
37
44
  isBootstrapPeer(peerId: PeerId): boolean;
38
45
  stop(): Promise<void>;
39
46
  private onEnrAdded;
47
+ private onPeerDiscovered;
48
+ /** Whether an ENR belongs in the persisted store: a validated, non-config peer that isn't ourselves. */
49
+ private shouldPersist;
40
50
  private isOurBootnode;
41
51
  private onDiscovered;
42
52
  private validateEnr;
43
53
  }
44
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzY1Y1X3NlcnZpY2UuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9kaXNjdjUvZGlzY1Y1X3NlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBR0EsT0FBTyxFQUFzQixLQUFLLGVBQWUsRUFBc0IsTUFBTSx5QkFBeUIsQ0FBQztBQUV2RyxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUVoRCxPQUFPLEVBQW1DLEtBQUssb0JBQW9CLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRyxPQUFPLEVBQUUsR0FBRyxFQUFlLE1BQU0sb0JBQW9CLENBQUM7QUFDdEQsT0FBTyxZQUFZLE1BQU0sUUFBUSxDQUFDO0FBRWxDLE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBSWpELE9BQU8sRUFBRSxLQUFLLG9CQUFvQixFQUFFLGtCQUFrQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBSTlFOztHQUVHO0FBQ0gscUJBQWEsYUFBYyxTQUFRLFlBQWEsWUFBVyxvQkFBb0I7SUEyQjNFLE9BQU8sQ0FBQyxNQUFNO0lBQ2QsT0FBTyxDQUFDLE1BQU07SUFDZCxPQUFPLENBQUMsUUFBUSxDQUFDLGNBQWM7SUFFL0IsT0FBTyxDQUFDLE1BQU07SUE5QmhCLDBCQUEwQjtJQUMxQixPQUFPLENBQUMsTUFBTSxDQUFxQjtJQUVuQywwQkFBMEI7SUFDMUIsT0FBTyxDQUFDLEdBQUcsQ0FBYztJQUV6QiwyQkFBMkI7SUFDM0IsT0FBTyxDQUFDLFFBQVEsQ0FBcUI7SUFFckMsT0FBTyxDQUFDLFlBQVksQ0FBOEI7SUFFbEQsT0FBTyxDQUFDLG9CQUFvQixDQUFnQjtJQUNyQyxpQkFBaUIsRUFBRSxHQUFHLEVBQUUsQ0FBTTtJQUNyQyxPQUFPLENBQUMsZUFBZSxDQUFhO0lBRXBDLE9BQU8sQ0FBQyxTQUFTLENBQUs7SUFFdEIsT0FBTyxDQUFDLFNBQVMsQ0FBcUI7SUFFdEMsT0FBTyxDQUFDLFFBQVEsQ0FJZDtJQUVGLFlBQ1UsTUFBTSxFQUFFLE1BQU0sRUFDZCxNQUFNLEVBQUUsU0FBUyxFQUNSLGNBQWMsRUFBRSxNQUFNLEVBQ3ZDLFNBQVMsR0FBRSxlQUFzQyxFQUN6QyxNQUFNLHlDQUFxQyxFQUNuRCxlQUFlLEdBQUUsT0FBTyxDQUFDLG9CQUFvQixDQUFNLEVBNkVwRDtJQUVELE9BQU8sQ0FBQyxrQkFBa0I7SUErQmIsS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0EwRGxDO0lBRVksbUJBQW1CLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQWlCaEQ7SUFFTSxZQUFZLElBQUksR0FBRyxFQUFFLENBRTNCO0lBRU0sTUFBTSxJQUFJLEdBQUcsQ0FFbkI7SUFFTSxTQUFTLElBQUksTUFBTSxDQUV6QjtJQUVNLFNBQVMsSUFBSSxrQkFBa0IsQ0FFckM7SUFFTSxlQUFlLENBQUMsTUFBTSxFQUFFLE1BQU0sR0FBRyxPQUFPLENBRTlDO0lBRVksSUFBSSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FXakM7WUFFYSxVQUFVO0lBT3hCLE9BQU8sQ0FBQyxhQUFhO0lBSXJCLE9BQU8sQ0FBQyxZQUFZO0lBcUJwQixPQUFPLENBQUMsV0FBVztDQTBCcEIifQ==
54
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzY1Y1X3NlcnZpY2UuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9kaXNjdjUvZGlzY1Y1X3NlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFekQsT0FBTyxFQUFzQixLQUFLLGVBQWUsRUFBc0IsTUFBTSx5QkFBeUIsQ0FBQztBQUV2RyxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUVoRCxPQUFPLEVBQW1DLEtBQUssb0JBQW9CLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRyxPQUFPLEVBQUUsR0FBRyxFQUFlLE1BQU0sb0JBQW9CLENBQUM7QUFDdEQsT0FBTyxZQUFZLE1BQU0sUUFBUSxDQUFDO0FBRWxDLE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBSWpELE9BQU8sRUFBRSxLQUFLLG9CQUFvQixFQUFFLGtCQUFrQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBUTlFOztHQUVHO0FBQ0gscUJBQWEsYUFBYyxTQUFRLFlBQWEsWUFBVyxvQkFBb0I7SUFnQzNFLE9BQU8sQ0FBQyxNQUFNO0lBQ2QsT0FBTyxDQUFDLE1BQU07SUFDZCxPQUFPLENBQUMsUUFBUSxDQUFDLGNBQWM7SUFFL0IsT0FBTyxDQUFDLE1BQU07SUFHZCxPQUFPLENBQUMsUUFBUSxDQUFDLFlBQVk7SUF0Qy9CLDBCQUEwQjtJQUMxQixPQUFPLENBQUMsTUFBTSxDQUFxQjtJQUVuQywwQkFBMEI7SUFDMUIsT0FBTyxDQUFDLEdBQUcsQ0FBYztJQUV6QiwyQkFBMkI7SUFDM0IsT0FBTyxDQUFDLFFBQVEsQ0FBcUI7SUFFckMsT0FBTyxDQUFDLFlBQVksQ0FBOEI7SUFFbEQsT0FBTyxDQUFDLG9CQUFvQixDQUFnQjtJQUNyQyxpQkFBaUIsRUFBRSxHQUFHLEVBQUUsQ0FBTTtJQUNyQyxPQUFPLENBQUMsZUFBZSxDQUFhO0lBQ3BDLHNHQUFzRztJQUN0RyxPQUFPLENBQUMsUUFBUSxDQUFDLHFCQUFxQixDQUFjO0lBRXBELE9BQU8sQ0FBQyxTQUFTLENBQUs7SUFFdEIsT0FBTyxDQUFDLFNBQVMsQ0FBcUI7SUFFdEMsMkZBQTJGO0lBQzNGLE9BQU8sQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQW9CO0lBRW5ELE9BQU8sQ0FBQyxRQUFRLENBSWQ7SUFFRixZQUNVLE1BQU0sRUFBRSxNQUFNLEVBQ2QsTUFBTSxFQUFFLFNBQVMsRUFDUixjQUFjLEVBQUUsTUFBTSxFQUN2QyxTQUFTLEdBQUUsZUFBc0MsRUFDekMsTUFBTSx5Q0FBcUMsRUFDbkQsS0FBSyxDQUFDLEVBQUUsaUJBQWlCLEVBQ3pCLGVBQWUsR0FBRSxPQUFPLENBQUMsb0JBQW9CLENBQU0sRUFDbEMsWUFBWSxHQUFFLFlBQWlDLEVBc0ZqRTtJQUVELE9BQU8sQ0FBQyxrQkFBa0I7SUErQmIsS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0E4RWxDO0lBRVksbUJBQW1CLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQWlCaEQ7SUFFTSxZQUFZLElBQUksR0FBRyxFQUFFLENBRTNCO0lBRU0sTUFBTSxJQUFJLEdBQUcsQ0FFbkI7SUFFTSxTQUFTLElBQUksTUFBTSxDQUV6QjtJQUVNLFNBQVMsSUFBSSxrQkFBa0IsQ0FFckM7SUFFTSxlQUFlLENBQUMsTUFBTSxFQUFFLE1BQU0sR0FBRyxPQUFPLENBRTlDO0lBRVksSUFBSSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FXakM7WUFFYSxVQUFVO1lBa0JWLGdCQUFnQjtJQVU5Qix3R0FBd0c7SUFDeEcsT0FBTyxDQUFDLGFBQWE7SUFJckIsT0FBTyxDQUFDLGFBQWE7SUFJckIsT0FBTyxDQUFDLFlBQVk7SUFxQnBCLE9BQU8sQ0FBQyxXQUFXO0NBcUNwQiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"discV5_service.d.ts","sourceRoot":"","sources":["../../../src/services/discv5/discV5_service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAEvG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAmC,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAE,GAAG,EAAe,MAAM,oBAAoB,CAAC;AACtD,OAAO,YAAY,MAAM,QAAQ,CAAC;AAElC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,OAAO,EAAE,KAAK,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAI9E;;GAEG;AACH,qBAAa,aAAc,SAAQ,YAAa,YAAW,oBAAoB;IA2B3E,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ,CAAC,cAAc;IAE/B,OAAO,CAAC,MAAM;IA9BhB,0BAA0B;IAC1B,OAAO,CAAC,MAAM,CAAqB;IAEnC,0BAA0B;IAC1B,OAAO,CAAC,GAAG,CAAc;IAEzB,2BAA2B;IAC3B,OAAO,CAAC,QAAQ,CAAqB;IAErC,OAAO,CAAC,YAAY,CAA8B;IAElD,OAAO,CAAC,oBAAoB,CAAgB;IACrC,iBAAiB,EAAE,GAAG,EAAE,CAAM;IACrC,OAAO,CAAC,eAAe,CAAa;IAEpC,OAAO,CAAC,SAAS,CAAK;IAEtB,OAAO,CAAC,SAAS,CAAqB;IAEtC,OAAO,CAAC,QAAQ,CAId;IAEF,YACU,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,SAAS,EACR,cAAc,EAAE,MAAM,EACvC,SAAS,GAAE,eAAsC,EACzC,MAAM,yCAAqC,EACnD,eAAe,GAAE,OAAO,CAAC,oBAAoB,CAAM,EA6EpD;IAED,OAAO,CAAC,kBAAkB;IA+Bb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CA0DlC;IAEY,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBhD;IAEM,YAAY,IAAI,GAAG,EAAE,CAE3B;IAEM,MAAM,IAAI,GAAG,CAEnB;IAEM,SAAS,IAAI,MAAM,CAEzB;IAEM,SAAS,IAAI,kBAAkB,CAErC;IAEM,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE9C;IAEY,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAWjC;YAEa,UAAU;IAOxB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,WAAW;CA0BpB"}
1
+ {"version":3,"file":"discV5_service.d.ts","sourceRoot":"","sources":["../../../src/services/discv5/discV5_service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EAAsB,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAEvG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAmC,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAE,GAAG,EAAe,MAAM,oBAAoB,CAAC;AACtD,OAAO,YAAY,MAAM,QAAQ,CAAC;AAElC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,OAAO,EAAE,KAAK,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAQ9E;;GAEG;AACH,qBAAa,aAAc,SAAQ,YAAa,YAAW,oBAAoB;IAgC3E,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ,CAAC,cAAc;IAE/B,OAAO,CAAC,MAAM;IAGd,OAAO,CAAC,QAAQ,CAAC,YAAY;IAtC/B,0BAA0B;IAC1B,OAAO,CAAC,MAAM,CAAqB;IAEnC,0BAA0B;IAC1B,OAAO,CAAC,GAAG,CAAc;IAEzB,2BAA2B;IAC3B,OAAO,CAAC,QAAQ,CAAqB;IAErC,OAAO,CAAC,YAAY,CAA8B;IAElD,OAAO,CAAC,oBAAoB,CAAgB;IACrC,iBAAiB,EAAE,GAAG,EAAE,CAAM;IACrC,OAAO,CAAC,eAAe,CAAa;IACpC,sGAAsG;IACtG,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAc;IAEpD,OAAO,CAAC,SAAS,CAAK;IAEtB,OAAO,CAAC,SAAS,CAAqB;IAEtC,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAoB;IAEnD,OAAO,CAAC,QAAQ,CAId;IAEF,YACU,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,SAAS,EACR,cAAc,EAAE,MAAM,EACvC,SAAS,GAAE,eAAsC,EACzC,MAAM,yCAAqC,EACnD,KAAK,CAAC,EAAE,iBAAiB,EACzB,eAAe,GAAE,OAAO,CAAC,oBAAoB,CAAM,EAClC,YAAY,GAAE,YAAiC,EAsFjE;IAED,OAAO,CAAC,kBAAkB;IA+Bb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CA8ElC;IAEY,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBhD;IAEM,YAAY,IAAI,GAAG,EAAE,CAE3B;IAEM,MAAM,IAAI,GAAG,CAEnB;IAEM,SAAS,IAAI,MAAM,CAEzB;IAEM,SAAS,IAAI,kBAAkB,CAErC;IAEM,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE9C;IAEY,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAWjC;YAEa,UAAU;YAkBV,gBAAgB;IAU9B,wGAAwG;IACxG,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,WAAW;CAqCpB"}
@@ -1,5 +1,6 @@
1
1
  import { createLogger } from '@aztec/foundation/log';
2
2
  import { sleep } from '@aztec/foundation/sleep';
3
+ import { DateProvider } from '@aztec/foundation/timer';
3
4
  import { checkCompressedComponentVersion } from '@aztec/stdlib/versioning';
4
5
  import { OtelMetricsAdapter, getTelemetryClient } from '@aztec/telemetry-client';
5
6
  import { multiaddr } from '@multiformats/multiaddr';
@@ -10,7 +11,9 @@ import { createNodeENR } from '../../enr/generate-enr.js';
10
11
  import { AZTEC_ENR_KEY, Discv5Event, PeerEvent } from '../../types/index.js';
11
12
  import { convertToMultiaddr } from '../../util.js';
12
13
  import { PeerDiscoveryState } from '../service.js';
14
+ import { PersistedEnrStore } from './persisted_enr_store.js';
13
15
  const delayBeforeStart = 2000; // 2sec
16
+ /** Upper bound on persisted peer ENRs, to keep the store bounded as peers churn. */ const MAX_PERSISTED_PEER_ENRS = 100;
14
17
  /**
15
18
  * Peer discovery service using Discv5.
16
19
  */ export class DiscV5Service extends EventEmitter {
@@ -18,6 +21,7 @@ const delayBeforeStart = 2000; // 2sec
18
21
  config;
19
22
  packageVersion;
20
23
  logger;
24
+ dateProvider;
21
25
  /** The Discv5 instance */ discv5;
22
26
  /** This instance's ENR */ enr;
23
27
  /** Version identifiers. */ versions;
@@ -25,20 +29,30 @@ const delayBeforeStart = 2000; // 2sec
25
29
  bootstrapNodePeerIds;
26
30
  bootstrapNodeEnrs;
27
31
  trustedPeerEnrs;
32
+ /** Node IDs of all config-provided peers (bootstrap, trusted, private); these are never persisted. */ configProvidedNodeIds;
28
33
  startTime;
29
34
  currentIp;
35
+ /** Persistent store of discovered peer ENRs, used to re-seed discovery after a restart. */ persistedEnrs;
30
36
  handlers;
31
- constructor(peerId, config, packageVersion, telemetry = getTelemetryClient(), logger = createLogger('p2p:discv5_service'), configOverrides = {}){
32
- super(), this.peerId = peerId, this.config = config, this.packageVersion = packageVersion, this.logger = logger, this.currentState = PeerDiscoveryState.STOPPED, this.bootstrapNodePeerIds = [], this.bootstrapNodeEnrs = [], this.trustedPeerEnrs = [], this.startTime = 0, this.handlers = {
37
+ constructor(peerId, config, packageVersion, telemetry = getTelemetryClient(), logger = createLogger('p2p:discv5_service'), store, configOverrides = {}, dateProvider = new DateProvider()){
38
+ super(), this.peerId = peerId, this.config = config, this.packageVersion = packageVersion, this.logger = logger, this.dateProvider = dateProvider, this.currentState = PeerDiscoveryState.STOPPED, this.bootstrapNodePeerIds = [], this.bootstrapNodeEnrs = [], this.trustedPeerEnrs = [], this.startTime = 0, this.handlers = {
33
39
  onMultiaddrUpdated: this.onMultiaddrUpdated.bind(this),
34
- onDiscovered: this.onDiscovered.bind(this),
40
+ onPeerDiscovered: this.onPeerDiscovered.bind(this),
35
41
  onEnrAdded: this.onEnrAdded.bind(this)
36
42
  };
43
+ this.persistedEnrs = store ? new PersistedEnrStore(store, MAX_PERSISTED_PEER_ENRS, createLogger(`${this.logger.module}:enr-store`)) : undefined;
37
44
  const { p2pIp, p2pPort, p2pBroadcastPort, bootstrapNodes, trustedPeers, privatePeers } = config;
38
45
  this.currentIp = p2pIp;
39
46
  this.bootstrapNodeEnrs = bootstrapNodes.map((x)=>ENR.decodeTxt(x));
40
47
  const privatePeerEnrs = new Set(privatePeers);
41
48
  this.trustedPeerEnrs = trustedPeers.filter((x)=>!privatePeerEnrs.has(x)).map((x)=>ENR.decodeTxt(x));
49
+ // Peers supplied via config are re-injected from config on every start, so they must never end up
50
+ // in the persisted (discovered-peer) store — private peers especially must not be written there.
51
+ this.configProvidedNodeIds = new Set([
52
+ ...bootstrapNodes,
53
+ ...trustedPeers,
54
+ ...privatePeers
55
+ ].map((x)=>ENR.decodeTxt(x).nodeId));
42
56
  // If no overridden broadcast port is provided, use the p2p port as the broadcast port
43
57
  if (!p2pBroadcastPort) {
44
58
  this.logger.warn('No p2pBroadcastPort provided, using p2pPort as broadcast port');
@@ -86,7 +100,7 @@ const delayBeforeStart = 2000; // 2sec
86
100
  return origOnEstablished(...args);
87
101
  }
88
102
  };
89
- this.discv5.on(Discv5Event.DISCOVERED, this.handlers.onDiscovered);
103
+ this.discv5.on(Discv5Event.DISCOVERED, this.handlers.onPeerDiscovered);
90
104
  this.discv5.on(Discv5Event.ENR_ADDED, this.handlers.onEnrAdded);
91
105
  this.discv5.on(Discv5Event.MULTIADDR_UPDATED, this.handlers.onMultiaddrUpdated);
92
106
  }
@@ -124,7 +138,7 @@ const delayBeforeStart = 2000; // 2sec
124
138
  }
125
139
  this.logger.debug('Starting DiscV5');
126
140
  await this.discv5.start();
127
- this.startTime = Date.now();
141
+ this.startTime = this.dateProvider.now();
128
142
  const enrUpdateEnabled = this.config.queryForIp || !this.config.p2pIp;
129
143
  this.logger.info(`DiscV5 service started`, {
130
144
  nodeId: this.enr.nodeId,
@@ -165,6 +179,23 @@ const delayBeforeStart = 2000; // 2sec
165
179
  this.discv5.addEnr(enr);
166
180
  }
167
181
  }
182
+ // Re-seed discovery from peers persisted on previous runs. This lets a node rejoin the network
183
+ // after a restart even when no bootstrap node is reachable: discv5 has no on-disk routing table,
184
+ // so without this its DHT would start empty and rediscovery would depend on inbound dials alone.
185
+ if (this.persistedEnrs) {
186
+ // Drop peers that are now config-provided (re-injected above) or fail version checks.
187
+ const enrs = await this.persistedEnrs.load((enr)=>this.validateEnr(enr) && !this.configProvidedNodeIds.has(enr.nodeId));
188
+ for (const enr of enrs){
189
+ try {
190
+ this.discv5.addEnr(enr);
191
+ } catch (err) {
192
+ this.logger.debug(`Error re-seeding persisted ENR ${enr.nodeId}`, err);
193
+ }
194
+ }
195
+ if (enrs.length > 0) {
196
+ this.logger.info(`Re-seeded discovery with ${enrs.length} persisted peer ENRs`);
197
+ }
198
+ }
168
199
  }
169
200
  async runRandomNodesQuery() {
170
201
  if (this.currentState !== PeerDiscoveryState.RUNNING) {
@@ -172,8 +203,8 @@ const delayBeforeStart = 2000; // 2sec
172
203
  }
173
204
  // First, wait some time before starting the peer discovery
174
205
  // reference: https://github.com/ChainSafe/lodestar/issues/3423
175
- const msSinceStart = Date.now() - this.startTime;
176
- if (Date.now() - this.startTime <= delayBeforeStart) {
206
+ const msSinceStart = this.dateProvider.now() - this.startTime;
207
+ if (this.dateProvider.now() - this.startTime <= delayBeforeStart) {
177
208
  await sleep(delayBeforeStart - msSinceStart);
178
209
  }
179
210
  try {
@@ -201,22 +232,48 @@ const delayBeforeStart = 2000; // 2sec
201
232
  if (this.currentState !== PeerDiscoveryState.RUNNING) {
202
233
  return;
203
234
  }
204
- await this.discv5.off(Discv5Event.DISCOVERED, this.handlers.onDiscovered);
235
+ await this.discv5.off(Discv5Event.DISCOVERED, this.handlers.onPeerDiscovered);
205
236
  await this.discv5.off(Discv5Event.ENR_ADDED, this.handlers.onEnrAdded);
206
237
  await this.discv5.off(Discv5Event.MULTIADDR_UPDATED, this.handlers.onMultiaddrUpdated);
207
238
  await this.discv5.stop();
208
239
  this.currentState = PeerDiscoveryState.STOPPED;
209
240
  }
210
241
  async onEnrAdded(enr) {
211
- const multiAddrTcp = await enr.getFullMultiaddr('tcp');
212
- const multiAddrUdp = await enr.getFullMultiaddr('udp');
213
- this.logger.debug(`Added ENR ${enr.encodeTxt()}`, {
214
- multiAddrTcp,
215
- multiAddrUdp,
216
- nodeId: enr.nodeId
217
- });
242
+ try {
243
+ const multiAddrTcp = await enr.getFullMultiaddr('tcp');
244
+ const multiAddrUdp = await enr.getFullMultiaddr('udp');
245
+ this.logger.debug(`Added ENR ${enr.encodeTxt()}`, {
246
+ multiAddrTcp,
247
+ multiAddrUdp,
248
+ nodeId: enr.nodeId
249
+ });
250
+ // A peer just entered the routing table — persist it so we can re-seed discovery after a restart.
251
+ if (this.persistedEnrs && this.shouldPersist(enr)) {
252
+ await this.persistedEnrs.persist(enr);
253
+ }
254
+ this.onDiscovered(enr);
255
+ } catch (err) {
256
+ // A malformed ENR address field (e.g. a 3-byte TCP port) makes @nethermindeth/enr throw while
257
+ // parsing the multiaddr. This is an async event listener, so an unhandled rejection would crash
258
+ // the process — catch, log, and drop the ENR instead.
259
+ this.logger.warn(`Dropping ENR with unparseable address`, {
260
+ nodeId: enr.nodeId,
261
+ err
262
+ });
263
+ }
264
+ }
265
+ async onPeerDiscovered(enr) {
266
+ // discv5 updates an existing routing-table entry on an ENR sequence bump without emitting enrAdded,
267
+ // so DISCOVERED is our only signal that a peer we already track may have changed address. Refresh
268
+ // the persisted copy (a no-op for peers we don't already store) so we don't keep dialing a stale ENR.
269
+ if (this.persistedEnrs && this.shouldPersist(enr)) {
270
+ await this.persistedEnrs.refresh(enr);
271
+ }
218
272
  this.onDiscovered(enr);
219
273
  }
274
+ /** Whether an ENR belongs in the persisted store: a validated, non-config peer that isn't ourselves. */ shouldPersist(enr) {
275
+ return !this.configProvidedNodeIds.has(enr.nodeId) && enr.nodeId !== this.enr.nodeId && this.validateEnr(enr);
276
+ }
220
277
  isOurBootnode(enr) {
221
278
  return this.bootstrapNodeEnrs.some((x)=>x.nodeId === enr.nodeId);
222
279
  }
@@ -247,6 +304,18 @@ const delayBeforeStart = 2000; // 2sec
247
304
  this.logger.debug(`Peer node ${enr.nodeId} does not have aztec key in ENR`);
248
305
  return false;
249
306
  }
307
+ // A malformed TCP field (e.g. a non-2-byte port) makes @nethermindeth/enr throw while building the
308
+ // multiaddr. Reject such an ENR here so it's never emitted as a full peer or re-parsed by an
309
+ // unguarded listener (which would crash the process). A missing TCP addr is allowed — those peers
310
+ // are simply skipped at dial time.
311
+ try {
312
+ enr.getLocationMultiaddr('tcp');
313
+ } catch (err) {
314
+ this.logger.debug(`Peer node ${enr.nodeId} has an unparseable TCP address`, {
315
+ err
316
+ });
317
+ return false;
318
+ }
250
319
  // And check it has the correct version
251
320
  let compressedVersion;
252
321
  try {
@@ -0,0 +1,38 @@
1
+ import { type Logger } from '@aztec/foundation/log';
2
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
3
+ import { ENR } from '@nethermindeth/enr';
4
+ /**
5
+ * A bounded, transactional store of discovered peer ENRs used to re-seed discovery after a restart.
6
+ *
7
+ * Entries are keyed by nodeId (so re-seeing a peer dedups in place) and carry a monotonic sequence so
8
+ * the store behaves as a FIFO: when it exceeds its cap, the oldest-seen peers are evicted first. All
9
+ * writes run in a transaction so concurrent discovery events can't race the size check.
10
+ */
11
+ export declare class PersistedEnrStore {
12
+ private readonly store;
13
+ private readonly maxEntries;
14
+ private readonly logger;
15
+ private readonly enrs;
16
+ /** Monotonic sequence stamped on each entry; seeded from the store's max in {@link load}. */
17
+ private seq;
18
+ constructor(store: AztecAsyncKVStore, maxEntries: number, logger?: Logger);
19
+ /** Adds or replaces a peer's ENR, stamping it as most-recently-seen and evicting the oldest if over cap. */
20
+ persist(enr: ENR): Promise<void>;
21
+ /**
22
+ * Refreshes an already-persisted peer's stored ENR when a newer one (higher ENR sequence) is seen.
23
+ * A no-op for peers we don't already track — we never add new peers via this path, only update.
24
+ *
25
+ * This exists because discv5 updates an existing routing-table entry on an ENR sequence bump without
26
+ * emitting an `enrAdded` event, so a peer that changes address would otherwise keep its stale ENR
27
+ * persisted until evicted.
28
+ */
29
+ refresh(enr: ENR): Promise<void>;
30
+ /**
31
+ * Loads all persisted ENRs, decoding each and keeping those the `accept` predicate allows. Entries
32
+ * that fail to decode or are rejected are removed from the store. The FIFO sequence resumes from the
33
+ * highest persisted value so eviction order survives restarts.
34
+ */
35
+ load(accept: (enr: ENR) => boolean): Promise<ENR[]>;
36
+ private decodeStoredEnr;
37
+ }
38
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVyc2lzdGVkX2Vucl9zdG9yZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3NlcnZpY2VzL2Rpc2N2NS9wZXJzaXN0ZWRfZW5yX3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBaUIsTUFBTSxpQkFBaUIsQ0FBQztBQUV4RSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUEwQnpDOzs7Ozs7R0FNRztBQUNILHFCQUFhLGlCQUFpQjtJQU0xQixPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUs7SUFDdEIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVO0lBQzNCLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTTtJQVB6QixPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBZ0M7SUFDckQsNkZBQTZGO0lBQzdGLE9BQU8sQ0FBQyxHQUFHLENBQUs7SUFFaEIsWUFDbUIsS0FBSyxFQUFFLGlCQUFpQixFQUN4QixVQUFVLEVBQUUsTUFBTSxFQUNsQixNQUFNLEdBQUUsTUFBNkMsRUFHdkU7SUFFRCw0R0FBNEc7SUFDL0YsT0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQXdCNUM7SUFFRDs7Ozs7OztPQU9HO0lBQ1UsT0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQXVCNUM7SUFFRDs7OztPQUlHO0lBQ1UsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQThCL0Q7SUFFRCxPQUFPLENBQUMsZUFBZTtDQVd4QiJ9
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persisted_enr_store.d.ts","sourceRoot":"","sources":["../../../src/services/discv5/persisted_enr_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AA0BzC;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAM1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;IACrD,6FAA6F;IAC7F,OAAO,CAAC,GAAG,CAAK;IAEhB,YACmB,KAAK,EAAE,iBAAiB,EACxB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,MAA6C,EAGvE;IAED,4GAA4G;IAC/F,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB5C;IAED;;;;;;;OAOG;IACU,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB5C;IAED;;;;OAIG;IACU,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CA8B/D;IAED,OAAO,CAAC,eAAe;CAWxB"}