@aztec/archiver 0.62.0 → 0.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dest/archiver/archiver.d.ts +11 -2
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +43 -19
  4. package/dest/archiver/archiver_store.d.ts +8 -2
  5. package/dest/archiver/archiver_store.d.ts.map +1 -1
  6. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.js +64 -25
  8. package/dest/archiver/config.d.ts +2 -6
  9. package/dest/archiver/config.d.ts.map +1 -1
  10. package/dest/archiver/config.js +3 -6
  11. package/dest/archiver/epoch_helpers.d.ts +10 -10
  12. package/dest/archiver/epoch_helpers.d.ts.map +1 -1
  13. package/dest/archiver/epoch_helpers.js +9 -10
  14. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +8 -2
  15. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  16. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +14 -1
  17. package/dest/archiver/kv_archiver_store/log_store.d.ts +8 -2
  18. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  19. package/dest/archiver/kv_archiver_store/log_store.js +144 -57
  20. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +13 -4
  21. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
  22. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +136 -31
  23. package/dest/factory.d.ts +5 -2
  24. package/dest/factory.d.ts.map +1 -1
  25. package/dest/factory.js +2 -2
  26. package/dest/rpc/index.d.ts +3 -2
  27. package/dest/rpc/index.d.ts.map +1 -1
  28. package/dest/rpc/index.js +10 -3
  29. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  30. package/dest/test/mock_l2_block_source.js +4 -2
  31. package/package.json +10 -10
  32. package/src/archiver/archiver.ts +54 -24
  33. package/src/archiver/archiver_store.ts +9 -2
  34. package/src/archiver/archiver_store_test_suite.ts +85 -26
  35. package/src/archiver/config.ts +11 -12
  36. package/src/archiver/epoch_helpers.ts +16 -12
  37. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +15 -2
  38. package/src/archiver/kv_archiver_store/log_store.ts +176 -58
  39. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +152 -27
  40. package/src/factory.ts +6 -3
  41. package/src/rpc/index.ts +11 -2
  42. package/src/test/mock_l2_block_source.ts +3 -1
  43. package/dest/rpc/archiver_client.d.ts +0 -3
  44. package/dest/rpc/archiver_client.d.ts.map +0 -1
  45. package/dest/rpc/archiver_client.js +0 -12
  46. package/dest/rpc/archiver_server.d.ts +0 -9
  47. package/dest/rpc/archiver_server.d.ts.map +0 -1
  48. package/dest/rpc/archiver_server.js +0 -20
  49. package/src/rpc/archiver_client.ts +0 -29
  50. package/src/rpc/archiver_server.ts +0 -35
