@aztec/archiver 0.57.0 → 0.59.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 (56) hide show
  1. package/dest/archiver/archiver.d.ts +5 -3
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +77 -21
  4. package/dest/archiver/archiver_store.d.ts +1 -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 +10 -14
  8. package/dest/archiver/data_retrieval.js +3 -3
  9. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +1 -2
  10. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  11. package/dest/archiver/kv_archiver_store/contract_class_store.js +2 -2
  12. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +1 -2
  13. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
  14. package/dest/archiver/kv_archiver_store/contract_instance_store.js +2 -2
  15. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +1 -2
  16. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  17. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +1 -1
  18. package/dest/archiver/kv_archiver_store/message_store.d.ts +1 -1
  19. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  20. package/dest/archiver/kv_archiver_store/message_store.js +8 -11
  21. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +2 -2
  22. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +1 -1
  23. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +9 -14
  24. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +1 -2
  25. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
  26. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +2 -2
  27. package/dest/factory.d.ts.map +1 -1
  28. package/dest/factory.js +17 -1
  29. package/dest/test/index.d.ts +2 -0
  30. package/dest/test/index.d.ts.map +1 -1
  31. package/dest/test/index.js +3 -1
  32. package/dest/test/mock_archiver.d.ts +22 -0
  33. package/dest/test/mock_archiver.d.ts.map +1 -0
  34. package/dest/test/mock_archiver.js +44 -0
  35. package/dest/test/mock_l1_to_l2_message_source.d.ts +16 -0
  36. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -0
  37. package/dest/test/mock_l1_to_l2_message_source.js +25 -0
  38. package/dest/test/mock_l2_block_source.d.ts +11 -9
  39. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  40. package/dest/test/mock_l2_block_source.js +36 -16
  41. package/package.json +10 -10
  42. package/src/archiver/archiver.ts +109 -34
  43. package/src/archiver/archiver_store.ts +5 -4
  44. package/src/archiver/archiver_store_test_suite.ts +20 -21
  45. package/src/archiver/data_retrieval.ts +2 -2
  46. package/src/archiver/kv_archiver_store/contract_class_store.ts +6 -4
  47. package/src/archiver/kv_archiver_store/contract_instance_store.ts +1 -2
  48. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +7 -6
  49. package/src/archiver/kv_archiver_store/message_store.ts +7 -18
  50. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +9 -19
  51. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +6 -4
  52. package/src/factory.ts +18 -0
  53. package/src/test/index.ts +2 -0
  54. package/src/test/mock_archiver.ts +55 -0
  55. package/src/test/mock_l1_to_l2_message_source.ts +31 -0
  56. package/src/test/mock_l2_block_source.ts +42 -17
