@aztec/p2p 4.0.0-nightly.20260112 → 4.0.0-nightly.20260114

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 (98) hide show
  1. package/dest/client/interface.d.ts +18 -5
  2. package/dest/client/interface.d.ts.map +1 -1
  3. package/dest/client/p2p_client.d.ts +9 -12
  4. package/dest/client/p2p_client.d.ts.map +1 -1
  5. package/dest/client/p2p_client.js +59 -103
  6. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +61 -42
  7. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  8. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  9. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  10. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +225 -262
  11. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +21 -18
  12. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  13. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +113 -108
  14. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +17 -16
  15. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  16. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +89 -128
  17. package/dest/mem_pools/attestation_pool/mocks.d.ts +7 -6
  18. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  19. package/dest/mem_pools/attestation_pool/mocks.js +9 -8
  20. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +4 -4
  21. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  22. package/dest/msg_validators/attestation_validator/attestation_validator.js +12 -10
  23. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +5 -5
  24. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  25. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +5 -5
  26. package/dest/msg_validators/index.d.ts +2 -2
  27. package/dest/msg_validators/index.d.ts.map +1 -1
  28. package/dest/msg_validators/index.js +1 -1
  29. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +9 -0
  30. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -0
  31. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +6 -0
  32. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +9 -0
  33. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -0
  34. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +6 -0
  35. package/dest/msg_validators/proposal_validator/index.d.ts +4 -0
  36. package/dest/msg_validators/proposal_validator/index.d.ts.map +1 -0
  37. package/dest/msg_validators/proposal_validator/index.js +3 -0
  38. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +13 -0
  39. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -0
  40. package/dest/msg_validators/{block_proposal_validator/block_proposal_validator.js → proposal_validator/proposal_validator.js} +19 -21
  41. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +23 -0
  42. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +1 -0
  43. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +183 -0
  44. package/dest/services/dummy_service.d.ts +6 -2
  45. package/dest/services/dummy_service.d.ts.map +1 -1
  46. package/dest/services/dummy_service.js +3 -0
  47. package/dest/services/encoding.d.ts +1 -1
  48. package/dest/services/encoding.d.ts.map +1 -1
  49. package/dest/services/encoding.js +4 -2
  50. package/dest/services/libp2p/libp2p_service.d.ts +26 -10
  51. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  52. package/dest/services/libp2p/libp2p_service.js +218 -88
  53. package/dest/services/reqresp/constants.d.ts +12 -0
  54. package/dest/services/reqresp/constants.d.ts.map +1 -0
  55. package/dest/services/reqresp/constants.js +7 -0
  56. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +1 -1
  57. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  58. package/dest/services/reqresp/protocols/block_txs/bitvector.js +7 -0
  59. package/dest/services/reqresp/protocols/status.d.ts +1 -1
  60. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  61. package/dest/services/reqresp/protocols/status.js +2 -1
  62. package/dest/services/service.d.ts +16 -3
  63. package/dest/services/service.d.ts.map +1 -1
  64. package/dest/testbench/p2p_client_testbench_worker.js +25 -11
  65. package/dest/testbench/worker_client_manager.d.ts +1 -1
  66. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  67. package/dest/testbench/worker_client_manager.js +6 -1
  68. package/package.json +14 -14
  69. package/src/client/interface.ts +19 -4
  70. package/src/client/p2p_client.ts +69 -110
  71. package/src/mem_pools/attestation_pool/attestation_pool.ts +68 -41
  72. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +231 -287
  73. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +162 -140
  74. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +141 -164
  75. package/src/mem_pools/attestation_pool/mocks.ts +13 -9
  76. package/src/msg_validators/attestation_validator/attestation_validator.ts +16 -13
  77. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +7 -7
  78. package/src/msg_validators/index.ts +1 -1
  79. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +10 -0
  80. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +13 -0
  81. package/src/msg_validators/proposal_validator/index.ts +3 -0
  82. package/src/msg_validators/{block_proposal_validator/block_proposal_validator.ts → proposal_validator/proposal_validator.ts} +23 -28
  83. package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +206 -0
  84. package/src/services/dummy_service.ts +6 -0
  85. package/src/services/encoding.ts +3 -1
  86. package/src/services/libp2p/libp2p_service.ts +258 -94
  87. package/src/services/reqresp/constants.ts +14 -0
  88. package/src/services/reqresp/protocols/block_txs/bitvector.ts +9 -0
  89. package/src/services/reqresp/protocols/status.ts +5 -3
  90. package/src/services/service.ts +19 -4
  91. package/src/testbench/p2p_client_testbench_worker.ts +34 -11
  92. package/src/testbench/worker_client_manager.ts +6 -1
  93. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +0 -12
  94. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +0 -1
  95. package/dest/msg_validators/block_proposal_validator/index.d.ts +0 -2
  96. package/dest/msg_validators/block_proposal_validator/index.d.ts.map +0 -1
  97. package/dest/msg_validators/block_proposal_validator/index.js +0 -1
  98. package/src/msg_validators/block_proposal_validator/index.ts +0 -1
