@aztec/archiver 0.0.1-commit.dbf9cec → 0.0.1-commit.df81a97b5

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 (69) hide show
  1. package/dest/archiver.d.ts +3 -4
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +67 -23
  4. package/dest/config.d.ts +3 -3
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +2 -1
  7. package/dest/errors.d.ts +21 -9
  8. package/dest/errors.d.ts.map +1 -1
  9. package/dest/errors.js +27 -14
  10. package/dest/factory.d.ts +4 -5
  11. package/dest/factory.d.ts.map +1 -1
  12. package/dest/factory.js +24 -21
  13. package/dest/modules/data_source_base.d.ts +5 -5
  14. package/dest/modules/data_source_base.d.ts.map +1 -1
  15. package/dest/modules/data_source_base.js +5 -5
  16. package/dest/modules/data_store_updater.d.ts +17 -12
  17. package/dest/modules/data_store_updater.d.ts.map +1 -1
  18. package/dest/modules/data_store_updater.js +78 -77
  19. package/dest/modules/l1_synchronizer.d.ts +2 -1
  20. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  21. package/dest/modules/l1_synchronizer.js +37 -7
  22. package/dest/store/block_store.d.ts +12 -13
  23. package/dest/store/block_store.d.ts.map +1 -1
  24. package/dest/store/block_store.js +61 -61
  25. package/dest/store/contract_class_store.d.ts +2 -3
  26. package/dest/store/contract_class_store.d.ts.map +1 -1
  27. package/dest/store/contract_class_store.js +7 -67
  28. package/dest/store/contract_instance_store.d.ts +1 -1
  29. package/dest/store/contract_instance_store.d.ts.map +1 -1
  30. package/dest/store/contract_instance_store.js +6 -2
  31. package/dest/store/kv_archiver_store.d.ts +28 -18
  32. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  33. package/dest/store/kv_archiver_store.js +34 -21
  34. package/dest/store/log_store.d.ts +6 -3
  35. package/dest/store/log_store.d.ts.map +1 -1
  36. package/dest/store/log_store.js +93 -16
  37. package/dest/store/message_store.d.ts +5 -1
  38. package/dest/store/message_store.d.ts.map +1 -1
  39. package/dest/store/message_store.js +14 -1
  40. package/dest/test/fake_l1_state.d.ts +8 -1
  41. package/dest/test/fake_l1_state.d.ts.map +1 -1
  42. package/dest/test/fake_l1_state.js +39 -5
  43. package/dest/test/mock_l2_block_source.d.ts +4 -3
  44. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  45. package/dest/test/mock_l2_block_source.js +7 -4
  46. package/dest/test/mock_structs.d.ts +4 -1
  47. package/dest/test/mock_structs.d.ts.map +1 -1
  48. package/dest/test/mock_structs.js +13 -1
  49. package/dest/test/noop_l1_archiver.d.ts +4 -1
  50. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  51. package/dest/test/noop_l1_archiver.js +5 -1
  52. package/package.json +13 -13
  53. package/src/archiver.ts +80 -23
  54. package/src/config.ts +8 -1
  55. package/src/errors.ts +40 -24
  56. package/src/factory.ts +23 -15
  57. package/src/modules/data_source_base.ts +11 -6
  58. package/src/modules/data_store_updater.ts +83 -107
  59. package/src/modules/l1_synchronizer.ts +47 -13
  60. package/src/store/block_store.ts +72 -69
  61. package/src/store/contract_class_store.ts +8 -106
  62. package/src/store/contract_instance_store.ts +8 -5
  63. package/src/store/kv_archiver_store.ts +43 -32
  64. package/src/store/log_store.ts +126 -27
  65. package/src/store/message_store.ts +20 -1
  66. package/src/test/fake_l1_state.ts +50 -9
  67. package/src/test/mock_l2_block_source.ts +15 -3
  68. package/src/test/mock_structs.ts +20 -6
  69. package/src/test/noop_l1_archiver.ts +7 -1
