@aztec/archiver 0.0.1-commit.2ed92850 → 0.0.1-commit.3469e52

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 (55) hide show
  1. package/README.md +0 -9
  2. package/dest/archiver.d.ts +4 -4
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +20 -19
  5. package/dest/errors.d.ts +1 -6
  6. package/dest/errors.d.ts.map +1 -1
  7. package/dest/errors.js +0 -8
  8. package/dest/factory.d.ts +2 -3
  9. package/dest/factory.d.ts.map +1 -1
  10. package/dest/factory.js +3 -5
  11. package/dest/l1/bin/retrieve-calldata.js +2 -2
  12. package/dest/l1/data_retrieval.d.ts +1 -1
  13. package/dest/l1/data_retrieval.d.ts.map +1 -1
  14. package/dest/l1/data_retrieval.js +2 -2
  15. package/dest/modules/data_source_base.d.ts +17 -16
  16. package/dest/modules/data_source_base.d.ts.map +1 -1
  17. package/dest/modules/data_source_base.js +52 -21
  18. package/dest/modules/data_store_updater.d.ts +19 -23
  19. package/dest/modules/data_store_updater.d.ts.map +1 -1
  20. package/dest/modules/data_store_updater.js +49 -47
  21. package/dest/modules/instrumentation.d.ts +3 -3
  22. package/dest/modules/instrumentation.d.ts.map +1 -1
  23. package/dest/modules/l1_synchronizer.js +7 -7
  24. package/dest/store/block_store.d.ts +19 -33
  25. package/dest/store/block_store.d.ts.map +1 -1
  26. package/dest/store/block_store.js +39 -80
  27. package/dest/store/kv_archiver_store.d.ts +22 -26
  28. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  29. package/dest/store/kv_archiver_store.js +14 -18
  30. package/dest/store/log_store.d.ts +4 -4
  31. package/dest/store/log_store.d.ts.map +1 -1
  32. package/dest/test/fake_l1_state.d.ts +4 -4
  33. package/dest/test/fake_l1_state.d.ts.map +1 -1
  34. package/dest/test/mock_archiver.js +1 -1
  35. package/dest/test/mock_l2_block_source.d.ts +18 -18
  36. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  37. package/dest/test/mock_l2_block_source.js +40 -39
  38. package/dest/test/mock_structs.js +4 -4
  39. package/package.json +13 -13
  40. package/src/archiver.ts +26 -24
  41. package/src/errors.ts +0 -12
  42. package/src/factory.ts +2 -4
  43. package/src/l1/bin/retrieve-calldata.ts +2 -7
  44. package/src/l1/data_retrieval.ts +3 -3
  45. package/src/modules/data_source_base.ts +76 -25
  46. package/src/modules/data_store_updater.ts +55 -59
  47. package/src/modules/instrumentation.ts +2 -2
  48. package/src/modules/l1_synchronizer.ts +9 -9
  49. package/src/store/block_store.ts +56 -103
  50. package/src/store/kv_archiver_store.ts +24 -32
  51. package/src/store/log_store.ts +8 -8
  52. package/src/test/fake_l1_state.ts +2 -2
  53. package/src/test/mock_archiver.ts +1 -1
  54. package/src/test/mock_l2_block_source.ts +60 -50
  55. package/src/test/mock_structs.ts +4 -4
package/src/archiver.ts CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  type ArchiverEmitter,
18
18
  type CheckpointId,
19
19
  GENESIS_CHECKPOINT_HEADER_HASH,
20
- L2Block,
20
+ L2BlockNew,
21
21
  type L2BlockSink,
22
22
  type L2Tips,
23
23
  type ValidateCheckpointResult,
@@ -46,7 +46,7 @@ export type { ArchiverEmitter };
46
46
 
47
47
  /** Request to add a block to the archiver, queued for processing by the sync loop. */
48
48
  type AddBlockRequest = {
49
- block: L2Block;
49
+ block: L2BlockNew;
50
50
  resolve: () => void;
51
51
  reject: (err: Error) => void;
52
52
  };
@@ -187,7 +187,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
187
187
  * @param block - The L2 block to add.
188
188
  * @returns A promise that resolves when the block has been added to the store, or rejects on error.
189
189
  */