@@ -1,21 +1,27 @@
1
- import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
1
+ import { GENESIS_BLOCK_HEADER_HASH } from '@aztec/constants';
2
2
  import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import { DateProvider } from '@aztec/foundation/timer';
5
- import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton } from '@aztec/kv-store';
5
+ import type { AztecAsyncKVStore, AztecAsyncSingleton } from '@aztec/kv-store';
6
+ import { L2TipsKVStore } from '@aztec/kv-store/stores';
6
7
  import {
7
8
  type EthAddress,
8
- type L2BlockId,
9
9
  type L2BlockNew,
10
10
  type L2BlockSource,
11
11
  L2BlockStream,
12
12
  type L2BlockStreamEvent,
13
13
  type L2Tips,
14
+ type L2TipsStore,
14
15
  } from '@aztec/stdlib/block';
15
16
  import type { ContractDataSource } from '@aztec/stdlib/contract';
16
17
  import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
17
18
  import { type PeerInfo, tryStop } from '@aztec/stdlib/interfaces/server';
18
- import { BlockAttestation, type BlockProposal, type P2PClientType } from '@aztec/stdlib/p2p';
19
+ import {
20
+ type BlockProposal,
21
+ CheckpointAttestation,
22
+ type CheckpointProposal,
23
+ type P2PClientType,
24
+ } from '@aztec/stdlib/p2p';
19
25
  import type { Tx, TxHash } from '@aztec/stdlib/tx';
20
26
  import { Attributes, type TelemetryClient, WithTracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
21
27
 
@@ -33,7 +39,7 @@ import {
33
39
  type ReqRespSubProtocolValidators,
34
40
  } from '../services/reqresp/interface.js';
35
41
  import { chunkTxHashesRequest } from '../services/reqresp/protocols/tx.js';
36
- import type { P2PBlockReceivedCallback, P2PService } from '../services/service.js';
42
+ import type { P2PBlockReceivedCallback, P2PCheckpointReceivedCallback, P2PService } from '../services/service.js';
37
43
  import { TxCollection } from '../services/tx_collection/tx_collection.js';
38
44
  import { TxProvider } from '../services/tx_provider.js';
39
45
  import { type P2P, P2PClientState, type P2PSyncState } from './interface.js';
@@ -55,10 +61,7 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
55
61
  private provenBlockNumberAtStart = -1;
56
62
  private finalizedBlockNumberAtStart = -1;
57
63
 
58
- private synchedBlockHashes: AztecAsyncMap<BlockNumber, string>;
59
- private synchedLatestBlockNumber: AztecAsyncSingleton<BlockNumber>;
60
- private synchedProvenBlockNumber: AztecAsyncSingleton<BlockNumber>;
61
- private synchedFinalizedBlockNumber: AztecAsyncSingleton<BlockNumber>;
64
+ private l2Tips: L2TipsStore;
62
65
  private synchedLatestSlot: AztecAsyncSingleton<bigint>;
63
66
 
64
67
  private txPool: TxPool;
@@ -107,7 +110,8 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
107
110
  );