package/src/archiver.ts CHANGED
@@ -22,16 +22,15 @@ import {
22
22
  import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
23
23
  import {
24
24
  type L1RollupConstants,
25
- getEpochNumberAtTimestamp,
25
+ getEpochAtSlot,
26
26
  getSlotAtNextL1Block,
27
- getSlotAtTimestamp,
28
27
  getSlotRangeForEpoch,
29
28
  getTimestampRangeForEpoch,
30
29
  } from '@aztec/stdlib/epoch-helpers';
31
30
  import { type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
32
31
 
33
32
  import { type ArchiverConfig, mapArchiverConfig } from './config.js';
34
- import { NoBlobBodiesFoundError } from './errors.js';
33
+ import { BlockAlreadyCheckpointedError, NoBlobBodiesFoundError } from './errors.js';
35
34
  import { validateAndLogTraceAvailability } from './l1/validate_trace.js';
36
35
  import { ArchiverDataSourceBase } from './modules/data_source_base.js';
37
36
  import { ArchiverDataStoreUpdater } from './modules/data_store_updater.js';
@@ -96,7 +95,6 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
96
95
  * @param dataStore - An archiver data store for storage & retrieval of blocks, encrypted logs & contract data.
97
96
  * @param config - Archiver configuration options.
98
97
  * @param blobClient - Client for retrieving blob data.
99
- * @param epochCache - Cache for epoch-related data.
100
98
  * @param dateProvider - Provider for current date/time.
101
99
  * @param instrumentation - Instrumentation for metrics and tracing.
102
100
  * @param l1Constants - L1 rollup constants.
@@ -120,7 +118,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
120
118
  },
121
119
  private readonly blobClient: BlobClientInterface,
122
120
  instrumentation: ArchiverInstrumentation,
123
- protected override readonly l1Constants: L1RollupConstants & { l1StartBlockHash: Buffer32; genesisArchiveRoot: Fr },
121
+ protected override readonly l1Constants: L1RollupConstants & {
122
+ l1StartBlockHash: Buffer32;
123
+ genesisArchiveRoot: Fr;
124
+ },
124
125
  synchronizer: ArchiverL1Synchronizer,
125
126
  events: ArchiverEmitter,
126
127
  l2TipsCache?: L2TipsCache,
@@ -133,7 +134,9 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
133
134
  this.synchronizer = synchronizer;
134
135
  this.events = events;
135
136
  this.l2TipsCache = l2TipsCache ?? new L2TipsCache(this.dataStore.blockStore);
136
- this.updater = new ArchiverDataStoreUpdater(this.dataStore, this.l2TipsCache);
137
+ this.updater = new ArchiverDataStoreUpdater(this.dataStore, this.l2TipsCache, {
138
+ rollupManaLimit: l1Constants.rollupManaLimit,
139
+ });
137
140
 
138
141
  // Running promise starts with a small interval inbetween runs, so all iterations needed for the initial sync
139
142
  // are done as fast as possible. This then gets updated once the initial sync completes.
@@ -238,10 +241,15 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
238
241
  }
239
242
 
240
243
  try {
241
- await this.updater.addProposedBlocks([block]);
244
+ await this.updater.addProposedBlock(block);
242
245
  this.log.debug(`Added block ${block.number} to store`);
243
246
  resolve();
244
247
  } catch (err: any) {
248
+ if (err instanceof BlockAlreadyCheckpointedError) {
249
+ this.log.debug(`Proposed block ${block.number} matches already checkpointed block, ignoring late proposal`);
250
+ resolve();
251
+ continue;
252
+ }
245
253
  this.log.error(`Failed to add block ${block.number} to store: ${err.message}`);
246
254
  reject(err);
247
255
  }
@@ -333,16 +341,49 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
333
341
  return Promise.resolve(this.synchronizer.getL1Timestamp());
334
342
  }
335
343
 
