@aztec/archiver 0.56.0 → 0.58.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 (79) hide show
  1. package/README.md +1 -1
  2. package/dest/archiver/archiver.d.ts +27 -22
  3. package/dest/archiver/archiver.d.ts.map +1 -1
  4. package/dest/archiver/archiver.js +421 -115
  5. package/dest/archiver/archiver_store.d.ts +39 -10
  6. package/dest/archiver/archiver_store.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  8. package/dest/archiver/archiver_store_test_suite.js +84 -31
  9. package/dest/archiver/config.js +6 -6
  10. package/dest/archiver/data_retrieval.d.ts +2 -3
  11. package/dest/archiver/data_retrieval.d.ts.map +1 -1
  12. package/dest/archiver/data_retrieval.js +23 -22
  13. package/dest/archiver/epoch_helpers.d.ts +15 -0
  14. package/dest/archiver/epoch_helpers.d.ts.map +1 -0
  15. package/dest/archiver/epoch_helpers.js +23 -0
  16. package/dest/archiver/kv_archiver_store/block_store.d.ts +20 -1
  17. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  18. package/dest/archiver/kv_archiver_store/block_store.js +62 -5
  19. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +3 -3
  20. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  21. package/dest/archiver/kv_archiver_store/contract_class_store.js +12 -5
  22. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +2 -2
  23. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
  24. package/dest/archiver/kv_archiver_store/contract_instance_store.js +5 -2
  25. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +29 -10
  26. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  27. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +57 -17
  28. package/dest/archiver/kv_archiver_store/log_store.d.ts +4 -5
  29. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  30. package/dest/archiver/kv_archiver_store/log_store.js +18 -14
  31. package/dest/archiver/kv_archiver_store/message_store.d.ts +2 -1
  32. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  33. package/dest/archiver/kv_archiver_store/message_store.js +17 -13
  34. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +3 -2
  35. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +1 -1
  36. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +12 -14
  37. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +23 -23
  38. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
  39. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +130 -70
  40. package/dest/factory.d.ts.map +1 -1
  41. package/dest/factory.js +17 -1
  42. package/dest/index.js +2 -1
  43. package/dest/test/index.d.ts +4 -0
  44. package/dest/test/index.d.ts.map +1 -0
  45. package/dest/test/index.js +4 -0
  46. package/dest/test/mock_archiver.d.ts +22 -0
  47. package/dest/test/mock_archiver.d.ts.map +1 -0
  48. package/dest/test/mock_archiver.js +44 -0
  49. package/dest/test/mock_l1_to_l2_message_source.d.ts +16 -0
  50. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -0
  51. package/dest/test/mock_l1_to_l2_message_source.js +25 -0
  52. package/dest/test/mock_l2_block_source.d.ts +75 -0
  53. package/dest/test/mock_l2_block_source.d.ts.map +1 -0
  54. package/dest/test/mock_l2_block_source.js +154 -0
  55. package/package.json +15 -11
  56. package/src/archiver/archiver.ts +553 -170
  57. package/src/archiver/archiver_store.ts +48 -19
  58. package/src/archiver/archiver_store_test_suite.ts +111 -73
  59. package/src/archiver/config.ts +5 -5
  60. package/src/archiver/data_retrieval.ts +25 -26
  61. package/src/archiver/epoch_helpers.ts +26 -0
  62. package/src/archiver/kv_archiver_store/block_store.ts +70 -2
  63. package/src/archiver/kv_archiver_store/contract_class_store.ts +24 -9
  64. package/src/archiver/kv_archiver_store/contract_instance_store.ts +5 -2
  65. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +71 -29
  66. package/src/archiver/kv_archiver_store/log_store.ts +18 -18
  67. package/src/archiver/kv_archiver_store/message_store.ts +16 -18
  68. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +13 -19
  69. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +154 -83
  70. package/src/factory.ts +18 -0
  71. package/src/index.ts +1 -0
  72. package/src/test/index.ts +3 -0
  73. package/src/test/mock_archiver.ts +55 -0
  74. package/src/test/mock_l1_to_l2_message_source.ts +31 -0
  75. package/src/test/mock_l2_block_source.ts +190 -0
  76. package/dest/archiver/kv_archiver_store/proven_store.d.ts +0 -14
  77. package/dest/archiver/kv_archiver_store/proven_store.d.ts.map +0 -1
  78. package/dest/archiver/kv_archiver_store/proven_store.js +0 -30
  79. package/src/archiver/kv_archiver_store/proven_store.ts +0 -34
@@ -1,11 +1,14 @@
1
1
  import {
2
2
  type FromLogType,
3
3
  type GetUnencryptedLogsResponse,
4
+ type InboxLeaf,
4
5
  type L1ToL2MessageSource,
5
6
  type L2Block,
7
+ type L2BlockId,
6
8
  type L2BlockL2Logs,
7
9
  type L2BlockSource,
8
10
  type L2LogsSource,
11
+ type L2Tips,
9
12
  type LogFilter,
10
13
  type LogType,
11
14
  type TxEffect,
@@ -13,14 +16,22 @@ import {
13
16
  type TxReceipt,
14
17
  type UnencryptedL2Log,
15
18
  } from '@aztec/circuit-types';
16
- import { ContractClassRegisteredEvent, type FunctionSelector } from '@aztec/circuits.js';
17
19
  import {
20
+ type ContractClassPublic,
21
+ ContractClassRegisteredEvent,
22
+ type ContractDataSource,
18
23
  ContractInstanceDeployedEvent,
24
+ type ContractInstanceWithAddress,
25
+ type ExecutablePrivateFunctionWithMembershipProof,
26
+ type FunctionSelector,
27
+ type Header,
19
28
  PrivateFunctionBroadcastedEvent,
29
+ type PublicFunction,
20
30
  UnconstrainedFunctionBroadcastedEvent,
31
+ type UnconstrainedFunctionWithMembershipProof,
21
32
  isValidPrivateFunctionMembershipProof,
22
33
  isValidUnconstrainedFunctionMembershipProof,
23
- } from '@aztec/circuits.js/contract';
34
+ } from '@aztec/circuits.js';
24
35
  import { createEthereumChain } from '@aztec/ethereum';