108
111
 
109
112
  // Default to collecting all txs when we see a valid proposal
110
- // This can be overridden by the validator client to attest, and it will call getTxsForBlockProposal on its own
113
+ // This can be overridden by the validator client to validate, and it will call getTxsForBlockProposal on its own
114
+ // Note: Validators do NOT attest to individual blocks - attestations are only for checkpoint proposals.
111
115
  // TODO(palla/txs): We should not trigger a request for txs on a proposal before fully validating it. We need to bring
112
116
  // validator-client code into here so we can validate a proposal is reasonable.
113
117
  this.registerBlockProposalHandler(async (block, sender) => {
@@ -116,21 +120,17 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
116
120
  const constants = this.txCollection.getConstants();
117
121
  const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(block.slotNumber + 1), constants));
118
122
  const deadline = new Date(nextSlotTimestampSeconds * 1000);
119
- const parentBlock = await this.l2BlockSource.getBlockHeaderByArchive(block.payload.header.lastArchiveRoot);
123
+ const parentBlock = await this.l2BlockSource.getBlockHeaderByArchive(block.blockHeader.lastArchive.root);
120
124
  if (!parentBlock) {
121
125
  this.log.debug(`Cannot collect txs for proposal as parent block not found`);
122
- return;
126
+ return false;
123
127
  }
124
128
  const blockNumber = BlockNumber(parentBlock.getBlockNumber() + 1);
125
129
  await this.txProvider.getTxsForBlockProposal(block, blockNumber, { pinnedPeer: sender, deadline });
126
- return undefined;
130
+ return true;
127
131
  });
128
132
 
129
- // REFACTOR: Try replacing these with an L2TipsStore
130
- this.synchedBlockHashes = store.openMap('p2p_pool_block_hashes');
131
- this.synchedLatestBlockNumber = store.openSingleton('p2p_pool_last_l2_block');
132
- this.synchedProvenBlockNumber = store.openSingleton('p2p_pool_last_proven_l2_block');
133
- this.synchedFinalizedBlockNumber = store.openSingleton('p2p_pool_last_finalized_l2_block');
133
+ this.l2Tips = new L2TipsKVStore(store, 'p2p_client');
134
134
  this.synchedLatestSlot = store.openSingleton('p2p_pool_last_l2_slot');
135
135
  }
136
136
 
@@ -156,7 +156,7 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
156
156
  }
157
157
 
158
158
  public getL2BlockHash(number: BlockNumber): Promise<string | undefined> {
159
- return this.synchedBlockHashes.getAsync(number);
159
+ return this.l2Tips.getL2BlockHash(number);
160
160
  }
161
161
 
162
162
  public updateP2PConfig(config: Partial<P2PConfig>): Promise<void> {
@@ -165,56 +165,20 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
165
165
  return Promise.resolve();
166
166
  }
167
167
 
