@aztec/archiver 0.0.1-commit.88e6f9396 → 0.0.1-commit.8c0b8ff

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 (65) hide show
  1. package/dest/archiver.d.ts +2 -1
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +22 -10
  4. package/dest/config.d.ts +3 -3
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +1 -2
  7. package/dest/errors.d.ts +1 -15
  8. package/dest/errors.d.ts.map +1 -1
  9. package/dest/errors.js +0 -18
  10. package/dest/factory.d.ts +3 -3
  11. package/dest/factory.d.ts.map +1 -1
  12. package/dest/factory.js +7 -4
  13. package/dest/l1/data_retrieval.d.ts +3 -6
  14. package/dest/l1/data_retrieval.d.ts.map +1 -1
  15. package/dest/l1/data_retrieval.js +6 -12
  16. package/dest/modules/data_source_base.d.ts +3 -3
  17. package/dest/modules/data_source_base.d.ts.map +1 -1
  18. package/dest/modules/data_source_base.js +4 -4
  19. package/dest/modules/data_store_updater.d.ts +1 -1
  20. package/dest/modules/data_store_updater.d.ts.map +1 -1
  21. package/dest/modules/data_store_updater.js +43 -10
  22. package/dest/modules/instrumentation.d.ts +1 -12
  23. package/dest/modules/instrumentation.d.ts.map +1 -1
  24. package/dest/modules/instrumentation.js +0 -10
  25. package/dest/modules/l1_synchronizer.d.ts +1 -1
  26. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  27. package/dest/modules/l1_synchronizer.js +13 -18
  28. package/dest/store/block_store.d.ts +2 -1
  29. package/dest/store/block_store.d.ts.map +1 -1
  30. package/dest/store/block_store.js +50 -5
  31. package/dest/store/contract_class_store.d.ts +1 -1
  32. package/dest/store/contract_class_store.d.ts.map +1 -1
  33. package/dest/store/contract_class_store.js +6 -2
  34. package/dest/store/contract_instance_store.d.ts +1 -1
  35. package/dest/store/contract_instance_store.d.ts.map +1 -1
  36. package/dest/store/contract_instance_store.js +6 -2
  37. package/dest/store/kv_archiver_store.d.ts +6 -11
  38. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  39. package/dest/store/kv_archiver_store.js +7 -13
  40. package/dest/store/log_store.d.ts +3 -6
  41. package/dest/store/log_store.d.ts.map +1 -1
  42. package/dest/store/log_store.js +6 -45
  43. package/dest/store/message_store.d.ts +1 -5
  44. package/dest/store/message_store.d.ts.map +1 -1
  45. package/dest/store/message_store.js +0 -13
  46. package/dest/test/fake_l1_state.d.ts +8 -1
  47. package/dest/test/fake_l1_state.d.ts.map +1 -1
  48. package/dest/test/fake_l1_state.js +16 -11
  49. package/package.json +13 -13
  50. package/src/archiver.ts +25 -10
  51. package/src/config.ts +1 -8
  52. package/src/errors.ts +0 -30
  53. package/src/factory.ts +13 -6
  54. package/src/l1/data_retrieval.ts +9 -17
  55. package/src/modules/data_source_base.ts +3 -8
  56. package/src/modules/data_store_updater.ts +51 -12
  57. package/src/modules/instrumentation.ts +0 -20
  58. package/src/modules/l1_synchronizer.ts +25 -29
  59. package/src/store/block_store.ts +62 -5
  60. package/src/store/contract_class_store.ts +7 -3
  61. package/src/store/contract_instance_store.ts +8 -5
  62. package/src/store/kv_archiver_store.ts +7 -19
  63. package/src/store/log_store.ts +15 -60
  64. package/src/store/message_store.ts +0 -19
  65. package/src/test/fake_l1_state.ts +20 -15
@@ -265,9 +265,6 @@ async function processCheckpointProposedLogs(
265
265
  checkpointNumber,
266
266
  expectedHashes,
267
267
  );