@@ -4,9 +4,11 @@ import {
4
4
  type InboxLeaf,
5
5
  type L1ToL2MessageSource,
6
6
  type L2Block,
7
+ type L2BlockId,
7
8
  type L2BlockL2Logs,
8
9
  type L2BlockSource,
9
10
  type L2LogsSource,
11
+ type L2Tips,
10
12
  type LogFilter,
11
13
  type LogType,
12
14
  type TxEffect,
@@ -15,12 +17,18 @@ import {
15
17
  type UnencryptedL2Log,
16
18
  } from '@aztec/circuit-types';
17
19
  import {
20
+ type ContractClassPublic,
18
21
  ContractClassRegisteredEvent,
22
+ type ContractDataSource,
19
23
  ContractInstanceDeployedEvent,
24
+ type ContractInstanceWithAddress,
25
+ type ExecutablePrivateFunctionWithMembershipProof,
20
26
  type FunctionSelector,
21
27
  type Header,
22
28
  PrivateFunctionBroadcastedEvent,
29
+ type PublicFunction,
23
30
  UnconstrainedFunctionBroadcastedEvent,
31
+ type UnconstrainedFunctionWithMembershipProof,
24
32
  isValidPrivateFunctionMembershipProof,
25
33
  isValidUnconstrainedFunctionMembershipProof,
26
34
  } from '@aztec/circuits.js';
@@ -33,16 +41,8 @@ import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
33
41
  import { RunningPromise } from '@aztec/foundation/running-promise';
34
42
  import { Timer } from '@aztec/foundation/timer';
35
43
  import { InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
36
- import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer';
44
+ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
37
45
  import { type TelemetryClient } from '@aztec/telemetry-client';
38
- import {
39
- type ContractClassPublic,
40
- type ContractDataSource,
41
- type ContractInstanceWithAddress,
42
- type ExecutablePrivateFunctionWithMembershipProof,
43
- type PublicFunction,
44
- type UnconstrainedFunctionWithMembershipProof,
45
- } from '@aztec/types/contracts';
46
46
 
47
47
  import groupBy from 'lodash.groupby';
48
48
  import {
@@ -246,7 +246,16 @@ export class Archiver implements ArchiveSource {
246
246
  await this.handleL1ToL2Messages(blockUntilSynced, messagesSynchedTo, currentL1BlockNumber);
247
247
 
248
248
  // ********** Events that are processed per L2 block **********
249
- await this.handleL2blocks(blockUntilSynced, blocksSynchedTo, currentL1BlockNumber);
249
+ if (currentL1BlockNumber > blocksSynchedTo) {
250
+ // First we retrieve new L2 blocks
251
+ const { provenBlockNumber } = await this.handleL2blocks(blockUntilSynced, blocksSynchedTo, currentL1BlockNumber);
252
+ // And then we prune the current epoch if it'd reorg on next submission.
253
+ // Note that we don't do this before retrieving L2 blocks because we may need to retrieve
254
+ // blocks from more than 2 epochs ago, so we want to make sure we have the latest view of
255
+ // the chain locally before we start unwinding stuff. This can be optimized by figuring out
256
+ // up to which point we're pruning, and then requesting L2 blocks up to that point only.
257
+ await this.handleEpochPrune(provenBlockNumber, currentL1BlockNumber);
258
+ }
250
259
 
251
260
  // Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
252
261
  if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
@@ -255,6 +264,27 @@ export class Archiver implements ArchiveSource {
255
264
  }
256
265
  }
257
266
 
267
+ /** Checks if there'd be a reorg for the next block submission and start pruning now. */
268
+ private async handleEpochPrune(provenBlockNumber: bigint, currentL1BlockNumber: bigint) {
269
+ const localPendingBlockNumber = BigInt(await this.getBlockNumber());
270
+
271
+ const canPrune =
272
+ localPendingBlockNumber > provenBlockNumber &&
273
+ (await this.rollup.read.canPrune({ blockNumber: currentL1BlockNumber }));
274
+
275
+ if (canPrune) {
276
+ this.log.verbose(`L2 prune will occur on next submission. Rolling back to last proven block.`);
277
+ const blocksToUnwind = localPendingBlockNumber - provenBlockNumber;
278
+ this.log.verbose(
279
+ `Unwinding ${blocksToUnwind} block${blocksToUnwind > 1n ? 's' : ''} from block ${localPendingBlockNumber}`,
280
+ );
281
+ await this.store.unwindBlocks(Number(localPendingBlockNumber), Number(blocksToUnwind));
282
+ // TODO(palla/reorg): Do we need to set the block synched L1 block number here?
283
+ // Seems like the next iteration should handle this.
284
+ // await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
285
+ }
286
+ }
287
+
258
288
  private async handleL1ToL2Messages(
259
289
  blockUntilSynced: boolean,
260
290
  messagesSynchedTo: bigint,
@@ -291,11 +321,11 @@ export class Archiver implements ArchiveSource {
291
321
  );
292
322
  }
293
323
 
294
- private async handleL2blocks(blockUntilSynced: boolean, blocksSynchedTo: bigint, currentL1BlockNumber: bigint) {
295
- if (currentL1BlockNumber <= blocksSynchedTo) {
296
- return;
297
- }
298
-
324
+ private async handleL2blocks(
325
+ blockUntilSynced: boolean,
326
+ blocksSynchedTo: bigint,
327
+ currentL1BlockNumber: bigint,
328
+ ): Promise<{ provenBlockNumber: bigint }> {
299
329
  const localPendingBlockNumber = BigInt(await this.getBlockNumber());
300
330
  const [
301
331
  provenBlockNumber,
@@ -304,7 +334,7 @@ export class Archiver implements ArchiveSource {
304
334
  pendingArchive,
305
335
  archiveForLocalPendingBlockNumber,
306
336
  provenEpochNumber,
307
- ] = await this.rollup.read.status([localPendingBlockNumber]);
337
+ ] = await this.rollup.read.status([localPendingBlockNumber], { blockNumber: currentL1BlockNumber });
308
338
 
309
339
  const updateProvenBlock = async () => {
310
340
  const localBlockForDestinationProvenBlockNumber = await this.getBlock(Number(provenBlockNumber));
@@ -317,6 +347,7 @@ export class Archiver implements ArchiveSource {
317
347
  // if we are here then we must have a valid proven epoch number
318
348
  await this.store.setProvenL2EpochNumber(Number(provenEpochNumber));
319
349
  }
350
+ this.instrumentation.updateLastProvenBlock(Number(provenBlockNumber));
320
351
  };
321
352
 
322
353
  // This is an edge case that we only hit if there are no proposed blocks.
@@ -325,7 +356,7 @@ export class Archiver implements ArchiveSource {
325
356
  if (noBlocks) {
326
357
  await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
327
358
  this.log.verbose(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
328
- return;
359
+ return { provenBlockNumber };
329
360
  }
330
361
 
331
362
  await updateProvenBlock();
@@ -342,7 +373,7 @@ export class Archiver implements ArchiveSource {
342
373
  if (noBlockSinceLast) {
343
374
  await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
344
375
  this.log.verbose(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
345
- return;
376
+ return { provenBlockNumber };
346
377
  }
347
378
 
348
379
  const localPendingBlockInChain = archiveForLocalPendingBlockNumber === localPendingBlock.archive.root.toString();
@@ -382,7 +413,7 @@ export class Archiver implements ArchiveSource {
382
413
  this.rollup,
383
414
  this.publicClient,
384
415
  blockUntilSynced,
385
- blocksSynchedTo + 1n,
416
+ blocksSynchedTo + 1n, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
386
417
  currentL1BlockNumber,
387
418
  this.log,
388
419
  );
@@ -390,8 +421,8 @@ export class Archiver implements ArchiveSource {
390
421
  if (retrievedBlocks.length === 0) {
391
422
  // We are not calling `setBlockSynchedL1BlockNumber` because it may cause sync issues if based off infura.
392
423
  // See further details in earlier comments.
393
- this.log.verbose(`Retrieved no new blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
394
- return;
424
+ this.log.verbose(`Retrieved no new L2 blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
425
+ return { provenBlockNumber };
395
426
  }
396
427
 
397
428
  this.log.debug(
@@ -402,14 +433,14 @@ export class Archiver implements ArchiveSource {
402
433
 
403
434
  const lastProcessedL1BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].l1.blockNumber;
404
435
 
405
- this.log.debug(
406
- `Processing retrieved blocks ${retrievedBlocks
407
- .map(b => b.data.number)
408
- .join(',')} with last processed L1 block ${lastProcessedL1BlockNumber}`,
409
- );
436
+ this.log.debug(`last processed L1 block: [${lastProcessedL1BlockNumber}]`);
437
+ for (const block of retrievedBlocks) {
438
+ this.log.debug(`ingesting new L2 block`, block.data.header.globalVariables.toFriendlyJSON());
439
+ }
410
440
 
411
441
  const timer = new Timer();
412
442
  await this.store.addBlocks(retrievedBlocks);
443
+
413
444
  // Important that we update AFTER inserting the blocks.
414
445
  await updateProvenBlock();
415
446
  this.instrumentation.processNewBlocks(
@@ -418,6 +449,8 @@ export class Archiver implements ArchiveSource {
418
449
  );
419
450
  const lastL2BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].data.number;
420
451
  this.log.verbose(`Processed ${retrievedBlocks.length} new L2 blocks up to ${lastL2BlockNumber}`);
452
+
453
+ return { provenBlockNumber };
421
454
  }
422
455
 
423
456
  /**
@@ -497,7 +530,10 @@ export class Archiver implements ArchiveSource {
497
530
  const [_startTimestamp, endTimestamp] = getTimestampRangeForEpoch(epochNumber, this.l1constants);
498
531
 
499
532
  // For this computation, we throw in a few extra seconds just for good measure,
500
- // since we know the next L1 block won't be mined within this range
533
+ // since we know the next L1 block won't be mined within this range. Remember that
534
+ // l1timestamp is the timestamp of the last l1 block we've seen, so this 3s rely on
535
+ // the fact that L1 won't mine two blocks within 3s of each other.
536
+ // TODO(palla/reorg): Is the above a safe assumption?
501
537
  const leeway = 3n;
502
538
  return l1Timestamp + leeway >= endTimestamp;
503
539
  }
@@ -537,8 +573,14 @@ export class Archiver implements ArchiveSource {
537
573
  if (number === 'latest') {
538
574
  number = await this.store.getSynchedL2BlockNumber();
539
575
  }
540
- const headers = await this.store.getBlockHeaders(number, 1);
541
- return headers.length === 0 ? undefined : headers[0];
576
+ try {
577
+ const headers = await this.store.getBlockHeaders(number, 1);
578
+ return headers.length === 0 ? undefined : headers[0];
579
+ } catch (e) {
580
+ // If the latest is 0, then getBlockHeaders will throw an error
581
+ this.log.error(`getBlockHeader: error fetching block number: ${number}`);
582
+ return undefined;
583
+ }
542
584
  }
543
585
 
544
586
  public getTxEffect(txHash: TxHash): Promise<TxEffect | undefined> {
@@ -653,6 +695,32 @@ export class Archiver implements ArchiveSource {
653
695
  getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
654
696
  return this.store.getContractArtifact(address);
655
697
  }
698
+
699
+ async getL2Tips(): Promise<L2Tips> {
700
+ const [latestBlockNumber, provenBlockNumber] = await Promise.all([
701
+ this.getBlockNumber(),
702
+ this.getProvenBlockNumber(),
703
+ ] as const);
704
+
705
+ const [latestBlockHeader, provenBlockHeader] = await Promise.all([
706
+ latestBlockNumber > 0 ? this.getBlockHeader(latestBlockNumber) : undefined,
707
+ provenBlockNumber > 0 ? this.getBlockHeader(provenBlockNumber) : undefined,
708
+ ] as const);
709
+
710
+ if (latestBlockNumber > 0 && !latestBlockHeader) {
711
+ throw new Error('Failed to retrieve latest block header');
712
+ }
713
+
714
+ if (provenBlockNumber > 0 && !provenBlockHeader) {
715
+ throw new Error('Failed to retrieve proven block header');
716
+ }
717
+
718
+ return {
719
+ latest: { number: latestBlockNumber, hash: latestBlockHeader?.hash().toString() } as L2BlockId,
720
+ proven: { number: provenBlockNumber, hash: provenBlockHeader?.hash().toString() } as L2BlockId,
721
+ finalized: { number: provenBlockNumber, hash: provenBlockHeader?.hash().toString() } as L2BlockId,
722
+ };
723
+ }
656
724
  }
657
725
 
658
726
  enum Operation {
@@ -688,9 +756,10 @@ class ArchiverStoreHelper
688
756
  * @param allLogs - All logs emitted in a bunch of blocks.
689
757
  */
690
758
  async #updateRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number, operation: Operation) {
691
- const contractClasses = ContractClassRegisteredEvent.fromLogs(allLogs, ClassRegistererAddress).map(e =>
692
- e.toContractClassPublic(),
693
- );
759
+ const contractClasses = ContractClassRegisteredEvent.fromLogs(
760
+ allLogs,
761
+ ProtocolContractAddress.ContractClassRegisterer,
762
+ ).map(e => e.toContractClassPublic());
694
763
  if (contractClasses.length > 0) {
695
764
  contractClasses.forEach(c => this.#log.verbose(`Registering contract class ${c.id.toString()}`));
696
765
  if (operation == Operation.Store) {
@@ -733,8 +802,14 @@ class ArchiverStoreHelper
733
802
  */
734
803
  async #storeBroadcastedIndividualFunctions(allLogs: UnencryptedL2Log[], _blockNum: number) {
735
804
  // Filter out private and unconstrained function broadcast events
736
- const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
737
- const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
805
+ const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(
806
+ allLogs,
807
+ ProtocolContractAddress.ContractClassRegisterer,
808
+ );
809
+ const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent.fromLogs(
810
+ allLogs,
811
+ ProtocolContractAddress.ContractClassRegisterer,
812
+ );
738
813
 
739
814
  // Group all events by contract class id
740
815
  for (const [classIdString, classEvents] of Object.entries(
@@ -10,15 +10,16 @@ import {
10
10
  type TxHash,
11
11
  type TxReceipt,
12
12
  } from '@aztec/circuit-types';
13
- import { type Fr, type Header } from '@aztec/circuits.js';
14
- import { type ContractArtifact } from '@aztec/foundation/abi';
15
- import { type AztecAddress } from '@aztec/foundation/aztec-address';
16
13
  import {
17
14
  type ContractClassPublic,
18
15
  type ContractInstanceWithAddress,
19
16
  type ExecutablePrivateFunctionWithMembershipProof,
17
+ type Fr,
18
+ type Header,
20
19
  type UnconstrainedFunctionWithMembershipProof,
21
- } from '@aztec/types/contracts';
20
+ } from '@aztec/circuits.js';
21
+ import { type ContractArtifact } from '@aztec/foundation/abi';
22
+ import { type AztecAddress } from '@aztec/foundation/aztec-address';
22
23
 
23
24
  import { type DataRetrieval } from './structs/data_retrieval.js';
24
25
  import { type L1Published } from './structs/published.js';
@@ -1,6 +1,14 @@
1
1
  import { InboxLeaf, L2Block, LogId, LogType, TxHash } from '@aztec/circuit-types';
2
2
  import '@aztec/circuit-types/jest';
3
- import { AztecAddress, Fr, INITIAL_L2_BLOCK_NUM, L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js';
3
+ import {
4
+ AztecAddress,
5
+ type ContractClassPublic,
6
+ type ContractInstanceWithAddress,
7
+ Fr,
8
+ INITIAL_L2_BLOCK_NUM,
9
+ L1_TO_L2_MSG_SUBTREE_HEIGHT,
10
+ SerializableContractInstance,
11
+ } from '@aztec/circuits.js';
4
12
  import {
5
13
  makeContractClassPublic,
6
14
  makeExecutablePrivateFunctionWithMembershipProof,
@@ -8,11 +16,6 @@ import {
8
16
  } from '@aztec/circuits.js/testing';
9
17
  import { times } from '@aztec/foundation/collection';
10
18
  import { randomBytes, randomInt } from '@aztec/foundation/crypto';
11
- import {
12
- type ContractClassPublic,
13
- type ContractInstanceWithAddress,
14
- SerializableContractInstance,
15
- } from '@aztec/types/contracts';
16
19
 
17
20
  import { type ArchiverDataStore, type ArchiverL1SynchPoint } from './archiver_store.js';
18
21
  import { type L1Published } from './structs/published.js';
@@ -118,7 +121,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
118
121
  it('returns the L1 block number that most recently added messages from inbox', async () => {
119
122
  await store.addL1ToL2Messages({
120
123
  lastProcessedL1BlockNumber: 1n,
121
- retrievedData: [new InboxLeaf(0n, 0n, Fr.ZERO)],
124
+ retrievedData: [new InboxLeaf(1n, Fr.ZERO)],
122
125
  });
123
126
  await expect(store.getSynchPoint()).resolves.toEqual({
124
127
  blocksSynchedTo: undefined,
@@ -226,7 +229,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
226
229
  const l1ToL2MessageSubtreeSize = 2 ** L1_TO_L2_MSG_SUBTREE_HEIGHT;
227
230
 
228
231
  const generateBlockMessages = (blockNumber: bigint, numMessages: number) =>
229
- Array.from({ length: numMessages }, (_, i) => new InboxLeaf(blockNumber, BigInt(i), Fr.random()));
232
+ Array.from(
233
+ { length: numMessages },
234
+ (_, i) => new InboxLeaf(InboxLeaf.smallestIndexFromL2Block(blockNumber) + BigInt(i), Fr.random()),
235
+ );
230
236
 
231
237
  it('returns messages in correct order', async () => {
232
238
  const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize);
@@ -241,8 +247,9 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
241
247
  it('throws if it is impossible to sequence messages correctly', async () => {
242
248
  const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize - 1);
243
249
  // We replace a message with index 4 with a message with index at the end of the tree
244
- // --> with that there will be a gap and it will be impossible to sequence the messages
245
- msgs[4] = new InboxLeaf(l2BlockNumber, BigInt(l1ToL2MessageSubtreeSize - 1), Fr.random());
250
+ // --> with that there will be a gap and it will be impossible to sequence the
251
+ // end of tree = start of next tree/block - 1
252
+ msgs[4] = new InboxLeaf(InboxLeaf.smallestIndexFromL2Block(l2BlockNumber + 1n) - 1n, Fr.random());
246
253
 
247
254
  await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
248
255
  await expect(async () => {
@@ -250,26 +257,18 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
250
257
  }).rejects.toThrow(`L1 to L2 message gap found in block ${l2BlockNumber}`);
251
258
  });
252
259
 
253
- it('throws if adding more messages than fits into a block', async () => {
254
- const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize + 1);
255
-
256
- await expect(async () => {
257
- await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
258
- }).rejects.toThrow(`Message index ${l1ToL2MessageSubtreeSize} out of subtree range`);
259
- });
260
-
261
260
  it('correctly handles duplicate messages', async () => {
262
261
  const messageHash = Fr.random();
263
-
264
- const msgs = [new InboxLeaf(1n, 0n, messageHash), new InboxLeaf(2n, 0n, messageHash)];
262
+ const msgs = [new InboxLeaf(0n, messageHash), new InboxLeaf(16n, messageHash)];
265
263
 
266
264
  await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
267
265
 
268
266
  const index1 = (await store.getL1ToL2MessageIndex(messageHash, 0n))!;
267
+ expect(index1).toBe(0n);
269
268
  const index2 = await store.getL1ToL2MessageIndex(messageHash, index1 + 1n);
270
-
271
269
  expect(index2).toBeDefined();
272
270
  expect(index2).toBeGreaterThan(index1);
271
+ expect(index2).toBe(16n);
273
272
  });
274
273
  });
275
274
 
@@ -194,8 +194,8 @@ export async function retrieveL1ToL2Messages(
194
194
  }
195
195
 
196
196
  for (const log of messageSentLogs) {
197
- const { l2BlockNumber, index, hash } = log.args;
198
- retrievedL1ToL2Messages.push(new InboxLeaf(l2BlockNumber!, index!, Fr.fromString(hash!)));
197
+ const { index, hash } = log.args;
198
+ retrievedL1ToL2Messages.push(new InboxLeaf(index!, Fr.fromString(hash!)));
199
199
  }
200
200
 
201
201
  // handles the case when there are no new messages:
@@ -1,12 +1,14 @@
1
- import { Fr, FunctionSelector, Vector } from '@aztec/circuits.js';
2
- import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
3
- import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
4
1
  import {
5
2
  type ContractClassPublic,
6
3
  type ContractClassPublicWithBlockNumber,
7
4
  type ExecutablePrivateFunctionWithMembershipProof,
5
+ Fr,
6
+ FunctionSelector,
8
7
  type UnconstrainedFunctionWithMembershipProof,
9
- } from '@aztec/types/contracts';
8
+ Vector,
9
+ } from '@aztec/circuits.js';
10
+ import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
11
+ import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
10
12
 
11
13
  /**
12
14
  * LMDB implementation of the ArchiverDataStore interface.
@@ -1,6 +1,5 @@
1
- import { type AztecAddress } from '@aztec/circuits.js';
1
+ import { type AztecAddress, type ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/circuits.js';
2
2
  import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
3
- import { type ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts';
4
3
 
5
4
  /**
6
5
  * LMDB implementation of the ArchiverDataStore interface.
@@ -10,17 +10,18 @@ import {
10
10
  type TxHash,
11
11
  type TxReceipt,
12
12
  } from '@aztec/circuit-types';
13
- import { type Fr, type Header } from '@aztec/circuits.js';
14
- import { type ContractArtifact } from '@aztec/foundation/abi';
15
- import { type AztecAddress } from '@aztec/foundation/aztec-address';
16
- import { createDebugLogger } from '@aztec/foundation/log';
17
- import { type AztecKVStore } from '@aztec/kv-store';
18
13
  import {
19
14
  type ContractClassPublic,
20
15
  type ContractInstanceWithAddress,
21
16
  type ExecutablePrivateFunctionWithMembershipProof,
17
+ type Fr,
18
+ type Header,
22
19
  type UnconstrainedFunctionWithMembershipProof,
23
- } from '@aztec/types/contracts';
20
+ } from '@aztec/circuits.js';
21
+ import { type ContractArtifact } from '@aztec/foundation/abi';
22
+ import { type AztecAddress } from '@aztec/foundation/aztec-address';
23
+ import { createDebugLogger } from '@aztec/foundation/log';
24
+ import { type AztecKVStore } from '@aztec/kv-store';
24
25
 
25
26
  import { type ArchiverDataStore, type ArchiverL1SynchPoint } from '../archiver_store.js';
26
27
  import { type DataRetrieval } from '../structs/data_retrieval.js';
@@ -1,10 +1,5 @@
1
- import { type InboxLeaf } from '@aztec/circuit-types';
2
- import {
3
- Fr,
4
- INITIAL_L2_BLOCK_NUM,
5
- L1_TO_L2_MSG_SUBTREE_HEIGHT,
6
- NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
7
- } from '@aztec/circuits.js';
1
+ import { InboxLeaf } from '@aztec/circuit-types';
2
+ import { Fr, L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js';
8
3
  import { createDebugLogger } from '@aztec/foundation/log';
9
4
  import { type AztecKVStore, type AztecMap, type AztecSingleton } from '@aztec/kv-store';
10
5
 
@@ -61,18 +56,11 @@ export class MessageStore {
61
56
  void this.#lastSynchedL1Block.set(messages.lastProcessedL1BlockNumber);
62
57
 
63
58
  for (const message of messages.retrievedData) {
64
- if (message.index >= this.#l1ToL2MessagesSubtreeSize) {
65
- throw new Error(`Message index ${message.index} out of subtree range`);
66
- }
67
- const key = `${message.blockNumber}-${message.index}`;
59
+ const key = `${message.index}`;
68
60
  void this.#l1ToL2Messages.setIfNotExists(key, message.leaf.toBuffer());
69
61
 
70
- const indexInTheWholeTree =
71
- (message.blockNumber - BigInt(INITIAL_L2_BLOCK_NUM)) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) +
72
- message.index;
73
-
74
62
  const indices = this.#l1ToL2MessageIndices.get(message.leaf.toString()) ?? [];
75
- indices.push(indexInTheWholeTree);
63
+ indices.push(message.index);
76
64
  void this.#l1ToL2MessageIndices.set(message.leaf.toString(), indices);
77
65
  }
78
66
 
@@ -98,9 +86,10 @@ export class MessageStore {
98
86
  getL1ToL2Messages(blockNumber: bigint): Fr[] {
99
87
  const messages: Fr[] = [];
100
88
  let undefinedMessageFound = false;
101
- for (let messageIndex = 0; messageIndex < this.#l1ToL2MessagesSubtreeSize; messageIndex++) {
89
+ const startIndex = Number(InboxLeaf.smallestIndexFromL2Block(blockNumber));
90
+ for (let i = startIndex; i < startIndex + this.#l1ToL2MessagesSubtreeSize; i++) {
102
91
  // This is inefficient but probably fine for now.
103
- const key = `${blockNumber}-${messageIndex}`;
92
+ const key = `${i}`;
104
93
  const message = this.#l1ToL2Messages.get(key);
105
94
  if (message) {
106
95
  if (undefinedMessageFound) {
@@ -1,9 +1,5 @@
1
- import { type InboxLeaf } from '@aztec/circuit-types';
2
- import {
3
- INITIAL_L2_BLOCK_NUM,
4
- L1_TO_L2_MSG_SUBTREE_HEIGHT,
5
- NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
6
- } from '@aztec/circuits.js/constants';
1
+ import { InboxLeaf } from '@aztec/circuit-types';
2
+ import { L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js/constants';
7
3
  import { type Fr } from '@aztec/foundation/fields';
8
4
 
9
5
  /**
@@ -11,7 +7,7 @@ import { type Fr } from '@aztec/foundation/fields';
11
7
  */
12
8
  export class L1ToL2MessageStore {
13
9
  /**
14
- * A map pointing from a key in a "blockNum-messageIndex" format to the corresponding L1 to L2 message hash.
10
+ * A map pointing from a key in a "messageIndex" format to the corresponding L1 to L2 message hash.
15
11
  */
16
12
  protected store: Map<string, Fr> = new Map();
17
13
 
@@ -24,20 +20,17 @@ export class L1ToL2MessageStore {
24
20
  }
25
21
 
26
22
  addMessage(message: InboxLeaf) {
27
- if (message.index >= this.#l1ToL2MessagesSubtreeSize) {
28
- throw new Error(`Message index ${message.index} out of subtree range`);
29
- }
30
- const key = `${message.blockNumber}-${message.index}`;
31
- this.store.set(key, message.leaf);
23
+ this.store.set(`${message.index}`, message.leaf);
32
24
  }
33
25
 
34
26
  getMessages(blockNumber: bigint): Fr[] {
35
27
  const messages: Fr[] = [];
36
28
  let undefinedMessageFound = false;
37
- for (let messageIndex = 0; messageIndex < this.#l1ToL2MessagesSubtreeSize; messageIndex++) {
29
+ const startIndex = Number(InboxLeaf.smallestIndexFromL2Block(blockNumber));
30
+
31
+ for (let i = startIndex; i < startIndex + this.#l1ToL2MessagesSubtreeSize; i++) {
38
32
  // This is inefficient but probably fine for now.
39
- const key = `${blockNumber}-${messageIndex}`;
40
- const message = this.store.get(key);
33
+ const message = this.store.get(`${i}`);
41
34
  if (message) {
42
35
  if (undefinedMessageFound) {
43
36
  throw new Error(`L1 to L2 message gap found in block ${blockNumber}`);
@@ -61,10 +54,7 @@ export class L1ToL2MessageStore {
61
54
  getMessageIndex(l1ToL2Message: Fr, startIndex: bigint): bigint | undefined {
62
55
  for (const [key, message] of this.store.entries()) {
63
56
  if (message.equals(l1ToL2Message)) {
64
- const keyParts = key.split('-');
65
- const [blockNumber, messageIndex] = [BigInt(keyParts[0]), BigInt(keyParts[1])];
66
- const indexInTheWholeTree =
67
- (blockNumber - BigInt(INITIAL_L2_BLOCK_NUM)) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) + messageIndex;
57
+ const indexInTheWholeTree = BigInt(key);
68
58
  if (indexInTheWholeTree < startIndex) {
69
59
  continue;
70
60
  }
@@ -15,16 +15,18 @@ import {
15
15
  TxReceipt,
16
16
  type UnencryptedL2BlockL2Logs,
17
17
  } from '@aztec/circuit-types';
18
- import { Fr, type Header, INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js';
19
- import { type ContractArtifact } from '@aztec/foundation/abi';
20
- import { type AztecAddress } from '@aztec/foundation/aztec-address';
21
18
  import {
22
19
  type ContractClassPublic,
23
20
  type ContractClassPublicWithBlockNumber,
24
21
  type ContractInstanceWithAddress,
25
22
  type ExecutablePrivateFunctionWithMembershipProof,
23
+ Fr,
24
+ type Header,
25
+ INITIAL_L2_BLOCK_NUM,
26
26
  type UnconstrainedFunctionWithMembershipProof,
27
- } from '@aztec/types/contracts';
27
+ } from '@aztec/circuits.js';
28
+ import { type ContractArtifact } from '@aztec/foundation/abi';
29
+ import { type AztecAddress } from '@aztec/foundation/aztec-address';
28
30
 
29
31
  import { type ArchiverDataStore, type ArchiverL1SynchPoint } from '../archiver_store.js';
30
32
  import { type DataRetrieval } from '../structs/data_retrieval.js';
package/src/factory.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import { type ContractClassPublic } from '@aztec/circuits.js';
1
2
  import { createDebugLogger } from '@aztec/foundation/log';
2
3
  import { createStore } from '@aztec/kv-store/utils';
4
+ import { getCanonicalProtocolContract, protocolContractNames } from '@aztec/protocol-contracts';
3
5
  import { type TelemetryClient } from '@aztec/telemetry-client';
4
6
  import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
5
7
 
@@ -16,8 +18,24 @@ export async function createArchiver(
16
18
  if (!config.archiverUrl) {
17
19
  const store = await createStore('archiver', config, createDebugLogger('aztec:archiver:lmdb'));
18
20
  const archiverStore = new KVArchiverDataStore(store, config.maxLogs);
21
+ await initWithProtocolContracts(archiverStore);
19
22
  return Archiver.createAndSync(config, archiverStore, telemetry, opts.blockUntilSync);
20
23
  } else {
21
24
  return createArchiverClient(config.archiverUrl);
22
25
  }
23
26
  }
27
+
28
+ async function initWithProtocolContracts(store: KVArchiverDataStore) {
29
+ const blockNumber = 0;
30
+ for (const name of protocolContractNames) {
31
+ const contract = getCanonicalProtocolContract(name);
32
+ const contractClassPublic: ContractClassPublic = {
33
+ ...contract.contractClass,
34
+ privateFunctions: [],
35
+ unconstrainedFunctions: [],
36
+ };
37
+ await store.addContractArtifact(contract.address, contract.artifact);
38
+ await store.addContractClasses([contractClassPublic], blockNumber);
39
+ await store.addContractInstances([contract.instance], blockNumber);
40
+ }
41
+ }
package/src/test/index.ts CHANGED
@@ -1 +1,3 @@
1
1
  export * from './mock_l2_block_source.js';
2
+ export * from './mock_l1_to_l2_message_source.js';
3
+ export * from './mock_archiver.js';