168
- public async getL2Tips(): Promise<L2Tips> {
169
- const latestBlockNumber = await this.getSyncedLatestBlockNum();
170
- let latestBlockHash: string | undefined;
171
-
172
- const provenBlockNumber = await this.getSyncedProvenBlockNum();
173
- let provenBlockHash: string | undefined;
174
-
175
- const finalizedBlockNumber = await this.getSyncedFinalizedBlockNum();
176
- let finalizedBlockHash: string | undefined;
177
-
178
- if (latestBlockNumber > 0) {
179
- latestBlockHash = await this.synchedBlockHashes.getAsync(latestBlockNumber);
180
- if (typeof latestBlockHash === 'undefined') {
181
- throw new Error(`Block hash for latest block ${latestBlockNumber} not found in p2p client`);
182
- }
183
- }
184
-
185
- if (provenBlockNumber > 0) {
186
- provenBlockHash = await this.synchedBlockHashes.getAsync(provenBlockNumber);
187
- if (typeof provenBlockHash === 'undefined') {
188
- throw new Error(`Block hash for proven block ${provenBlockNumber} not found in p2p client`);
189
- }
190
- }
191
-
192
- if (finalizedBlockNumber > 0) {
193
- finalizedBlockHash = await this.synchedBlockHashes.getAsync(finalizedBlockNumber);
194
- if (typeof finalizedBlockHash === 'undefined') {
195
- throw new Error(`Block hash for finalized block ${finalizedBlockNumber} not found in p2p client`);
196
- }
197
- }
198
-
199
- const genesisHash = GENESIS_BLOCK_HEADER_HASH.toString();
200
-
201
- return {
202
- latest: { hash: latestBlockHash ?? genesisHash, number: latestBlockNumber },
203
- proven: { hash: provenBlockHash ?? genesisHash, number: provenBlockNumber },
204
- finalized: { hash: finalizedBlockHash ?? genesisHash, number: finalizedBlockNumber },
205
- };
168
+ public getL2Tips(): Promise<L2Tips> {
169
+ return this.l2Tips.getL2Tips();
206
170
  }
207
171
 
208
172
  public async handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
209
173
  this.log.debug(`Handling block stream event ${event.type}`);
174
+
210
175
  switch (event.type) {
211
176
  case 'blocks-added':
212
- await this.handleLatestL2Blocks(event.blocks.map(b => b.block.toL2Block()));
177
+ await this.handleLatestL2Blocks(event.blocks);
213
178
  break;
214
179
  case 'chain-finalized': {
215
- // TODO (alexg): I think we can prune the block hashes map here
216
- await this.setBlockHash(event.block);
217
- const from = BlockNumber((await this.getSyncedFinalizedBlockNum()) + 1);
180
+ const oldFinalizedBlockNum = await this.getSyncedFinalizedBlockNum();
181
+ const from = BlockNumber(oldFinalizedBlockNum + 1);
218
182
  const limit = event.block.number - from + 1;
219
183
  if (limit > 0) {
220
184
  const oldBlocks = await this.l2BlockSource.getBlocks(from, limit);
@@ -222,28 +186,24 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
222
186
  }
223
187
  break;
224
188
  }
225
- case 'chain-proven': {
226
- await this.setBlockHash(event.block);
189
+ case 'chain-proven':
227
190
  this.txCollection.stopCollectingForBlocksUpTo(event.block.number);
228
- await this.synchedProvenBlockNumber.set(event.block.number);
229
191
  break;
230
- }
231
192
  case 'chain-pruned':
232
- await this.setBlockHash(event.block);
233
193
  this.txCollection.stopCollectingForBlocksAfter(event.block.number);
234
194
  await this.handlePruneL2Blocks(event.block.number);
235
195
  break;
196
+ case 'chain-checkpointed':
197
+ break;
236
198
  default: {
237
199
  const _: never = event;
238
200
  break;
239
201
  }
240
202
  }
241
- }
242
203
 
243
- private async setBlockHash(block: L2BlockId): Promise<void> {
244
- if (block.hash !== undefined) {
245
- await this.synchedBlockHashes.set(block.number, block.hash.toString());
246
- }
204
+ // Pass the event through to our l2 tips store
205
+ await this.l2Tips.handleBlockStreamEvent(event);
206
+ await this.startServiceIfSynched();
247
207
  }
248
208
 
249
209
  #assertIsReady() {
@@ -267,9 +227,9 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
267
227
 
268
228
  // get the current latest block numbers
269
229
  const latestBlockNumbers = await this.l2BlockSource.getL2Tips();
270
- this.latestBlockNumberAtStart = latestBlockNumbers.latest.number;
271
- this.provenBlockNumberAtStart = latestBlockNumbers.proven.number;
272
- this.finalizedBlockNumberAtStart = latestBlockNumbers.finalized.number;
230
+ this.latestBlockNumberAtStart = latestBlockNumbers.proposed.number;
231
+ this.provenBlockNumberAtStart = latestBlockNumbers.proven.block.number;
232
+ this.finalizedBlockNumberAtStart = latestBlockNumbers.finalized.block.number;
273
233
 