25
36
  import { type ContractArtifact } from '@aztec/foundation/abi';
26
37
  import { type AztecAddress } from '@aztec/foundation/aztec-address';
@@ -30,16 +41,8 @@ import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
30
41
  import { RunningPromise } from '@aztec/foundation/running-promise';
31
42
  import { Timer } from '@aztec/foundation/timer';
32
43
  import { InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
33
- import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer';
44
+ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
34
45
  import { type TelemetryClient } from '@aztec/telemetry-client';
35
- import {
36
- type ContractClassPublic,
37
- type ContractDataSource,
38
- type ContractInstanceWithAddress,
39
- type ExecutablePrivateFunctionWithMembershipProof,
40
- type PublicFunction,
41
- type UnconstrainedFunctionWithMembershipProof,
42
- } from '@aztec/types/contracts';
43
46
 
44
47
  import groupBy from 'lodash.groupby';
45
48
  import {
@@ -52,11 +55,18 @@ import {
52
55
  http,
53
56
  } from 'viem';
54
57
 
55
- import { type ArchiverDataStore } from './archiver_store.js';
58
+ import { type ArchiverDataStore, type ArchiverL1SynchPoint } from './archiver_store.js';
56
59
  import { type ArchiverConfig } from './config.js';
57
60
  import { retrieveBlockFromRollup, retrieveL1ToL2Messages } from './data_retrieval.js';
61
+ import {
62
+ getEpochNumberAtTimestamp,
63
+ getSlotAtTimestamp,
64
+ getSlotRangeForEpoch,
65
+ getTimestampRangeForEpoch,
66
+ } from './epoch_helpers.js';
58
67
  import { ArchiverInstrumentation } from './instrumentation.js';
59
- import { type SingletonDataRetrieval } from './structs/data_retrieval.js';
68
+ import { type DataRetrieval } from './structs/data_retrieval.js';
69
+ import { type L1Published } from './structs/published.js';
60
70
 
61
71
  /**
62
72
  * Helper interface to combine all sources this archiver implementation provides.
@@ -77,6 +87,11 @@ export class Archiver implements ArchiveSource {
77
87
  private rollup: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, Chain>>;
78
88
  private inbox: GetContractReturnType<typeof InboxAbi, PublicClient<HttpTransport, Chain>>;
79
89
 
90
+ private store: ArchiverStoreHelper;
91
+
92
+ public l1BlockNumber: bigint | undefined;
93
+ public l1Timestamp: bigint | undefined;
94
+
80
95
  /**
81
96
  * Creates a new instance of the Archiver.
82
97
  * @param publicClient - A client for interacting with the Ethereum node.
@@ -90,14 +105,16 @@ export class Archiver implements ArchiveSource {
90
105
  constructor(
91
106
  private readonly publicClient: PublicClient<HttpTransport, Chain>,
92
107
  private readonly rollupAddress: EthAddress,
93
- private readonly inboxAddress: EthAddress,
108
+ readonly inboxAddress: EthAddress,
94
109
  private readonly registryAddress: EthAddress,
95
- private readonly store: ArchiverDataStore,
96
- private readonly pollingIntervalMs = 10_000,
110
+ readonly dataStore: ArchiverDataStore,
111
+ private readonly pollingIntervalMs: number,
97
112
  private readonly instrumentation: ArchiverInstrumentation,
98
- private readonly l1StartBlock: bigint = 0n,
113
+ private readonly l1constants: L1RollupConstants = EmptyL1RollupConstants,
99
114
  private readonly log: DebugLogger = createDebugLogger('aztec:archiver'),
100
115
  ) {
116
+ this.store = new ArchiverStoreHelper(dataStore);
117
+
101
118
  this.rollup = getContract({
102
119
  address: rollupAddress.toString(),
103
120
  abi: RollupAbi,
@@ -137,7 +154,10 @@ export class Archiver implements ArchiveSource {
137
154
  client: publicClient,
138
155
  });
139
156
 
140
- const l1StartBlock = await rollup.read.L1_BLOCK_AT_GENESIS();
157
+ const [l1StartBlock, l1GenesisTime] = await Promise.all([
158
+ rollup.read.L1_BLOCK_AT_GENESIS(),
159
+ rollup.read.GENESIS_TIME(),
160
+ ] as const);
141
161
 
142
162
  const archiver = new Archiver(
143
163
  publicClient,
@@ -145,9 +165,9 @@ export class Archiver implements ArchiveSource {
145
165
  config.l1Contracts.inboxAddress,
146
166
  config.l1Contracts.registryAddress,
147
167
  archiverStore,
148
- config.archiverPollingIntervalMS,
168
+ config.archiverPollingIntervalMS ?? 10_000,
149
169
  new ArchiverInstrumentation(telemetry),
150
- BigInt(l1StartBlock),
170
+ { l1StartBlock, l1GenesisTime },
151
171
  );
152
172
  await archiver.start(blockUntilSynced);
153
173
  return archiver;
@@ -199,11 +219,8 @@ export class Archiver implements ArchiveSource {
199
219
  *
200
220
  * This code does not handle reorgs.
201
221
  */
202
- const {
203
- blocksSynchedTo = this.l1StartBlock,
204
- messagesSynchedTo = this.l1StartBlock,
205
- provenLogsSynchedTo = this.l1StartBlock,
206
- } = await this.store.getSynchPoint();
222
+ const { l1StartBlock } = this.l1constants;
223
+ const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = l1StartBlock } = await this.store.getSynchPoint();
207
224
  const currentL1BlockNumber = await this.publicClient.getBlockNumber();
208
225
 
209
226
  // ********** Ensuring Consistency of data pulled from L1 **********
@@ -225,14 +242,47 @@ export class Archiver implements ArchiveSource {
225
242
  * in future but for the time being it should give us the guarantees that we need
226
243
  */
227
244
 
228
- await this.updateLastProvenL2Block(provenLogsSynchedTo, currentL1BlockNumber);
229
-
230
245
  // ********** Events that are processed per L1 block **********
231
-
232
246
  await this.handleL1ToL2Messages(blockUntilSynced, messagesSynchedTo, currentL1BlockNumber);
233
247
 
234
248
  // ********** Events that are processed per L2 block **********
235
- 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
+ }
259
+
260
+ // Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
261
+ if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
262
+ this.l1Timestamp = await this.publicClient.getBlock({ blockNumber: currentL1BlockNumber }).then(b => b.timestamp);
263
+ this.l1BlockNumber = currentL1BlockNumber;
264
+ }
265
+ }
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
+ }
236
286
  }