@@ -1,5 +1,5 @@
1
1
  import {
2
- type EncryptedL2NoteLog,
2
+ type EncryptedL2Log,
3
3
  type FromLogType,
4
4
  type GetUnencryptedLogsResponse,
5
5
  type InboxLeaf,
@@ -15,6 +15,7 @@ import {
15
15
  type TxEffect,
16
16
  type TxHash,
17
17
  type TxReceipt,
18
+ type TxScopedL2Log,
18
19
  type UnencryptedL2Log,
19
20
  } from '@aztec/circuit-types';
20
21
  import {
@@ -160,6 +161,8 @@ export class Archiver implements ArchiveSource {
160
161
  rollup.read.GENESIS_TIME(),
161
162
  ] as const);
162
163
 
164
+ const { aztecEpochDuration: epochDuration, aztecSlotDuration: slotDuration, ethereumSlotDuration } = config;
165
+
163
166
  const archiver = new Archiver(
164
167
  publicClient,
165
168
  config.l1Contracts.rollupAddress,
@@ -168,7 +171,7 @@ export class Archiver implements ArchiveSource {
168
171
  archiverStore,
169
172
  config.archiverPollingIntervalMS ?? 10_000,
170
173
  new ArchiverInstrumentation(telemetry),
171
- { l1StartBlock, l1GenesisTime },
174
+ { l1StartBlock, l1GenesisTime, epochDuration, slotDuration, ethereumSlotDuration },
172
175
  );
173
176
  await archiver.start(blockUntilSynced);
174
177
  return archiver;
@@ -246,6 +249,12 @@ export class Archiver implements ArchiveSource {
246
249
  // ********** Events that are processed per L1 block **********
247
250
  await this.handleL1ToL2Messages(blockUntilSynced, messagesSynchedTo, currentL1BlockNumber);
248
251
 
252
+ // Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
253
+ if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
254
+ this.l1Timestamp = (await this.publicClient.getBlock({ blockNumber: currentL1BlockNumber })).timestamp;
255
+ this.l1BlockNumber = currentL1BlockNumber;
256
+ }
257
+
249
258
  // ********** Events that are processed per L2 block **********
250
259
  if (currentL1BlockNumber > blocksSynchedTo) {
251
260
  // First we retrieve new L2 blocks
@@ -257,21 +266,17 @@ export class Archiver implements ArchiveSource {
257
266
  // up to which point we're pruning, and then requesting L2 blocks up to that point only.
258
267
  await this.handleEpochPrune(provenBlockNumber, currentL1BlockNumber);
259
268
  }
260
-
261
- // Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
262
- if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
263
- this.l1Timestamp = await this.publicClient.getBlock({ blockNumber: currentL1BlockNumber }).then(b => b.timestamp);
264
- this.l1BlockNumber = currentL1BlockNumber;
265
- }
266
269
  }
267
270
 
268
271
  /** Checks if there'd be a reorg for the next block submission and start pruning now. */
269
272
  private async handleEpochPrune(provenBlockNumber: bigint, currentL1BlockNumber: bigint) {
270
273
  const localPendingBlockNumber = BigInt(await this.getBlockNumber());
271
274
 
275
+ const time = (this.l1Timestamp ?? 0n) + BigInt(this.l1constants.ethereumSlotDuration);
276
+
272
277
  const canPrune =
273
278
  localPendingBlockNumber > provenBlockNumber &&
274
- (await this.rollup.read.canPrune({ blockNumber: currentL1BlockNumber }));
279
+ (await this.rollup.read.canPruneAtTime([time], { blockNumber: currentL1BlockNumber }));
275
280
 
276
281
  if (canPrune) {
277
282
  this.log.verbose(`L2 prune will occur on next submission. Rolling back to last proven block.`);
@@ -499,7 +504,7 @@ export class Archiver implements ArchiveSource {
499
504
  }
500
505
 
501
506
  public async getBlocksForEpoch(epochNumber: bigint): Promise<L2Block[]> {
502
- const [start, end] = getSlotRangeForEpoch(epochNumber);
507
+ const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
503
508
  const blocks: L2Block[] = [];
504
509
 
505
510
  // Walk the list of blocks backwards and filter by slots matching the requested epoch.
@@ -520,7 +525,7 @@ export class Archiver implements ArchiveSource {
520
525
  // The epoch is complete if the current L2 block is the last one in the epoch (or later)
521
526
  const header = await this.getBlockHeader('latest');
522
527
  const slot = header?.globalVariables.slotNumber.toBigInt();
523
- const [_startSlot, endSlot] = getSlotRangeForEpoch(epochNumber);
528
+ const [_startSlot, endSlot] = getSlotRangeForEpoch(epochNumber, this.l1constants);
524
529
  if (slot && slot >= endSlot) {
525
530
  return true;
526
531
  }
@@ -634,7 +639,7 @@ export class Archiver implements ArchiveSource {
634
639
  * @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
635
640
  * that tag.
636
641
  */
637
- getLogsByTags(tags: Fr[]): Promise<EncryptedL2NoteLog[][]> {
642
+ getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
638
643
  return this.store.getLogsByTags(tags);
639
644
  }
640
645
 
@@ -647,6 +652,15 @@ export class Archiver implements ArchiveSource {
647
652
  return this.store.getUnencryptedLogs(filter);
648
653
  }
649
654
 
655
+ /**
656
+ * Gets contract class logs based on the provided filter.
657
+ * @param filter - The filter to apply to the logs.
658
+ * @returns The requested logs.
659
+ */
660
+ getContractClassLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
661
+ return this.store.getContractClassLogs(filter);
662
+ }
663
+
650
664
  /**
651
665
  * Gets the number of the latest L2 block processed by the block source implementation.
652
666
  * @returns The number of the latest L2 block processed by the block source implementation.
@@ -785,7 +799,7 @@ class ArchiverStoreHelper
785
799
  * Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
786
800
  * @param allLogs - All logs emitted in a bunch of blocks.
787
801
  */
788
- async #updateDeployedContractInstances(allLogs: UnencryptedL2Log[], blockNum: number, operation: Operation) {
802
+ async #updateDeployedContractInstances(allLogs: EncryptedL2Log[], blockNum: number, operation: Operation) {
789
803
  const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs).map(e => e.toContractInstance());
790
804
  if (contractInstances.length > 0) {
791
805
  contractInstances.forEach(c =>
@@ -864,15 +878,18 @@ class ArchiverStoreHelper
864
878
  // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
865
879
  ...(await Promise.all(
866
880
  blocks.map(async block => {
867
- const blockLogs = block.data.body.txEffects
868
- .flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
881
+ const contractClassLogs = block.data.body.txEffects
882
+ .flatMap(txEffect => (txEffect ? [txEffect.contractClassLogs] : []))
883
+ .flatMap(txLog => txLog.unrollLogs());
884
+ // ContractInstanceDeployed event logs are now broadcast in .encryptedLogs
885
+ const allEncryptedLogs = block.data.body.txEffects
886
+ .flatMap(txEffect => (txEffect ? [txEffect.encryptedLogs] : []))
869
887
  .flatMap(txLog => txLog.unrollLogs());
870
-
871
888
  return (
872
889
  await Promise.all([
873
- this.#updateRegisteredContractClasses(blockLogs, block.data.number, Operation.Store),
874
- this.#updateDeployedContractInstances(blockLogs, block.data.number, Operation.Store),
875
- this.#storeBroadcastedIndividualFunctions(blockLogs, block.data.number),
890
+ this.#updateRegisteredContractClasses(contractClassLogs, block.data.number, Operation.Store),
891
+ this.#updateDeployedContractInstances(allEncryptedLogs, block.data.number, Operation.Store),
892
+ this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.data.number),
876
893
  ])
877
894
  ).every(Boolean);
878
895
  }),
@@ -894,11 +911,15 @@ class ArchiverStoreHelper
894
911
  // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
895
912
  ...(await Promise.all(
896
913
  blocks.map(async block => {
897
- const blockLogs = block.data.body.txEffects
898
- .flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
914
+ const contractClassLogs = block.data.body.txEffects
915
+ .flatMap(txEffect => (txEffect ? [txEffect.contractClassLogs] : []))
899
916
  .flatMap(txLog => txLog.unrollLogs());
900
- await this.#updateRegisteredContractClasses(blockLogs, block.data.number, Operation.Delete);
901
- await this.#updateDeployedContractInstances(blockLogs, block.data.number, Operation.Delete);
917
+ // ContractInstanceDeployed event logs are now broadcast in .encryptedLogs
918
+ const allEncryptedLogs = block.data.body.txEffects
919
+ .flatMap(txEffect => (txEffect ? [txEffect.encryptedLogs] : []))
920
+ .flatMap(txLog => txLog.unrollLogs());
921
+ await this.#updateRegisteredContractClasses(contractClassLogs, block.data.number, Operation.Delete);
922
+ await this.#updateDeployedContractInstances(allEncryptedLogs, block.data.number, Operation.Delete);
902
923
  }),
903
924
  )),
904
925
  this.store.deleteLogs(blocks.map(b => b.data)),
@@ -934,12 +955,15 @@ class ArchiverStoreHelper
934
955
  ): Promise<L2BlockL2Logs<FromLogType<TLogType>>[]> {
935
956
  return this.store.getLogs(from, limit, logType);
936
957
  }
937
- getLogsByTags(tags: Fr[]): Promise<EncryptedL2NoteLog[][]> {
958
+ getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
938
959
  return this.store.getLogsByTags(tags);
939
960
  }
940
961
  getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
941
962
  return this.store.getUnencryptedLogs(filter);
942
963
  }
964
+ getContractClassLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
965
+ return this.store.getContractClassLogs(filter);
966
+ }
943
967
  getSynchedL2BlockNumber(): Promise<number> {
944
968
  return this.store.getSynchedL2BlockNumber();
945
969
  }
@@ -987,9 +1011,15 @@ class ArchiverStoreHelper
987
1011
  type L1RollupConstants = {
988
1012
  l1StartBlock: bigint;
989
1013
  l1GenesisTime: bigint;
1014
+ slotDuration: number;
1015
+ epochDuration: number;
1016
+ ethereumSlotDuration: number;
990
1017
  };
991
1018
 
992
1019
  const EmptyL1RollupConstants: L1RollupConstants = {
993
1020
  l1StartBlock: 0n,
994
1021
  l1GenesisTime: 0n,
1022
+ epochDuration: 0,
1023
+ slotDuration: 0,
1024
+ ethereumSlotDuration: 0,
995
1025
  };
@@ -1,5 +1,4 @@
1
1
  import {
2
- type EncryptedL2NoteLog,
3
2
  type FromLogType,
4
3
  type GetUnencryptedLogsResponse,
5
4
  type InboxLeaf,
@@ -10,6 +9,7 @@ import {
10
9
  type TxEffect,
11
10
  type TxHash,
12
11
  type TxReceipt,
12
+ type TxScopedL2Log,
13
13
  } from '@aztec/circuit-types';
14
14
  import {
15
15
  type ContractClassPublic,
@@ -142,7 +142,7 @@ export interface ArchiverDataStore {
142
142
  * @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
143
143
  * that tag.
144
144
  */
145
- getLogsByTags(tags: Fr[]): Promise<EncryptedL2NoteLog[][]>;
145
+ getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]>;
146
146
 
147
147
  /**
148
148
  * Gets unencrypted logs based on the provided filter.
@@ -151,6 +151,13 @@ export interface ArchiverDataStore {
151
151
  */
152
152
  getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse>;
153
153
 
154
+ /**
155
+ * Gets contract class logs based on the provided filter.
156
+ * @param filter - The filter to apply to the logs.
157
+ * @returns The requested logs.
158
+ */
159
+ getContractClassLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse>;
160
+
154
161
  /**
155
162
  * Gets the number of the latest L2 block processed.
156
163
  * @returns The number of the latest L2 block processed.
@@ -344,14 +344,24 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
344
344
  describe('getLogsByTags', () => {
345
345
  const txsPerBlock = 4;
346
346
  const numPrivateFunctionCalls = 3;
347
- const numNoteEncryptedLogs = 2;
347
+ const numPublicFunctionCalls = 1;
348
+ const numEncryptedLogsPerFn = 2;
349
+ const numUnencryptedLogsPerFn = 1;
348
350
  const numBlocks = 10;
349
351
  let blocks: L1Published<L2Block>[];
350
- let tags: { [i: number]: { [j: number]: Buffer[] } } = {};
352
+ let encryptedLogTags: { [i: number]: { [j: number]: Buffer[] } } = {};
353
+ let unencryptedLogTags: { [i: number]: { [j: number]: Buffer[] } } = {};
351
354
 
352
355
  beforeEach(async () => {
353
356
  blocks = times(numBlocks, (index: number) => ({
354
- data: L2Block.random(index + 1, txsPerBlock, numPrivateFunctionCalls, 2, numNoteEncryptedLogs, 2),
357
+ data: L2Block.random(
358
+ index + 1,
359
+ txsPerBlock,
360
+ numPrivateFunctionCalls,
361
+ numPublicFunctionCalls,
362
+ numEncryptedLogsPerFn,
363
+ numUnencryptedLogsPerFn,
364
+ ),
355
365
  l1: { blockNumber: BigInt(index), blockHash: `0x${index}`, timestamp: BigInt(index) },
356
366
  }));
357
367
  // Last block has the note encrypted log tags of the first tx copied from the previous block
@@ -373,46 +383,94 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
373
383
  await store.addBlocks(blocks);
374
384
  await store.addLogs(blocks.map(b => b.data));
375
385
 
376
- tags = {};
386
+ encryptedLogTags = {};
387
+ unencryptedLogTags = {};
377
388
  blocks.forEach((b, blockIndex) => {
378
- if (!tags[blockIndex]) {
379
- tags[blockIndex] = {};
389
+ if (!encryptedLogTags[blockIndex]) {
390
+ encryptedLogTags[blockIndex] = {};
391
+ }
392
+ if (!unencryptedLogTags[blockIndex]) {
393
+ unencryptedLogTags[blockIndex] = {};
380
394
  }
381
395
  b.data.body.noteEncryptedLogs.txLogs.forEach((txLogs, txIndex) => {
382
- if (!tags[blockIndex][txIndex]) {
383
- tags[blockIndex][txIndex] = [];
396
+ if (!encryptedLogTags[blockIndex][txIndex]) {
397
+ encryptedLogTags[blockIndex][txIndex] = [];
398
+ }
399
+ encryptedLogTags[blockIndex][txIndex].push(...txLogs.unrollLogs().map(log => log.data.subarray(0, 32)));
400
+ });
401
+ b.data.body.unencryptedLogs.txLogs.forEach((txLogs, txIndex) => {
402
+ if (!unencryptedLogTags[blockIndex][txIndex]) {
403
+ unencryptedLogTags[blockIndex][txIndex] = [];
384
404
  }
385
- tags[blockIndex][txIndex].push(...txLogs.unrollLogs().map(log => log.data.subarray(0, 32)));
405
+ unencryptedLogTags[blockIndex][txIndex].push(...txLogs.unrollLogs().map(log => log.data.subarray(0, 32)));
386
406
  });
387
407
  });
388
408
  });
389
409
 
390
- it('is possible to batch request all logs of a tx via tags', async () => {
410
+ it('is possible to batch request encrypted logs of a tx via tags', async () => {
391
411
  // get random tx from any block that's not the last one
392
412
  const targetBlockIndex = randomInt(numBlocks - 2);
393
413
  const targetTxIndex = randomInt(txsPerBlock);
394
414
 
395
415
  const logsByTags = await store.getLogsByTags(
396
- tags[targetBlockIndex][targetTxIndex].map(buffer => new Fr(buffer)),
416
+ encryptedLogTags[targetBlockIndex][targetTxIndex].map(buffer => new Fr(buffer)),
397
417
  );
398
418
 
399
- const expectedResponseSize = numPrivateFunctionCalls * numNoteEncryptedLogs;
419
+ const expectedResponseSize = numPrivateFunctionCalls * numEncryptedLogsPerFn;
400
420
  expect(logsByTags.length).toEqual(expectedResponseSize);
401
421
 
402
422
  logsByTags.forEach((logsByTag, logIndex) => {
403
423
  expect(logsByTag).toHaveLength(1);
404
- const [log] = logsByTag;
405
- expect(log).toEqual(
406
- blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex],
424
+ const [scopedLog] = logsByTag;
425
+ expect(scopedLog.txHash).toEqual(blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash);
426
+ expect(scopedLog.logData).toEqual(
427
+ blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex].data,
428
+ );
429
+ });
430
+ });
431
+
432
+ // TODO: Allow this test when #9835 is fixed and tags can be correctly decoded
433
+ it.skip('is possible to batch request all logs (encrypted and unencrypted) of a tx via tags', async () => {
434
+ // get random tx from any block that's not the last one
435
+ const targetBlockIndex = randomInt(numBlocks - 2);
436
+ const targetTxIndex = randomInt(txsPerBlock);
437
+
438
+ const logsByTags = await store.getLogsByTags(
439
+ encryptedLogTags[targetBlockIndex][targetTxIndex]
440
+ .concat(unencryptedLogTags[targetBlockIndex][targetTxIndex])
441
+ .map(buffer => new Fr(buffer)),
442
+ );
443
+
444
+ const expectedResponseSize =
445
+ numPrivateFunctionCalls * numEncryptedLogsPerFn + numPublicFunctionCalls * numUnencryptedLogsPerFn;
446
+ expect(logsByTags.length).toEqual(expectedResponseSize);
447
+
448
+ const encryptedLogsByTags = logsByTags.slice(0, numPrivateFunctionCalls * numEncryptedLogsPerFn);
449
+ const unencryptedLogsByTags = logsByTags.slice(numPrivateFunctionCalls * numEncryptedLogsPerFn);
450
+ encryptedLogsByTags.forEach((logsByTag, logIndex) => {
451
+ expect(logsByTag).toHaveLength(1);
452
+ const [scopedLog] = logsByTag;
453
+ expect(scopedLog.txHash).toEqual(blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash);
454
+ expect(scopedLog.logData).toEqual(
455
+ blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex].data,
456
+ );
457
+ });
458
+ unencryptedLogsByTags.forEach((logsByTag, logIndex) => {
459
+ expect(logsByTag).toHaveLength(1);
460
+ const [scopedLog] = logsByTag;
461
+ expect(scopedLog.logData).toEqual(
462
+ blocks[targetBlockIndex].data.body.unencryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex].data,
407
463
  );
408
464
  });
409
465
  });
410
466
 
411
- it('is possible to batch request all logs of different blocks via tags', async () => {
467
+ it('is possible to batch request logs of different blocks via tags', async () => {
412
468
  // get first tx of first block and second tx of second block
413
- const logsByTags = await store.getLogsByTags([...tags[0][0], ...tags[1][1]].map(buffer => new Fr(buffer)));
469
+ const logsByTags = await store.getLogsByTags(
470
+ [...encryptedLogTags[0][0], ...encryptedLogTags[1][1]].map(buffer => new Fr(buffer)),
471
+ );
414
472
 
415
- const expectedResponseSize = 2 * numPrivateFunctionCalls * numNoteEncryptedLogs;
473
+ const expectedResponseSize = 2 * numPrivateFunctionCalls * numEncryptedLogsPerFn;
416
474
  expect(logsByTags.length).toEqual(expectedResponseSize);
417
475
 
418
476
  logsByTags.forEach(logsByTag => expect(logsByTag).toHaveLength(1));
@@ -420,14 +478,14 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
420
478
 
421
479
  it('is possible to batch request logs that have the same tag but different content', async () => {
422
480
  // get first tx of last block
423
- const logsByTags = await store.getLogsByTags(tags[numBlocks - 1][0].map(buffer => new Fr(buffer)));
481
+ const logsByTags = await store.getLogsByTags(encryptedLogTags[numBlocks - 1][0].map(buffer => new Fr(buffer)));
424
482
 
425
- const expectedResponseSize = numPrivateFunctionCalls * numNoteEncryptedLogs;
483
+ const expectedResponseSize = numPrivateFunctionCalls * numEncryptedLogsPerFn;
426
484
  expect(logsByTags.length).toEqual(expectedResponseSize);
427
485
 
428
486
  logsByTags.forEach(logsByTag => {
429
487
  expect(logsByTag).toHaveLength(2);
430
- const [tag0, tag1] = logsByTag.map(log => new Fr(log.data.subarray(0, 32)));
488
+ const [tag0, tag1] = logsByTag.map(scopedLog => new Fr(scopedLog.logData.subarray(0, 32)));
431
489
  expect(tag0).toEqual(tag1);
432
490
  });
433
491
  });
@@ -439,10 +497,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
439
497
 
440
498
  const logsByTags = await store.getLogsByTags([
441
499
  Fr.random(),
442
- ...tags[targetBlockIndex][targetTxIndex].slice(1).map(buffer => new Fr(buffer)),
500
+ ...encryptedLogTags[targetBlockIndex][targetTxIndex].slice(1).map(buffer => new Fr(buffer)),
443
501
  ]);
444
502
 
445
- const expectedResponseSize = numPrivateFunctionCalls * numNoteEncryptedLogs;
503
+ const expectedResponseSize = numPrivateFunctionCalls * numEncryptedLogsPerFn;
446
504
  expect(logsByTags.length).toEqual(expectedResponseSize);
447
505
 
448
506
  const [emptyLogsByTag, ...populatedLogsByTags] = logsByTags;
@@ -450,9 +508,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
450
508
 
451
509
  populatedLogsByTags.forEach((logsByTag, logIndex) => {
452
510
  expect(logsByTag).toHaveLength(1);
453
- const [log] = logsByTag;
454
- expect(log).toEqual(
455
- blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex + 1],
511
+ const [scopedLog] = logsByTag;
512
+ expect(scopedLog.txHash).toEqual(blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash);
513
+ expect(scopedLog.logData).toEqual(
514
+ blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex + 1].data,
456
515
  );
457
516
  });
458
517
  });
@@ -1,4 +1,10 @@
1
- import { type L1ContractAddresses, type L1ReaderConfig, l1ReaderConfigMappings } from '@aztec/ethereum';
1
+ import {
2
+ type L1ContractAddresses,
3
+ type L1ContractsConfig,
4
+ type L1ReaderConfig,
5
+ l1ContractsConfigMappings,
6
+ l1ReaderConfigMappings,
7
+ } from '@aztec/ethereum';
2
8
  import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } from '@aztec/foundation/config';
3
9
 
4
10
  /**
@@ -32,14 +38,10 @@ export type ArchiverConfig = {
32
38
  */
33
39
  l1Contracts: L1ContractAddresses;
34
40
 
35
- /**
36
- * Optional dir to store data. If omitted will store in memory.
37
- */
38
- dataDirectory: string | undefined;
39
-
40
41
  /** The max number of logs that can be obtained in 1 "getUnencryptedLogs" call. */
41
42
  maxLogs?: number;
42
- } & L1ReaderConfig;
43
+ } & L1ReaderConfig &
44
+ L1ContractsConfig;
43
45
 
44
46
  export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
45
47
  archiverUrl: {
@@ -50,11 +52,7 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
50
52
  archiverPollingIntervalMS: {
51
53
  env: 'ARCHIVER_POLLING_INTERVAL_MS',
52
54
  description: 'The polling interval in ms for retrieving new L2 blocks and encrypted logs.',
53
- ...numberConfigHelper(1000),
54
- },
55
- dataDirectory: {
56
- env: 'DATA_DIRECTORY',
57
- description: 'Optional dir to store data. If omitted will store in memory.',
55
+ ...numberConfigHelper(1_000),
58
56
  },
59
57
  maxLogs: {
60
58
  env: 'ARCHIVER_MAX_LOGS',
@@ -67,6 +65,7 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
67
65
  description: 'The polling interval viem uses in ms',
68
66
  ...numberConfigHelper(1000),
69
67
  },
68
+ ...l1ContractsConfigMappings,
70
69
  };
71
70
 
72
71
  /**
@@ -1,26 +1,30 @@
1
- import { AZTEC_EPOCH_DURATION, AZTEC_SLOT_DURATION } from '@aztec/circuits.js';
1
+ type TimeConstants = {
2
+ l1GenesisTime: bigint;
3
+ epochDuration: number;
4
+ slotDuration: number;
5
+ };
2
6
 
3
7
  /** Returns the slot number for a given timestamp. */
4
- export function getSlotAtTimestamp(ts: bigint, constants: { l1GenesisTime: bigint }) {
5
- return ts < constants.l1GenesisTime ? 0n : (ts - constants.l1GenesisTime) / BigInt(AZTEC_SLOT_DURATION);
8
+ export function getSlotAtTimestamp(ts: bigint, constants: Pick<TimeConstants, 'l1GenesisTime' | 'slotDuration'>) {
9
+ return ts < constants.l1GenesisTime ? 0n : (ts - constants.l1GenesisTime) / BigInt(constants.slotDuration);
6
10
  }
7
11
 
8
12
  /** Returns the epoch number for a given timestamp. */
9
- export function getEpochNumberAtTimestamp(ts: bigint, constants: { l1GenesisTime: bigint }) {
10
- return getSlotAtTimestamp(ts, constants) / BigInt(AZTEC_EPOCH_DURATION);
13
+ export function getEpochNumberAtTimestamp(ts: bigint, constants: TimeConstants) {
14
+ return getSlotAtTimestamp(ts, constants) / BigInt(constants.epochDuration);
11
15
  }
12
16
 
13
17
  /** Returns the range of slots (inclusive) for a given epoch number. */
14
- export function getSlotRangeForEpoch(epochNumber: bigint) {
15
- const startSlot = epochNumber * BigInt(AZTEC_EPOCH_DURATION);
16
- return [startSlot, startSlot + BigInt(AZTEC_EPOCH_DURATION) - 1n];
18
+ export function getSlotRangeForEpoch(epochNumber: bigint, constants: Pick<TimeConstants, 'epochDuration'>) {
19
+ const startSlot = epochNumber * BigInt(constants.epochDuration);
20
+ return [startSlot, startSlot + BigInt(constants.epochDuration) - 1n];
17
21
  }
18
22
 
19
23
  /** Returns the range of L1 timestamps (inclusive) for a given epoch number. */
20
- export function getTimestampRangeForEpoch(epochNumber: bigint, constants: { l1GenesisTime: bigint }) {
21
- const [startSlot, endSlot] = getSlotRangeForEpoch(epochNumber);
24
+ export function getTimestampRangeForEpoch(epochNumber: bigint, constants: TimeConstants) {
25
+ const [startSlot, endSlot] = getSlotRangeForEpoch(epochNumber, constants);
22
26
  return [
23
- constants.l1GenesisTime + startSlot * BigInt(AZTEC_SLOT_DURATION),
24
- constants.l1GenesisTime + endSlot * BigInt(AZTEC_SLOT_DURATION),
27
+ constants.l1GenesisTime + startSlot * BigInt(constants.slotDuration),
28
+ constants.l1GenesisTime + endSlot * BigInt(constants.slotDuration),
25
29
  ];
26
30
  }
@@ -1,5 +1,4 @@
1
1
  import {
2
- type EncryptedL2NoteLog,
3
2
  type FromLogType,
4
3
  type GetUnencryptedLogsResponse,
5
4
  type InboxLeaf,
@@ -10,6 +9,7 @@ import {
10
9
  type TxEffect,
11
10
  type TxHash,
12
11
  type TxReceipt,
12
+ type TxScopedL2Log,
13
13
  } from '@aztec/circuit-types';
14
14
  import {
15
15
  type ContractClassPublic,
@@ -245,7 +245,7 @@ export class KVArchiverDataStore implements ArchiverDataStore {
245
245
  * @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
246
246
  * that tag.
247
247
  */
248
- getLogsByTags(tags: Fr[]): Promise<EncryptedL2NoteLog[][]> {
248
+ getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
249
249
  try {
250
250
  return this.#logStore.getLogsByTags(tags);
251
251
  } catch (err) {
@@ -266,6 +266,19 @@ export class KVArchiverDataStore implements ArchiverDataStore {
266
266
  }
267
267
  }
268
268
 
269
+ /**
270
+ * Gets contract class logs based on the provided filter.
271
+ * @param filter - The filter to apply to the logs.
272
+ * @returns The requested logs.
273
+ */
274
+ getContractClassLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
275
+ try {
276
+ return Promise.resolve(this.#logStore.getContractClassLogs(filter));
277
+ } catch (err) {
278
+ return Promise.reject(err);
279
+ }
280
+ }
281
+
269
282
  /**
270
283
  * Gets the number of the latest L2 block processed.
271
284
  * @returns The number of the latest L2 block processed.