190
- public addBlock(block: L2Block): Promise<void> {
190
+ public addBlock(block: L2BlockNew): Promise<void> {
191
191
  return new Promise<void>((resolve, reject) => {
192
192
  this.blockQueue.push({ block, resolve, reject });
193
193
  this.log.debug(`Queued block ${block.number} for processing`);
@@ -215,7 +215,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
215
215
  // Process each block individually to properly resolve/reject each promise
216
216
  for (const { block, resolve, reject } of queuedItems) {
217
217
  try {
218
- await this.updater.addProposedBlocks([block]);
218
+ await this.updater.addBlocks([block]);
219
219
  this.log.debug(`Added block ${block.number} to store`);
220
220
  resolve();
221
221
  } catch (err: any) {
@@ -323,11 +323,8 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
323
323
  }
324
324
 
325
325
  public async isEpochComplete(epochNumber: EpochNumber): Promise<boolean> {
326
- // The epoch is complete if the current checkpointed L2 block is the last one in the epoch (or later).
327
- // We use the checkpointed block number (synced from L1) instead of 'latest' to avoid returning true
328
- // prematurely when proposed blocks have been pushed to the archiver but not yet checkpointed on L1.
329
- const checkpointedBlockNumber = await this.getCheckpointedL2BlockNumber();
330
- const header = checkpointedBlockNumber > 0 ? await this.getBlockHeader(checkpointedBlockNumber) : undefined;
326
+ // The epoch is complete if the current L2 block is the last one in the epoch (or later)
327
+ const header = await this.getBlockHeader('latest');
331
328
  const slot = header ? header.globalVariables.slotNumber : undefined;
332
329
  const [_startSlot, endSlot] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
333
330
  if (slot && slot >= endSlot) {
@@ -358,8 +355,8 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
358
355
  return this.initialSyncComplete;
359
356
  }
360
357
 
361
- public removeCheckpointsAfter(checkpointNumber: CheckpointNumber): Promise<boolean> {
362
- return this.updater.removeCheckpointsAfter(checkpointNumber);
358
+ public unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number): Promise<boolean> {
359
+ return this.updater.unwindCheckpoints(from, checkpointsToUnwind);
363
360
  }
364
361
 
365
362
  /** Used by TXE to add checkpoints directly without syncing from L1. */
@@ -367,18 +364,22 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
367
364
  checkpoints: PublishedCheckpoint[],
368
365
  pendingChainValidationStatus?: ValidateCheckpointResult,
369
366
  ): Promise<boolean> {
370
- await this.updater.addCheckpoints(checkpoints, pendingChainValidationStatus);
367
+ await this.updater.setNewCheckpointData(checkpoints, pendingChainValidationStatus);
371
368
  return true;
372
369
  }
373
370
 
374
371
  public async getL2Tips(): Promise<L2Tips> {
375
- const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
372
+ const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber] = await Promise.all([
376
373
  this.getBlockNumber(),
377
374
  this.getProvenBlockNumber(),
378
- this.getCheckpointedL2BlockNumber(),
379
- this.getFinalizedL2BlockNumber(),
375
+ this.getCheckpointedBlockNumber(),
380
376
  ] as const);
381
377
 
378
+ // TODO(#13569): Compute proper finalized block number based on L1 finalized block.
379
+ // We just force it 2 epochs worth of proven data for now.
380
+ // NOTE: update end-to-end/src/e2e_epochs/epochs_empty_blocks.test.ts as that uses finalized blocks in computations
381
+ const finalizedBlockNumber = BlockNumber(Math.max(provenBlockNumber - this.l1Constants.epochDuration * 2, 0));
382
+
382
383
  const beforeInitialblockNumber = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
383
384
 
384
385
  // Get the latest block header and checkpointed blocks for proven, finalised and checkpointed blocks
@@ -424,12 +425,14 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
424
425
  // Now attempt to retrieve checkpoints for proven, finalised and checkpointed blocks
425
426
  const [[provenBlockCheckpoint], [finalizedBlockCheckpoint], [checkpointedBlockCheckpoint]] = await Promise.all([
426
427
  provenCheckpointedBlock !== undefined
427
- ? await this.getCheckpoints(provenCheckpointedBlock?.checkpointNumber, 1)
428
+ ? await this.getPublishedCheckpoints(provenCheckpointedBlock?.checkpointNumber, 1)
428
429
  : [undefined],
429
430
  finalizedCheckpointedBlock !== undefined
430
- ? await this.getCheckpoints(finalizedCheckpointedBlock?.checkpointNumber, 1)
431
+ ? await this.getPublishedCheckpoints(finalizedCheckpointedBlock?.checkpointNumber, 1)
432
+ : [undefined],
433
+ checkpointedBlock !== undefined
434
+ ? await this.getPublishedCheckpoints(checkpointedBlock?.checkpointNumber, 1)
431
435
  : [undefined],
432
- checkpointedBlock !== undefined ? await this.getCheckpoints(checkpointedBlock?.checkpointNumber, 1) : [undefined],
433
436
  ]);
434
437
 
435
438
  const initialcheckpointId: CheckpointId = {
@@ -487,12 +490,13 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
487
490
  if (targetL2BlockNumber >= currentL2Block) {
488
491
  throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
489
492
  }
493
+ const blocksToUnwind = currentL2Block - targetL2BlockNumber;
490
494
  const targetL2Block = await this.store.getCheckpointedBlock(targetL2BlockNumber);
491
495
  if (!targetL2Block) {
492
496
  throw new Error(`Target L2 block ${targetL2BlockNumber} not found`);
493
497
  }
494
498
  const targetL1BlockNumber = targetL2Block.l1.blockNumber;
495
- const targetCheckpointNumber = targetL2Block.checkpointNumber;
499
+ const targetCheckpointNumber = CheckpointNumber.fromBlockNumber(targetL2BlockNumber);
496
500
  const targetL1Block = await this.publicClient.getBlock({
497
501
  blockNumber: targetL1BlockNumber,
498
502
  includeTransactions: false,
@@ -501,11 +505,9 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
501
505
  throw new Error(`Missing L1 block ${targetL1BlockNumber}`);
502
506
  }
503
507
  const targetL1BlockHash = Buffer32.fromString(targetL1Block.hash);
504
- this.log.info(
505
- `Removing checkpoints after checkpoint ${targetCheckpointNumber} (target block ${targetL2BlockNumber})`,
506
- );
507
- await this.updater.removeCheckpointsAfter(targetCheckpointNumber);
508
- this.log.info(`Rolling back L1 to L2 messages to checkpoint ${targetCheckpointNumber}`);
508
+ this.log.info(`Unwinding ${blocksToUnwind} checkpoints from L2 block ${currentL2Block}`);
509
+ await this.updater.unwindCheckpoints(CheckpointNumber(currentL2Block), blocksToUnwind);
510
+ this.log.info(`Unwinding L1 to L2 messages to checkpoint ${targetCheckpointNumber}`);
509
511
  await this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
510
512
  this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`);
511
513
  await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
package/src/errors.ts CHANGED
@@ -88,15 +88,3 @@ export class BlockNotFoundError extends Error {
88
88
  super(`Failed to find expected block number ${blockNumber}`);
89
89
  }
90
90
  }
91
-
92
- export class CannotOverwriteCheckpointedBlockError extends Error {
93
- constructor(
94
- public readonly blockNumber: number,
95
- public readonly lastCheckpointedBlock: number,
96
- ) {
97
- super(
98
- `Cannot add block ${blockNumber}: would overwrite checkpointed data (checkpointed up to block ${lastCheckpointedBlock})`,
99
- );
100
- this.name = 'CannotOverwriteCheckpointedBlockError';
101
- }
102
- }
package/src/factory.ts CHANGED
@@ -15,7 +15,6 @@ import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/prov
15
15
  import { FunctionType, decodeFunctionSignature } from '@aztec/stdlib/abi';
16
16
  import type { ArchiverEmitter } from '@aztec/stdlib/block';
17
17
  import { type ContractClassPublic, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
18
- import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
19
18
  import { getTelemetryClient } from '@aztec/telemetry-client';
20
19
 
21
20
  import { EventEmitter } from 'events';
@@ -32,14 +31,13 @@ export const ARCHIVER_STORE_NAME = 'archiver';
32
31
  /** Creates an archiver store. */
33
32
  export async function createArchiverStore(
34
33
  userConfig: Pick<ArchiverConfig, 'archiverStoreMapSizeKb' | 'maxLogs'> & DataStoreConfig,
35
- l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
36
34
  ) {
37
35
  const config = {
38
36
  ...userConfig,
39
37
  dataStoreMapSizeKb: userConfig.archiverStoreMapSizeKb ?? userConfig.dataStoreMapSizeKb,
40
38
  };
41
39
  const store = await createStore(ARCHIVER_STORE_NAME, ARCHIVER_DB_VERSION, config, createLogger('archiver:lmdb'));
42
- return new KVArchiverDataStore(store, config.maxLogs, l1Constants);
40
+ return new KVArchiverDataStore(store, config.maxLogs);
43
41
  }
44
42
 
45
43
  /**
@@ -54,7 +52,7 @@ export async function createArchiver(
54
52
  deps: ArchiverDeps,
55
53
  opts: { blockUntilSync: boolean } = { blockUntilSync: true },
56
54
  ): Promise<Archiver> {
57
- const archiverStore = await createArchiverStore(config, { epochDuration: config.aztecEpochDuration });
55
+ const archiverStore = await createArchiverStore(config);
58
56
  await registerProtocolContracts(archiverStore);
59
57
 
60
58
  // Create Ethereum clients
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
3
- import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
3
+ import { CheckpointNumber } from '@aztec/foundation/branded-types';
4
4
  import { EthAddress } from '@aztec/foundation/eth-address';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
6
 
@@ -142,12 +142,7 @@ async function main() {
142
142
  logger.info('');
143
143
 
144
144
  // For this script, we don't have blob hashes or expected hashes, so pass empty arrays/objects
145
- const result = await retriever.getCheckpointFromRollupTx(
146
- txHash,
147
- [],
148
- CheckpointNumber.fromBlockNumber(BlockNumber(l2BlockNumber)),
149
- {},
150
- );
145
+ const result = await retriever.getCheckpointFromRollupTx(txHash, [], CheckpointNumber(l2BlockNumber), {});
151
146
 
152
147
  logger.info(' Successfully retrieved block header!');
153
148
  logger.info('');
@@ -20,7 +20,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
20
20
  import { EthAddress } from '@aztec/foundation/eth-address';
21
21
  import { type Logger, createLogger } from '@aztec/foundation/log';
22
22
  import { RollupAbi } from '@aztec/l1-artifacts';
23
- import { Body, CommitteeAttestation, L2Block } from '@aztec/stdlib/block';
23
+ import { Body, CommitteeAttestation, L2BlockNew } from '@aztec/stdlib/block';
24
24
  import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
25
25
  import { Proof } from '@aztec/stdlib/proofs';
26
26
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
@@ -69,7 +69,7 @@ export async function retrievedToPublishedCheckpoint({
69
69
  const l1toL2MessageTreeRoot = blocksBlobData[0].l1ToL2MessageRoot!;
70
70
 
71
71
  const spongeBlob = SpongeBlob.init();
72
- const l2Blocks: L2Block[] = [];
72
+ const l2Blocks: L2BlockNew[] = [];
73
73
  for (let i = 0; i < blocksBlobData.length; i++) {
74
74
  const blockBlobData = blocksBlobData[i];
75
75
  const { blockEndMarker, blockEndStateField, lastArchiveRoot, noteHashRoot, nullifierRoot, publicDataRoot } =
@@ -119,7 +119,7 @@ export async function retrievedToPublishedCheckpoint({
119
119
 
120
120
  const newArchive = new AppendOnlyTreeSnapshot(newArchiveRoots[i], l2BlockNumber + 1);
121
121
 
122
- l2Blocks.push(new L2Block(newArchive, header, body, checkpointNumber, IndexWithinCheckpoint(i)));
122
+ l2Blocks.push(new L2BlockNew(newArchive, header, body, checkpointNumber, IndexWithinCheckpoint(i)));
123
123
  }
124
124
 
125
125
  const lastBlock = l2Blocks.at(-1)!;
@@ -4,7 +4,7 @@ import type { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { isDefined } from '@aztec/foundation/types';
5
5
  import type { FunctionSelector } from '@aztec/stdlib/abi';
6
6
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
7
- import { CheckpointedL2Block, CommitteeAttestation, L2Block, type L2Tips } from '@aztec/stdlib/block';
7
+ import { CheckpointedL2Block, CommitteeAttestation, L2BlockNew, type L2Tips } from '@aztec/stdlib/block';
8
8
  import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
9
9
  import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
10
10
  import { type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
@@ -87,14 +87,10 @@ export abstract class ArchiverDataSourceBase
87
87
  return this.store.getCheckpointedBlock(number);
88
88
  }
89
89
 
90
- public getCheckpointedL2BlockNumber(): Promise<BlockNumber> {
90
+ public getCheckpointedBlockNumber(): Promise<BlockNumber> {
91
91
  return this.store.getCheckpointedL2BlockNumber();
92
92
  }
93
93
 
94
- public getFinalizedL2BlockNumber(): Promise<BlockNumber> {
95
- return this.store.getFinalizedL2BlockNumber();
96
- }
97
-
98
94
  public async getCheckpointHeader(number: CheckpointNumber | 'latest'): Promise<CheckpointHeader | undefined> {
99
95
  if (number === 'latest') {
100
96
  number = await this.store.getSynchedCheckpointNumber();
@@ -117,8 +113,18 @@ export abstract class ArchiverDataSourceBase
117
113
  return BlockNumber(checkpointData.startBlock + checkpointData.numBlocks - 1);
118
114
  }
119
115
 
120
- public getCheckpointedBlocks(from: BlockNumber, limit: number): Promise<CheckpointedL2Block[]> {
121
- return this.store.getCheckpointedBlocks(from, limit);
116
+ public async getCheckpointedBlocks(
117
+ from: BlockNumber,
118
+ limit: number,
119
+ proven?: boolean,
120
+ ): Promise<CheckpointedL2Block[]> {
121
+ const blocks = await this.store.getCheckpointedBlocks(from, limit);
122
+
123
+ if (proven === true) {
124
+ const provenBlockNumber = await this.store.getProvenBlockNumber();
125
+ return blocks.filter(b => b.block.number <= provenBlockNumber);
126
+ }
127
+ return blocks;
122
128
  }
123
129
 
124
130
  public getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
@@ -129,7 +135,7 @@ export abstract class ArchiverDataSourceBase
129
135
  return this.store.getBlockHeaderByArchive(archive);
130
136
  }
131
137
 
132
- public async getL2Block(number: BlockNumber): Promise<L2Block | undefined> {
138
+ public async getL2BlockNew(number: BlockNumber): Promise<L2BlockNew | undefined> {
133
139
  // If the number provided is -ve, then return the latest block.
134
140
  if (number < 0) {
135
141
  number = await this.store.getLatestBlockNumber();
@@ -157,6 +163,16 @@ export abstract class ArchiverDataSourceBase
157
163
  return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
158
164
  }
159
165
 
166
+ public async getL2BlocksNew(from: BlockNumber, limit: number, proven?: boolean): Promise<L2BlockNew[]> {
167
+ const blocks = await this.store.getBlocks(from, limit);
168
+
169
+ if (proven === true) {
170
+ const provenBlockNumber = await this.store.getProvenBlockNumber();
171
+ return blocks.filter(b => b.number <= provenBlockNumber);
172
+ }
173
+ return blocks;
174
+ }
175
+
160
176
  public getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
161
177
  return this.store.getPrivateLogsByTags(tags, page);
162
178
  }
@@ -221,7 +237,10 @@ export abstract class ArchiverDataSourceBase
221
237
  return this.store.getL1ToL2MessageIndex(l1ToL2Message);
222
238
  }
223
239
 
224
- public async getCheckpoints(checkpointNumber: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
240
+ public async getPublishedCheckpoints(
241
+ checkpointNumber: CheckpointNumber,
242
+ limit: number,
243
+ ): Promise<PublishedCheckpoint[]> {
225
244
  const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
226
245
  const blocks = (
227
246
  await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
@@ -247,17 +266,17 @@ export abstract class ArchiverDataSourceBase
247
266
  return fullCheckpoints;
248
267
  }
249
268
 
250
- public getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
269
+ public getBlocksForSlot(slotNumber: SlotNumber): Promise<L2BlockNew[]> {
251
270
  return this.store.getBlocksForSlot(slotNumber);
252
271
  }
253
272
 
254
- public async getCheckpointedBlocksForEpoch(epochNumber: EpochNumber): Promise<CheckpointedL2Block[]> {
273
+ public async getBlocksForEpoch(epochNumber: EpochNumber): Promise<L2BlockNew[]> {
255
274
  if (!this.l1Constants) {
256
275
  throw new Error('L1 constants not set');
257
276
  }
258
277
 
259
278
  const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
260
- const blocks: CheckpointedL2Block[] = [];
279
+ const blocks: L2BlockNew[] = [];
261
280
 
262
281
  // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
263
282
  // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
@@ -268,9 +287,9 @@ export abstract class ArchiverDataSourceBase
268
287
  // push the blocks on backwards
269
288
  const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
270
289
  for (let i = endBlock; i >= checkpoint.startBlock; i--) {
271
- const checkpointedBlock = await this.getCheckpointedBlock(BlockNumber(i));
272
- if (checkpointedBlock) {
273
- blocks.push(checkpointedBlock);
290
+ const block = await this.getBlock(BlockNumber(i));
291
+ if (block) {
292
+ blocks.push(block);
274
293
  }
275
294
  }
276
295
  }
@@ -280,7 +299,7 @@ export abstract class ArchiverDataSourceBase
280
299
  return blocks.reverse();
281
300
  }
282
301
 
283
- public async getCheckpointedBlockHeadersForEpoch(epochNumber: EpochNumber): Promise<BlockHeader[]> {
302
+ public async getBlockHeadersForEpoch(epochNumber: EpochNumber): Promise<BlockHeader[]> {
284
303
  if (!this.l1Constants) {
285
304
  throw new Error('L1 constants not set');
286
305
  }
@@ -323,7 +342,7 @@ export abstract class ArchiverDataSourceBase
323
342
  while (checkpointData && slot(checkpointData) >= start) {
324
343
  if (slot(checkpointData) <= end) {
325
344
  // push the checkpoints on backwards
326
- const [checkpoint] = await this.getCheckpoints(checkpointData.checkpointNumber, 1);
345
+ const [checkpoint] = await this.getPublishedCheckpoints(checkpointData.checkpointNumber, 1);
327
346
  checkpoints.push(checkpoint.checkpoint);
328
347
  }
329
348
  checkpointData = await this.store.getCheckpointData(CheckpointNumber(checkpointData.checkpointNumber - 1));
@@ -332,7 +351,33 @@ export abstract class ArchiverDataSourceBase
332
351
  return checkpoints.reverse();
333
352
  }
334
353
 
335
- public async getBlock(number: BlockNumber): Promise<L2Block | undefined> {
354
+ public async getPublishedBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<CheckpointedL2Block[]> {
355
+ const checkpoints = await this.store.getRangeOfCheckpoints(CheckpointNumber(from), limit);
356
+ const provenCheckpointNumber = await this.store.getProvenCheckpointNumber();
357
+ const blocks = (
358
+ await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
359
+ ).filter(isDefined);
360
+
361
+ const publishedBlocks: CheckpointedL2Block[] = [];
362
+ for (let i = 0; i < checkpoints.length; i++) {
363
+ const blockForCheckpoint = blocks[i][0];
364
+ const checkpoint = checkpoints[i];
365
+ if (checkpoint.checkpointNumber > provenCheckpointNumber && proven === true) {
366
+ // this checkpoint isn't proven and we only want proven
367
+ continue;
368
+ }
369
+ const publishedBlock = new CheckpointedL2Block(
370
+ checkpoint.checkpointNumber,
371
+ blockForCheckpoint,
372
+ checkpoint.l1,
373
+ checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
374
+ );
375
+ publishedBlocks.push(publishedBlock);
376
+ }
377
+ return publishedBlocks;
378
+ }
379
+
380
+ public async getBlock(number: BlockNumber): Promise<L2BlockNew | undefined> {
336
381
  // If the number provided is -ve, then return the latest block.
337
382
  if (number < 0) {
338
383
  number = await this.store.getLatestBlockNumber();
@@ -343,24 +388,30 @@ export abstract class ArchiverDataSourceBase
343
388
  return this.store.getBlock(number);
344
389
  }
345
390
 
346
- public getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
347
- return this.store.getBlocks(from, limit);
391
+ public async getBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<L2BlockNew[]> {
392
+ const blocks = await this.store.getBlocks(from, limit);
393
+
394
+ if (proven === true) {
395
+ const provenBlockNumber = await this.store.getProvenBlockNumber();
396
+ return blocks.filter(b => b.number <= provenBlockNumber);
397
+ }
398
+ return blocks;
348
399
  }
349
400
 
350
- public getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
401
+ public getPublishedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
351
402
  return this.store.getCheckpointedBlockByHash(blockHash);
352
403
  }
353
404
 
354
- public getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
405
+ public getPublishedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
355
406
  return this.store.getCheckpointedBlockByArchive(archive);
356
407
  }
357
408
 
358
- public async getL2BlockByHash(blockHash: Fr): Promise<L2Block | undefined> {
409
+ public async getL2BlockNewByHash(blockHash: Fr): Promise<L2BlockNew | undefined> {
359
410
  const checkpointedBlock = await this.store.getCheckpointedBlockByHash(blockHash);
360
411
  return checkpointedBlock?.block;
361
412
  }
362
413
 
363
- public async getL2BlockByArchive(archive: Fr): Promise<L2Block | undefined> {
414
+ public async getL2BlockNewByArchive(archive: Fr): Promise<L2BlockNew | undefined> {
364
415
  const checkpointedBlock = await this.store.getCheckpointedBlockByArchive(archive);
365
416
  return checkpointedBlock?.block;
366
417
  }
@@ -1,4 +1,4 @@
1
- import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
1
+ import { BlockNumber, type CheckpointNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import {
@@ -10,7 +10,7 @@ import {
10
10
  ContractInstancePublishedEvent,
11
11
  ContractInstanceUpdatedEvent,
12
12
  } from '@aztec/protocol-contracts/instance-registry';
13
- import type { L2Block, ValidateCheckpointResult } from '@aztec/stdlib/block';
13
+ import type { L2BlockNew, ValidateCheckpointResult } from '@aztec/stdlib/block';
14
14
  import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
15
15
  import {
16
16
  type ExecutablePrivateFunctionWithMembershipProof,
@@ -35,7 +35,7 @@ enum Operation {
35
35
  /** Result of adding checkpoints with information about any pruned blocks. */
36
36
  type ReconcileCheckpointsResult = {
37
37
  /** Blocks that were pruned due to conflict with L1 checkpoints. */
38
- prunedBlocks: L2Block[] | undefined;
38
+ prunedBlocks: L2BlockNew[] | undefined;
39
39
  /** Last block number that was already inserted locally, or undefined if none. */
40
40
  lastAlreadyInsertedBlockNumber: BlockNumber | undefined;
41
41
  };
@@ -47,21 +47,17 @@ export class ArchiverDataStoreUpdater {
47
47
  constructor(private store: KVArchiverDataStore) {}
48
48
 
49
49
  /**
50
- * Adds proposed blocks to the store with contract class/instance extraction from logs.
51
- * These are uncheckpointed blocks that have been proposed by the sequencer but not yet included in a checkpoint on L1.
50
+ * Adds blocks to the store with contract class/instance extraction from logs.
52
51
  * Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events,
53
52
  * and individually broadcasted functions from the block logs.
54
53
  *
55
- * @param blocks - The proposed L2 blocks to add.
54
+ * @param blocks - The L2 blocks to add.
56
55
  * @param pendingChainValidationStatus - Optional validation status to set.
57
56
  * @returns True if the operation is successful.
58
57
  */
59
- public addProposedBlocks(
60
- blocks: L2Block[],
61
- pendingChainValidationStatus?: ValidateCheckpointResult,
62
- ): Promise<boolean> {
58
+ public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?: ValidateCheckpointResult): Promise<boolean> {
63
59
  return this.store.transactionAsync(async () => {
64
- await this.store.addProposedBlocks(blocks);
60
+ await this.store.addBlocks(blocks);
65
61
 
66
62
  const opResults = await Promise.all([
67
63
  // Update the pending chain validation status if provided
@@ -69,7 +65,7 @@ export class ArchiverDataStoreUpdater {
69
65
  // Add any logs emitted during the retrieved blocks
70
66
  this.store.addLogs(blocks),
71
67
  // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
72
- ...blocks.map(block => this.addContractDataToDb(block)),
68
+ ...blocks.map(block => this.addBlockDataToDB(block)),
73
69
  ]);
74
70
 
75
71
  return opResults.every(Boolean);
@@ -78,7 +74,7 @@ export class ArchiverDataStoreUpdater {
78
74
 
79
75
  /**
80
76
  * Reconciles local blocks with incoming checkpoints from L1.
81
- * Adds new checkpoints to the store with contract class/instance extraction from logs.
77
+ * Adds checkpoints to the store with contract class/instance extraction from logs.
82
78
  * Prunes any local blocks that conflict with checkpoint data (by comparing archive roots).
83
79
  * Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events,
84
80
  * and individually broadcasted functions from the checkpoint block logs.
@@ -87,7 +83,7 @@ export class ArchiverDataStoreUpdater {
87
83
  * @param pendingChainValidationStatus - Optional validation status to set.
88
84
  * @returns Result with information about any pruned blocks.
89
85
  */
90
- public addCheckpoints(
86
+ public setNewCheckpointData(
91
87
  checkpoints: PublishedCheckpoint[],
92
88
  pendingChainValidationStatus?: ValidateCheckpointResult,
93
89
  ): Promise<ReconcileCheckpointsResult> {
@@ -97,7 +93,7 @@ export class ArchiverDataStoreUpdater {
97
93
 
98
94
  await this.store.addCheckpoints(checkpoints);
99
95
 
100
- // Filter out blocks that were already inserted via addProposedBlocks() to avoid duplicating logs/contract data
96
+ // Filter out blocks that were already inserted via addBlocks() to avoid duplicating logs/contract data
101
97
  const newBlocks = checkpoints
102
98
  .flatMap((ch: PublishedCheckpoint) => ch.checkpoint.blocks)
103
99
  .filter(b => lastAlreadyInsertedBlockNumber === undefined || b.number > lastAlreadyInsertedBlockNumber);
@@ -108,7 +104,7 @@ export class ArchiverDataStoreUpdater {
108
104
  // Add any logs emitted during the retrieved blocks
109
105
  this.store.addLogs(newBlocks),
110
106
  // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
111
- ...newBlocks.map(block => this.addContractDataToDb(block)),
107
+ ...newBlocks.map(block => this.addBlockDataToDB(block)),
112
108
  ]);
113
109
 
114
110
  return { prunedBlocks, lastAlreadyInsertedBlockNumber };
@@ -189,80 +185,80 @@ export class ArchiverDataStoreUpdater {
189
185
  }
190
186
 
191
187
  /**
192
- * Removes all uncheckpointed blocks strictly after the specified block number and cleans up associated contract data.
188
+ * Removes all blocks strictly after the specified block number and cleans up associated contract data.
193
189
  * This handles removal of provisionally added blocks along with their contract classes/instances.
194
- * Verifies that each block being removed is not part of a stored checkpoint.
195
190
  *
196
191
  * @param blockNumber - Remove all blocks with number greater than this.
197
192
  * @returns The removed blocks.
198
- * @throws Error if any block to be removed is checkpointed.
199
193
  */
200
- public removeUncheckpointedBlocksAfter(blockNumber: BlockNumber): Promise<L2Block[]> {
194
+ public removeBlocksAfter(blockNumber: BlockNumber): Promise<L2BlockNew[]> {
201
195
  return this.store.transactionAsync(async () => {
202
- // Verify we're only removing uncheckpointed blocks
203
- const lastCheckpointedBlockNumber = await this.store.getCheckpointedL2BlockNumber();
204
- if (blockNumber < lastCheckpointedBlockNumber) {
205
- throw new Error(
206
- `Cannot remove blocks after ${blockNumber} because checkpointed blocks exist up to ${lastCheckpointedBlockNumber}`,
207
- );
208
- }
209
-
210
- return await this.removeBlocksAfter(blockNumber);
211
- });
212
- }
196
+ // First get the blocks to be removed so we can clean up contract data
197
+ const removedBlocks = await this.store.removeBlocksAfter(blockNumber);
213
198
 
214
- /**
215
- * Removes all blocks strictly after the given block number along with any logs and contract data.
216
- * Does not remove their checkpoints.
217
- */
218
- private async removeBlocksAfter(blockNumber: BlockNumber): Promise<L2Block[]> {
219
- // First get the blocks to be removed so we can clean up contract data
220
- const removedBlocks = await this.store.removeBlocksAfter(blockNumber);
221
-
222
- // Clean up contract data and logs for the removed blocks
223
- await Promise.all([
224
- this.store.deleteLogs(removedBlocks),
225
- ...removedBlocks.map(block => this.removeContractDataFromDb(block)),
226
- ]);
199
+ // Clean up contract data and logs for the removed blocks
200
+ await Promise.all([
201
+ this.store.deleteLogs(removedBlocks),
202
+ ...removedBlocks.map(block => this.removeBlockDataFromDB(block)),
203
+ ]);
227
204
 
228
- return removedBlocks;
205
+ return removedBlocks;
206
+ });
229
207
  }
230
208
 
231
209
  /**
232
- * Removes all checkpoints after the given checkpoint number.
210
+ * Unwinds checkpoints from the store with reverse contract extraction.
233
211
  * Deletes ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated data
234
- * that was stored for the removed checkpoints. Also removes ALL blocks (both checkpointed
235
- * and uncheckpointed) after the last block of the given checkpoint.
212
+ * that was stored for the unwound checkpoints.
236
213
  *
237
- * @param checkpointNumber - Remove all checkpoints strictly after this one.
214
+ * @param from - The checkpoint number to unwind from (must be the current tip).
215
+ * @param checkpointsToUnwind - The number of checkpoints to unwind.
238
216
  * @returns True if the operation is successful.
239
217
  */
240
- public async removeCheckpointsAfter(checkpointNumber: CheckpointNumber): Promise<boolean> {
241
- const { blocksRemoved = [] } = await this.store.removeCheckpointsAfter(checkpointNumber);
218
+ public async unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number): Promise<boolean> {
219
+ if (checkpointsToUnwind <= 0) {
220
+ throw new Error(`Cannot unwind ${checkpointsToUnwind} blocks`);
221
+ }
222
+
223
+ const last = await this.store.getSynchedCheckpointNumber();
224
+ if (from != last) {
225
+ throw new Error(`Cannot unwind checkpoints from checkpoint ${from} when the last checkpoint is ${last}`);
226
+ }
227
+
228
+ const blocks = [];
229
+ const lastCheckpointNumber = from + checkpointsToUnwind - 1;
230
+ for (let checkpointNumber = from; checkpointNumber <= lastCheckpointNumber; checkpointNumber++) {
231
+ const blocksForCheckpoint = await this.store.getBlocksForCheckpoint(checkpointNumber);
232
+ if (!blocksForCheckpoint) {
233
+ continue;
234
+ }
235
+ blocks.push(...blocksForCheckpoint);
236
+ }
242
237
 
243
238
  const opResults = await Promise.all([
244
239
  // Prune rolls back to the last proven block, which is by definition valid
245
240
  this.store.setPendingChainValidationStatus({ valid: true }),
246
- // Remove contract data for all blocks being removed
247
- ...blocksRemoved.map(block => this.removeContractDataFromDb(block)),
248
- this.store.deleteLogs(blocksRemoved),
241
+ // Remove contract data for all blocks being unwound
242
+ ...blocks.map(block => this.removeBlockDataFromDB(block)),
243
+ this.store.deleteLogs(blocks),
244
+ this.store.unwindCheckpoints(from, checkpointsToUnwind),
249
245
  ]);
250
246
 
251
247
  return opResults.every(Boolean);
252
248
  }
253
249
 
254
250
  /** Extracts and stores contract data from a single block. */
255
- private addContractDataToDb(block: L2Block): Promise<boolean> {
256
- return this.updateContractDataOnDb(block, Operation.Store);
251
+ private addBlockDataToDB(block: L2BlockNew): Promise<boolean> {
252
+ return this.editContractBlockData(block, Operation.Store);
257
253
  }
258
254
 
259
255
  /** Removes contract data associated with a block. */
260
- private removeContractDataFromDb(block: L2Block): Promise<boolean> {
261
- return this.updateContractDataOnDb(block, Operation.Delete);
256
+ private removeBlockDataFromDB(block: L2BlockNew): Promise<boolean> {
257
+ return this.editContractBlockData(block, Operation.Delete);
262
258
  }
263
259
 
264
260
  /** Adds or remove contract data associated with a block. */
265
- private async updateContractDataOnDb(block: L2Block, operation: Operation): Promise<boolean> {
261
+ private async editContractBlockData(block: L2BlockNew, operation: Operation): Promise<boolean> {
266
262
  const contractClassLogs = block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
267
263
  const privateLogs = block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
268
264
  const publicLogs = block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);