237
287
 
238
288
  private async handleL1ToL2Messages(
@@ -244,14 +294,10 @@ export class Archiver implements ArchiveSource {
244
294
  return;
245
295
  }
246
296
 
247
- const retrievedL1ToL2Messages = await retrieveL1ToL2Messages(
248
- this.inbox,
249
- blockUntilSynced,
250
- messagesSynchedTo + 1n,
251
- currentL1BlockNumber,
252
- );
297
+ const localTotalMessageCount = await this.store.getTotalL1ToL2MessageCount();
298
+ const destinationTotalMessageCount = await this.inbox.read.totalMessagesInserted();
253
299
 
254
- if (retrievedL1ToL2Messages.retrievedData.length === 0) {
300
+ if (localTotalMessageCount === destinationTotalMessageCount) {
255
301
  await this.store.setMessageSynchedL1BlockNumber(currentL1BlockNumber);
256
302
  this.log.verbose(
257
303
  `Retrieved no new L1 -> L2 messages between L1 blocks ${messagesSynchedTo + 1n} and ${currentL1BlockNumber}.`,
@@ -259,6 +305,13 @@ export class Archiver implements ArchiveSource {
259
305
  return;
260
306
  }
261
307
 
308
+ const retrievedL1ToL2Messages = await retrieveL1ToL2Messages(
309
+ this.inbox,
310
+ blockUntilSynced,
311
+ messagesSynchedTo + 1n,
312
+ currentL1BlockNumber,
313
+ );
314
+
262
315
  await this.store.addL1ToL2Messages(retrievedL1ToL2Messages);
263
316
 
264
317
  this.log.verbose(
@@ -268,46 +321,91 @@ export class Archiver implements ArchiveSource {
268
321
  );
269
322
  }
270
323
 
271
- private async updateLastProvenL2Block(provenSynchedTo: bigint, currentL1BlockNumber: bigint) {
272
- if (currentL1BlockNumber <= provenSynchedTo) {
273
- return;
274
- }
275
-
276
- const provenBlockNumber = await this.rollup.read.getProvenBlockNumber();
277
- if (provenBlockNumber) {
278
- await this.store.setProvenL2BlockNumber({
279
- retrievedData: Number(provenBlockNumber),
280
- lastProcessedL1BlockNumber: currentL1BlockNumber,
281
- });
282
- }
283
- }
324
+ private async handleL2blocks(
325
+ blockUntilSynced: boolean,
326
+ blocksSynchedTo: bigint,
327
+ currentL1BlockNumber: bigint,
328
+ ): Promise<{ provenBlockNumber: bigint }> {
329
+ const localPendingBlockNumber = BigInt(await this.getBlockNumber());
330
+ const [
331
+ provenBlockNumber,
332
+ provenArchive,
333
+ pendingBlockNumber,
334
+ pendingArchive,
335
+ archiveForLocalPendingBlockNumber,
336
+ provenEpochNumber,
337
+ ] = await this.rollup.read.status([localPendingBlockNumber], { blockNumber: currentL1BlockNumber });
338
+
339
+ const updateProvenBlock = async () => {
340
+ const localBlockForDestinationProvenBlockNumber = await this.getBlock(Number(provenBlockNumber));
341
+ if (
342
+ localBlockForDestinationProvenBlockNumber &&
343
+ provenArchive === localBlockForDestinationProvenBlockNumber.archive.root.toString()
344
+ ) {
345
+ this.log.info(`Updating the proven block number to ${provenBlockNumber} and epoch to ${provenEpochNumber}`);
346
+ await this.store.setProvenL2BlockNumber(Number(provenBlockNumber));
347
+ // if we are here then we must have a valid proven epoch number
348
+ await this.store.setProvenL2EpochNumber(Number(provenEpochNumber));
349
+ }
350
+ this.instrumentation.updateLastProvenBlock(Number(provenBlockNumber));
351
+ };
284
352
 
285
- private async handleL2blocks(blockUntilSynced: boolean, blocksSynchedTo: bigint, currentL1BlockNumber: bigint) {
286
- if (currentL1BlockNumber <= blocksSynchedTo) {
287
- return;
353
+ // This is an edge case that we only hit if there are no proposed blocks.
354
+ // If we have 0 blocks locally and there are no blocks onchain there is nothing to do.
355
+ const noBlocks = localPendingBlockNumber === 0n && pendingBlockNumber === 0n;
356
+ if (noBlocks) {
357
+ await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
358
+ this.log.verbose(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
359
+ return { provenBlockNumber };
288
360
  }
289
361
 
290
- const lastBlock = await this.getBlock(-1);
362
+ await updateProvenBlock();
291
363
 
292
- const [, , pendingBlockNumber, pendingArchive, archiveOfMyBlock] = await this.rollup.read.status([
293
- BigInt(lastBlock?.number ?? 0),
294
- ]);
364
+ // Related to the L2 reorgs of the pending chain. We are only interested in actually addressing a reorg if there
365
+ // are any state that could be impacted by it. If we have no blocks, there is no impact.
366
+ if (localPendingBlockNumber > 0) {
367
+ const localPendingBlock = await this.getBlock(Number(localPendingBlockNumber));
368
+ if (localPendingBlock === undefined) {
369
+ throw new Error(`Missing block ${localPendingBlockNumber}`);
370
+ }
295
371
 
296
- const noBlocksButInitial = lastBlock === undefined && pendingBlockNumber == 0n;
297
- const noBlockSinceLast =
298
- lastBlock &&
299
- pendingBlockNumber === BigInt(lastBlock.number) &&
300
- pendingArchive === lastBlock.archive.root.toString();
372
+ const noBlockSinceLast = localPendingBlock && pendingArchive === localPendingBlock.archive.root.toString();
373
+ if (noBlockSinceLast) {
374
+ await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
375
+ this.log.verbose(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
376
+ return { provenBlockNumber };
377
+ }
301
378
 
302
- if (noBlocksButInitial || noBlockSinceLast) {
303
- await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
304
- this.log.verbose(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
305
- return;
306
- }
379
+ const localPendingBlockInChain = archiveForLocalPendingBlockNumber === localPendingBlock.archive.root.toString();
380
+ if (!localPendingBlockInChain) {
381
+ // If our local pending block tip is not in the chain on L1 a "prune" must have happened
382
+ // or the L1 have reorged.
383
+ // In any case, we have to figure out how far into the past the action will take us.
384
+ // For simplicity here, we will simply rewind until we end in a block that is also on the chain on L1.
385
+ this.log.verbose(`L2 prune have occurred, unwind state`);
386
+
387
+ let tipAfterUnwind = localPendingBlockNumber;
388
+ while (true) {
389
+ const candidateBlock = await this.getBlock(Number(tipAfterUnwind));
390
+ if (candidateBlock === undefined) {
391
+ break;
392
+ }
393
+
394
+ const archiveAtContract = await this.rollup.read.archiveAt([BigInt(candidateBlock.number)]);
395
+
396
+ if (archiveAtContract === candidateBlock.archive.root.toString()) {
397
+ break;
398
+ }
399
+ tipAfterUnwind--;
400
+ }
401
+
402
+ const blocksToUnwind = localPendingBlockNumber - tipAfterUnwind;
403
+ this.log.verbose(
404
+ `Unwinding ${blocksToUnwind} block${blocksToUnwind > 1n ? 's' : ''} from block ${localPendingBlockNumber}`,
405
+ );
307
406
 
308
- if (lastBlock && archiveOfMyBlock !== lastBlock.archive.root.toString()) {
309
- // @todo Either `prune` have been called, or L1 have re-orged deep enough to remove a block.
310
- // Issue#8620 and Issue#8621
407
+ await this.store.unwindBlocks(Number(localPendingBlockNumber), Number(blocksToUnwind));
408
+ }
311
409
  }
312
410
 
313
411
  this.log.debug(`Retrieving blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
@@ -315,15 +413,16 @@ export class Archiver implements ArchiveSource {
315
413
  this.rollup,
316
414
  this.publicClient,
317
415
  blockUntilSynced,
318
- blocksSynchedTo + 1n,
416
+ blocksSynchedTo + 1n, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
319
417
  currentL1BlockNumber,
320
418
  this.log,
321
419
  );
322
420
 
323
421
  if (retrievedBlocks.length === 0) {
324
- await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
325
- this.log.verbose(`Retrieved no new blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
326
- return;
422
+ // We are not calling `setBlockSynchedL1BlockNumber` because it may cause sync issues if based off infura.
423
+ // See further details in earlier comments.
424
+ this.log.verbose(`Retrieved no new L2 blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
425
+ return { provenBlockNumber };
327
426
  }
328
427
 
329
428
  this.log.debug(
@@ -334,110 +433,24 @@ export class Archiver implements ArchiveSource {
334
433
 
335
434
  const lastProcessedL1BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].l1.blockNumber;
336
435
 
337
- this.log.debug(
338
- `Processing retrieved blocks ${retrievedBlocks
339
- .map(b => b.data.number)
340
- .join(',')} with last processed L1 block ${lastProcessedL1BlockNumber}`,
341
- );
342
-
343
- await Promise.all(
344
- retrievedBlocks.map(block => {
345
- return this.store.addLogs(
346
- block.data.body.noteEncryptedLogs,
347
- block.data.body.encryptedLogs,
348
- block.data.body.unencryptedLogs,
349
- block.data.number,
350
- );
351
- }),
352
- );
353
-
354
- // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
355
- await Promise.all(
356
- retrievedBlocks.map(async block => {
357
- const blockLogs = block.data.body.txEffects
358
- .flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
359
- .flatMap(txLog => txLog.unrollLogs());
360
- await this.storeRegisteredContractClasses(blockLogs, block.data.number);
361
- await this.storeDeployedContractInstances(blockLogs, block.data.number);
362
- await this.storeBroadcastedIndividualFunctions(blockLogs, block.data.number);
363
- }),
364
- );
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
+ }
365
440
 
366
441
  const timer = new Timer();
367
442
  await this.store.addBlocks(retrievedBlocks);
443
+
444
+ // Important that we update AFTER inserting the blocks.
445
+ await updateProvenBlock();
368
446
  this.instrumentation.processNewBlocks(
369
447
  timer.ms() / retrievedBlocks.length,
370
448
  retrievedBlocks.map(b => b.data),
371
449
  );
372
450
  const lastL2BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].data.number;
373
451
  this.log.verbose(`Processed ${retrievedBlocks.length} new L2 blocks up to ${lastL2BlockNumber}`);
374
- }
375
-
376
- /**
377
- * Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract.
378
- * @param allLogs - All logs emitted in a bunch of blocks.
379
- */
380
- private async storeRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number) {
381
- const contractClasses = ContractClassRegisteredEvent.fromLogs(allLogs, ClassRegistererAddress).map(e =>
382
- e.toContractClassPublic(),
383
- );
384
- if (contractClasses.length > 0) {
385
- contractClasses.forEach(c => this.log.verbose(`Registering contract class ${c.id.toString()}`));
386
- await this.store.addContractClasses(contractClasses, blockNum);
387
- }
388
- }
389
-
390
- /**
391
- * Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
392
- * @param allLogs - All logs emitted in a bunch of blocks.
393
- */
394
- private async storeDeployedContractInstances(allLogs: UnencryptedL2Log[], blockNum: number) {
395
- const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs).map(e => e.toContractInstance());
396
- if (contractInstances.length > 0) {
397
- contractInstances.forEach(c => this.log.verbose(`Storing contract instance at ${c.address.toString()}`));
398
- await this.store.addContractInstances(contractInstances, blockNum);
399
- }
400
- }
401
-
402
- private async storeBroadcastedIndividualFunctions(allLogs: UnencryptedL2Log[], _blockNum: number) {
403
- // Filter out private and unconstrained function broadcast events
404
- const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
405
- const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
406
-
407
- // Group all events by contract class id
408
- for (const [classIdString, classEvents] of Object.entries(
409
- groupBy([...privateFnEvents, ...unconstrainedFnEvents], e => e.contractClassId.toString()),
410
- )) {
411
- const contractClassId = Fr.fromString(classIdString);
412
- const contractClass = await this.store.getContractClass(contractClassId);
413
- if (!contractClass) {
414
- this.log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`);
415
- continue;
416
- }
417
-
418
- // Split private and unconstrained functions, and filter out invalid ones
419
- const allFns = classEvents.map(e => e.toFunctionWithMembershipProof());
420
- const privateFns = allFns.filter(
421
- (fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'unconstrainedFunctionsArtifactTreeRoot' in fn,
422
- );
423
- const unconstrainedFns = allFns.filter(
424
- (fn): fn is UnconstrainedFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn,
425
- );
426
- const validPrivateFns = privateFns.filter(fn => isValidPrivateFunctionMembershipProof(fn, contractClass));
427
- const validUnconstrainedFns = unconstrainedFns.filter(fn =>
428
- isValidUnconstrainedFunctionMembershipProof(fn, contractClass),
429
- );
430
- const validFnCount = validPrivateFns.length + validUnconstrainedFns.length;
431
- if (validFnCount !== allFns.length) {
432
- this.log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`);
433
- }
434
452
 
435
- // Store the functions in the contract class in a single operation
436
- if (validFnCount > 0) {
437
- this.log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
438
- }
439
- await this.store.addFunctions(contractClassId, validPrivateFns, validUnconstrainedFns);
440
- }
453
+ return { provenBlockNumber };
441
454
  }
442
455
 
443
456
  /**
@@ -460,6 +473,71 @@ export class Archiver implements ArchiveSource {
460
473
  return Promise.resolve(this.registryAddress);
461
474
  }
462
475
 
476
+ public getL1BlockNumber(): bigint {
477
+ const l1BlockNumber = this.l1BlockNumber;
478
+ if (!l1BlockNumber) {
479
+ throw new Error('L1 block number not yet available. Complete an initial sync first.');
480
+ }
481
+ return l1BlockNumber;
482
+ }
483
+
484
+ public getL1Timestamp(): bigint {
485
+ const l1Timestamp = this.l1Timestamp;
486
+ if (!l1Timestamp) {
487
+ throw new Error('L1 timestamp not yet available. Complete an initial sync first.');
488
+ }
489
+ return l1Timestamp;
490
+ }
491
+
492
+ public getL2SlotNumber(): Promise<bigint> {
493
+ return Promise.resolve(getSlotAtTimestamp(this.getL1Timestamp(), this.l1constants));
494
+ }
495
+
496
+ public getL2EpochNumber(): Promise<bigint> {
497
+ return Promise.resolve(getEpochNumberAtTimestamp(this.getL1Timestamp(), this.l1constants));
498
+ }
499
+
500
+ public async getBlocksForEpoch(epochNumber: bigint): Promise<L2Block[]> {
501
+ const [start, end] = getSlotRangeForEpoch(epochNumber);
502
+ const blocks: L2Block[] = [];
503
+
504
+ // Walk the list of blocks backwards and filter by slots matching the requested epoch.
505
+ // We'll typically ask for blocks for a very recent epoch, so we shouldn't need an index here.
506
+ let block = await this.getBlock(await this.store.getSynchedL2BlockNumber());
507
+ const slot = (b: L2Block) => b.header.globalVariables.slotNumber.toBigInt();
508
+ while (block && slot(block) >= start) {
509
+ if (slot(block) <= end) {
510
+ blocks.push(block);
511
+ }
512
+ block = await this.getBlock(block.number - 1);
513
+ }
514
+
515
+ return blocks.reverse();
516
+ }
517
+
518
+ public async isEpochComplete(epochNumber: bigint): Promise<boolean> {
519
+ // The epoch is complete if the current L2 block is the last one in the epoch (or later)
520
+ const header = await this.getBlockHeader('latest');
521
+ const slot = header?.globalVariables.slotNumber.toBigInt();
522
+ const [_startSlot, endSlot] = getSlotRangeForEpoch(epochNumber);
523
+ if (slot && slot >= endSlot) {
524
+ return true;
525
+ }
526
+
527
+ // If not, the epoch may also be complete if the L2 slot has passed without a block
528
+ // We compute this based on the timestamp for the given epoch and the timestamp of the last L1 block
529
+ const l1Timestamp = this.getL1Timestamp();
530
+ const [_startTimestamp, endTimestamp] = getTimestampRangeForEpoch(epochNumber, this.l1constants);
531
+
532
+ // For this computation, we throw in a few extra seconds just for good measure,
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?
537
+ const leeway = 3n;
538
+ return l1Timestamp + leeway >= endTimestamp;
539
+ }
540
+
463
541
  /**
464
542
  * Gets up to `limit` amount of L2 blocks starting from `from`.
465
543
  * @param from - Number of the first block to return (inclusive).
@@ -476,7 +554,7 @@ export class Archiver implements ArchiveSource {
476
554
 
477
555
  /**
478
556
  * Gets an l2 block.
479
- * @param number - The block number to return (inclusive).
557
+ * @param number - The block number to return.
480
558
  * @returns The requested L2 block.
481
559
  */
482
560
  public async getBlock(number: number): Promise<L2Block | undefined> {
@@ -484,10 +562,27 @@ export class Archiver implements ArchiveSource {
484
562
  if (number < 0) {
485
563
  number = await this.store.getSynchedL2BlockNumber();
486
564
  }
565
+ if (number == 0) {
566
+ return undefined;
567
+ }
487
568
  const blocks = await this.store.getBlocks(number, 1);
488
569
  return blocks.length === 0 ? undefined : blocks[0].data;
489
570
  }
490
571
 
572
+ public async getBlockHeader(number: number | 'latest'): Promise<Header | undefined> {
573
+ if (number === 'latest') {
574
+ number = await this.store.getSynchedL2BlockNumber();
575
+ }
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
+ }
584
+ }
585
+
491
586
  public getTxEffect(txHash: TxHash): Promise<TxEffect | undefined> {
492
587
  return this.store.getTxEffect(txHash);
493
588
  }
@@ -553,9 +648,13 @@ export class Archiver implements ArchiveSource {
553
648
  return this.store.getProvenL2BlockNumber();
554
649
  }
555
650
 
651
+ public getProvenL2EpochNumber(): Promise<number | undefined> {
652
+ return this.store.getProvenL2EpochNumber();
653
+ }
654
+
556
655
  /** Forcefully updates the last proven block number. Use for testing. */
557
- public setProvenBlockNumber(block: SingletonDataRetrieval<number>): Promise<void> {
558
- return this.store.setProvenL2BlockNumber(block);
656
+ public setProvenBlockNumber(blockNumber: number): Promise<void> {
657
+ return this.store.setProvenL2BlockNumber(blockNumber);
559
658
  }
560
659
 
561
660
  public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
@@ -596,4 +695,288 @@ export class Archiver implements ArchiveSource {
596
695
  getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
597
696
  return this.store.getContractArtifact(address);
598
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
+ }
724
+ }
725
+
726
+ enum Operation {
727
+ Store,
728
+ Delete,
599
729
  }
730
+
731
+ /**
732
+ * A helper class that we use to deal with some of the logic needed when adding blocks.
733
+ *
734
+ * I would have preferred to not have this type. But it is useful for handling the logic that any
735
+ * store would need to include otherwise while exposing fewer functions and logic directly to the archiver.
736
+ */
737
+ class ArchiverStoreHelper
738
+ implements
739
+ Omit<
740
+ ArchiverDataStore,
741
+ | 'addLogs'
742
+ | 'deleteLogs'
743
+ | 'addContractClasses'
744
+ | 'deleteContractClasses'
745
+ | 'addContractInstances'
746
+ | 'deleteContractInstances'
747
+ | 'addFunctions'
748
+ >
749
+ {
750
+ #log = createDebugLogger('aztec:archiver:block-helper');
751
+
752
+ constructor(private readonly store: ArchiverDataStore) {}
753
+
754
+ /**
755
+ * Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract.
756
+ * @param allLogs - All logs emitted in a bunch of blocks.
757
+ */
758
+ async #updateRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number, operation: Operation) {
759
+ const contractClasses = ContractClassRegisteredEvent.fromLogs(
760
+ allLogs,
761
+ ProtocolContractAddress.ContractClassRegisterer,
762
+ ).map(e => e.toContractClassPublic());
763
+ if (contractClasses.length > 0) {
764
+ contractClasses.forEach(c => this.#log.verbose(`Registering contract class ${c.id.toString()}`));
765
+ if (operation == Operation.Store) {
766
+ return await this.store.addContractClasses(contractClasses, blockNum);
767
+ } else if (operation == Operation.Delete) {
768
+ return await this.store.deleteContractClasses(contractClasses, blockNum);
769
+ }
770
+ }
771
+ return true;
772
+ }
773
+
774
+ /**
775
+ * Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
776
+ * @param allLogs - All logs emitted in a bunch of blocks.
777
+ */
778
+ async #updateDeployedContractInstances(allLogs: UnencryptedL2Log[], blockNum: number, operation: Operation) {
779
+ const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs).map(e => e.toContractInstance());
780
+ if (contractInstances.length > 0) {
781
+ contractInstances.forEach(c =>
782
+ this.#log.verbose(`${Operation[operation]} contract instance at ${c.address.toString()}`),
783
+ );
784
+ if (operation == Operation.Store) {
785
+ return await this.store.addContractInstances(contractInstances, blockNum);
786
+ } else if (operation == Operation.Delete) {
787
+ return await this.store.deleteContractInstances(contractInstances, blockNum);
788
+ }
789
+ }
790
+ return true;
791
+ }
792
+
793
+ /**
794
+ * Stores the functions that was broadcasted individually
795
+ *
796
+ * @dev Beware that there is not a delete variant of this, since they are added to contract classes
797
+ * and will be deleted as part of the class if needed.
798
+ *
799
+ * @param allLogs - The logs from the block
800
+ * @param _blockNum - The block number
801
+ * @returns
802
+ */
803
+ async #storeBroadcastedIndividualFunctions(allLogs: UnencryptedL2Log[], _blockNum: number) {
804
+ // Filter out private and unconstrained function broadcast events
805
+ const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(
806
+ allLogs,
807
+ ProtocolContractAddress.ContractClassRegisterer,
808
+ );
809
+ const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent.fromLogs(
810
+ allLogs,
811
+ ProtocolContractAddress.ContractClassRegisterer,
812
+ );
813
+
814
+ // Group all events by contract class id
815
+ for (const [classIdString, classEvents] of Object.entries(
816
+ groupBy([...privateFnEvents, ...unconstrainedFnEvents], e => e.contractClassId.toString()),
817
+ )) {
818
+ const contractClassId = Fr.fromString(classIdString);
819
+ const contractClass = await this.getContractClass(contractClassId);
820
+ if (!contractClass) {
821
+ this.#log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`);
822
+ continue;
823
+ }
824
+
825
+ // Split private and unconstrained functions, and filter out invalid ones
826
+ const allFns = classEvents.map(e => e.toFunctionWithMembershipProof());
827
+ const privateFns = allFns.filter(
828
+ (fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'unconstrainedFunctionsArtifactTreeRoot' in fn,
829
+ );
830
+ const unconstrainedFns = allFns.filter(
831
+ (fn): fn is UnconstrainedFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn,
832
+ );
833
+ const validPrivateFns = privateFns.filter(fn => isValidPrivateFunctionMembershipProof(fn, contractClass));
834
+ const validUnconstrainedFns = unconstrainedFns.filter(fn =>
835
+ isValidUnconstrainedFunctionMembershipProof(fn, contractClass),
836
+ );
837
+ const validFnCount = validPrivateFns.length + validUnconstrainedFns.length;
838
+ if (validFnCount !== allFns.length) {
839
+ this.#log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`);
840
+ }
841
+
842
+ // Store the functions in the contract class in a single operation
843
+ if (validFnCount > 0) {
844
+ this.#log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
845
+ }
846
+ return await this.store.addFunctions(contractClassId, validPrivateFns, validUnconstrainedFns);
847
+ }
848
+ return true;
849
+ }
850
+
851
+ async addBlocks(blocks: L1Published<L2Block>[]): Promise<boolean> {
852
+ return [
853
+ this.store.addLogs(blocks.map(block => block.data)),
854
+ // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
855
+ ...(await Promise.all(
856
+ blocks.map(async block => {
857
+ const blockLogs = block.data.body.txEffects
858
+ .flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
859
+ .flatMap(txLog => txLog.unrollLogs());
860
+
861
+ return (
862
+ await Promise.all([
863
+ this.#updateRegisteredContractClasses(blockLogs, block.data.number, Operation.Store),
864
+ this.#updateDeployedContractInstances(blockLogs, block.data.number, Operation.Store),
865
+ this.#storeBroadcastedIndividualFunctions(blockLogs, block.data.number),
866
+ ])
867
+ ).every(Boolean);
868
+ }),
869
+ )),
870
+ this.store.addBlocks(blocks),
871
+ ].every(Boolean);
872
+ }
873
+
874
+ async unwindBlocks(from: number, blocksToUnwind: number): Promise<boolean> {
875
+ const last = await this.getSynchedL2BlockNumber();
876
+ if (from != last) {
877
+ throw new Error(`Can only remove from the tip`);
878
+ }
879
+
880
+ // from - blocksToUnwind = the new head, so + 1 for what we need to remove
881
+ const blocks = await this.getBlocks(from - blocksToUnwind + 1, blocksToUnwind);
882
+
883
+ return [
884
+ // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
885
+ ...(await Promise.all(
886
+ blocks.map(async block => {
887
+ const blockLogs = block.data.body.txEffects
888
+ .flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
889
+ .flatMap(txLog => txLog.unrollLogs());
890
+ await this.#updateRegisteredContractClasses(blockLogs, block.data.number, Operation.Delete);
891
+ await this.#updateDeployedContractInstances(blockLogs, block.data.number, Operation.Delete);
892
+ }),
893
+ )),
894
+ this.store.deleteLogs(blocks.map(b => b.data)),
895
+ this.store.unwindBlocks(from, blocksToUnwind),
896
+ ].every(Boolean);
897
+ }
898
+
899
+ getBlocks(from: number, limit: number): Promise<L1Published<L2Block>[]> {
900
+ return this.store.getBlocks(from, limit);
901
+ }
902
+ getBlockHeaders(from: number, limit: number): Promise<Header[]> {
903
+ return this.store.getBlockHeaders(from, limit);
904
+ }
905
+ getTxEffect(txHash: TxHash): Promise<TxEffect | undefined> {
906
+ return this.store.getTxEffect(txHash);
907
+ }
908
+ getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
909
+ return this.store.getSettledTxReceipt(txHash);
910
+ }
911
+ addL1ToL2Messages(messages: DataRetrieval<InboxLeaf>): Promise<boolean> {
912
+ return this.store.addL1ToL2Messages(messages);
913
+ }
914
+ getL1ToL2Messages(blockNumber: bigint): Promise<Fr[]> {
915
+ return this.store.getL1ToL2Messages(blockNumber);
916
+ }
917
+ getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise<bigint | undefined> {
918
+ return this.store.getL1ToL2MessageIndex(l1ToL2Message, startIndex);
919
+ }
920
+ getLogs<TLogType extends LogType>(
921
+ from: number,
922
+ limit: number,
923
+ logType: TLogType,
924
+ ): Promise<L2BlockL2Logs<FromLogType<TLogType>>[]> {
925
+ return this.store.getLogs(from, limit, logType);
926
+ }
927
+ getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
928
+ return this.store.getUnencryptedLogs(filter);
929
+ }
930
+ getSynchedL2BlockNumber(): Promise<number> {
931
+ return this.store.getSynchedL2BlockNumber();
932
+ }
933
+ getProvenL2BlockNumber(): Promise<number> {
934
+ return this.store.getProvenL2BlockNumber();
935
+ }
936
+ getProvenL2EpochNumber(): Promise<number | undefined> {
937
+ return this.store.getProvenL2EpochNumber();
938
+ }
939
+ setProvenL2BlockNumber(l2BlockNumber: number): Promise<void> {
940
+ return this.store.setProvenL2BlockNumber(l2BlockNumber);
941
+ }
942
+ setProvenL2EpochNumber(l2EpochNumber: number): Promise<void> {
943
+ return this.store.setProvenL2EpochNumber(l2EpochNumber);
944
+ }
945
+ setBlockSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
946
+ return this.store.setBlockSynchedL1BlockNumber(l1BlockNumber);
947
+ }
948
+ setMessageSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
949
+ return this.store.setMessageSynchedL1BlockNumber(l1BlockNumber);
950
+ }
951
+ getSynchPoint(): Promise<ArchiverL1SynchPoint> {
952
+ return this.store.getSynchPoint();
953
+ }
954
+ getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
955
+ return this.store.getContractClass(id);
956
+ }
957
+ getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
958
+ return this.store.getContractInstance(address);
959
+ }
960
+ getContractClassIds(): Promise<Fr[]> {
961
+ return this.store.getContractClassIds();
962
+ }
963
+ addContractArtifact(address: AztecAddress, contract: ContractArtifact): Promise<void> {
964
+ return this.store.addContractArtifact(address, contract);
965
+ }
966
+ getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
967
+ return this.store.getContractArtifact(address);
968
+ }
969
+ getTotalL1ToL2MessageCount(): Promise<bigint> {
970
+ return this.store.getTotalL1ToL2MessageCount();
971
+ }
972
+ }
973
+
974
+ type L1RollupConstants = {
975
+ l1StartBlock: bigint;
976
+ l1GenesisTime: bigint;
977
+ };
978
+
979
+ const EmptyL1RollupConstants: L1RollupConstants = {
980
+ l1StartBlock: 0n,
981
+ l1GenesisTime: 0n,
982
+ };