274
234
  const syncedLatestBlock = (await this.getSyncedLatestBlockNum()) + 1;
275
235
  const syncedProvenBlock = (await this.getSyncedProvenBlockNum()) + 1;
@@ -371,23 +331,32 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
371
331
  return this.p2pService.propagate(proposal);
372
332
  }
373
333
 
374
- public async broadcastAttestations(attestations: BlockAttestation[]): Promise<void> {
375
- this.log.verbose(`Broadcasting ${attestations.length} attestations to peers`);
376
- await Promise.all(attestations.map(att => this.p2pService.propagate(att)));
334
+ @trackSpan('p2pClient.broadcastCheckpointProposal', async proposal => ({
335
+ [Attributes.SLOT_NUMBER]: proposal.slotNumber,
336
+ [Attributes.BLOCK_ARCHIVE]: proposal.archive.toString(),
337
+ [Attributes.P2P_ID]: (await proposal.p2pMessageLoggingIdentifier()).toString(),
338
+ }))
339
+ public broadcastCheckpointProposal(proposal: CheckpointProposal): Promise<void> {
340
+ this.log.verbose(`Broadcasting checkpoint proposal for slot ${proposal.slotNumber} to peers`);
341
+ return this.p2pService.propagate(proposal);
377
342
  }
378
343
 
379
- public async getAttestationsForSlot(slot: SlotNumber, proposalId?: string): Promise<BlockAttestation[]> {
380
- return await (proposalId
381
- ? this.attestationPool.getAttestationsForSlotAndProposal(slot, proposalId)
382
- : this.attestationPool.getAttestationsForSlot(slot));
344
+ public async broadcastCheckpointAttestations(attestations: CheckpointAttestation[]): Promise<void> {
345
+ this.log.verbose(`Broadcasting ${attestations.length} checkpoint attestations to peers`);
346
+ await Promise.all(attestations.map(att => this.p2pService.propagate(att)));
383
347
  }
384
348
 
385
- public addAttestations(attestations: BlockAttestation[]): Promise<void> {
386
- return this.attestationPool.addAttestations(attestations);
349
+ public async getCheckpointAttestationsForSlot(
350
+ slot: SlotNumber,
351
+ proposalId?: string,
352
+ ): Promise<CheckpointAttestation[]> {
353
+ return await (proposalId
354
+ ? this.attestationPool.getCheckpointAttestationsForSlotAndProposal(slot, proposalId)
355
+ : this.attestationPool.getCheckpointAttestationsForSlot(slot));
387
356
  }
388
357
 
389
- public deleteAttestation(attestation: BlockAttestation): Promise<void> {
390
- return this.attestationPool.deleteAttestations([attestation]);
358
+ public addCheckpointAttestations(attestations: CheckpointAttestation[]): Promise<void> {
359
+ return this.attestationPool.addCheckpointAttestations(attestations);
391
360
  }
392
361
 
393
362
  // REVIEW: https://github.com/AztecProtocol/aztec-packages/issues/7963
@@ -396,6 +365,10 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
396
365
  this.p2pService.registerBlockReceivedCallback(handler);
397
366
  }
398
367
 
368
+ public registerCheckpointProposalHandler(handler: P2PCheckpointReceivedCallback): void {
369
+ this.p2pService.registerCheckpointReceivedCallback(handler);
370
+ }
371
+
399
372
  /**
400
373
  * Uses the batched Request Response protocol to request a set of transactions from the network.
401
374
  */
@@ -638,7 +611,8 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
638
611
  * @returns Block number of latest L2 Block we've synced with.
639
612
  */
640
613
  public async getSyncedLatestBlockNum(): Promise<BlockNumber> {
641
- return (await this.synchedLatestBlockNumber.getAsync()) ?? BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
614
+ const tips = await this.l2Tips.getL2Tips();
615
+ return tips.proposed.number;
642
616
  }
643
617
 
644
618
  /**
@@ -646,11 +620,13 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
646
620
  * @returns Block number of latest proven L2 Block we've synced with.
647
621
  */
648
622
  public async getSyncedProvenBlockNum(): Promise<BlockNumber> {
649
- return (await this.synchedProvenBlockNumber.getAsync()) ?? BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
623
+ const tips = await this.l2Tips.getL2Tips();
624
+ return tips.proven.block.number;
650
625
  }
651
626
 
652
627
  public async getSyncedFinalizedBlockNum(): Promise<BlockNumber> {
653
- return (await this.synchedFinalizedBlockNumber.getAsync()) ?? BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
628
+ const tips = await this.l2Tips.getL2Tips();
629
+ return tips.finalized.block.number;
654
630
  }
655
631
 
656
632
  /** Returns latest L2 slot for which we have seen an L2 block. */
@@ -705,20 +681,8 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
705
681
  await this.startCollectingMissingTxs(blocks);
706
682
 
707
683
  const lastBlock = blocks.at(-1)!;
708
-
709
- await Promise.all(
710
- blocks.map(async block =>
711
- this.setBlockHash({
712
- number: block.number,
713
- hash: await block.hash().then(h => h.toString()),
714
- }),
715
- ),
716
- );
717
-
718
- await this.synchedLatestBlockNumber.set(lastBlock.number);
719
684
  await this.synchedLatestSlot.set(BigInt(lastBlock.header.getSlot()));
720
685
  this.log.verbose(`Synched to latest block ${lastBlock.number}`);
721
- await this.startServiceIfSynched();
722
686
  }
723
687
 
724
688
  /** Request txs for unproven blocks so the prover node has more chances to get them. */
@@ -769,12 +733,9 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
769
733
  await this.txPool.deleteTxs(txHashes, { permanently: true });
770
734
  await this.txPool.cleanupDeletedMinedTxs(lastBlockNum);
771
735
 
772
- await this.attestationPool.deleteAttestationsOlderThan(lastBlockSlot);
736
+ await this.attestationPool.deleteCheckpointAttestationsOlderThan(lastBlockSlot);
773
737
 
774
- await this.synchedFinalizedBlockNumber.set(lastBlockNum);
775
738
  this.log.debug(`Synched to finalized block ${lastBlockNum} at slot ${lastBlockSlot}`);
776
-
777
- await this.startServiceIfSynched();
778
739
  }
779
740
 
780
741
  /**
@@ -822,18 +783,16 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
822
783
  } else {
823
784
  await this.txPool.markMinedAsPending(minedTxsFromReorg, latestBlock);
824
785
  }
825
-
826
- await this.synchedLatestBlockNumber.set(latestBlock);
827
- // no need to update block hashes, as they will be updated as new blocks are added
828
786
  }
829
787
 
830
788
  private async startServiceIfSynched() {
831
789
  if (this.currentState !== P2PClientState.SYNCHING) {
832
790
  return;
833
791
  }
834
- const syncedFinalizedBlock = await this.getSyncedFinalizedBlockNum();
835
- const syncedProvenBlock = await this.getSyncedProvenBlockNum();
836
- const syncedLatestBlock = await this.getSyncedLatestBlockNum();
792
+ const tips = await this.l2Tips.getL2Tips();
793
+ const syncedFinalizedBlock = tips.finalized.block.number;
794
+ const syncedProvenBlock = tips.proven.block.number;
795
+ const syncedLatestBlock = tips.proposed.number;
837
796
 
838
797
  if (
839
798
  syncedLatestBlock >= this.latestBlockNumberAtStart &&
@@ -1,5 +1,10 @@
1
1
  import type { SlotNumber } from '@aztec/foundation/branded-types';
2
- import type { BlockAttestation, BlockProposal } from '@aztec/stdlib/p2p';
2
+ import type {
3
+ BlockProposal,
4
+ CheckpointAttestation,
5
+ CheckpointProposal,
6
+ CheckpointProposalCore,
7
+ } from '@aztec/stdlib/p2p';
3
8
 
4
9
  /**
5
10
  * An Attestation Pool contains attestations collected by a validator
@@ -32,75 +37,74 @@ export interface AttestationPool {
32
37
  hasBlockProposal(idOrProposal: string | BlockProposal): Promise<boolean>;
33
38
 
34
39
  /**
35
- * AddAttestations
40
+ * Adds a checkpoint proposal to the pool.
36
41
  *
37
- * @param attestations - Attestations to add into the pool
42
+ * If the proposal contains a lastBlock, the BlockProposal is automatically extracted
43
+ * and stored separately via addBlockProposal. The checkpoint proposal is then stored
44
+ * without the lastBlock info (as CheckpointProposalCore).
45
+ *
46
+ * @param proposal - The checkpoint proposal to add
47
+ * @throws ProposalSlotCapExceededError if the slot has reached the maximum number of proposals
38
48
  */
39
- addAttestations(attestations: BlockAttestation[]): Promise<void>;
49
+ addCheckpointProposal(proposal: CheckpointProposal): Promise<void>;
40
50
 
41
51
  /**
42
- * DeleteAttestation
52
+ * Get checkpoint proposal by its ID.
53
+ *
54
+ * Returns a CheckpointProposalCore (without lastBlock info) since the lastBlock
55
+ * is extracted and stored separately as a BlockProposal when added.
43
56
  *
44
- * @param attestations - Attestations to remove from the pool
57
+ * @param id - The ID of the checkpoint proposal to retrieve (proposal.archive)
58
+ * @return The checkpoint proposal core if it exists, otherwise undefined.
45
59
  */
46
- deleteAttestations(attestations: BlockAttestation[]): Promise<void>;
60
+ getCheckpointProposal(id: string): Promise<CheckpointProposalCore | undefined>;
47
61
 
48
62
  /**
49
- * Delete Attestations with a slot number smaller than the given slot
50
- *
51
- * Removes all attestations associated with a slot
63
+ * Check if a checkpoint proposal exists in the pool
52
64
  *
53
- * @param slot - The oldest slot to keep.
65
+ * @param idOrProposal - The ID of the checkpoint proposal or the proposal itself
66
+ * @return True if the proposal exists, false otherwise.
54
67
  */
55
- deleteAttestationsOlderThan(slot: SlotNumber): Promise<void>;
68
+ hasCheckpointProposal(idOrProposal: string | CheckpointProposal): Promise<boolean>;
56
69
 
57
70
  /**
58
- * Delete Attestations for slot
59
- *
60
- * Removes all attestations associated with a slot
71
+ * Add checkpoint attestations to the pool
61
72
  *
62
- * @param slot - The slot to delete.
73
+ * @param attestations - Checkpoint attestations to add into the pool
63
74
  */
64
- deleteAttestationsForSlot(slot: SlotNumber): Promise<void>;
75
+ addCheckpointAttestations(attestations: CheckpointAttestation[]): Promise<void>;
65
76
 
66
77
  /**
67
- * Delete Attestations for slot and proposal
68
- *
69
- * Removes all attestations associated with a slot and proposal
78
+ * Delete checkpoint attestations older than the given slot
70
79
  *
71
- * @param slot - The slot to delete.
72
- * @param proposalId - The proposal to delete.
80
+ * @param slot - The oldest slot to keep.
73
81
  */
74
- deleteAttestationsForSlotAndProposal(slot: SlotNumber, proposalId: string): Promise<void>;
82
+ deleteCheckpointAttestationsOlderThan(slot: SlotNumber): Promise<void>;
75
83
 
76
84
  /**
77
- * Get all Attestations for all proposals for a given slot
78
- *
79
- * Retrieve all of the attestations observed pertaining to a given slot
85
+ * Get all checkpoint attestations for a given slot
80
86
  *
81
87
  * @param slot - The slot to query
82
- * @return BlockAttestations
88
+ * @return CheckpointAttestations
83
89
  */
84
- getAttestationsForSlot(slot: SlotNumber): Promise<BlockAttestation[]>;
90
+ getCheckpointAttestationsForSlot(slot: SlotNumber): Promise<CheckpointAttestation[]>;
85
91
 
86
92
  /**
87
- * Get Attestations for slot and given proposal
88
- *
89
- * Retrieve all of the attestations observed pertaining to a given slot
93
+ * Get checkpoint attestations for slot and given proposal
90
94
  *
91
95
  * @param slot - The slot to query
92
96
  * @param proposalId - The proposal to query
93
- * @return BlockAttestations
97
+ * @return CheckpointAttestations
94
98
  */
95
- getAttestationsForSlotAndProposal(slot: SlotNumber, proposalId: string): Promise<BlockAttestation[]>;
99
+ getCheckpointAttestationsForSlotAndProposal(slot: SlotNumber, proposalId: string): Promise<CheckpointAttestation[]>;
96
100
 
97
101
  /**
98
- * Check if a specific attestation exists in the pool
102
+ * Check if a specific checkpoint attestation exists in the pool
99
103
  *
100
104
  * @param attestation - The attestation to check
101
105
  * @return True if the attestation exists, false otherwise
102
106
  */
103
- hasAttestation(attestation: BlockAttestation): Promise<boolean>;
107
+ hasCheckpointAttestation(attestation: CheckpointAttestation): Promise<boolean>;
104
108
 
105
109
  /**
106
110
  * Returns whether adding this proposal is permitted at current capacity:
@@ -114,16 +118,39 @@ export interface AttestationPool {
114
118
  canAddProposal(block: BlockProposal): Promise<boolean>;
115
119
 
116
120
  /**
117
- * Returns whether an attestation would be accepted for (slot, proposalId):
118
- * - True if the attestation already exists for this sender.
119
- * - True if the attestation cap for (slot, proposalId) has not been reached.
120
- * - False if the cap is reached and this attestation would be a new unique entry.
121
+ * Returns whether adding this checkpoint proposal is permitted at current capacity.
122
+ *
123
+ * @param proposal - The checkpoint proposal to check
124
+ * @returns True if the proposal can be added, false otherwise.
125
+ */
126
+ canAddCheckpointProposal(proposal: CheckpointProposal): Promise<boolean>;
127
+
128
+ /**
129
+ * Returns whether a checkpoint attestation would be accepted for (slot, proposalId).
121
130
  *
122
131
  * @param attestation - The attestation to check
123
- * @param committeeSize - Committee size for the attestation's slot, implementation may add a small buffer
132
+ * @param committeeSize - Committee size for the attestation's slot
124
133
  * @returns True if the attestation can be added, false otherwise.
125
134
  */
126
- canAddAttestation(attestation: BlockAttestation, committeeSize: number): Promise<boolean>;
135
+ canAddCheckpointAttestation(attestation: CheckpointAttestation, committeeSize: number): Promise<boolean>;
136
+
137
+ /**
138
+ * Returns whether the checkpoint proposal cap for the given slot has been reached.
139
+ *
140
+ * @param slot - The slot to check
141
+ * @returns True if the cap has been reached, false otherwise.
142
+ */
143
+ hasReachedCheckpointProposalCap(slot: SlotNumber): Promise<boolean>;
144
+
145
+ /**
146
+ * Returns whether the checkpoint attestation cap for the given slot and proposal has been reached.
147
+ *
148
+ * @param slot - The slot to check
149
+ * @param proposalId - The proposal to check
150
+ * @param committeeSize - Committee size for the slot
151
+ * @returns True if the cap has been reached, false otherwise.
152
+ */
153
+ hasReachedCheckpointAttestationCap(slot: SlotNumber, proposalId: string, committeeSize: number): Promise<boolean>;
127
154
 
128
155
  /** Returns whether the pool is empty. */
129
156
  isEmpty(): Promise<boolean>;