268
- const { timestamp, parentBeaconBlockRoot } = await getL1Block(publicClient, log.l1BlockNumber);
269
- const l1 = new L1PublishedData(log.l1BlockNumber, timestamp, log.l1BlockHash.toString());
270
-
271
268
  const checkpointBlobData = await getCheckpointBlobDataFromBlobs(
272
269
  blobClient,
273
270
  checkpoint.blockHash,
@@ -275,8 +272,12 @@ async function processCheckpointProposedLogs(
275
272
  checkpointNumber,
276
273
  logger,
277
274
  isHistoricalSync,
278
- parentBeaconBlockRoot,
279
- timestamp,
275
+ );
276
+
277
+ const l1 = new L1PublishedData(
278
+ log.l1BlockNumber,
279
+ await getL1BlockTime(publicClient, log.l1BlockNumber),
280
+ log.l1BlockHash.toString(),
280
281
  );
281
282
 
282
283
  retrievedCheckpoints.push({ ...checkpoint, checkpointBlobData, l1, chainId, version });
@@ -297,12 +298,9 @@ async function processCheckpointProposedLogs(
297
298
  return retrievedCheckpoints;
298
299
  }
299
300
 
300
- export async function getL1Block(
301
- publicClient: ViemPublicClient,
302
- blockNumber: bigint,
303
- ): Promise<{ timestamp: bigint; parentBeaconBlockRoot: string | undefined }> {
301
+ export async function getL1BlockTime(publicClient: ViemPublicClient, blockNumber: bigint): Promise<bigint> {
304
302
  const block = await publicClient.getBlock({ blockNumber, includeTransactions: false });
305
- return { timestamp: block.timestamp, parentBeaconBlockRoot: block.parentBeaconBlockRoot };
303
+ return block.timestamp;
306
304
  }
307
305
 
308
306
  export async function getCheckpointBlobDataFromBlobs(
@@ -312,14 +310,8 @@ export async function getCheckpointBlobDataFromBlobs(
312
310
  checkpointNumber: CheckpointNumber,
313
311
  logger: Logger,
314
312
  isHistoricalSync: boolean,
315
- parentBeaconBlockRoot?: string,
316
- l1BlockTimestamp?: bigint,
317
313
  ): Promise<CheckpointBlobData> {
318
- const blobBodies = await blobClient.getBlobSidecar(blockHash, blobHashes, {
319
- isHistoricalSync,
320
- parentBeaconBlockRoot,
321
- l1BlockTimestamp,
322
- });
314
+ const blobBodies = await blobClient.getBlobSidecar(blockHash, blobHashes, { isHistoricalSync });
323
315
  if (blobBodies.length === 0) {
324
316
  throw new NoBlobBodiesFoundError(checkpointNumber);
325
317
  }
@@ -165,21 +165,16 @@ export abstract class ArchiverDataSourceBase
165
165
  return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
166
166
  }
167
167
 
168
- public getPrivateLogsByTags(
169
- tags: SiloedTag[],
170
- page?: number,
171
- upToBlockNumber?: BlockNumber,
172
- ): Promise<TxScopedL2Log[][]> {
173
- return this.store.getPrivateLogsByTags(tags, page, upToBlockNumber);
168
+ public getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
169
+ return this.store.getPrivateLogsByTags(tags, page);
174
170
  }
175
171
 
176
172
  public getPublicLogsByTagsFromContract(
177
173
  contractAddress: AztecAddress,
178
174
  tags: Tag[],
179
175
  page?: number,
180
- upToBlockNumber?: BlockNumber,
181
176
  ): Promise<TxScopedL2Log[][]> {
182
- return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
177
+ return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page);
183
178
  }
184
179
 
185
180
  public getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
@@ -1,4 +1,5 @@
1
1
  import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
+ import { filterAsync } from '@aztec/foundation/collection';
2
3
  import { Fr } from '@aztec/foundation/curves/bn254';
3
4
  import { createLogger } from '@aztec/foundation/log';
4
5
  import {
@@ -13,9 +14,11 @@ import {
13
14
  import type { L2Block, ValidateCheckpointResult } from '@aztec/stdlib/block';
14
15
  import { type PublishedCheckpoint, validateCheckpoint } from '@aztec/stdlib/checkpoint';
15
16
  import {
17
+ type ContractClassPublicWithCommitment,
16
18
  type ExecutablePrivateFunctionWithMembershipProof,
17
19
  type UtilityFunctionWithMembershipProof,
18
- computePublicBytecodeCommitment,
20
+ computeContractAddressFromInstance,
21
+ computeContractClassId,
19
22
  isValidPrivateFunctionMembershipProof,
20
23
  isValidUtilityFunctionMembershipProof,
21
24
  } from '@aztec/stdlib/contract';
@@ -332,18 +335,37 @@ export class ArchiverDataStoreUpdater {
332
335
  .filter(log => ContractClassPublishedEvent.isContractClassPublishedEvent(log))
333
336
  .map(log => ContractClassPublishedEvent.fromLog(log));
334
337
 
335
- const contractClasses = await Promise.all(contractClassPublishedEvents.map(e => e.toContractClassPublic()));
336
- if (contractClasses.length > 0) {
337
- contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
338
- if (operation == Operation.Store) {
339
- // TODO: Will probably want to create some worker threads to compute these bytecode commitments as they are expensive
340
- const commitments = await Promise.all(
341
- contractClasses.map(c => computePublicBytecodeCommitment(c.packedBytecode)),
342
- );
343
- return await this.store.addContractClasses(contractClasses, commitments, blockNum);
344
- } else if (operation == Operation.Delete) {
338
+ if (operation == Operation.Delete) {
339
+ const contractClasses = contractClassPublishedEvents.map(e => e.toContractClassPublic());
340
+ if (contractClasses.length > 0) {
341
+ contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
345
342
  return await this.store.deleteContractClasses(contractClasses, blockNum);
346
343
  }
344
+ return true;
345
+ }
346
+
347
+ // Compute bytecode commitments and validate class IDs in a single pass.
348
+ const contractClasses: ContractClassPublicWithCommitment[] = [];
349
+ for (const event of contractClassPublishedEvents) {
350
+ const contractClass = await event.toContractClassPublicWithBytecodeCommitment();
351
+ const computedClassId = await computeContractClassId({
352
+ artifactHash: contractClass.artifactHash,
353
+ privateFunctionsRoot: contractClass.privateFunctionsRoot,
354
+ publicBytecodeCommitment: contractClass.publicBytecodeCommitment,
355
+ });
356
+ if (!computedClassId.equals(contractClass.id)) {
357
+ this.log.warn(
358
+ `Skipping contract class with mismatched id at block ${blockNum}. Claimed ${contractClass.id}, computed ${computedClassId}`,
359
+ { blockNum, contractClassId: event.contractClassId.toString() },
360
+ );
361
+ continue;
362
+ }
363
+ contractClasses.push(contractClass);
364
+ }
365
+
366
+ if (contractClasses.length > 0) {
367
+ contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
368
+ return await this.store.addContractClasses(contractClasses, blockNum);
347
369
  }
348
370
  return true;
349
371
  }
@@ -356,10 +378,27 @@ export class ArchiverDataStoreUpdater {
356
378
  blockNum: BlockNumber,
357
379
  operation: Operation,
358
380
  ): Promise<boolean> {
359
- const contractInstances = allLogs
381
+ const allInstances = allLogs
360
382
  .filter(log => ContractInstancePublishedEvent.isContractInstancePublishedEvent(log))
361
383
  .map(log => ContractInstancePublishedEvent.fromLog(log))
362
384
  .map(e => e.toContractInstance());
385
+
386
+ // Verify that each instance's address matches the one derived from its fields if we're adding
387
+ const contractInstances =
388
+ operation === Operation.Delete
389
+ ? allInstances
390
+ : await filterAsync(allInstances, async instance => {
391
+ const computedAddress = await computeContractAddressFromInstance(instance);
392
+ if (!computedAddress.equals(instance.address)) {
393
+ this.log.warn(
394
+ `Found contract instance with mismatched address at block ${blockNum}. Claimed ${instance.address} but computed ${computedAddress}.`,
395
+ { instanceAddress: instance.address.toString(), computedAddress: computedAddress.toString(), blockNum },
396
+ );
397
+ return false;
398
+ }
399
+ return true;
400
+ });
401
+
363
402
  if (contractInstances.length > 0) {
364
403
  contractInstances.forEach(c =>
365
404
  this.log.verbose(`${Operation[operation]} contract instance at ${c.address.toString()}`),
@@ -1,9 +1,6 @@
1
- import type { SlotNumber } from '@aztec/foundation/branded-types';
2
1
  import { createLogger } from '@aztec/foundation/log';
3
2
  import type { L2Block } from '@aztec/stdlib/block';
4
3
  import type { CheckpointData } from '@aztec/stdlib/checkpoint';
5
- import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
6
- import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
7
4
  import {
8
5
  Attributes,
9
6
  type Gauge,
@@ -41,8 +38,6 @@ export class ArchiverInstrumentation {
41
38
 
42
39
  private blockProposalTxTargetCount: UpDownCounter;
43
40
 
44
- private checkpointL1InclusionDelay: Histogram;
45
-
46
41
  private log = createLogger('archiver:instrumentation');
47
42
 
48
43
  private constructor(
@@ -90,8 +85,6 @@ export class ArchiverInstrumentation {
90
85
  },
91
86
  );
92
87
 
93
- this.checkpointL1InclusionDelay = meter.createHistogram(Metrics.ARCHIVER_CHECKPOINT_L1_INCLUSION_DELAY);
94
-
95
88
  this.dbMetrics = new LmdbMetrics(
96
89
  meter,
97
90
  {
@@ -168,17 +161,4 @@ export class ArchiverInstrumentation {
168
161
  [Attributes.L1_BLOCK_PROPOSAL_USED_TRACE]: usedTrace,
169
162
  });
170
163
  }
171
-
172
- /**
173
- * Records L1 inclusion timing for a checkpoint observed on L1 (seconds into the L2 slot).
174
- */
175
- public processCheckpointL1Timing(data: {
176
- slotNumber: SlotNumber;
177
- l1Timestamp: bigint;
178
- l1Constants: Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>;
179
- }): void {
180
- const slotStartTs = getTimestampForSlot(data.slotNumber, data.l1Constants);
181
- const inclusionDelaySeconds = Number(data.l1Timestamp - slotStartTs);
182
- this.checkpointL1InclusionDelay.record(inclusionDelaySeconds);
183
- }
184
164
  }
@@ -3,7 +3,6 @@ import { EpochCache } from '@aztec/epoch-cache';
3
3
  import { InboxContract, RollupContract } from '@aztec/ethereum/contracts';
4
4
  import type { L1BlockId } from '@aztec/ethereum/l1-types';
5
5
  import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
6
- import { asyncPool } from '@aztec/foundation/async-pool';
7
6
  import { maxBigint } from '@aztec/foundation/bigint';
8
7
  import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
9
8
  import { Buffer32 } from '@aztec/foundation/buffer';
@@ -246,13 +245,22 @@ export class ArchiverL1Synchronizer implements Traceable {
246
245
  const localFinalizedCheckpointNumber = await this.store.getFinalizedCheckpointNumber();
247
246
  if (localFinalizedCheckpointNumber !== finalizedCheckpointNumber) {
248
247
  await this.updater.setFinalizedCheckpointNumber(finalizedCheckpointNumber);
249
- this.log.info(`Updated finalized chain to checkpoint ${finalizedCheckpointNumber}`, {
250
- finalizedCheckpointNumber,
251
- finalizedL1BlockNumber,
252
- });
248
+ const finalizedL2BlockNumber = await this.store.getFinalizedL2BlockNumber();
249
+ this.log.info(
250
+ `Updated finalized chain to checkpoint ${finalizedCheckpointNumber} (L2 block ${finalizedL2BlockNumber})`,
251
+ {
252
+ finalizedCheckpointNumber,
253
+ previousFinalizedCheckpointNumber: localFinalizedCheckpointNumber,
254
+ finalizedL2BlockNumber,
255
+ finalizedL1BlockNumber,
256
+ },
257
+ );
258
+ }
259
+ } catch (err: any) {
260
+ // The rollup contract may not exist at the finalized L1 block right after deployment.
261
+ if (!err?.message?.includes('returned no data')) {
262
+ this.log.warn(`Failed to update finalized checkpoint: ${err}`);
253
263
  }
254
- } catch (err) {
255
- this.log.warn(`Failed to update finalized checkpoint: ${err}`);
256
264
  }
257
265
  }
258
266
 
@@ -334,20 +342,17 @@ export class ArchiverL1Synchronizer implements Traceable {
334
342
 
335
343
  const checkpointsToUnwind = localPendingCheckpointNumber - provenCheckpointNumber;
336
344
 
337
- // Fetch checkpoints and blocks in bounded batches to avoid unbounded concurrent
338
- // promises when the gap between local pending and proven checkpoint numbers is large.
339
- const BATCH_SIZE = 10;
340
- const indices = Array.from({ length: checkpointsToUnwind }, (_, i) => CheckpointNumber(i + pruneFrom));
341
- const checkpoints = (await asyncPool(BATCH_SIZE, indices, idx => this.store.getCheckpointData(idx))).filter(
342
- isDefined,
345
+ const checkpointPromises = Array.from({ length: checkpointsToUnwind })
346
+ .fill(0)
347
+ .map((_, i) => this.store.getCheckpointData(CheckpointNumber(i + pruneFrom)));
348
+ const checkpoints = await Promise.all(checkpointPromises);
349
+
350
+ const blockPromises = await Promise.all(
351
+ checkpoints
352
+ .filter(isDefined)
353
+ .map(cp => this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber))),
343
354
  );
344
- const newBlocks = (
345
- await asyncPool(BATCH_SIZE, checkpoints, cp =>
346
- this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
347
- )
348
- )
349
- .filter(isDefined)
350
- .flat();
355
+ const newBlocks = blockPromises.filter(isDefined).flat();
351
356
 
352
357
  // Emit an event for listening services to react to the chain prune
353
358
  this.events.emit(L2BlockSourceEvents.L2PruneUnproven, {
@@ -395,7 +400,6 @@ export class ArchiverL1Synchronizer implements Traceable {
395
400
  const localMessagesInserted = await this.store.getTotalL1ToL2MessageCount();
396
401
  const localLastMessage = await this.store.getLastL1ToL2Message();
397
402
  const remoteMessagesState = await this.inbox.getState({ blockNumber: currentL1BlockNumber });
398
- await this.store.setInboxTreeInProgress(remoteMessagesState.treeInProgress);
399
403
 
400
404
  this.log.trace(`Retrieved remote inbox state at L1 block ${currentL1BlockNumber}.`, {
401
405
  localMessagesInserted,
@@ -830,14 +834,6 @@ export class ArchiverL1Synchronizer implements Traceable {
830
834
  );
831
835
  }
832
836
 
833
- for (const published of validCheckpoints) {
834
- this.instrumentation.processCheckpointL1Timing({
835
- slotNumber: published.checkpoint.header.slotNumber,
836
- l1Timestamp: published.l1.timestamp,
837
- l1Constants: this.l1Constants,
838
- });
839
- }
840
-
841
837
  try {
842
838
  const updatedValidationResult =
843
839
  rollupStatus.validationResult === initialValidationResult ? undefined : rollupStatus.validationResult;
@@ -227,21 +227,34 @@ export class BlockStore {
227
227
  }
228
228
 
229
229
  return await this.db.transactionAsync(async () => {
230
- // Check that the checkpoint immediately before the first block to be added is present in the store.
231
230
  const firstCheckpointNumber = checkpoints[0].checkpoint.number;
232
231
  const previousCheckpointNumber = await this.getLatestCheckpointNumber();
233
232
 
234
- if (previousCheckpointNumber !== firstCheckpointNumber - 1 && !opts.force) {
233
+ // Handle already-stored checkpoints at the start of the batch.
234
+ // This can happen after an L1 reorg re-includes a checkpoint in a different L1 block.
235
+ // We accept them if archives match (same content) and update their L1 metadata.
236
+ if (!opts.force && firstCheckpointNumber <= previousCheckpointNumber) {
237
+ checkpoints = await this.skipOrUpdateAlreadyStoredCheckpoints(checkpoints, previousCheckpointNumber);
238
+ if (checkpoints.length === 0) {
239
+ return true;
240
+ }
241
+ // Re-check sequentiality after skipping
242
+ const newFirstNumber = checkpoints[0].checkpoint.number;
243
+ if (previousCheckpointNumber !== newFirstNumber - 1) {
244
+ throw new InitialCheckpointNumberNotSequentialError(newFirstNumber, previousCheckpointNumber);
245
+ }
246
+ } else if (previousCheckpointNumber !== firstCheckpointNumber - 1 && !opts.force) {
235
247
  throw new InitialCheckpointNumberNotSequentialError(firstCheckpointNumber, previousCheckpointNumber);
236
248
  }
237
249
 
238
250
  // Extract the previous checkpoint if there is one
251
+ const currentFirstCheckpointNumber = checkpoints[0].checkpoint.number;
239
252
  let previousCheckpointData: CheckpointData | undefined = undefined;
240
- if (previousCheckpointNumber !== INITIAL_CHECKPOINT_NUMBER - 1) {
253
+ if (currentFirstCheckpointNumber - 1 !== INITIAL_CHECKPOINT_NUMBER - 1) {
241
254
  // There should be a previous checkpoint
242
- previousCheckpointData = await this.getCheckpointData(previousCheckpointNumber);
255
+ previousCheckpointData = await this.getCheckpointData(CheckpointNumber(currentFirstCheckpointNumber - 1));
243
256
  if (previousCheckpointData === undefined) {
244
- throw new CheckpointNotFoundError(previousCheckpointNumber);
257
+ throw new CheckpointNotFoundError(CheckpointNumber(currentFirstCheckpointNumber - 1));
245
258
  }
246
259
  }
247
260
 
@@ -331,6 +344,50 @@ export class BlockStore {
331
344
  });
332
345
  }
333
346
 
347
+ /**
348
+ * Handles checkpoints at the start of a batch that are already stored (e.g. due to L1 reorg).
349
+ * Verifies the archive root matches, updates L1 metadata, and returns only the new checkpoints.
350
+ */
351
+ private async skipOrUpdateAlreadyStoredCheckpoints(
352
+ checkpoints: PublishedCheckpoint[],
353
+ latestStored: CheckpointNumber,
354
+ ): Promise<PublishedCheckpoint[]> {
355
+ let i = 0;
356
+ for (; i < checkpoints.length && checkpoints[i].checkpoint.number <= latestStored; i++) {
357
+ const incoming = checkpoints[i];
358
+ const stored = await this.getCheckpointData(incoming.checkpoint.number);
359
+ if (!stored) {
360
+ // Should not happen if latestStored is correct, but be safe
361
+ break;
362
+ }
363
+ // Verify the checkpoint content matches (archive root)
364
+ if (!stored.archive.root.equals(incoming.checkpoint.archive.root)) {
365
+ throw new Error(
366
+ `Checkpoint ${incoming.checkpoint.number} already exists in store but with a different archive root. ` +
367
+ `Stored: ${stored.archive.root}, incoming: ${incoming.checkpoint.archive.root}`,
368
+ );
369
+ }
370
+ // Update L1 metadata and attestations for the already-stored checkpoint
371
+ this.#log.warn(
372
+ `Checkpoint ${incoming.checkpoint.number} already stored, updating L1 info ` +
373
+ `(L1 block ${stored.l1.blockNumber} -> ${incoming.l1.blockNumber})`,
374
+ );
375
+ await this.#checkpoints.set(incoming.checkpoint.number, {
376
+ header: incoming.checkpoint.header.toBuffer(),
377
+ archive: incoming.checkpoint.archive.toBuffer(),
378
+ checkpointOutHash: incoming.checkpoint.getCheckpointOutHash().toBuffer(),
379
+ l1: incoming.l1.toBuffer(),
380
+ attestations: incoming.attestations.map(a => a.toBuffer()),
381
+ checkpointNumber: incoming.checkpoint.number,
382
+ startBlock: incoming.checkpoint.blocks[0].number,
383
+ blockCount: incoming.checkpoint.blocks.length,
384
+ });
385
+ // Update the sync point to reflect the new L1 block
386
+ await this.#lastSynchedL1Block.set(incoming.l1.blockNumber);
387
+ }
388
+ return checkpoints.slice(i);
389
+ }
390
+
334
391
  private async addBlockToDatabase(block: L2Block, checkpointNumber: number, indexWithinCheckpoint: number) {
335
392
  const blockHash = await block.hash();
336
393
 
@@ -29,11 +29,15 @@ export class ContractClassStore {
29
29
  blockNumber: number,
30
30
  ): Promise<void> {
31
31
  await this.db.transactionAsync(async () => {
32
- await this.#contractClasses.setIfNotExists(
33
- contractClass.id.toString(),
32
+ const key = contractClass.id.toString();
33
+ if (await this.#contractClasses.hasAsync(key)) {
34
+ throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`);
35
+ }
36
+ await this.#contractClasses.set(
37
+ key,
34
38
  serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
35
39
  );
36
- await this.#bytecodeCommitments.setIfNotExists(contractClass.id.toString(), bytecodeCommitment.toBuffer());
40
+ await this.#bytecodeCommitments.set(key, bytecodeCommitment.toBuffer());
37
41
  });
38
42
  }
39
43
 
@@ -27,11 +27,14 @@ export class ContractInstanceStore {
27
27
 
28
28
  addContractInstance(contractInstance: ContractInstanceWithAddress, blockNumber: number): Promise<void> {
29
29
  return this.db.transactionAsync(async () => {
30
- await this.#contractInstances.set(
31
- contractInstance.address.toString(),
32
- new SerializableContractInstance(contractInstance).toBuffer(),
33
- );
34
- await this.#contractInstancePublishedAt.set(contractInstance.address.toString(), blockNumber);
30
+ const key = contractInstance.address.toString();
31
+ if (await this.#contractInstances.hasAsync(key)) {
32
+ throw new Error(
33
+ `Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`,
34
+ );
35
+ }
36
+ await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer());
37
+ await this.#contractInstancePublishedAt.set(key, blockNumber);
35
38
  });
36
39
  }
37
40
 
@@ -16,6 +16,7 @@ import {
16
16
  import type { CheckpointData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
17
17
  import type {
18
18
  ContractClassPublic,
19
+ ContractClassPublicWithCommitment,
19
20
  ContractDataSource,
20
21
  ContractInstanceUpdateWithAddress,
21
22
  ContractInstanceWithAddress,
@@ -167,19 +168,14 @@ export class KVArchiverDataStore implements ContractDataSource {
167
168
 
168
169
  /**
169
170
  * Add new contract classes from an L2 block to the store's list.
170
- * @param data - List of contract classes to be added.
171
- * @param bytecodeCommitments - Bytecode commitments for the contract classes.
171
+ * @param data - List of contract classes (with bytecode commitments) to be added.
172
172
  * @param blockNumber - Number of the L2 block the contracts were registered in.
173
173
  * @returns True if the operation is successful.
174
174
  */
175
- async addContractClasses(
176
- data: ContractClassPublic[],
177
- bytecodeCommitments: Fr[],
178
- blockNumber: BlockNumber,
179
- ): Promise<boolean> {
175
+ async addContractClasses(data: ContractClassPublicWithCommitment[], blockNumber: BlockNumber): Promise<boolean> {
180
176
  return (
181
177
  await Promise.all(
182
- data.map((c, i) => this.#contractClassStore.addContractClass(c, bytecodeCommitments[i], blockNumber)),
178
+ data.map(c => this.#contractClassStore.addContractClass(c, c.publicBytecodeCommitment, blockNumber)),
183
179
  )
184
180
  ).every(Boolean);
185
181
  }
@@ -474,11 +470,10 @@ export class KVArchiverDataStore implements ContractDataSource {
474
470
  * array implies no logs match that tag.
475
471
  * @param tags - The tags to search for.
476
472
  * @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
477
- * @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
478
473
  */
479
- getPrivateLogsByTags(tags: SiloedTag[], page?: number, upToBlockNumber?: BlockNumber): Promise<TxScopedL2Log[][]> {
474
+ getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
480
475
  try {
481
- return this.#logStore.getPrivateLogsByTags(tags, page, upToBlockNumber);
476
+ return this.#logStore.getPrivateLogsByTags(tags, page);
482
477
  } catch (err) {
483
478
  return Promise.reject(err);
484
479
  }
@@ -490,16 +485,14 @@ export class KVArchiverDataStore implements ContractDataSource {
490
485
  * @param contractAddress - The contract address to search logs for.
491
486
  * @param tags - The tags to search for.
492
487
  * @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
493
- * @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
494
488
  */
495
489
  getPublicLogsByTagsFromContract(
496
490
  contractAddress: AztecAddress,
497
491
  tags: Tag[],
498
492
  page?: number,
499
- upToBlockNumber?: BlockNumber,
500
493
  ): Promise<TxScopedL2Log[][]> {
501
494
  try {
502
- return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
495
+ return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page);
503
496
  } catch (err) {
504
497
  return Promise.reject(err);
505
498
  }
@@ -606,11 +599,6 @@ export class KVArchiverDataStore implements ContractDataSource {
606
599
  return this.#messageStore.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
607
600
  }
608
601
 
609
- /** Persists the inbox tree-in-progress checkpoint number from L1 state. */
610
- public setInboxTreeInProgress(value: bigint): Promise<void> {
611
- return this.#messageStore.setInboxTreeInProgress(value);
612
- }
613
-
614
602
  /** Returns an async iterator to all L1 to L2 messages on the range. */
615
603
  public iterateL1ToL2Messages(range: CustomRange<bigint> = {}): AsyncIterableIterator<InboxMessage> {
616
604
  return this.#messageStore.iterateL1ToL2Messages(range);
@@ -22,7 +22,6 @@ import {
22
22
  } from '@aztec/stdlib/logs';
23
23
  import { TxHash } from '@aztec/stdlib/tx';
24
24
 
25
- import { OutOfOrderLogInsertionError } from '../errors.js';
26
25
  import type { BlockStore } from './block_store.js';
27
26
 
28
27
  /**
@@ -166,21 +165,10 @@ export class LogStore {
166
165
 
167
166
  for (const taggedLogBuffer of currentPrivateTaggedLogs) {
168
167
  if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
169
- const newLogs = privateTaggedLogs.get(taggedLogBuffer.tag)!;
170
- if (newLogs.length === 0) {
171
- continue;
172
- }
173
- const lastExisting = TxScopedL2Log.fromBuffer(taggedLogBuffer.logBuffers.at(-1)!);
174
- const firstNew = TxScopedL2Log.fromBuffer(newLogs[0]);
175
- if (lastExisting.blockNumber > firstNew.blockNumber) {
176
- throw new OutOfOrderLogInsertionError(
177
- 'private',
178
- taggedLogBuffer.tag,
179
- lastExisting.blockNumber,
180
- firstNew.blockNumber,
181
- );
182
- }
183
- privateTaggedLogs.set(taggedLogBuffer.tag, taggedLogBuffer.logBuffers.concat(newLogs));
168
+ privateTaggedLogs.set(
169
+ taggedLogBuffer.tag,
170
+ taggedLogBuffer.logBuffers!.concat(privateTaggedLogs.get(taggedLogBuffer.tag)!),
171
+ );
184
172
  }
185
173
  }
186
174
 
@@ -212,21 +200,10 @@ export class LogStore {
212
200
 
213
201
  for (const taggedLogBuffer of currentPublicTaggedLogs) {
214
202
  if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
215
- const newLogs = publicTaggedLogs.get(taggedLogBuffer.tag)!;
216
- if (newLogs.length === 0) {
217
- continue;
218
- }
219
- const lastExisting = TxScopedL2Log.fromBuffer(taggedLogBuffer.logBuffers.at(-1)!);
220
- const firstNew = TxScopedL2Log.fromBuffer(newLogs[0]);
221
- if (lastExisting.blockNumber > firstNew.blockNumber) {
222
- throw new OutOfOrderLogInsertionError(
223
- 'public',
224
- taggedLogBuffer.tag,
225
- lastExisting.blockNumber,
226
- firstNew.blockNumber,
227
- );
228
- }
229
- publicTaggedLogs.set(taggedLogBuffer.tag, taggedLogBuffer.logBuffers.concat(newLogs));
203
+ publicTaggedLogs.set(
204
+ taggedLogBuffer.tag,
205
+ taggedLogBuffer.logBuffers!.concat(publicTaggedLogs.get(taggedLogBuffer.tag)!),
206
+ );
230
207
  }
231
208
  }
232
209
 
@@ -376,30 +353,17 @@ export class LogStore {
376
353
  * array implies no logs match that tag.
377
354
  * @param tags - The tags to search for.
378
355
  * @param page - The page number (0-indexed) for pagination.
379
- * @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
380
356
  * @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
381
357
  * MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
382
358
  */
383
- async getPrivateLogsByTags(
384
- tags: SiloedTag[],
385
- page: number = 0,
386
- upToBlockNumber?: BlockNumber,
387
- ): Promise<TxScopedL2Log[][]> {
359
+ async getPrivateLogsByTags(tags: SiloedTag[], page: number = 0): Promise<TxScopedL2Log[][]> {
388
360
  const logs = await Promise.all(tags.map(tag => this.#privateLogsByTag.getAsync(tag.toString())));
389
-
390
361
  const start = page * MAX_LOGS_PER_TAG;
391
362
  const end = start + MAX_LOGS_PER_TAG;
392
363
 
393
- return logs.map(logBuffers => {
394
- const deserialized = logBuffers?.slice(start, end).map(buf => TxScopedL2Log.fromBuffer(buf)) ?? [];
395
- if (upToBlockNumber !== undefined) {
396
- const cutoff = deserialized.findIndex(log => log.blockNumber > upToBlockNumber);
397
- if (cutoff !== -1) {
398
- return deserialized.slice(0, cutoff);
399
- }
400
- }
401
- return deserialized;
402
- });
364
+ return logs.map(
365
+ logBuffers => logBuffers?.slice(start, end).map(logBuffer => TxScopedL2Log.fromBuffer(logBuffer)) ?? [],
366
+ );
403
367
  }
404
368
 
405
369
  /**
@@ -408,7 +372,6 @@ export class LogStore {
408
372
  * @param contractAddress - The contract address to search logs for.
409
373
  * @param tags - The tags to search for.
410
374
  * @param page - The page number (0-indexed) for pagination.
411
- * @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
412
375
  * @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
413
376
  * MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
414
377
  */
@@ -416,7 +379,6 @@ export class LogStore {
416
379
  contractAddress: AztecAddress,
417
380
  tags: Tag[],
418
381
  page: number = 0,
419
- upToBlockNumber?: BlockNumber,
420
382
  ): Promise<TxScopedL2Log[][]> {
421
383
  const logs = await Promise.all(
422
384
  tags.map(tag => {
@@ -427,16 +389,9 @@ export class LogStore {
427
389
  const start = page * MAX_LOGS_PER_TAG;
428
390
  const end = start + MAX_LOGS_PER_TAG;
429
391
 
430
- return logs.map(logBuffers => {
431
- const deserialized = logBuffers?.slice(start, end).map(buf => TxScopedL2Log.fromBuffer(buf)) ?? [];
432
- if (upToBlockNumber !== undefined) {
433
- const cutoff = deserialized.findIndex(log => log.blockNumber > upToBlockNumber);
434
- if (cutoff !== -1) {
435
- return deserialized.slice(0, cutoff);
436
- }
437
- }
438
- return deserialized;
439
- });
392
+ return logs.map(
393
+ logBuffers => logBuffers?.slice(start, end).map(logBuffer => TxScopedL2Log.fromBuffer(logBuffer)) ?? [],
394
+ );
440
395
  }
441
396
 
442
397
  /**