@aztec/archiver 0.55.1 → 0.57.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 (78) hide show
  1. package/README.md +1 -1
  2. package/dest/archiver/archiver.d.ts +27 -25
  3. package/dest/archiver/archiver.d.ts.map +1 -1
  4. package/dest/archiver/archiver.js +391 -169
  5. package/dest/archiver/archiver_store.d.ts +47 -23
  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 +75 -42
  9. package/dest/archiver/config.js +6 -6
  10. package/dest/archiver/data_retrieval.d.ts +32 -5
  11. package/dest/archiver/data_retrieval.d.ts.map +1 -1
  12. package/dest/archiver/data_retrieval.js +126 -16
  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 +22 -3
  17. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  18. package/dest/archiver/kv_archiver_store/block_store.js +75 -12
  19. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +2 -1
  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 +11 -4
  22. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +1 -0
  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 +4 -1
  25. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +31 -23
  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 +65 -38
  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 -0
  32. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  33. package/dest/archiver/kv_archiver_store/message_store.js +18 -8
  34. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +1 -0
  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 +4 -1
  37. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +23 -39
  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 +132 -91
  40. package/dest/index.d.ts +0 -1
  41. package/dest/index.d.ts.map +1 -1
  42. package/dest/index.js +2 -2
  43. package/dest/test/index.d.ts +2 -0
  44. package/dest/test/index.d.ts.map +1 -0
  45. package/dest/test/index.js +2 -0
  46. package/dest/test/mock_l2_block_source.d.ts +73 -0
  47. package/dest/test/mock_l2_block_source.d.ts.map +1 -0
  48. package/dest/test/mock_l2_block_source.js +134 -0
  49. package/package.json +15 -11
  50. package/src/archiver/archiver.ts +531 -248
  51. package/src/archiver/archiver_store.ts +53 -31
  52. package/src/archiver/archiver_store_test_suite.ts +93 -81
  53. package/src/archiver/config.ts +5 -5
  54. package/src/archiver/data_retrieval.ts +189 -30
  55. package/src/archiver/epoch_helpers.ts +26 -0
  56. package/src/archiver/kv_archiver_store/block_store.ts +87 -12
  57. package/src/archiver/kv_archiver_store/contract_class_store.ts +18 -5
  58. package/src/archiver/kv_archiver_store/contract_instance_store.ts +4 -0
  59. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +74 -47
  60. package/src/archiver/kv_archiver_store/log_store.ts +18 -18
  61. package/src/archiver/kv_archiver_store/message_store.ts +18 -5
  62. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +4 -0
  63. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +155 -108
  64. package/src/index.ts +1 -2
  65. package/src/test/index.ts +1 -0
  66. package/src/test/mock_l2_block_source.ts +165 -0
  67. package/dest/archiver/eth_log_handlers.d.ts +0 -59
  68. package/dest/archiver/eth_log_handlers.d.ts.map +0 -1
  69. package/dest/archiver/eth_log_handlers.js +0 -155
  70. package/dest/archiver/kv_archiver_store/block_body_store.d.ts +0 -34
  71. package/dest/archiver/kv_archiver_store/block_body_store.d.ts.map +0 -1
  72. package/dest/archiver/kv_archiver_store/block_body_store.js +0 -65
  73. package/dest/archiver/kv_archiver_store/proven_store.d.ts +0 -14
  74. package/dest/archiver/kv_archiver_store/proven_store.d.ts.map +0 -1
  75. package/dest/archiver/kv_archiver_store/proven_store.js +0 -30
  76. package/src/archiver/eth_log_handlers.ts +0 -213
  77. package/src/archiver/kv_archiver_store/block_body_store.ts +0 -74
  78. package/src/archiver/kv_archiver_store/proven_store.ts +0 -34
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  type FromLogType,
3
3
  type GetUnencryptedLogsResponse,
4
+ type InboxLeaf,
4
5
  type L1ToL2MessageSource,
5
6
  type L2Block,
6
7
  type L2BlockL2Logs,
@@ -13,24 +14,25 @@ import {
13
14
  type TxReceipt,
14
15
  type UnencryptedL2Log,
15
16
  } from '@aztec/circuit-types';
16
- import { ContractClassRegisteredEvent, type FunctionSelector } from '@aztec/circuits.js';
17
17
  import {
18
+ ContractClassRegisteredEvent,
18
19
  ContractInstanceDeployedEvent,
20
+ type FunctionSelector,
21
+ type Header,
19
22
  PrivateFunctionBroadcastedEvent,
20
23
  UnconstrainedFunctionBroadcastedEvent,
21
24
  isValidPrivateFunctionMembershipProof,
22
25
  isValidUnconstrainedFunctionMembershipProof,
23
- } from '@aztec/circuits.js/contract';
26
+ } from '@aztec/circuits.js';
24
27
  import { createEthereumChain } from '@aztec/ethereum';
25
28
  import { type ContractArtifact } from '@aztec/foundation/abi';
26
29
  import { type AztecAddress } from '@aztec/foundation/aztec-address';
27
- import { compactArray, unique } from '@aztec/foundation/collection';
28
30
  import { type EthAddress } from '@aztec/foundation/eth-address';
29
31
  import { Fr } from '@aztec/foundation/fields';
30
32
  import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
31
33
  import { RunningPromise } from '@aztec/foundation/running-promise';
32
34
  import { Timer } from '@aztec/foundation/timer';