336
- public getL2SlotNumber(): Promise<SlotNumber | undefined> {
344
+ public async getSyncedL2SlotNumber(): Promise<SlotNumber | undefined> {
345
+ // The synced L2 slot is the latest slot for which we have all L1 data,
346
+ // either because we have seen all L1 blocks for that slot, or because
347
+ // we have seen the corresponding checkpoint.
348
+
349
+ let slotFromL1Sync: SlotNumber | undefined;
337
350
  const l1Timestamp = this.synchronizer.getL1Timestamp();
338
- return Promise.resolve(l1Timestamp === undefined ? undefined : getSlotAtTimestamp(l1Timestamp, this.l1Constants));
351
+ if (l1Timestamp !== undefined) {
352
+ const nextL1BlockSlot = getSlotAtNextL1Block(l1Timestamp, this.l1Constants);
353
+ if (Number(nextL1BlockSlot) > 0) {
354
+ slotFromL1Sync = SlotNumber.add(nextL1BlockSlot, -1);
355
+ }
356
+ }
357
+
358
+ let slotFromCheckpoint: SlotNumber | undefined;
359
+ const latestCheckpointNumber = await this.store.getSynchedCheckpointNumber();
360
+ if (latestCheckpointNumber > 0) {
361
+ const checkpointData = await this.store.getCheckpointData(latestCheckpointNumber);
362
+ if (checkpointData) {
363
+ slotFromCheckpoint = checkpointData.header.slotNumber;
364
+ }
365
+ }
366
+
367
+ if (slotFromL1Sync === undefined && slotFromCheckpoint === undefined) {
368
+ return undefined;
369
+ }
370
+ return SlotNumber(Math.max(slotFromL1Sync ?? 0, slotFromCheckpoint ?? 0));
339
371
  }
340
372
 
341
- public getL2EpochNumber(): Promise<EpochNumber | undefined> {
342
- const l1Timestamp = this.synchronizer.getL1Timestamp();
343
- return Promise.resolve(
344
- l1Timestamp === undefined ? undefined : getEpochNumberAtTimestamp(l1Timestamp, this.l1Constants),
345
- );
373
+ public async getSyncedL2EpochNumber(): Promise<EpochNumber | undefined> {
374
+ const syncedSlot = await this.getSyncedL2SlotNumber();
375
+ if (syncedSlot === undefined) {
376
+ return undefined;
377
+ }
378
+ // An epoch is fully synced when all its slots are synced.
379
+ // We check if syncedSlot is the last slot of its epoch; if so, that epoch is fully synced.
380
+ // Otherwise, only the previous epoch is fully synced.
381
+ const epoch = getEpochAtSlot(syncedSlot, this.l1Constants);
382
+ const [, endSlot] = getSlotRangeForEpoch(epoch, this.l1Constants);
383
+ if (syncedSlot >= endSlot) {
384
+ return epoch;
385
+ }
386
+ return Number(epoch) > 0 ? EpochNumber(Number(epoch) - 1) : undefined;
346
387
  }
347
388
 
348
389
  public async isEpochComplete(epochNumber: EpochNumber): Promise<boolean> {
@@ -399,7 +440,6 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
399
440
  }
400
441
 
401
442
  public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
402
- // TODO(pw/mbps): This still assumes 1 block per checkpoint
403
443
  const currentBlocks = await this.getL2Tips();
404
444
  const currentL2Block = currentBlocks.proposed.number;
405
445
  const currentProvenBlock = currentBlocks.proven.block.number;
@@ -411,8 +451,25 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
411
451
  if (!targetL2Block) {
412
452
  throw new Error(`Target L2 block ${targetL2BlockNumber} not found`);
413
453
  }
414
- const targetL1BlockNumber = targetL2Block.l1.blockNumber;
415
454
  const targetCheckpointNumber = targetL2Block.checkpointNumber;
455
+
456
+ // Rollback operates at checkpoint granularity: the target block must be the last block of its checkpoint.
457
+ const checkpointData = await this.store.getCheckpointData(targetCheckpointNumber);
458
+ if (checkpointData) {
459
+ const lastBlockInCheckpoint = BlockNumber(checkpointData.startBlock + checkpointData.blockCount - 1);
460
+ if (targetL2BlockNumber !== lastBlockInCheckpoint) {
461
+ const previousCheckpointBoundary =
462
+ checkpointData.startBlock > 1 ? BlockNumber(checkpointData.startBlock - 1) : BlockNumber(0);
463
+ throw new Error(
464
+ `Target L2 block ${targetL2BlockNumber} is not at a checkpoint boundary. ` +
465
+ `Checkpoint ${targetCheckpointNumber} spans blocks ${checkpointData.startBlock} to ${lastBlockInCheckpoint}. ` +
466
+ `Use block ${lastBlockInCheckpoint} to roll back to this checkpoint, ` +
467
+ `or block ${previousCheckpointBoundary} to roll back to the previous one.`,
468
+ );
469
+ }
470
+ }
471
+
472
+ const targetL1BlockNumber = targetL2Block.l1.blockNumber;
416
473
  const targetL1Block = await this.publicClient.getBlock({
417
474
  blockNumber: targetL1BlockNumber,
418
475
  includeTransactions: false,
@@ -431,13 +488,13 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
431
488
  await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
432
489
  await this.store.setMessageSynchedL1Block({ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash });
433
490
  if (targetL2BlockNumber < currentProvenBlock) {
434
- this.log.info(`Clearing proven L2 block number`);
435
- await this.updater.setProvenCheckpointNumber(CheckpointNumber.ZERO);
491
+ this.log.info(`Rolling back proven L2 checkpoint to ${targetCheckpointNumber}`);
492
+ await this.updater.setProvenCheckpointNumber(targetCheckpointNumber);
493
+ }
494
+ const currentFinalizedBlock = currentBlocks.finalized.block.number;
495
+ if (targetL2BlockNumber < currentFinalizedBlock) {
496
+ this.log.info(`Rolling back finalized L2 checkpoint to ${targetCheckpointNumber}`);
497
+ await this.updater.setFinalizedCheckpointNumber(targetCheckpointNumber);
436
498
  }
437
- // TODO(palla/reorg): Set the finalized block when we add support for it.
438
- // if (targetL2BlockNumber < currentFinalizedBlock) {
439
- // this.log.info(`Clearing finalized L2 block number`);
440
- // await this.store.setFinalizedL2BlockNumber(0);
441
- // }
442
499
  }
443
500
  }
package/src/config.ts CHANGED
@@ -8,7 +8,12 @@ import {
8
8
  getConfigFromMappings,
9
9
  numberConfigHelper,
10
10
  } from '@aztec/foundation/config';
11
- import { type ChainConfig, chainConfigMappings } from '@aztec/stdlib/config';
11
+ import {
12
+ type ChainConfig,
13
+ type PipelineConfig,
14
+ chainConfigMappings,
15
+ pipelineConfigMappings,
16
+ } from '@aztec/stdlib/config';
12
17
  import type { ArchiverSpecificConfig } from '@aztec/stdlib/interfaces/server';
13
18
 
14
19
  /**
@@ -21,11 +26,13 @@ import type { ArchiverSpecificConfig } from '@aztec/stdlib/interfaces/server';
21
26
  export type ArchiverConfig = ArchiverSpecificConfig &
22
27
  L1ReaderConfig &
23
28
  L1ContractsConfig &
29
+ PipelineConfig & // required to pass through to epoch cache
24
30
  BlobClientConfig &
25
31
  ChainConfig;
26
32
 
27
33
  export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
28
34
  ...blobClientConfigMapping,
35
+ ...pipelineConfigMappings,
29
36
  archiverPollingIntervalMS: {
30
37
  env: 'ARCHIVER_POLLING_INTERVAL_MS',
31
38
  description: 'The polling interval in ms for retrieving new L2 blocks and encrypted logs.',
package/src/errors.ts CHANGED
@@ -6,24 +6,9 @@ export class NoBlobBodiesFoundError extends Error {
6
6
  }
7
7
  }
8
8
 
9
- export class InitialBlockNumberNotSequentialError extends Error {
10
- constructor(
11
- public readonly newBlockNumber: number,
12
- public readonly previousBlockNumber: number | undefined,
13
- ) {
14
- super(
15
- `Cannot insert new block ${newBlockNumber} given previous block number in store is ${
16
- previousBlockNumber ?? 'undefined'
17
- }`,
18
- );
19
- }
20
- }
21
-
22
9
  export class BlockNumberNotSequentialError extends Error {
23
10
  constructor(newBlockNumber: number, previous: number | undefined) {
24
- super(
25
- `Cannot insert new block ${newBlockNumber} given previous block number in batch is ${previous ?? 'undefined'}`,
26
- );
11
+ super(`Cannot insert new block ${newBlockNumber} given previous block number is ${previous ?? 'undefined'}`);
27
12
  }
28
13
  }
29
14
 
@@ -48,14 +33,6 @@ export class CheckpointNumberNotSequentialError extends Error {
48
33
  }
49
34
  }
50
35
 
51
- export class CheckpointNumberNotConsistentError extends Error {
52
- constructor(newCheckpointNumber: number, previous: number | undefined) {
53
- super(
54
- `Cannot insert block for new checkpoint ${newCheckpointNumber} given previous block was checkpoint ${previous ?? 'undefined'}`,
55
- );
56
- }
57
- }
58
-
59
36
  export class BlockIndexNotSequentialError extends Error {
60
37
  constructor(newBlockIndex: number, previousBlockIndex: number | undefined) {
61
38
  super(
@@ -89,6 +66,45 @@ export class BlockNotFoundError extends Error {
89
66
  }
90
67
  }
91
68
 
69
+ /** Thrown when a proposed block matches a block that was already checkpointed. This is expected for late proposals. */
70
+ export class BlockAlreadyCheckpointedError extends Error {
71
+ constructor(public readonly blockNumber: number) {
72
+ super(`Block ${blockNumber} has already been checkpointed with the same content`);
73
+ this.name = 'BlockAlreadyCheckpointedError';
74
+ }
75
+ }
76
+
77
+ /** Thrown when logs are added for a tag whose last stored log has a higher block number than the new log. */
78
+ export class OutOfOrderLogInsertionError extends Error {
79
+ constructor(
80
+ public readonly logType: 'private' | 'public',
81
+ public readonly tag: string,
82
+ public readonly lastBlockNumber: number,
83
+ public readonly newBlockNumber: number,
84
+ ) {
85
+ super(
86
+ `Out-of-order ${logType} log insertion for tag ${tag}: ` +
87
+ `last existing log is from block ${lastBlockNumber} but new log is from block ${newBlockNumber}`,
88
+ );
89
+ this.name = 'OutOfOrderLogInsertionError';
90
+ }
91
+ }
92
+
93
+ /** Thrown when L1 to L2 messages are requested for a checkpoint whose message tree hasn't been sealed yet. */
94
+ export class L1ToL2MessagesNotReadyError extends Error {
95
+ constructor(
96
+ public readonly checkpointNumber: number,
97
+ public readonly inboxTreeInProgress: bigint,
98
+ ) {
99
+ super(
100
+ `Cannot get L1 to L2 messages for checkpoint ${checkpointNumber}: ` +
101
+ `inbox tree in progress is ${inboxTreeInProgress}, messages not yet sealed`,
102
+ );
103
+ this.name = 'L1ToL2MessagesNotReadyError';
104
+ }
105
+ }
106
+
107
+ /** Thrown when a proposed block conflicts with an already checkpointed block (different content). */
92
108
  export class CannotOverwriteCheckpointedBlockError extends Error {
93
109
  constructor(
94
110
  public readonly blockNumber: number,
package/src/factory.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { EpochCache } from '@aztec/epoch-cache';
2
2
  import { createEthereumChain } from '@aztec/ethereum/chain';
3
+ import { makeL1HttpTransport } from '@aztec/ethereum/client';
3
4
  import { InboxContract, RollupContract } from '@aztec/ethereum/contracts';
4
5
  import type { ViemPublicDebugClient } from '@aztec/ethereum/types';
5
6
  import { BlockNumber } from '@aztec/foundation/branded-types';
@@ -7,18 +8,17 @@ import { Buffer32 } from '@aztec/foundation/buffer';
7
8
  import { merge } from '@aztec/foundation/collection';
8
9
  import { Fr } from '@aztec/foundation/curves/bn254';
9
10
  import { DateProvider } from '@aztec/foundation/timer';
10
- import type { DataStoreConfig } from '@aztec/kv-store/config';
11
11
  import { createStore } from '@aztec/kv-store/lmdb-v2';
12
12
  import { protocolContractNames } from '@aztec/protocol-contracts';
13
13
  import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle';
14
14
  import { FunctionType, decodeFunctionSignature } from '@aztec/stdlib/abi';
15
15
  import type { ArchiverEmitter } from '@aztec/stdlib/block';
16
- import { type ContractClassPublic, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
17
- import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
16
+ import { type ContractClassPublicWithCommitment, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
17
+ import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
18
18
  import { getTelemetryClient } from '@aztec/telemetry-client';
19
19
 
20
20
  import { EventEmitter } from 'events';
21
- import { createPublicClient, fallback, http } from 'viem';
21
+ import { createPublicClient } from 'viem';
22
22
 
23
23
  import { Archiver, type ArchiverDeps } from './archiver.js';
24
24
  import { type ArchiverConfig, mapArchiverConfig } from './config.js';
@@ -32,14 +32,13 @@ export const ARCHIVER_STORE_NAME = 'archiver';
32
32
  /** Creates an archiver store. */
33
33
  export async function createArchiverStore(
34
34
  userConfig: Pick<ArchiverConfig, 'archiverStoreMapSizeKb' | 'maxLogs'> & DataStoreConfig,
35
- l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
36
35
  ) {
37
36
  const config = {
38
37
  ...userConfig,
39
38
  dataStoreMapSizeKb: userConfig.archiverStoreMapSizeKb ?? userConfig.dataStoreMapSizeKb,
40
39
  };
41
40
  const store = await createStore(ARCHIVER_STORE_NAME, ARCHIVER_DB_VERSION, config);
42
- return new KVArchiverDataStore(store, config.maxLogs, l1Constants);
41
+ return new KVArchiverDataStore(store, config.maxLogs);
43
42
  }
44
43
 
45
44
  /**
@@ -54,14 +53,15 @@ export async function createArchiver(
54
53
  deps: ArchiverDeps,
55
54
  opts: { blockUntilSync: boolean } = { blockUntilSync: true },
56
55
  ): Promise<Archiver> {
57
- const archiverStore = await createArchiverStore(config, { epochDuration: config.aztecEpochDuration });
56
+ const archiverStore = await createArchiverStore(config);
58
57
  await registerProtocolContracts(archiverStore);
59
58
 
60
59
  // Create Ethereum clients
61
60
  const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
61
+ const httpTimeout = config.l1HttpTimeoutMS;
62
62
  const publicClient = createPublicClient({
63
63
  chain: chain.chainInfo,
64
- transport: fallback(config.l1RpcUrls.map(url => http(url, { batch: false }))),
64
+ transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: httpTimeout }),
65
65
  pollingInterval: config.viemPollingIntervalMS,
66
66
  });
67
67
 
@@ -69,7 +69,7 @@ export async function createArchiver(
69
69
  const debugRpcUrls = config.l1DebugRpcUrls.length > 0 ? config.l1DebugRpcUrls : config.l1RpcUrls;
70
70
  const debugClient = createPublicClient({
71
71
  chain: chain.chainInfo,
72
- transport: fallback(debugRpcUrls.map(url => http(url, { batch: false }))),
72
+ transport: makeL1HttpTransport(debugRpcUrls, { timeout: httpTimeout }),
73
73
  pollingInterval: config.viemPollingIntervalMS,
74
74
  }) as ViemPublicDebugClient;
75
75
 
@@ -85,6 +85,7 @@ export async function createArchiver(
85
85
  genesisArchiveRoot,
86
86
  slashingProposerAddress,
87
87
  targetCommitteeSize,
88
+ rollupManaLimit,
88
89
  ] = await Promise.all([
89
90
  rollup.getL1StartBlock(),
90
91
  rollup.getL1GenesisTime(),
@@ -92,6 +93,7 @@ export async function createArchiver(
92
93
  rollup.getGenesisArchiveTreeRoot(),
93
94
  rollup.getSlashingProposerAddress(),
94
95
  rollup.getTargetCommitteeSize(),
96
+ rollup.getManaLimit(),
95
97
  ] as const);
96
98
 
97
99
  const l1StartBlockHash = await publicClient
@@ -110,6 +112,7 @@ export async function createArchiver(
110
112
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
111
113
  targetCommitteeSize,
112
114
  genesisArchiveRoot: Fr.fromString(genesisArchiveRoot.toString()),
115
+ rollupManaLimit: Number(rollupManaLimit),
113
116
  };
114
117
 
115
118
  const archiverConfig = merge(
@@ -170,16 +173,22 @@ export async function createArchiver(
170
173
  return archiver;
171
174
  }
172
175
 
173
- /** Registers protocol contracts in the archiver store. */
176
+ /** Registers protocol contracts in the archiver store. Idempotent — skips contracts that already exist (e.g. on node restart). */
174
177
  export async function registerProtocolContracts(store: KVArchiverDataStore) {
175
178
  const blockNumber = 0;
176
179
  for (const name of protocolContractNames) {
177
180
  const provider = new BundledProtocolContractsProvider();
178
181
  const contract = await provider.getProtocolContractArtifact(name);
179
- const contractClassPublic: ContractClassPublic = {
182
+
183
+ // Skip if already registered (happens on node restart with a persisted store).
184
+ if (await store.getContractClass(contract.contractClass.id)) {
185
+ continue;
186
+ }
187
+
188
+ const publicBytecodeCommitment = await computePublicBytecodeCommitment(contract.contractClass.packedBytecode);
189
+ const contractClassPublic: ContractClassPublicWithCommitment = {
180
190
  ...contract.contractClass,
181
- privateFunctions: [],
182
- utilityFunctions: [],
191
+ publicBytecodeCommitment,
183
192
  };
184
193
 
185
194
  const publicFunctionSignatures = contract.artifact.functions
@@ -187,8 +196,7 @@ export async function registerProtocolContracts(store: KVArchiverDataStore) {
187
196
  .map(fn => decodeFunctionSignature(fn.name, fn.parameters));
188
197
 
189
198
  await store.registerContractFunctionSignatures(publicFunctionSignatures);
190
- const bytecodeCommitment = await computePublicBytecodeCommitment(contractClassPublic.packedBytecode);
191
- await store.addContractClasses([contractClassPublic], [bytecodeCommitment], BlockNumber(blockNumber));
199
+ await store.addContractClasses([contractClassPublic], BlockNumber(blockNumber));
192
200
  await store.addContractInstances([contract.instance], BlockNumber(blockNumber));
193
201
  }
194
202
  }
@@ -46,9 +46,9 @@ export abstract class ArchiverDataSourceBase
46
46
 
47
47
  abstract getL2Tips(): Promise<L2Tips>;
48
48
 
49
- abstract getL2SlotNumber(): Promise<SlotNumber | undefined>;
49
+ abstract getSyncedL2SlotNumber(): Promise<SlotNumber | undefined>;
50
50
 
51
- abstract getL2EpochNumber(): Promise<EpochNumber | undefined>;
51
+ abstract getSyncedL2EpochNumber(): Promise<EpochNumber | undefined>;
52
52
 
53
53
  abstract isEpochComplete(epochNumber: EpochNumber): Promise<boolean>;
54
54
 
@@ -154,7 +154,7 @@ export abstract class ArchiverDataSourceBase
154
154
  }
155
155
 
156
156
  public getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
157
- return this.store.getSettledTxReceipt(txHash);
157
+ return this.store.getSettledTxReceipt(txHash, this.l1Constants);
158
158
  }
159
159
 
160
160
  public isPendingChainInvalid(): Promise<boolean> {
@@ -165,16 +165,21 @@ export abstract class ArchiverDataSourceBase
165
165
  return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
166
166
  }
167
167
 
168
- public getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
169
- return this.store.getPrivateLogsByTags(tags, page);
168
+ public getPrivateLogsByTags(
169
+ tags: SiloedTag[],
170
+ page?: number,
171
+ upToBlockNumber?: BlockNumber,
172
+ ): Promise<TxScopedL2Log[][]> {
173
+ return this.store.getPrivateLogsByTags(tags, page, upToBlockNumber);
170
174
  }
171
175
 
172
176
  public getPublicLogsByTagsFromContract(
173
177
  contractAddress: AztecAddress,
174
178
  tags: Tag[],
175
179
  page?: number,
180
+ upToBlockNumber?: BlockNumber,
176
181
  ): Promise<TxScopedL2Log[][]> {
177
- return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page);
182
+ return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
178
183
  }
179
184
 
180
185
  public getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {