@aztec/archiver 3.0.0-nightly.20251106 → 3.0.0-nightly.20251108

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.
@@ -48,11 +48,12 @@ export declare class Archiver extends Archiver_base implements ArchiveSource, Tr
48
48
  private config;
49
49
  private readonly blobSinkClient;
50
50
  private readonly epochCache;
51
+ private readonly dateProvider;
51
52
  private readonly instrumentation;
52
53
  private readonly l1constants;
53
54
  private readonly log;
54
55
  /** A loop in which we will be continually fetching new L2 blocks. */
55
- private runningPromise?;
56
+ private runningPromise;
56
57
  private rollup;
57
58
  private inbox;
58
59
  private store;
@@ -79,7 +80,8 @@ export declare class Archiver extends Archiver_base implements ArchiveSource, Tr
79
80
  pollingIntervalMs: number;
80
81
  batchSize: number;
81
82
  skipValidateBlockAttestations?: boolean;
82
- }, blobSinkClient: BlobSinkClientInterface, epochCache: EpochCache, instrumentation: ArchiverInstrumentation, l1constants: L1RollupConstants & {
83
+ maxAllowedEthClientDriftSeconds: number;
84
+ }, blobSinkClient: BlobSinkClientInterface, epochCache: EpochCache, dateProvider: DateProvider, instrumentation: ArchiverInstrumentation, l1constants: L1RollupConstants & {
83
85
  l1StartBlockHash: Buffer32;
84
86
  genesisArchiveRoot: Fr;
85
87
  }, log?: Logger);
@@ -100,7 +102,8 @@ export declare class Archiver extends Archiver_base implements ArchiveSource, Tr
100
102
  start(blockUntilSynced: boolean): Promise<void>;
101
103
  syncImmediate(): Promise<void>;
102
104
  waitForInitialSync(): Promise<void>;
103
- private syncSafe;
105
+ /** Checks that the ethereum node we are connected to has a latest timestamp no more than the allowed drift. Throw if not. */
106
+ private testEthereumNodeSynced;
104
107
  /**
105
108
  * Fetches logs from L1 contracts and processes them.
106
109
  */
@@ -1 +1 @@
1
- {"version":3,"file":"archiver.d.ts","sourceRoot":"","sources":["../../src/archiver/archiver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"archiver.d.ts","sourceRoot":"","sources":["../../src/archiver/archiver.ts"],"names":[],"mappings":"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"}
@@ -13,15 +13,14 @@ import { Fr } from '@aztec/foundation/fields';
13
13
  import { createLogger } from '@aztec/foundation/log';
14
14
  import { promiseWithResolvers } from '@aztec/foundation/promise';
15
15
  import { RunningPromise, makeLoggingErrorHandler } from '@aztec/foundation/running-promise';
16
- import { sleep } from '@aztec/foundation/sleep';
17
16
  import { count } from '@aztec/foundation/string';
18
- import { Timer, elapsed } from '@aztec/foundation/timer';
17
+ import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
19
18
  import { ContractClassPublishedEvent, PrivateFunctionBroadcastedEvent, UtilityFunctionBroadcastedEvent } from '@aztec/protocol-contracts/class-registry';
20
19
  import { ContractInstancePublishedEvent, ContractInstanceUpdatedEvent } from '@aztec/protocol-contracts/instance-registry';
21
20
  import { L2BlockSourceEvents } from '@aztec/stdlib/block';
22
21
  import { computePublicBytecodeCommitment, isValidPrivateFunctionMembershipProof, isValidUtilityFunctionMembershipProof } from '@aztec/stdlib/contract';
23
22
  import { getEpochAtSlot, getEpochNumberAtTimestamp, getSlotAtTimestamp, getSlotRangeForEpoch, getTimestampRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
24
- import { Attributes, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
23
+ import { getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
25
24
  import { EventEmitter } from 'events';
26
25
  import groupBy from 'lodash.groupby';
27
26
  import { createPublicClient, fallback, http } from 'viem';
@@ -33,7 +32,8 @@ function mapArchiverConfig(config) {
33
32
  return {
34
33
  pollingIntervalMs: config.archiverPollingIntervalMS,
35
34
  batchSize: config.archiverBatchSize,
36
- skipValidateBlockAttestations: config.skipValidateBlockAttestations
35
+ skipValidateBlockAttestations: config.skipValidateBlockAttestations,
36
+ maxAllowedEthClientDriftSeconds: config.maxAllowedEthClientDriftSeconds
37
37
  };
38
38
  }
39
39
  /**
@@ -47,6 +47,7 @@ function mapArchiverConfig(config) {
47
47
  config;
48
48
  blobSinkClient;
49
49
  epochCache;
50
+ dateProvider;
50
51
  instrumentation;
51
52
  l1constants;
52
53
  log;
@@ -68,13 +69,16 @@ function mapArchiverConfig(config) {
68
69
  * @param pollingIntervalMs - The interval for polling for L1 logs (in milliseconds).
69
70
  * @param store - An archiver data store for storage & retrieval of blocks, encrypted logs & contract data.
70
71
  * @param log - A logger.
71
- */ constructor(publicClient, l1Addresses, dataStore, config, blobSinkClient, epochCache, instrumentation, l1constants, log = createLogger('archiver')){
72
- super(), this.publicClient = publicClient, this.l1Addresses = l1Addresses, this.dataStore = dataStore, this.config = config, this.blobSinkClient = blobSinkClient, this.epochCache = epochCache, this.instrumentation = instrumentation, this.l1constants = l1constants, this.log = log, this.initialSyncComplete = false;
72
+ */ constructor(publicClient, l1Addresses, dataStore, config, blobSinkClient, epochCache, dateProvider, instrumentation, l1constants, log = createLogger('archiver')){
73
+ super(), this.publicClient = publicClient, this.l1Addresses = l1Addresses, this.dataStore = dataStore, this.config = config, this.blobSinkClient = blobSinkClient, this.epochCache = epochCache, this.dateProvider = dateProvider, this.instrumentation = instrumentation, this.l1constants = l1constants, this.log = log, this.initialSyncComplete = false;
73
74
  this.tracer = instrumentation.tracer;
74
75
  this.store = new ArchiverStoreHelper(dataStore);
75
76
  this.rollup = new RollupContract(publicClient, l1Addresses.rollupAddress);
76
77
  this.inbox = new InboxContract(publicClient, l1Addresses.inboxAddress);
77
78
  this.initialSyncPromise = promiseWithResolvers();
79
+ // Running promise starts with a small interval inbetween runs, so all iterations needed for the initial sync
80
+ // are done as fast as possible. This then gets updated once the initial sync completes.
81
+ this.runningPromise = new RunningPromise(()=>this.sync(), this.log, this.config.pollingIntervalMs / 10, makeLoggingErrorHandler(this.log, NoBlobBodiesFoundError, BlockTagTooOldError));
78
82
  }
79
83
  /**
80
84
  * Creates a new instance of the Archiver and blocks until it syncs from chain.
@@ -113,11 +117,12 @@ function mapArchiverConfig(config) {
113
117
  };
114
118
  const opts = merge({
115
119
  pollingIntervalMs: 10_000,
116
- batchSize: 100
120
+ batchSize: 100,
121
+ maxAllowedEthClientDriftSeconds: 300
117
122
  }, mapArchiverConfig(config));
118
123
  const epochCache = deps.epochCache ?? await EpochCache.create(config.l1Contracts.rollupAddress, config, deps);
119
124
  const telemetry = deps.telemetry ?? getTelemetryClient();
120
- const archiver = new Archiver(publicClient, config.l1Contracts, archiverStore, opts, deps.blobSinkClient, epochCache, await ArchiverInstrumentation.new(telemetry, ()=>archiverStore.estimateSize()), l1Constants);
125
+ const archiver = new Archiver(publicClient, config.l1Contracts, archiverStore, opts, deps.blobSinkClient, epochCache, deps.dateProvider ?? new DateProvider(), await ArchiverInstrumentation.new(telemetry, ()=>archiverStore.estimateSize()), l1Constants);
121
126
  await archiver.start(blockUntilSynced);
122
127
  return archiver;
123
128
  }
@@ -128,48 +133,48 @@ function mapArchiverConfig(config) {
128
133
  * Starts sync process.
129
134
  * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
130
135
  */ async start(blockUntilSynced) {
131
- if (this.runningPromise) {
136
+ if (this.runningPromise.isRunning()) {
132
137
  throw new Error('Archiver is already running');
133
138
  }
134
139
  await this.blobSinkClient.testSources();
140
+ await this.testEthereumNodeSynced();
141
+ // Log initial state for the archiver
142
+ const { l1StartBlock } = this.l1constants;
143
+ const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = l1StartBlock } = await this.store.getSynchPoint();
144
+ const currentL2Block = await this.getBlockNumber();
145
+ this.log.info(`Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo} and L2 block ${currentL2Block}`, {
146
+ blocksSynchedTo,
147
+ messagesSynchedTo,
148
+ currentL2Block
149
+ });
150
+ // Start sync loop, and return the wait for initial sync if we are asked to block until synced
151
+ this.runningPromise.start();
135
152
  if (blockUntilSynced) {
136
- while(!await this.syncSafe(true)){
137
- this.log.info(`Retrying initial archiver sync in ${this.config.pollingIntervalMs}ms`);
138
- await sleep(this.config.pollingIntervalMs);
139
- }
153
+ return this.waitForInitialSync();
140
154
  }
141
- this.runningPromise = new RunningPromise(()=>this.sync(false), this.log, this.config.pollingIntervalMs, makeLoggingErrorHandler(this.log, // Ignored errors will not log to the console
142
- // We ignore NoBlobBodiesFound as the message may not have been passed to the blob sink yet
143
- NoBlobBodiesFoundError));
144
- this.runningPromise.start();
145
155
  }
146
156
  syncImmediate() {
147
- if (!this.runningPromise) {
148
- throw new Error('Archiver is not running');
149
- }
150
157
  return this.runningPromise.trigger();
151
158
  }
152
159
  waitForInitialSync() {
153
160
  return this.initialSyncPromise.promise;
154
161
  }
155
- async syncSafe(initialRun) {
156
- try {
157
- await this.sync(initialRun);
158
- return true;
159
- } catch (error) {
160
- if (error instanceof NoBlobBodiesFoundError) {
161
- this.log.error(`Error syncing archiver: ${error.message}`);
162
- } else if (error instanceof BlockTagTooOldError) {
163
- this.log.warn(`Re-running archiver sync: ${error.message}`);
164
- } else {
165
- this.log.error('Error during archiver sync', error);
166
- }
167
- return false;
162
+ /** Checks that the ethereum node we are connected to has a latest timestamp no more than the allowed drift. Throw if not. */ async testEthereumNodeSynced() {
163
+ const maxAllowedDelay = this.config.maxAllowedEthClientDriftSeconds;
164
+ if (maxAllowedDelay === 0) {
165
+ return;
166
+ }
167
+ const { number, timestamp: l1Timestamp } = await this.publicClient.getBlock({
168
+ includeTransactions: false
169
+ });
170
+ const currentTime = BigInt(this.dateProvider.nowInSeconds());
171
+ if (currentTime - l1Timestamp > BigInt(maxAllowedDelay)) {
172
+ throw new Error(`Ethereum node is out of sync (last block synced ${number} at ${l1Timestamp} vs current time ${currentTime})`);
168
173
  }
169
174
  }
170
175
  /**
171
176
  * Fetches logs from L1 contracts and processes them.
172
- */ async sync(initialRun) {
177
+ */ async sync() {
173
178
  /**
174
179
  * We keep track of three "pointers" to L1 blocks:
175
180
  * 1. the last L1 block that published an L2 block
@@ -179,8 +184,6 @@ function mapArchiverConfig(config) {
179
184
  * We do this to deal with L1 data providers that are eventually consistent (e.g. Infura).
180
185
  * We guard against seeing block X with no data at one point, and later, the provider processes the block and it has data.
181
186
  * The archiver will stay back, until there's data on L1 that will move the pointers forward.
182
- *
183
- * This code does not handle reorgs.
184
187
  */ const { l1StartBlock, l1StartBlockHash } = this.l1constants;
185
188
  const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = {
186
189
  l1BlockNumber: l1StartBlock,
@@ -191,12 +194,12 @@ function mapArchiverConfig(config) {
191
194
  });
192
195
  const currentL1BlockNumber = currentL1Block.number;
193
196
  const currentL1BlockHash = Buffer32.fromString(currentL1Block.hash);
194
- if (initialRun) {
195
- this.log.info(`Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo}` + ` to current L1 block ${currentL1BlockNumber} with hash ${currentL1BlockHash.toString()}`, {
196
- blocksSynchedTo,
197
- messagesSynchedTo
198
- });
199
- }
197
+ this.log.trace(`Starting new archiver sync iteration`, {
198
+ blocksSynchedTo,
199
+ messagesSynchedTo,
200
+ currentL1BlockNumber,
201
+ currentL1BlockHash
202
+ });
200
203
  // ********** Ensuring Consistency of data pulled from L1 **********
201
204
  /**
202
205
  * There are a number of calls in this sync operation to L1 for retrieving
@@ -219,6 +222,17 @@ function mapArchiverConfig(config) {
219
222
  const currentL1Timestamp = !this.l1Timestamp || !this.l1BlockNumber || this.l1BlockNumber !== currentL1BlockNumber ? (await this.publicClient.getBlock({
220
223
  blockNumber: currentL1BlockNumber
221
224
  })).timestamp : this.l1Timestamp;
225
+ // Warn if the latest L1 block timestamp is too old
226
+ const maxAllowedDelay = this.config.maxAllowedEthClientDriftSeconds;
227
+ const now = this.dateProvider.nowInSeconds();
228
+ if (maxAllowedDelay > 0 && Number(currentL1Timestamp) <= now - maxAllowedDelay) {
229
+ this.log.warn(`Latest L1 block ${currentL1BlockNumber} timestamp ${currentL1Timestamp} is too old. Make sure your Ethereum node is synced.`, {
230
+ currentL1BlockNumber,
231
+ currentL1Timestamp,
232
+ now,
233
+ maxAllowedDelay
234
+ });
235
+ }
222
236
  // ********** Events that are processed per L2 block **********
223
237
  if (currentL1BlockNumber > blocksSynchedTo) {
224
238
  // First we retrieve new L2 blocks and store them in the DB. This will also update the
@@ -230,6 +244,12 @@ function mapArchiverConfig(config) {
230
244
  // the chain locally before we start unwinding stuff. This can be optimized by figuring out
231
245
  // up to which point we're pruning, and then requesting L2 blocks up to that point only.
232
246
  const { rollupCanPrune } = await this.handleEpochPrune(rollupStatus.provenBlockNumber, currentL1BlockNumber, currentL1Timestamp);
247
+ // If the last block we processed had an invalid attestation, we manually advance the L1 syncpoint
248
+ // past it, since otherwise we'll keep downloading it and reprocessing it on every iteration until
249
+ // we get a valid block to advance the syncpoint.
250
+ if (!rollupStatus.validationResult?.valid && rollupStatus.lastL1BlockWithL2Blocks !== undefined) {
251
+ await this.store.setBlockSynchedL1BlockNumber(rollupStatus.lastL1BlockWithL2Blocks);
252
+ }
233
253
  // And lastly we check if we are missing any L2 blocks behind us due to a possible L1 reorg.
234
254
  // We only do this if rollup cant prune on the next submission. Otherwise we will end up
235
255
  // re-syncing the blocks we have just unwound above. We also dont do this if the last block is invalid,
@@ -244,14 +264,17 @@ function mapArchiverConfig(config) {
244
264
  // but the corresponding blocks have not been processed (see #12631).
245
265
  this.l1Timestamp = currentL1Timestamp;
246
266
  this.l1BlockNumber = currentL1BlockNumber;
247
- this.initialSyncComplete = true;
248
- this.initialSyncPromise.resolve();
249
- if (initialRun) {
250
- this.log.info(`Initial archiver sync to L1 block ${currentL1BlockNumber} complete.`, {
267
+ // We resolve the initial sync only once we've caught up with the latest L1 block number (with 1 block grace)
268
+ // so if the initial sync took too long, we still go for another iteration.
269
+ if (!this.initialSyncComplete && currentL1BlockNumber + 1n >= await this.publicClient.getBlockNumber()) {
270
+ this.log.info(`Initial archiver sync to L1 block ${currentL1BlockNumber} complete`, {
251
271
  l1BlockNumber: currentL1BlockNumber,
252
272
  syncPoint: await this.store.getSynchPoint(),
253
273
  ...await this.getL2Tips()
254
274
  });
275
+ this.runningPromise.setPollingIntervalMS(this.config.pollingIntervalMs);
276
+ this.initialSyncComplete = true;
277
+ this.initialSyncPromise.resolve();
255
278
  }
256
279
  }
257
280
  /** Queries the rollup contract on whether a prune can be executed on the immediate next L1 block. */ async canPrune(currentL1BlockNumber, currentL1Timestamp) {
@@ -334,7 +357,7 @@ function mapArchiverConfig(config) {
334
357
  });
335
358
  // Compare message count and rolling hash. If they match, no need to retrieve anything.
336
359
  if (remoteMessagesState.totalMessagesInserted === localMessagesInserted && remoteMessagesState.messagesRollingHash.equals(localLastMessage?.rollingHash ?? Buffer16.ZERO)) {
337
- this.log.debug(`No L1 to L2 messages to query between L1 blocks ${messagesSyncPoint.l1BlockNumber} and ${currentL1BlockNumber}.`);
360
+ this.log.trace(`No L1 to L2 messages to query between L1 blocks ${messagesSyncPoint.l1BlockNumber} and ${currentL1BlockNumber}.`);
338
361
  return;
339
362
  }
340
363
  // Check if our syncpoint is still valid. If not, there was an L1 reorg and we need to re-retrieve messages.
@@ -570,6 +593,10 @@ function mapArchiverConfig(config) {
570
593
  break;
571
594
  }
572
595
  const archiveAtContract = await this.rollup.archiveAt(BigInt(candidateBlock.number));
596
+ this.log.trace(`Checking local block ${candidateBlock.number} with archive ${candidateBlock.archive.root}`, {
597
+ archiveAtContract,
598
+ archiveLocal: candidateBlock.archive.root.toString()
599
+ });
573
600
  if (archiveAtContract === candidateBlock.archive.root.toString()) {
574
601
  break;
575
602
  }
@@ -585,6 +612,7 @@ function mapArchiverConfig(config) {
585
612
  let searchStartBlock = blocksSynchedTo;
586
613
  let searchEndBlock = blocksSynchedTo;
587
614
  let lastRetrievedBlock;
615
+ let lastL1BlockWithL2Blocks = undefined;
588
616
  do {
589
617
  [searchStartBlock, searchEndBlock] = this.nextRange(searchEndBlock, currentL1BlockNumber);
590
618
  this.log.trace(`Retrieving L2 blocks from L1 block ${searchStartBlock} to ${searchEndBlock}`);
@@ -596,8 +624,11 @@ function mapArchiverConfig(config) {
596
624
  this.log.trace(`Retrieved no new L2 blocks from L1 block ${searchStartBlock} to ${searchEndBlock}`);
597
625
  continue;
598
626
  }
599
- const lastProcessedL1BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].l1.blockNumber;
600
- this.log.debug(`Retrieved ${retrievedBlocks.length} new L2 blocks between L1 blocks ${searchStartBlock} and ${searchEndBlock} with last processed L1 block ${lastProcessedL1BlockNumber}.`);
627
+ this.log.debug(`Retrieved ${retrievedBlocks.length} new L2 blocks between L1 blocks ${searchStartBlock} and ${searchEndBlock}`, {
628
+ lastProcessedL1Block: retrievedBlocks[retrievedBlocks.length - 1].l1,
629
+ searchStartBlock,
630
+ searchEndBlock
631
+ });
601
632
  const publishedBlocks = await Promise.all(retrievedBlocks.map((b)=>retrievedBlockToPublishedL2Block(b)));
602
633
  const validBlocks = [];
603
634
  for (const block of publishedBlocks){
@@ -663,12 +694,14 @@ function mapArchiverConfig(config) {
663
694
  });
664
695
  }
665
696
  lastRetrievedBlock = validBlocks.at(-1) ?? lastRetrievedBlock;
697
+ lastL1BlockWithL2Blocks = publishedBlocks.at(-1)?.l1.blockNumber ?? lastL1BlockWithL2Blocks;
666
698
  }while (searchEndBlock < currentL1BlockNumber)
667
699
  // Important that we update AFTER inserting the blocks.
668
700
  await updateProvenBlock();
669
701
  return {
670
702
  ...rollupStatus,
671
- lastRetrievedBlock
703
+ lastRetrievedBlock,
704
+ lastL1BlockWithL2Blocks
672
705
  };
673
706
  }
674
707
  async checkForNewBlocksBeforeL1SyncPoint(status, blocksSynchedTo, currentL1BlockNumber) {
@@ -701,9 +734,6 @@ function mapArchiverConfig(config) {
701
734
  }
702
735
  }
703
736
  /** Resumes the archiver after a stop. */ resume() {
704
- if (!this.runningPromise) {
705
- throw new Error(`Archiver was never started`);
706
- }
707
737
  if (this.runningPromise.isRunning()) {
708
738
  this.log.warn(`Archiver already running`);
709
739
  }
@@ -715,7 +745,7 @@ function mapArchiverConfig(config) {
715
745
  * @returns A promise signalling completion of the stop process.
716
746
  */ async stop() {
717
747
  this.log.debug('Stopping...');
718
- await this.runningPromise?.stop();
748
+ await this.runningPromise.stop();
719
749
  this.log.info('Stopped.');
720
750
  return Promise.resolve();
721
751
  }
@@ -1030,9 +1060,7 @@ function mapArchiverConfig(config) {
1030
1060
  }
1031
1061
  }
1032
1062
  _ts_decorate([
1033
- trackSpan('Archiver.sync', (initialRun)=>({
1034
- [Attributes.INITIAL_SYNC]: initialRun
1035
- }))
1063
+ trackSpan('Archiver.sync')
1036
1064
  ], Archiver.prototype, "sync", null);
1037
1065
  var Operation = /*#__PURE__*/ function(Operation) {
1038
1066
  Operation[Operation["Store"] = 0] = "Store";
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/archiver/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAyB,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAIpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,kBAAkB,EAIxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,sBAAsB,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,sBAAsB,GAAG,cAAc,GAAG,iBAAiB,GAAG,cAAc,GAAG,WAAW,CAAC;AAExH,eAAO,MAAM,sBAAsB,EAAE,kBAAkB,CAAC,cAAc,CAsCrE,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,cAAc,CAEzD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/archiver/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAyB,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAIpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,kBAAkB,EAIxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,sBAAsB,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,sBAAsB,GAAG,cAAc,GAAG,iBAAiB,GAAG,cAAc,GAAG,WAAW,CAAC;AAExH,eAAO,MAAM,sBAAsB,EAAE,kBAAkB,CAAC,cAAc,CA2CrE,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,cAAc,CAEzD"}
@@ -28,6 +28,11 @@ export const archiverConfigMappings = {
28
28
  description: 'Whether to skip validating block attestations (use only for testing).',
29
29
  ...booleanConfigHelper(false)
30
30
  },
31
+ maxAllowedEthClientDriftSeconds: {
32
+ env: 'MAX_ALLOWED_ETH_CLIENT_DRIFT_SECONDS',
33
+ description: 'Maximum allowed drift in seconds between the Ethereum client and current time.',
34
+ ...numberConfigHelper(300)
35
+ },
31
36
  ...chainConfigMappings,
32
37
  ...l1ReaderConfigMappings,
33
38
  viemPollingIntervalMS: {
@@ -1 +1 @@
1
- {"version":3,"file":"data_retrieval.d.ts","sourceRoot":"","sources":["../../src/archiver/data_retrieval.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data_retrieval.d.ts","sourceRoot":"","sources":["../../src/archiver/data_retrieval.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAEV,UAAU,EAGV,gBAAgB,EAEjB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAQ,oBAAoB,EAA0B,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC3G,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EAAmB,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEnE,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,GAAG,EAKT,MAAM,MAAM,CAAC;AAGd,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,EAAE,CAAC;IAChB,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,EAAE,EAAE,CAAC;IACjB,EAAE,EAAE,eAAe,CAAC;IACpB,OAAO,EAAE,EAAE,CAAC;IACZ,OAAO,EAAE,EAAE,CAAC;IACZ,YAAY,EAAE,oBAAoB,EAAE,CAAC;CACtC,CAAC;AAEF,wBAAsB,gCAAgC,CAAC,cAAc,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqDlH;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,qBAAqB,CAAC,OAAO,SAAS,EAAE,gBAAgB,CAAC,EACjE,YAAY,EAAE,gBAAgB,EAC9B,cAAc,EAAE,uBAAuB,EACvC,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,MAAM,GAAE,MAAiC,GACxC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAuD7B;AA6DD,wBAAsB,cAAc,CAAC,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGzG;AAsJD,iHAAiH;AACjH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,qBAAqB,CAAC,OAAO,QAAQ,EAAE,UAAU,CAAC,EACzD,IAAI,EAAE,EAAE,EACR,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAKnC;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,qBAAqB,CAAC,OAAO,QAAQ,EAAE,UAAU,CAAC,EACzD,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC,CAgBzB;AAgBD,iEAAiE;AACjE,wBAAsB,6BAA6B,CACjD,YAAY,EAAE,gBAAgB,EAC9B,aAAa,EAAE,UAAU,EACzB,gBAAgB,EAAE,MAAM,EACxB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,EAAE,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,EAAE,CAAC,CAexF;AAED,yDAAyD;AACzD,wBAAsB,0BAA0B,CAC9C,YAAY,EAAE,gBAAgB,EAC9B,aAAa,EAAE,UAAU,EACzB,gBAAgB,EAAE,MAAM,EACxB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC,CAatG;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,EAAE,CAAC;IAChB,QAAQ,EAAE,EAAE,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,gBAAgB,EAC9B,MAAM,EAAE,KAAK,MAAM,EAAE,EACrB,gBAAgB,EAAE,EAAE,GACnB,OAAO,CAAC,gBAAgB,CAAC,CAmC3B"}
@@ -191,6 +191,7 @@ export async function getL1BlockTime(publicClient, blockNumber) {
191
191
  * @param l2BlockNumber - L2 block number.
192
192
  * @returns L2 block from the calldata, deserialized
193
193
  */ async function getBlockFromRollupTx(publicClient, blobSinkClient, txHash, blobHashes, l2BlockNumber, rollupAddress, targetCommitteeSize, logger) {
194
+ logger.trace(`Fetching L2 block ${l2BlockNumber} from rollup tx ${txHash}`);
194
195
  const { input: forwarderData, blockHash } = await publicClient.getTransaction({
195
196
  hash: txHash
196
197
  });
@@ -209,6 +210,7 @@ export async function getL1BlockTime(publicClient, blockNumber) {
209
210
  archive: decodedArgs.archive,
210
211
  stateReference: decodedArgs.stateReference,
211
212
  header: decodedArgs.header,
213
+ l1BlockHash: blockHash,
212
214
  blobHashes,
213
215
  attestations,
214
216
  packedAttestations,
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/archiver/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,mBAAmB,EAEzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,iBAAiB,EAAkB,MAAM,6BAA6B,CAAC;AAErF,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,cAAc,EAAE,gBAAgB,EAChC,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EACnD,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CA4E9B"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/archiver/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,mBAAmB,EAEzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,iBAAiB,EAAkB,MAAM,6BAA6B,CAAC;AAErF,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,cAAc,EAAE,gBAAgB,EAChC,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EACnD,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAgF9B"}
@@ -32,7 +32,6 @@ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
32
32
  valid: true
33
33
  };
34
34
  }
35
- const committeeSet = new Set(committee.map((member)=>member.toString()));
36
35
  const requiredAttestationCount = Math.floor(committee.length * 2 / 3) + 1;
37
36
  const failedValidationResult = (reason)=>({
38
37
  valid: false,
@@ -58,11 +57,12 @@ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
58
57
  invalidIndex: i
59
58
  };
60
59
  }
61
- // Check if the attestor is in the committee
60
+ // Check if the attestor at this index matches the committee member at the same index
62
61
  if (info.status === 'recovered-from-signature' || info.status === 'provided-as-address') {
63
62
  const signer = info.address.toString();
64
- if (!committeeSet.has(signer)) {
65
- logger?.warn(`Attestation from non-committee member ${signer} at slot ${slot}`, {
63
+ const expectedCommitteeMember = committee[i]?.toString();
64
+ if (!expectedCommitteeMember || signer !== expectedCommitteeMember) {
65
+ logger?.warn(`Attestation at index ${i} from ${signer} does not match expected committee member ${expectedCommitteeMember} at slot ${slot}`, {
66
66
  committee,
67
67
  invalidIndex: i,
68
68
  ...logData
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/archiver",
3
- "version": "3.0.0-nightly.20251106",
3
+ "version": "3.0.0-nightly.20251108",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -66,18 +66,18 @@
66
66
  ]
67
67
  },
68
68
  "dependencies": {
69
- "@aztec/blob-lib": "3.0.0-nightly.20251106",
70
- "@aztec/blob-sink": "3.0.0-nightly.20251106",
71
- "@aztec/constants": "3.0.0-nightly.20251106",
72
- "@aztec/epoch-cache": "3.0.0-nightly.20251106",
73
- "@aztec/ethereum": "3.0.0-nightly.20251106",
74
- "@aztec/foundation": "3.0.0-nightly.20251106",
75
- "@aztec/kv-store": "3.0.0-nightly.20251106",
76
- "@aztec/l1-artifacts": "3.0.0-nightly.20251106",
77
- "@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251106",
78
- "@aztec/protocol-contracts": "3.0.0-nightly.20251106",
79
- "@aztec/stdlib": "3.0.0-nightly.20251106",
80
- "@aztec/telemetry-client": "3.0.0-nightly.20251106",
69
+ "@aztec/blob-lib": "3.0.0-nightly.20251108",
70
+ "@aztec/blob-sink": "3.0.0-nightly.20251108",
71
+ "@aztec/constants": "3.0.0-nightly.20251108",
72
+ "@aztec/epoch-cache": "3.0.0-nightly.20251108",
73
+ "@aztec/ethereum": "3.0.0-nightly.20251108",
74
+ "@aztec/foundation": "3.0.0-nightly.20251108",
75
+ "@aztec/kv-store": "3.0.0-nightly.20251108",
76
+ "@aztec/l1-artifacts": "3.0.0-nightly.20251108",
77
+ "@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251108",
78
+ "@aztec/protocol-contracts": "3.0.0-nightly.20251108",
79
+ "@aztec/stdlib": "3.0.0-nightly.20251108",
80
+ "@aztec/telemetry-client": "3.0.0-nightly.20251108",
81
81
  "lodash.groupby": "^4.6.0",
82
82
  "lodash.omit": "^4.5.0",
83
83
  "tsc-watch": "^6.0.0",
@@ -16,7 +16,6 @@ import { Fr } from '@aztec/foundation/fields';
16
16
  import { type Logger, createLogger } from '@aztec/foundation/log';
17
17
  import { type PromiseWithResolvers, promiseWithResolvers } from '@aztec/foundation/promise';
18
18
  import { RunningPromise, makeLoggingErrorHandler } from '@aztec/foundation/running-promise';
19
- import { sleep } from '@aztec/foundation/sleep';
20
19
  import { count } from '@aztec/foundation/string';
21
20
  import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
22
21
  import type { CustomRange } from '@aztec/kv-store';
@@ -65,7 +64,6 @@ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
65
64
  import { type BlockHeader, type IndexedTxEffect, TxHash, TxReceipt } from '@aztec/stdlib/tx';
66
65
  import type { UInt64 } from '@aztec/stdlib/types';
67
66
  import {
68
- Attributes,
69
67
  type TelemetryClient,
70
68
  type Traceable,
71
69
  type Tracer,
@@ -75,7 +73,7 @@ import {
75
73
 
76
74
  import { EventEmitter } from 'events';
77
75
  import groupBy from 'lodash.groupby';
78
- import { type GetContractReturnType, createPublicClient, fallback, http } from 'viem';
76
+ import { type GetContractReturnType, type Hex, createPublicClient, fallback, http } from 'viem';
79
77
 
80
78
  import type { ArchiverDataStore, ArchiverL1SynchPoint } from './archiver_store.js';
81
79
  import type { ArchiverConfig } from './config.js';
@@ -108,9 +106,20 @@ function mapArchiverConfig(config: Partial<ArchiverConfig>) {
108
106
  pollingIntervalMs: config.archiverPollingIntervalMS,
109
107
  batchSize: config.archiverBatchSize,
110
108
  skipValidateBlockAttestations: config.skipValidateBlockAttestations,
109
+ maxAllowedEthClientDriftSeconds: config.maxAllowedEthClientDriftSeconds,
111
110
  };
112
111
  }
113
112
 
113
+ type RollupStatus = {
114
+ provenBlockNumber: number;
115
+ provenArchive: Hex;
116
+ pendingBlockNumber: number;
117
+ pendingArchive: Hex;
118
+ validationResult: ValidateBlockResult | undefined;
119
+ lastRetrievedBlock?: PublishedL2Block;
120
+ lastL1BlockWithL2Blocks?: bigint;
121
+ };
122
+
114
123
  /**
115
124
  * Pulls L2 blocks in a non-blocking manner and provides interface for their retrieval.
116
125
  * Responsible for handling robust L1 polling so that other components do not need to
@@ -118,7 +127,7 @@ function mapArchiverConfig(config: Partial<ArchiverConfig>) {
118
127
  */
119
128
  export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implements ArchiveSource, Traceable {
120
129
  /** A loop in which we will be continually fetching new L2 blocks. */
121
- private runningPromise?: RunningPromise;
130
+ private runningPromise: RunningPromise;
122
131
 
123
132
  private rollup: RollupContract;
124
133
  private inbox: InboxContract;
@@ -146,9 +155,15 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
146
155
  private readonly publicClient: ViemPublicClient,
147
156
  private readonly l1Addresses: { rollupAddress: EthAddress; inboxAddress: EthAddress; registryAddress: EthAddress },
148
157
  readonly dataStore: ArchiverDataStore,
149
- private config: { pollingIntervalMs: number; batchSize: number; skipValidateBlockAttestations?: boolean },
158
+ private config: {
159
+ pollingIntervalMs: number;
160
+ batchSize: number;
161
+ skipValidateBlockAttestations?: boolean;
162
+ maxAllowedEthClientDriftSeconds: number;
163
+ },
150
164
  private readonly blobSinkClient: BlobSinkClientInterface,
151
165
  private readonly epochCache: EpochCache,
166
+ private readonly dateProvider: DateProvider,
152
167
  private readonly instrumentation: ArchiverInstrumentation,
153
168
  private readonly l1constants: L1RollupConstants & { l1StartBlockHash: Buffer32; genesisArchiveRoot: Fr },
154
169
  private readonly log: Logger = createLogger('archiver'),
@@ -161,6 +176,15 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
161
176
  this.rollup = new RollupContract(publicClient, l1Addresses.rollupAddress);
162
177
  this.inbox = new InboxContract(publicClient, l1Addresses.inboxAddress);
163
178
  this.initialSyncPromise = promiseWithResolvers();
179
+
180
+ // Running promise starts with a small interval inbetween runs, so all iterations needed for the initial sync
181
+ // are done as fast as possible. This then gets updated once the initial sync completes.
182
+ this.runningPromise = new RunningPromise(
183
+ () => this.sync(),
184
+ this.log,
185
+ this.config.pollingIntervalMs / 10,
186
+ makeLoggingErrorHandler(this.log, NoBlobBodiesFoundError, BlockTagTooOldError),
187
+ );
164
188
  }
165
189
 
166
190
  /**
@@ -209,7 +233,10 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
209
233
  genesisArchiveRoot: Fr.fromHexString(genesisArchiveRoot),
210
234
  };
211
235
 
212
- const opts = merge({ pollingIntervalMs: 10_000, batchSize: 100 }, mapArchiverConfig(config));
236
+ const opts = merge(
237
+ { pollingIntervalMs: 10_000, batchSize: 100, maxAllowedEthClientDriftSeconds: 300 },
238
+ mapArchiverConfig(config),
239
+ );
213
240
 
214
241
  const epochCache = deps.epochCache ?? (await EpochCache.create(config.l1Contracts.rollupAddress, config, deps));
215
242
  const telemetry = deps.telemetry ?? getTelemetryClient();
@@ -221,6 +248,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
221
248
  opts,
222
249
  deps.blobSinkClient,
223
250
  epochCache,
251
+ deps.dateProvider ?? new DateProvider(),
224
252
  await ArchiverInstrumentation.new(telemetry, () => archiverStore.estimateSize()),
225
253
  l1Constants,
226
254
  );
@@ -238,38 +266,30 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
238
266
  * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
239
267
  */
240
268
  public async start(blockUntilSynced: boolean): Promise<void> {
241
- if (this.runningPromise) {
269
+ if (this.runningPromise.isRunning()) {
242
270
  throw new Error('Archiver is already running');
243
271
  }
244
272
 
245
273
  await this.blobSinkClient.testSources();
246
-
247
- if (blockUntilSynced) {
248
- while (!(await this.syncSafe(true))) {
249
- this.log.info(`Retrying initial archiver sync in ${this.config.pollingIntervalMs}ms`);
250
- await sleep(this.config.pollingIntervalMs);
251
- }
252
- }
253
-
254
- this.runningPromise = new RunningPromise(
255
- () => this.sync(false),
256
- this.log,
257
- this.config.pollingIntervalMs,
258
- makeLoggingErrorHandler(
259
- this.log,
260
- // Ignored errors will not log to the console
261
- // We ignore NoBlobBodiesFound as the message may not have been passed to the blob sink yet
262
- NoBlobBodiesFoundError,
263
- ),
274
+ await this.testEthereumNodeSynced();
275
+
276
+ // Log initial state for the archiver
277
+ const { l1StartBlock } = this.l1constants;
278
+ const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = l1StartBlock } = await this.store.getSynchPoint();
279
+ const currentL2Block = await this.getBlockNumber();
280
+ this.log.info(
281
+ `Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo} and L2 block ${currentL2Block}`,
282
+ { blocksSynchedTo, messagesSynchedTo, currentL2Block },
264
283
  );
265
284
 
285
+ // Start sync loop, and return the wait for initial sync if we are asked to block until synced
266
286
  this.runningPromise.start();
287
+ if (blockUntilSynced) {
288
+ return this.waitForInitialSync();
289
+ }
267
290
  }
268
291
 
269
292
  public syncImmediate() {
270
- if (!this.runningPromise) {
271
- throw new Error('Archiver is not running');
272
- }
273
293
  return this.runningPromise.trigger();
274
294
  }
275
295
 
@@ -277,27 +297,26 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
277
297
  return this.initialSyncPromise.promise;
278
298
  }
279
299
 
280
- private async syncSafe(initialRun: boolean) {
281
- try {
282
- await this.sync(initialRun);
283
- return true;
284
- } catch (error) {
285
- if (error instanceof NoBlobBodiesFoundError) {
286
- this.log.error(`Error syncing archiver: ${error.message}`);
287
- } else if (error instanceof BlockTagTooOldError) {
288
- this.log.warn(`Re-running archiver sync: ${error.message}`);
289
- } else {
290
- this.log.error('Error during archiver sync', error);
291
- }
292
- return false;
300
+ /** Checks that the ethereum node we are connected to has a latest timestamp no more than the allowed drift. Throw if not. */
301
+ private async testEthereumNodeSynced() {
302
+ const maxAllowedDelay = this.config.maxAllowedEthClientDriftSeconds;
303
+ if (maxAllowedDelay === 0) {
304
+ return;
305
+ }
306
+ const { number, timestamp: l1Timestamp } = await this.publicClient.getBlock({ includeTransactions: false });
307
+ const currentTime = BigInt(this.dateProvider.nowInSeconds());
308
+ if (currentTime - l1Timestamp > BigInt(maxAllowedDelay)) {
309
+ throw new Error(
310
+ `Ethereum node is out of sync (last block synced ${number} at ${l1Timestamp} vs current time ${currentTime})`,
311
+ );
293
312
  }
294
313
  }
295
314
 
296
315
  /**
297
316
  * Fetches logs from L1 contracts and processes them.
298
317
  */
299
- @trackSpan('Archiver.sync', initialRun => ({ [Attributes.INITIAL_SYNC]: initialRun }))
300
- private async sync(initialRun: boolean) {
318
+ @trackSpan('Archiver.sync')
319
+ private async sync() {
301
320
  /**
302
321
  * We keep track of three "pointers" to L1 blocks:
303
322
  * 1. the last L1 block that published an L2 block
@@ -307,8 +326,6 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
307
326
  * We do this to deal with L1 data providers that are eventually consistent (e.g. Infura).
308
327
  * We guard against seeing block X with no data at one point, and later, the provider processes the block and it has data.
309
328
  * The archiver will stay back, until there's data on L1 that will move the pointers forward.
310
- *
311
- * This code does not handle reorgs.
312
329
  */
313
330
  const { l1StartBlock, l1StartBlockHash } = this.l1constants;
314
331
  const {
@@ -320,13 +337,12 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
320
337
  const currentL1BlockNumber = currentL1Block.number;
321
338
  const currentL1BlockHash = Buffer32.fromString(currentL1Block.hash);
322
339
 
323
- if (initialRun) {
324
- this.log.info(
325
- `Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo}` +
326
- ` to current L1 block ${currentL1BlockNumber} with hash ${currentL1BlockHash.toString()}`,
327
- { blocksSynchedTo, messagesSynchedTo },
328
- );
329
- }
340
+ this.log.trace(`Starting new archiver sync iteration`, {
341
+ blocksSynchedTo,
342
+ messagesSynchedTo,
343
+ currentL1BlockNumber,
344
+ currentL1BlockHash,
345
+ });
330
346
 
331
347
  // ********** Ensuring Consistency of data pulled from L1 **********
332
348
 
@@ -356,6 +372,16 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
356
372
  ? (await this.publicClient.getBlock({ blockNumber: currentL1BlockNumber })).timestamp
357
373
  : this.l1Timestamp;
358
374
 
375
+ // Warn if the latest L1 block timestamp is too old
376
+ const maxAllowedDelay = this.config.maxAllowedEthClientDriftSeconds;
377
+ const now = this.dateProvider.nowInSeconds();
378
+ if (maxAllowedDelay > 0 && Number(currentL1Timestamp) <= now - maxAllowedDelay) {
379
+ this.log.warn(
380
+ `Latest L1 block ${currentL1BlockNumber} timestamp ${currentL1Timestamp} is too old. Make sure your Ethereum node is synced.`,
381
+ { currentL1BlockNumber, currentL1Timestamp, now, maxAllowedDelay },
382
+ );
383
+ }
384
+
359
385
  // ********** Events that are processed per L2 block **********
360
386
  if (currentL1BlockNumber > blocksSynchedTo) {
361
387
  // First we retrieve new L2 blocks and store them in the DB. This will also update the
@@ -372,6 +398,13 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
372
398
  currentL1Timestamp,
373
399
  );
374
400
 
401
+ // If the last block we processed had an invalid attestation, we manually advance the L1 syncpoint
402
+ // past it, since otherwise we'll keep downloading it and reprocessing it on every iteration until
403
+ // we get a valid block to advance the syncpoint.
404
+ if (!rollupStatus.validationResult?.valid && rollupStatus.lastL1BlockWithL2Blocks !== undefined) {
405
+ await this.store.setBlockSynchedL1BlockNumber(rollupStatus.lastL1BlockWithL2Blocks);
406
+ }
407
+
375
408
  // And lastly we check if we are missing any L2 blocks behind us due to a possible L1 reorg.
376
409
  // We only do this if rollup cant prune on the next submission. Otherwise we will end up
377
410
  // re-syncing the blocks we have just unwound above. We also dont do this if the last block is invalid,
@@ -388,15 +421,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
388
421
  // but the corresponding blocks have not been processed (see #12631).
389
422
  this.l1Timestamp = currentL1Timestamp;
390
423
  this.l1BlockNumber = currentL1BlockNumber;
391
- this.initialSyncComplete = true;
392
- this.initialSyncPromise.resolve();
393
424
 
394
- if (initialRun) {
395
- this.log.info(`Initial archiver sync to L1 block ${currentL1BlockNumber} complete.`, {
425
+ // We resolve the initial sync only once we've caught up with the latest L1 block number (with 1 block grace)
426
+ // so if the initial sync took too long, we still go for another iteration.
427
+ if (!this.initialSyncComplete && currentL1BlockNumber + 1n >= (await this.publicClient.getBlockNumber())) {
428
+ this.log.info(`Initial archiver sync to L1 block ${currentL1BlockNumber} complete`, {
396
429
  l1BlockNumber: currentL1BlockNumber,
397
430
  syncPoint: await this.store.getSynchPoint(),
398
431
  ...(await this.getL2Tips()),
399
432
  });
433
+ this.runningPromise.setPollingIntervalMS(this.config.pollingIntervalMs);
434
+ this.initialSyncComplete = true;
435
+ this.initialSyncPromise.resolve();
400
436
  }
401
437
  }
402
438
 
@@ -497,7 +533,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
497
533
  remoteMessagesState.totalMessagesInserted === localMessagesInserted &&
498
534
  remoteMessagesState.messagesRollingHash.equals(localLastMessage?.rollingHash ?? Buffer16.ZERO)
499
535
  ) {
500
- this.log.debug(
536
+ this.log.trace(
501
537
  `No L1 to L2 messages to query between L1 blocks ${messagesSyncPoint.l1BlockNumber} and ${currentL1BlockNumber}.`,
502
538
  );
503
539
  return;
@@ -629,7 +665,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
629
665
  return Buffer32.fromString(block.hash);
630
666
  }
631
667
 
632
- private async handleL2blocks(blocksSynchedTo: bigint, currentL1BlockNumber: bigint) {
668
+ private async handleL2blocks(blocksSynchedTo: bigint, currentL1BlockNumber: bigint): Promise<RollupStatus> {
633
669
  const localPendingBlockNumber = await this.getBlockNumber();
634
670
  const initialValidationResult: ValidateBlockResult | undefined = await this.store.getPendingChainValidationStatus();
635
671
  const [provenBlockNumber, provenArchive, pendingBlockNumber, pendingArchive, archiveForLocalPendingBlockNumber] =
@@ -759,7 +795,10 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
759
795
  }
760
796
 
761
797
  const archiveAtContract = await this.rollup.archiveAt(BigInt(candidateBlock.number));
762
-
798
+ this.log.trace(`Checking local block ${candidateBlock.number} with archive ${candidateBlock.archive.root}`, {
799
+ archiveAtContract,
800
+ archiveLocal: candidateBlock.archive.root.toString(),
801
+ });
763
802
  if (archiveAtContract === candidateBlock.archive.root.toString()) {
764
803
  break;
765
804
  }
@@ -782,6 +821,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
782
821
  let searchStartBlock: bigint = blocksSynchedTo;
783
822
  let searchEndBlock: bigint = blocksSynchedTo;
784
823
  let lastRetrievedBlock: PublishedL2Block | undefined;
824
+ let lastL1BlockWithL2Blocks: bigint | undefined = undefined;
785
825
 
786
826
  do {
787
827
  [searchStartBlock, searchEndBlock] = this.nextRange(searchEndBlock, currentL1BlockNumber);
@@ -805,9 +845,9 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
805
845
  continue;
806
846
  }
807
847
 
808
- const lastProcessedL1BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].l1.blockNumber;
809
848
  this.log.debug(
810
- `Retrieved ${retrievedBlocks.length} new L2 blocks between L1 blocks ${searchStartBlock} and ${searchEndBlock} with last processed L1 block ${lastProcessedL1BlockNumber}.`,
849
+ `Retrieved ${retrievedBlocks.length} new L2 blocks between L1 blocks ${searchStartBlock} and ${searchEndBlock}`,
850
+ { lastProcessedL1Block: retrievedBlocks[retrievedBlocks.length - 1].l1, searchStartBlock, searchEndBlock },
811
851
  );
812
852
 
813
853
  const publishedBlocks = await Promise.all(retrievedBlocks.map(b => retrievedBlockToPublishedL2Block(b)));
@@ -900,12 +940,13 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
900
940
  });
901
941
  }
902
942
  lastRetrievedBlock = validBlocks.at(-1) ?? lastRetrievedBlock;
943
+ lastL1BlockWithL2Blocks = publishedBlocks.at(-1)?.l1.blockNumber ?? lastL1BlockWithL2Blocks;
903
944
  } while (searchEndBlock < currentL1BlockNumber);
904
945
 
905
946
  // Important that we update AFTER inserting the blocks.
906
947
  await updateProvenBlock();
907
948
 
908
- return { ...rollupStatus, lastRetrievedBlock };
949
+ return { ...rollupStatus, lastRetrievedBlock, lastL1BlockWithL2Blocks };
909
950
  }
910
951
 
911
952
  private async checkForNewBlocksBeforeL1SyncPoint(
@@ -955,9 +996,6 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
955
996
 
956
997
  /** Resumes the archiver after a stop. */
957
998
  public resume() {
958
- if (!this.runningPromise) {
959
- throw new Error(`Archiver was never started`);
960
- }
961
999
  if (this.runningPromise.isRunning()) {
962
1000
  this.log.warn(`Archiver already running`);
963
1001
  }
@@ -971,7 +1009,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
971
1009
  */
972
1010
  public async stop(): Promise<void> {
973
1011
  this.log.debug('Stopping...');
974
- await this.runningPromise?.stop();
1012
+ await this.runningPromise.stop();
975
1013
 
976
1014
  this.log.info('Stopped.');
977
1015
  return Promise.resolve();
@@ -50,6 +50,11 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
50
50
  description: 'Whether to skip validating block attestations (use only for testing).',
51
51
  ...booleanConfigHelper(false),
52
52
  },
53
+ maxAllowedEthClientDriftSeconds: {
54
+ env: 'MAX_ALLOWED_ETH_CLIENT_DRIFT_SECONDS',
55
+ description: 'Maximum allowed drift in seconds between the Ethereum client and current time.',
56
+ ...numberConfigHelper(300),
57
+ },
53
58
  ...chainConfigMappings,
54
59
  ...l1ReaderConfigMappings,
55
60
  viemPollingIntervalMS: {
@@ -309,6 +309,7 @@ async function getBlockFromRollupTx(
309
309
  targetCommitteeSize: number,
310
310
  logger: Logger,
311
311
  ): Promise<Omit<RetrievedL2Block, 'l1' | 'chainId' | 'version'>> {
312
+ logger.trace(`Fetching L2 block ${l2BlockNumber} from rollup tx ${txHash}`);
312
313
  const { input: forwarderData, blockHash } = await publicClient.getTransaction({ hash: txHash });
313
314
 
314
315
  const rollupData = extractRollupProposeCalldata(forwarderData, rollupAddress);
@@ -344,6 +345,7 @@ async function getBlockFromRollupTx(
344
345
  archive: decodedArgs.archive,
345
346
  stateReference: decodedArgs.stateReference,
346
347
  header: decodedArgs.header,
348
+ l1BlockHash: blockHash,
347
349
  blobHashes,
348
350
  attestations,
349
351
  packedAttestations,
@@ -43,7 +43,6 @@ export async function validateBlockAttestations(
43
43
  return { valid: true };
44
44
  }
45
45
 
46
- const committeeSet = new Set(committee.map(member => member.toString()));
47
46
  const requiredAttestationCount = Math.floor((committee.length * 2) / 3) + 1;
48
47
 
49
48
  const failedValidationResult = <TReason extends ValidateBlockNegativeResult['reason']>(reason: TReason) => ({
@@ -70,15 +69,20 @@ export async function validateBlockAttestations(
70
69
  return { ...failedValidationResult('invalid-attestation'), invalidIndex: i };
71
70
  }
72
71
 
73
- // Check if the attestor is in the committee
72
+ // Check if the attestor at this index matches the committee member at the same index
74
73
  if (info.status === 'recovered-from-signature' || info.status === 'provided-as-address') {
75
74
  const signer = info.address.toString();
76
- if (!committeeSet.has(signer)) {
77
- logger?.warn(`Attestation from non-committee member ${signer} at slot ${slot}`, {
78
- committee,
79
- invalidIndex: i,
80
- ...logData,
81
- });
75
+ const expectedCommitteeMember = committee[i]?.toString();
76
+
77
+ if (!expectedCommitteeMember || signer !== expectedCommitteeMember) {
78
+ logger?.warn(
79
+ `Attestation at index ${i} from ${signer} does not match expected committee member ${expectedCommitteeMember} at slot ${slot}`,
80
+ {
81
+ committee,
82
+ invalidIndex: i,
83
+ ...logData,
84
+ },
85
+ );
82
86
  return { ...failedValidationResult('invalid-attestation'), invalidIndex: i };
83
87
  }
84
88
  }