33
- import { RollupAbi } from '@aztec/l1-artifacts';
35
+ import { InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
34
36
  import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer';
35
37
  import { type TelemetryClient } from '@aztec/telemetry-client';
36
38
  import {
@@ -43,14 +45,28 @@ import {
43
45
  } from '@aztec/types/contracts';
44
46
 
45
47
  import groupBy from 'lodash.groupby';
46
- import { type Chain, type HttpTransport, type PublicClient, createPublicClient, getContract, http } from 'viem';
47
-
48
- import { type ArchiverDataStore } from './archiver_store.js';
48
+ import {
49
+ type Chain,
50
+ type GetContractReturnType,
51
+ type HttpTransport,
52
+ type PublicClient,
53
+ createPublicClient,
54
+ getContract,
55
+ http,
56
+ } from 'viem';
57
+
58
+ import { type ArchiverDataStore, type ArchiverL1SynchPoint } from './archiver_store.js';
49
59
  import { type ArchiverConfig } from './config.js';
50
- import { retrieveBlockFromRollup, retrieveL1ToL2Messages, retrieveL2ProofVerifiedEvents } from './data_retrieval.js';
51
- import { getL1BlockTime } from './eth_log_handlers.js';
60
+ import { retrieveBlockFromRollup, retrieveL1ToL2Messages } from './data_retrieval.js';
61
+ import {
62
+ getEpochNumberAtTimestamp,
63
+ getSlotAtTimestamp,
64
+ getSlotRangeForEpoch,
65
+ getTimestampRangeForEpoch,
66
+ } from './epoch_helpers.js';
52
67
  import { ArchiverInstrumentation } from './instrumentation.js';
53
- 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';
54
70
 
55
71
  /**
56
72
  * Helper interface to combine all sources this archiver implementation provides.
@@ -68,6 +84,14 @@ export class Archiver implements ArchiveSource {
68
84
  */
69
85
  private runningPromise?: RunningPromise;
70
86
 
87
+ private rollup: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, Chain>>;
88
+ private inbox: GetContractReturnType<typeof InboxAbi, PublicClient<HttpTransport, Chain>>;
89
+
90
+ private store: ArchiverStoreHelper;
91
+
92
+ public l1BlockNumber: bigint | undefined;
93
+ public l1Timestamp: bigint | undefined;
94
+
71
95
  /**
72
96
  * Creates a new instance of the Archiver.
73
97
  * @param publicClient - A client for interacting with the Ethereum node.
@@ -81,14 +105,28 @@ export class Archiver implements ArchiveSource {
81
105
  constructor(
82
106
  private readonly publicClient: PublicClient<HttpTransport, Chain>,
83
107
  private readonly rollupAddress: EthAddress,
84
- private readonly inboxAddress: EthAddress,
108
+ readonly inboxAddress: EthAddress,
85
109
  private readonly registryAddress: EthAddress,
86
- private readonly store: ArchiverDataStore,
87
- private readonly pollingIntervalMs = 10_000,
110
+ readonly dataStore: ArchiverDataStore,
111
+ private readonly pollingIntervalMs: number,
88
112
  private readonly instrumentation: ArchiverInstrumentation,
89
- private readonly l1StartBlock: bigint = 0n,
113
+ private readonly l1constants: L1RollupConstants = EmptyL1RollupConstants,
90
114
  private readonly log: DebugLogger = createDebugLogger('aztec:archiver'),
91
- ) {}
115
+ ) {
116
+ this.store = new ArchiverStoreHelper(dataStore);
117
+
118
+ this.rollup = getContract({
119
+ address: rollupAddress.toString(),
120
+ abi: RollupAbi,
121
+ client: publicClient,
122
+ });
123
+
124
+ this.inbox = getContract({
125
+ address: inboxAddress.toString(),
126
+ abi: InboxAbi,
127
+ client: publicClient,
128
+ });
129
+ }
92
130
 
93
131
  /**
94
132
  * Creates a new instance of the Archiver and blocks until it syncs from chain.
@@ -116,7 +154,10 @@ export class Archiver implements ArchiveSource {
116
154
  client: publicClient,
117
155
  });
118
156
 
119
- 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);
120
161
 
121
162
  const archiver = new Archiver(
122
163
  publicClient,
@@ -124,9 +165,9 @@ export class Archiver implements ArchiveSource {
124
165
  config.l1Contracts.inboxAddress,
125
166
  config.l1Contracts.registryAddress,
126
167
  archiverStore,
127
- config.archiverPollingIntervalMS,
168
+ config.archiverPollingIntervalMS ?? 10_000,
128
169
  new ArchiverInstrumentation(telemetry),
129
- BigInt(l1StartBlock),
170
+ { l1StartBlock, l1GenesisTime },
130
171
  );
131
172
  await archiver.start(blockUntilSynced);
132
173
  return archiver;
@@ -178,32 +219,10 @@ export class Archiver implements ArchiveSource {
178
219
  *
179
220
  * This code does not handle reorgs.
180
221
  */
181
- const {
182
- blockBodiesSynchedTo = this.l1StartBlock,
183
- blocksSynchedTo = this.l1StartBlock,
184
- messagesSynchedTo = this.l1StartBlock,
185
- provenLogsSynchedTo = this.l1StartBlock,
186
- } = await this.store.getSynchPoint();
222
+ const { l1StartBlock } = this.l1constants;
223
+ const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = l1StartBlock } = await this.store.getSynchPoint();
187
224
  const currentL1BlockNumber = await this.publicClient.getBlockNumber();
188
225
 
189
- if (
190
- currentL1BlockNumber <= blocksSynchedTo &&
191
- currentL1BlockNumber <= messagesSynchedTo &&
192
- currentL1BlockNumber <= blockBodiesSynchedTo &&
193
- currentL1BlockNumber <= provenLogsSynchedTo
194
- ) {
195
- // chain hasn't moved forward
196
- // or it's been rolled back
197
- this.log.debug(`Nothing to sync`, {
198
- currentL1BlockNumber,
199
- blocksSynchedTo,
200
- messagesSynchedTo,
201
- provenLogsSynchedTo,
202
- blockBodiesSynchedTo,
203
- });
204
- return;
205
- }
206
-
207
226
  // ********** Ensuring Consistency of data pulled from L1 **********
208
227
 
209
228
  /**
@@ -224,245 +243,181 @@ export class Archiver implements ArchiveSource {
224
243
  */
225
244
 
226
245
  // ********** Events that are processed per L1 block **********
246
+ await this.handleL1ToL2Messages(blockUntilSynced, messagesSynchedTo, currentL1BlockNumber);
227
247
 
228
248
  // ********** Events that are processed per L2 block **********
249
+ await this.handleL2blocks(blockUntilSynced, blocksSynchedTo, currentL1BlockNumber);
229
250
 
230
- const retrievedL1ToL2Messages = await retrieveL1ToL2Messages(
231
- this.publicClient,
232
- this.inboxAddress,
233
- blockUntilSynced,
234
- messagesSynchedTo + 1n,
235
- currentL1BlockNumber,
236
- );
251
+ // Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
252
+ if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
253
+ this.l1Timestamp = await this.publicClient.getBlock({ blockNumber: currentL1BlockNumber }).then(b => b.timestamp);
254
+ this.l1BlockNumber = currentL1BlockNumber;
255
+ }
256
+ }
237
257
 
238
- if (retrievedL1ToL2Messages.retrievedData.length !== 0) {
239
- this.log.verbose(
240
- `Retrieved ${retrievedL1ToL2Messages.retrievedData.length} new L1 -> L2 messages between L1 blocks ${
241
- messagesSynchedTo + 1n
242
- } and ${currentL1BlockNumber}.`,
243
- );
258
+ private async handleL1ToL2Messages(
259
+ blockUntilSynced: boolean,
260
+ messagesSynchedTo: bigint,
261
+ currentL1BlockNumber: bigint,
262
+ ) {
263
+ if (currentL1BlockNumber <= messagesSynchedTo) {
264
+ return;
244
265
  }
245
266
 
246
- await this.store.addL1ToL2Messages(retrievedL1ToL2Messages);
267
+ const localTotalMessageCount = await this.store.getTotalL1ToL2MessageCount();
268
+ const destinationTotalMessageCount = await this.inbox.read.totalMessagesInserted();
247
269
 
248
- // Read all data from chain and then write to our stores at the end
249
- const nextExpectedL2BlockNum = BigInt((await this.store.getSynchedL2BlockNumber()) + 1);
270
+ if (localTotalMessageCount === destinationTotalMessageCount) {
271
+ await this.store.setMessageSynchedL1BlockNumber(currentL1BlockNumber);
272
+ this.log.verbose(
273
+ `Retrieved no new L1 -> L2 messages between L1 blocks ${messagesSynchedTo + 1n} and ${currentL1BlockNumber}.`,
274
+ );
275
+ return;
276
+ }
250
277
 
251
- this.log.debug(`Retrieving blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
252
- const retrievedBlocks = await retrieveBlockFromRollup(
253
- this.publicClient,
254
- this.rollupAddress,
278
+ const retrievedL1ToL2Messages = await retrieveL1ToL2Messages(
279
+ this.inbox,
255
280
  blockUntilSynced,
256
- blocksSynchedTo + 1n,
281
+ messagesSynchedTo + 1n,
257
282
  currentL1BlockNumber,
258
- nextExpectedL2BlockNum,
259
283
  );
260
284
 
261
- // Add the body
285
+ await this.store.addL1ToL2Messages(retrievedL1ToL2Messages);
262
286
 
263
- (retrievedBlocks.length ? this.log.verbose : this.log.debug)(
264
- `Retrieved ${retrievedBlocks.length || 'no'} new L2 blocks between L1 blocks ${
265
- blocksSynchedTo + 1n
287
+ this.log.verbose(
288
+ `Retrieved ${retrievedL1ToL2Messages.retrievedData.length} new L1 -> L2 messages between L1 blocks ${
289
+ messagesSynchedTo + 1n
266
290
  } and ${currentL1BlockNumber}.`,
267
291
  );
292
+ }
268
293
 
269
- const lastProcessedL1BlockNumber =
270
- retrievedBlocks.length > 0 ? retrievedBlocks[retrievedBlocks.length - 1].l1.blockNumber : blocksSynchedTo;
294
+ private async handleL2blocks(blockUntilSynced: boolean, blocksSynchedTo: bigint, currentL1BlockNumber: bigint) {
295
+ if (currentL1BlockNumber <= blocksSynchedTo) {
296
+ return;
297
+ }
271
298
 
272
- this.log.debug(
273
- `Processing retrieved blocks ${retrievedBlocks
274
- .map(b => b.data.number)
275
- .join(',')} with last processed L1 block ${lastProcessedL1BlockNumber}`,
276
- );
299
+ const localPendingBlockNumber = BigInt(await this.getBlockNumber());
300
+ const [
301
+ provenBlockNumber,
302
+ provenArchive,
303
+ pendingBlockNumber,
304
+ pendingArchive,
305
+ archiveForLocalPendingBlockNumber,
306
+ provenEpochNumber,
307
+ ] = await this.rollup.read.status([localPendingBlockNumber]);
308
+
309
+ const updateProvenBlock = async () => {
310
+ const localBlockForDestinationProvenBlockNumber = await this.getBlock(Number(provenBlockNumber));
311
+ if (
312
+ localBlockForDestinationProvenBlockNumber &&
313
+ provenArchive === localBlockForDestinationProvenBlockNumber.archive.root.toString()
314
+ ) {
315
+ this.log.info(`Updating the proven block number to ${provenBlockNumber} and epoch to ${provenEpochNumber}`);
316
+ await this.store.setProvenL2BlockNumber(Number(provenBlockNumber));
317
+ // if we are here then we must have a valid proven epoch number
318
+ await this.store.setProvenL2EpochNumber(Number(provenEpochNumber));
319
+ }
320
+ };
321
+
322
+ // This is an edge case that we only hit if there are no proposed blocks.
323
+ // If we have 0 blocks locally and there are no blocks onchain there is nothing to do.
324
+ const noBlocks = localPendingBlockNumber === 0n && pendingBlockNumber === 0n;
325
+ if (noBlocks) {
326
+ await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
327
+ this.log.verbose(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
328
+ return;
329
+ }
277
330
 
278
- if (retrievedBlocks.length > 0) {
279
- await Promise.all(
280
- retrievedBlocks.map(block => {
281
- const noteEncryptedLogs = block.data.body.noteEncryptedLogs;
282
- const encryptedLogs = block.data.body.encryptedLogs;
283
- const unencryptedLogs = block.data.body.unencryptedLogs;
284
- return this.store.addLogs(noteEncryptedLogs, encryptedLogs, unencryptedLogs, block.data.number);
285
- }),
286
- );
331
+ await updateProvenBlock();
287
332
 
288
- // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
289
- await Promise.all(
290
- retrievedBlocks.map(async block => {
291
- const blockLogs = block.data.body.txEffects
292
- .flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
293
- .flatMap(txLog => txLog.unrollLogs());
294
- await this.storeRegisteredContractClasses(blockLogs, block.data.number);
295
- await this.storeDeployedContractInstances(blockLogs, block.data.number);
296
- await this.storeBroadcastedIndividualFunctions(blockLogs, block.data.number);
297
- }),
298
- );
333
+ // Related to the L2 reorgs of the pending chain. We are only interested in actually addressing a reorg if there
334
+ // are any state that could be impacted by it. If we have no blocks, there is no impact.
335
+ if (localPendingBlockNumber > 0) {
336
+ const localPendingBlock = await this.getBlock(Number(localPendingBlockNumber));
337
+ if (localPendingBlock === undefined) {
338
+ throw new Error(`Missing block ${localPendingBlockNumber}`);
339
+ }
299
340
 
300
- const timer = new Timer();
301
- await this.store.addBlockBodies({
302
- lastProcessedL1BlockNumber: lastProcessedL1BlockNumber,
303
- retrievedData: retrievedBlocks.map(b => b.data.body),
304
- });
305
- await this.store.addBlocks(retrievedBlocks);
306
- this.instrumentation.processNewBlocks(
307
- timer.ms() / retrievedBlocks.length,
308
- retrievedBlocks.map(b => b.data),
309
- );
310
- const lastL2BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].data.number;
311
- this.log.verbose(`Processed ${retrievedBlocks.length} new L2 blocks up to ${lastL2BlockNumber}`);
312
- }
341
+ const noBlockSinceLast = localPendingBlock && pendingArchive === localPendingBlock.archive.root.toString();
342
+ if (noBlockSinceLast) {
343
+ await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
344
+ this.log.verbose(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
345
+ return;
346
+ }
313
347
 
314
- // Fetch the logs for proven blocks in the block range and update the last proven block number.
315
- if (currentL1BlockNumber > provenLogsSynchedTo) {
316
- await this.updateLastProvenL2Block(provenLogsSynchedTo + 1n, currentL1BlockNumber);
317
- }
348
+ const localPendingBlockInChain = archiveForLocalPendingBlockNumber === localPendingBlock.archive.root.toString();
349
+ if (!localPendingBlockInChain) {
350
+ // If our local pending block tip is not in the chain on L1 a "prune" must have happened
351
+ // or the L1 have reorged.
352
+ // In any case, we have to figure out how far into the past the action will take us.
353
+ // For simplicity here, we will simply rewind until we end in a block that is also on the chain on L1.
354
+ this.log.verbose(`L2 prune have occurred, unwind state`);
355
+
356
+ let tipAfterUnwind = localPendingBlockNumber;
357
+ while (true) {
358
+ const candidateBlock = await this.getBlock(Number(tipAfterUnwind));
359
+ if (candidateBlock === undefined) {
360
+ break;
361
+ }
318
362
 
319
- if (retrievedBlocks.length > 0 || blockUntilSynced) {
320
- (blockUntilSynced ? this.log.info : this.log.verbose)(`Synced to L1 block ${currentL1BlockNumber}`);
321
- }
322
- }
363
+ const archiveAtContract = await this.rollup.read.archiveAt([BigInt(candidateBlock.number)]);
323
364
 
324
- private async updateLastProvenL2Block(fromBlock: bigint, toBlock: bigint) {
325
- const logs = await retrieveL2ProofVerifiedEvents(this.publicClient, this.rollupAddress, fromBlock, toBlock);
326
- const lastLog = logs[logs.length - 1];
327
- if (!lastLog) {
328
- return;
329
- }
365
+ if (archiveAtContract === candidateBlock.archive.root.toString()) {
366
+ break;
367
+ }
368
+ tipAfterUnwind--;
369
+ }
330
370
 
331
- const provenBlockNumber = lastLog.l2BlockNumber;
332
- if (!provenBlockNumber) {
333
- throw new Error(`Missing argument blockNumber from L2ProofVerified event`);
334
- }
371
+ const blocksToUnwind = localPendingBlockNumber - tipAfterUnwind;
372
+ this.log.verbose(
373
+ `Unwinding ${blocksToUnwind} block${blocksToUnwind > 1n ? 's' : ''} from block ${localPendingBlockNumber}`,
374
+ );
335
375
 
336
- await this.emitProofVerifiedMetrics(logs);
337
-
338
- const currentProvenBlockNumber = await this.store.getProvenL2BlockNumber();
339
- if (provenBlockNumber > currentProvenBlockNumber) {
340
- // Update the last proven block number
341
- this.log.verbose(`Updated last proven block number from ${currentProvenBlockNumber} to ${provenBlockNumber}`);
342
- await this.store.setProvenL2BlockNumber({
343
- retrievedData: Number(provenBlockNumber),
344
- lastProcessedL1BlockNumber: lastLog.l1BlockNumber,
345
- });
346
- this.instrumentation.updateLastProvenBlock(Number(provenBlockNumber));
347
- } else {
348
- // We set the last processed L1 block number to the last L1 block number in the range to avoid duplicate processing
349
- await this.store.setProvenL2BlockNumber({
350
- retrievedData: Number(currentProvenBlockNumber),
351
- lastProcessedL1BlockNumber: lastLog.l1BlockNumber,
352
- });
376
+ await this.store.unwindBlocks(Number(localPendingBlockNumber), Number(blocksToUnwind));
377
+ }
353
378
  }
354
- }
355
379
 
356
- /**
357
- * Emits as metrics the block number proven, who proved it, and how much time passed since it was submitted.
358
- * @param logs - The ProofVerified logs to emit metrics for, as collected from `retrieveL2ProofVerifiedEvents`.
359
- **/
360
- private async emitProofVerifiedMetrics(logs: { l1BlockNumber: bigint; l2BlockNumber: bigint; proverId: Fr }[]) {
361
- if (!logs.length || !this.instrumentation.isEnabled()) {
380
+ this.log.debug(`Retrieving blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
381
+ const retrievedBlocks = await retrieveBlockFromRollup(
382
+ this.rollup,
383
+ this.publicClient,
384
+ blockUntilSynced,
385
+ blocksSynchedTo + 1n,
386
+ currentL1BlockNumber,
387
+ this.log,
388
+ );
389
+
390
+ if (retrievedBlocks.length === 0) {
391
+ // We are not calling `setBlockSynchedL1BlockNumber` because it may cause sync issues if based off infura.
392
+ // See further details in earlier comments.
393
+ this.log.verbose(`Retrieved no new blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
362
394
  return;
363
395
  }
364
396
 
365
- const l1BlockTimes = new Map(
366
- await Promise.all(
367
- unique(logs.map(log => log.l1BlockNumber)).map(
368
- async blockNumber => [blockNumber, await getL1BlockTime(this.publicClient, blockNumber)] as const,
369
- ),
370
- ),
397
+ this.log.debug(
398
+ `Retrieved ${retrievedBlocks.length} new L2 blocks between L1 blocks ${
399
+ blocksSynchedTo + 1n
400
+ } and ${currentL1BlockNumber}.`,
371
401
  );
372
402
 
373
- // Collect L2 block times for all the blocks verified, this is the time in which the block proven was
374
- // originally submitted to L1, using the L1 timestamp of the transaction.
375
- const getL2BlockTime = async (blockNumber: bigint) =>
376
- (await this.store.getBlocks(Number(blockNumber), 1))[0]?.l1.timestamp;
377
-
378
- const l2BlockTimes = new Map(
379
- await Promise.all(
380
- unique(logs.map(log => log.l2BlockNumber)).map(
381
- async blockNumber => [blockNumber, await getL2BlockTime(blockNumber)] as const,
382
- ),
383
- ),
384
- );
403
+ const lastProcessedL1BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].l1.blockNumber;
385
404
 
386
- // Emit the prover id and the time difference between block submission and proof.
387
- this.instrumentation.processProofsVerified(
388
- compactArray(
389
- logs.map(log => {
390
- const l1BlockTime = l1BlockTimes.get(log.l1BlockNumber)!;
391
- const l2BlockTime = l2BlockTimes.get(log.l2BlockNumber);
392
- if (!l2BlockTime) {
393
- return undefined;
394
- }
395
- return { ...log, delay: l1BlockTime - l2BlockTime, proverId: log.proverId.toString() };
396
- }),
397
- ),
405
+ this.log.debug(
406
+ `Processing retrieved blocks ${retrievedBlocks
407
+ .map(b => b.data.number)
408
+ .join(',')} with last processed L1 block ${lastProcessedL1BlockNumber}`,
398
409
  );
399
- }
400
410
 
401
- /**
402
- * Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract.
403
- * @param allLogs - All logs emitted in a bunch of blocks.
404
- */
405
- private async storeRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number) {
406
- const contractClasses = ContractClassRegisteredEvent.fromLogs(allLogs, ClassRegistererAddress).map(e =>
407
- e.toContractClassPublic(),
411
+ const timer = new Timer();
412
+ await this.store.addBlocks(retrievedBlocks);
413
+ // Important that we update AFTER inserting the blocks.
414
+ await updateProvenBlock();
415
+ this.instrumentation.processNewBlocks(
416
+ timer.ms() / retrievedBlocks.length,
417
+ retrievedBlocks.map(b => b.data),
408
418
  );
409
- if (contractClasses.length > 0) {
410
- contractClasses.forEach(c => this.log.verbose(`Registering contract class ${c.id.toString()}`));
411
- await this.store.addContractClasses(contractClasses, blockNum);
412
- }
413
- }
414
-
415
- /**
416
- * Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
417
- * @param allLogs - All logs emitted in a bunch of blocks.
418
- */
419
- private async storeDeployedContractInstances(allLogs: UnencryptedL2Log[], blockNum: number) {
420
- const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs).map(e => e.toContractInstance());
421
- if (contractInstances.length > 0) {
422
- contractInstances.forEach(c => this.log.verbose(`Storing contract instance at ${c.address.toString()}`));
423
- await this.store.addContractInstances(contractInstances, blockNum);
424
- }
425
- }
426
-
427
- private async storeBroadcastedIndividualFunctions(allLogs: UnencryptedL2Log[], _blockNum: number) {
428
- // Filter out private and unconstrained function broadcast events
429
- const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
430
- const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
431
-
432
- // Group all events by contract class id
433
- for (const [classIdString, classEvents] of Object.entries(
434
- groupBy([...privateFnEvents, ...unconstrainedFnEvents], e => e.contractClassId.toString()),
435
- )) {
436
- const contractClassId = Fr.fromString(classIdString);
437
- const contractClass = await this.store.getContractClass(contractClassId);
438
- if (!contractClass) {
439
- this.log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`);
440
- continue;
441
- }
442
-
443
- // Split private and unconstrained functions, and filter out invalid ones
444
- const allFns = classEvents.map(e => e.toFunctionWithMembershipProof());
445
- const privateFns = allFns.filter(
446
- (fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'unconstrainedFunctionsArtifactTreeRoot' in fn,
447
- );
448
- const unconstrainedFns = allFns.filter(
449
- (fn): fn is UnconstrainedFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn,
450
- );
451
- const validPrivateFns = privateFns.filter(fn => isValidPrivateFunctionMembershipProof(fn, contractClass));
452
- const validUnconstrainedFns = unconstrainedFns.filter(fn =>
453
- isValidUnconstrainedFunctionMembershipProof(fn, contractClass),
454
- );
455
- const validFnCount = validPrivateFns.length + validUnconstrainedFns.length;
456
- if (validFnCount !== allFns.length) {
457
- this.log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`);
458
- }
459
-
460
- // Store the functions in the contract class in a single operation
461
- if (validFnCount > 0) {
462
- this.log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
463
- }
464
- await this.store.addFunctions(contractClassId, validPrivateFns, validUnconstrainedFns);
465
- }
419
+ const lastL2BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].data.number;
420
+ this.log.verbose(`Processed ${retrievedBlocks.length} new L2 blocks up to ${lastL2BlockNumber}`);
466
421
  }
467
422
 
468
423
  /**
@@ -485,6 +440,68 @@ export class Archiver implements ArchiveSource {
485
440
  return Promise.resolve(this.registryAddress);
486
441
  }
487
442
 
443
+ public getL1BlockNumber(): bigint {
444
+ const l1BlockNumber = this.l1BlockNumber;
445
+ if (!l1BlockNumber) {
446
+ throw new Error('L1 block number not yet available. Complete an initial sync first.');
447
+ }
448
+ return l1BlockNumber;
449
+ }
450
+
451
+ public getL1Timestamp(): bigint {
452
+ const l1Timestamp = this.l1Timestamp;
453
+ if (!l1Timestamp) {
454
+ throw new Error('L1 timestamp not yet available. Complete an initial sync first.');
455
+ }
456
+ return l1Timestamp;
457
+ }
458
+
459
+ public getL2SlotNumber(): Promise<bigint> {
460
+ return Promise.resolve(getSlotAtTimestamp(this.getL1Timestamp(), this.l1constants));
461
+ }
462
+
463
+ public getL2EpochNumber(): Promise<bigint> {
464
+ return Promise.resolve(getEpochNumberAtTimestamp(this.getL1Timestamp(), this.l1constants));
465
+ }
466
+
467
+ public async getBlocksForEpoch(epochNumber: bigint): Promise<L2Block[]> {
468
+ const [start, end] = getSlotRangeForEpoch(epochNumber);
469
+ const blocks: L2Block[] = [];
470
+
471
+ // Walk the list of blocks backwards and filter by slots matching the requested epoch.
472
+ // We'll typically ask for blocks for a very recent epoch, so we shouldn't need an index here.
473
+ let block = await this.getBlock(await this.store.getSynchedL2BlockNumber());
474
+ const slot = (b: L2Block) => b.header.globalVariables.slotNumber.toBigInt();
475
+ while (block && slot(block) >= start) {
476
+ if (slot(block) <= end) {
477
+ blocks.push(block);
478
+ }
479
+ block = await this.getBlock(block.number - 1);
480
+ }
481
+
482
+ return blocks.reverse();
483
+ }
484
+
485
+ public async isEpochComplete(epochNumber: bigint): Promise<boolean> {
486
+ // The epoch is complete if the current L2 block is the last one in the epoch (or later)
487
+ const header = await this.getBlockHeader('latest');
488
+ const slot = header?.globalVariables.slotNumber.toBigInt();
489
+ const [_startSlot, endSlot] = getSlotRangeForEpoch(epochNumber);
490
+ if (slot && slot >= endSlot) {
491
+ return true;
492
+ }
493
+
494
+ // If not, the epoch may also be complete if the L2 slot has passed without a block
495
+ // We compute this based on the timestamp for the given epoch and the timestamp of the last L1 block
496
+ const l1Timestamp = this.getL1Timestamp();
497
+ const [_startTimestamp, endTimestamp] = getTimestampRangeForEpoch(epochNumber, this.l1constants);
498
+
499
+ // 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
501
+ const leeway = 3n;
502
+ return l1Timestamp + leeway >= endTimestamp;
503
+ }
504
+
488
505
  /**
489
506
  * Gets up to `limit` amount of L2 blocks starting from `from`.
490
507
  * @param from - Number of the first block to return (inclusive).
@@ -501,7 +518,7 @@ export class Archiver implements ArchiveSource {
501
518
 
502
519
  /**
503
520
  * Gets an l2 block.
504
- * @param number - The block number to return (inclusive).
521
+ * @param number - The block number to return.
505
522
  * @returns The requested L2 block.
506
523
  */
507
524
  public async getBlock(number: number): Promise<L2Block | undefined> {
@@ -509,10 +526,21 @@ export class Archiver implements ArchiveSource {
509
526
  if (number < 0) {
510
527
  number = await this.store.getSynchedL2BlockNumber();
511
528
  }
529
+ if (number == 0) {
530
+ return undefined;
531
+ }
512
532
  const blocks = await this.store.getBlocks(number, 1);
513
533
  return blocks.length === 0 ? undefined : blocks[0].data;
514
534
  }
515
535
 
536
+ public async getBlockHeader(number: number | 'latest'): Promise<Header | undefined> {
537
+ if (number === 'latest') {
538
+ number = await this.store.getSynchedL2BlockNumber();
539
+ }
540
+ const headers = await this.store.getBlockHeaders(number, 1);
541
+ return headers.length === 0 ? undefined : headers[0];
542
+ }
543
+
516
544
  public getTxEffect(txHash: TxHash): Promise<TxEffect | undefined> {
517
545
  return this.store.getTxEffect(txHash);
518
546
  }
@@ -578,9 +606,13 @@ export class Archiver implements ArchiveSource {
578
606
  return this.store.getProvenL2BlockNumber();
579
607
  }
580
608
 
609
+ public getProvenL2EpochNumber(): Promise<number | undefined> {
610
+ return this.store.getProvenL2EpochNumber();
611
+ }
612
+
581
613
  /** Forcefully updates the last proven block number. Use for testing. */
582
- public setProvenBlockNumber(block: SingletonDataRetrieval<number>): Promise<void> {
583
- return this.store.setProvenL2BlockNumber(block);
614
+ public setProvenBlockNumber(blockNumber: number): Promise<void> {
615
+ return this.store.setProvenL2BlockNumber(blockNumber);
584
616
  }
585
617
 
586
618
  public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
@@ -622,3 +654,254 @@ export class Archiver implements ArchiveSource {
622
654
  return this.store.getContractArtifact(address);
623
655
  }
624
656
  }
657
+
658
+ enum Operation {
659
+ Store,
660
+ Delete,
661
+ }
662
+
663
+ /**
664
+ * A helper class that we use to deal with some of the logic needed when adding blocks.
665
+ *
666
+ * I would have preferred to not have this type. But it is useful for handling the logic that any
667
+ * store would need to include otherwise while exposing fewer functions and logic directly to the archiver.
668
+ */
669
+ class ArchiverStoreHelper
670
+ implements
671
+ Omit<
672
+ ArchiverDataStore,
673
+ | 'addLogs'
674
+ | 'deleteLogs'
675
+ | 'addContractClasses'
676
+ | 'deleteContractClasses'
677
+ | 'addContractInstances'
678
+ | 'deleteContractInstances'
679
+ | 'addFunctions'
680
+ >
681
+ {
682
+ #log = createDebugLogger('aztec:archiver:block-helper');
683
+
684
+ constructor(private readonly store: ArchiverDataStore) {}
685
+
686
+ /**
687
+ * Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract.
688
+ * @param allLogs - All logs emitted in a bunch of blocks.
689
+ */
690
+ async #updateRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number, operation: Operation) {
691
+ const contractClasses = ContractClassRegisteredEvent.fromLogs(allLogs, ClassRegistererAddress).map(e =>
692
+ e.toContractClassPublic(),
693
+ );
694
+ if (contractClasses.length > 0) {
695
+ contractClasses.forEach(c => this.#log.verbose(`Registering contract class ${c.id.toString()}`));
696
+ if (operation == Operation.Store) {
697
+ return await this.store.addContractClasses(contractClasses, blockNum);
698
+ } else if (operation == Operation.Delete) {
699
+ return await this.store.deleteContractClasses(contractClasses, blockNum);
700
+ }
701
+ }
702
+ return true;
703
+ }
704
+
705
+ /**
706
+ * Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
707
+ * @param allLogs - All logs emitted in a bunch of blocks.
708
+ */
709
+ async #updateDeployedContractInstances(allLogs: UnencryptedL2Log[], blockNum: number, operation: Operation) {
710
+ const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs).map(e => e.toContractInstance());
711
+ if (contractInstances.length > 0) {
712
+ contractInstances.forEach(c =>
713
+ this.#log.verbose(`${Operation[operation]} contract instance at ${c.address.toString()}`),
714
+ );
715
+ if (operation == Operation.Store) {
716
+ return await this.store.addContractInstances(contractInstances, blockNum);
717
+ } else if (operation == Operation.Delete) {
718
+ return await this.store.deleteContractInstances(contractInstances, blockNum);
719
+ }
720
+ }
721
+ return true;
722
+ }
723
+
724
+ /**
725
+ * Stores the functions that was broadcasted individually
726
+ *
727
+ * @dev Beware that there is not a delete variant of this, since they are added to contract classes
728
+ * and will be deleted as part of the class if needed.
729
+ *
730
+ * @param allLogs - The logs from the block
731
+ * @param _blockNum - The block number
732
+ * @returns
733
+ */
734
+ async #storeBroadcastedIndividualFunctions(allLogs: UnencryptedL2Log[], _blockNum: number) {
735
+ // Filter out private and unconstrained function broadcast events
736
+ const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
737
+ const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress);
738
+
739
+ // Group all events by contract class id
740
+ for (const [classIdString, classEvents] of Object.entries(
741
+ groupBy([...privateFnEvents, ...unconstrainedFnEvents], e => e.contractClassId.toString()),
742
+ )) {
743
+ const contractClassId = Fr.fromString(classIdString);
744
+ const contractClass = await this.getContractClass(contractClassId);
745
+ if (!contractClass) {
746
+ this.#log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`);
747
+ continue;
748
+ }
749
+
750
+ // Split private and unconstrained functions, and filter out invalid ones
751
+ const allFns = classEvents.map(e => e.toFunctionWithMembershipProof());
752
+ const privateFns = allFns.filter(
753
+ (fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'unconstrainedFunctionsArtifactTreeRoot' in fn,
754
+ );
755
+ const unconstrainedFns = allFns.filter(
756
+ (fn): fn is UnconstrainedFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn,
757
+ );
758
+ const validPrivateFns = privateFns.filter(fn => isValidPrivateFunctionMembershipProof(fn, contractClass));
759
+ const validUnconstrainedFns = unconstrainedFns.filter(fn =>
760
+ isValidUnconstrainedFunctionMembershipProof(fn, contractClass),
761
+ );
762
+ const validFnCount = validPrivateFns.length + validUnconstrainedFns.length;
763
+ if (validFnCount !== allFns.length) {
764
+ this.#log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`);
765
+ }
766
+
767
+ // Store the functions in the contract class in a single operation
768
+ if (validFnCount > 0) {
769
+ this.#log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
770
+ }
771
+ return await this.store.addFunctions(contractClassId, validPrivateFns, validUnconstrainedFns);
772
+ }
773
+ return true;
774
+ }
775
+
776
+ async addBlocks(blocks: L1Published<L2Block>[]): Promise<boolean> {
777
+ return [
778
+ this.store.addLogs(blocks.map(block => block.data)),
779
+ // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
780
+ ...(await Promise.all(
781
+ blocks.map(async block => {
782
+ const blockLogs = block.data.body.txEffects
783
+ .flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
784
+ .flatMap(txLog => txLog.unrollLogs());
785
+
786
+ return (
787
+ await Promise.all([
788
+ this.#updateRegisteredContractClasses(blockLogs, block.data.number, Operation.Store),
789
+ this.#updateDeployedContractInstances(blockLogs, block.data.number, Operation.Store),
790
+ this.#storeBroadcastedIndividualFunctions(blockLogs, block.data.number),
791
+ ])
792
+ ).every(Boolean);
793
+ }),
794
+ )),
795
+ this.store.addBlocks(blocks),
796
+ ].every(Boolean);
797
+ }
798
+
799
+ async unwindBlocks(from: number, blocksToUnwind: number): Promise<boolean> {
800
+ const last = await this.getSynchedL2BlockNumber();
801
+ if (from != last) {
802
+ throw new Error(`Can only remove from the tip`);
803
+ }
804
+
805
+ // from - blocksToUnwind = the new head, so + 1 for what we need to remove
806
+ const blocks = await this.getBlocks(from - blocksToUnwind + 1, blocksToUnwind);
807
+
808
+ return [
809
+ // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
810
+ ...(await Promise.all(
811
+ blocks.map(async block => {
812
+ const blockLogs = block.data.body.txEffects
813
+ .flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
814
+ .flatMap(txLog => txLog.unrollLogs());
815
+ await this.#updateRegisteredContractClasses(blockLogs, block.data.number, Operation.Delete);
816
+ await this.#updateDeployedContractInstances(blockLogs, block.data.number, Operation.Delete);
817
+ }),
818
+ )),
819
+ this.store.deleteLogs(blocks.map(b => b.data)),
820
+ this.store.unwindBlocks(from, blocksToUnwind),
821
+ ].every(Boolean);
822
+ }
823
+
824
+ getBlocks(from: number, limit: number): Promise<L1Published<L2Block>[]> {
825
+ return this.store.getBlocks(from, limit);
826
+ }
827
+ getBlockHeaders(from: number, limit: number): Promise<Header[]> {
828
+ return this.store.getBlockHeaders(from, limit);
829
+ }
830
+ getTxEffect(txHash: TxHash): Promise<TxEffect | undefined> {
831
+ return this.store.getTxEffect(txHash);
832
+ }
833
+ getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
834
+ return this.store.getSettledTxReceipt(txHash);
835
+ }
836
+ addL1ToL2Messages(messages: DataRetrieval<InboxLeaf>): Promise<boolean> {
837
+ return this.store.addL1ToL2Messages(messages);
838
+ }
839
+ getL1ToL2Messages(blockNumber: bigint): Promise<Fr[]> {
840
+ return this.store.getL1ToL2Messages(blockNumber);
841
+ }
842
+ getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise<bigint | undefined> {
843
+ return this.store.getL1ToL2MessageIndex(l1ToL2Message, startIndex);
844
+ }
845
+ getLogs<TLogType extends LogType>(
846
+ from: number,
847
+ limit: number,
848
+ logType: TLogType,
849
+ ): Promise<L2BlockL2Logs<FromLogType<TLogType>>[]> {
850
+ return this.store.getLogs(from, limit, logType);
851
+ }
852
+ getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
853
+ return this.store.getUnencryptedLogs(filter);
854
+ }
855
+ getSynchedL2BlockNumber(): Promise<number> {
856
+ return this.store.getSynchedL2BlockNumber();
857
+ }
858
+ getProvenL2BlockNumber(): Promise<number> {
859
+ return this.store.getProvenL2BlockNumber();
860
+ }
861
+ getProvenL2EpochNumber(): Promise<number | undefined> {
862
+ return this.store.getProvenL2EpochNumber();
863
+ }
864
+ setProvenL2BlockNumber(l2BlockNumber: number): Promise<void> {
865
+ return this.store.setProvenL2BlockNumber(l2BlockNumber);
866
+ }
867
+ setProvenL2EpochNumber(l2EpochNumber: number): Promise<void> {
868
+ return this.store.setProvenL2EpochNumber(l2EpochNumber);
869
+ }
870
+ setBlockSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
871
+ return this.store.setBlockSynchedL1BlockNumber(l1BlockNumber);
872
+ }
873
+ setMessageSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
874
+ return this.store.setMessageSynchedL1BlockNumber(l1BlockNumber);
875
+ }
876
+ getSynchPoint(): Promise<ArchiverL1SynchPoint> {
877
+ return this.store.getSynchPoint();
878
+ }
879
+ getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
880
+ return this.store.getContractClass(id);
881
+ }
882
+ getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
883
+ return this.store.getContractInstance(address);
884
+ }
885
+ getContractClassIds(): Promise<Fr[]> {
886
+ return this.store.getContractClassIds();
887
+ }
888
+ addContractArtifact(address: AztecAddress, contract: ContractArtifact): Promise<void> {
889
+ return this.store.addContractArtifact(address, contract);
890
+ }
891
+ getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
892
+ return this.store.getContractArtifact(address);
893
+ }
894
+ getTotalL1ToL2MessageCount(): Promise<bigint> {
895
+ return this.store.getTotalL1ToL2MessageCount();
896
+ }
897
+ }
898
+
899
+ type L1RollupConstants = {
900
+ l1StartBlock: bigint;
901
+ l1GenesisTime: bigint;
902
+ };
903
+
904
+ const EmptyL1RollupConstants: L1RollupConstants = {
905
+ l1StartBlock: 0n,
906
+ l1GenesisTime: 0n,
907
+ };