@aztec/archiver 0.0.1-commit.d3ec352c → 0.0.1-commit.fcb71a6

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 (122) hide show
  1. package/dest/archiver/archiver.d.ts +84 -70
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +439 -228
  4. package/dest/archiver/archiver_store.d.ts +95 -43
  5. package/dest/archiver/archiver_store.d.ts.map +1 -1
  6. package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
  7. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  8. package/dest/archiver/archiver_store_test_suite.js +1847 -366
  9. package/dest/archiver/config.d.ts +5 -4
  10. package/dest/archiver/config.d.ts.map +1 -1
  11. package/dest/archiver/config.js +10 -3
  12. package/dest/archiver/errors.d.ts +25 -1
  13. package/dest/archiver/errors.d.ts.map +1 -1
  14. package/dest/archiver/errors.js +37 -0
  15. package/dest/archiver/index.d.ts +2 -2
  16. package/dest/archiver/index.d.ts.map +1 -1
  17. package/dest/archiver/instrumentation.d.ts +3 -1
  18. package/dest/archiver/instrumentation.d.ts.map +1 -1
  19. package/dest/archiver/instrumentation.js +11 -0
  20. package/dest/archiver/kv_archiver_store/block_store.d.ts +50 -18
  21. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  22. package/dest/archiver/kv_archiver_store/block_store.js +320 -84
  23. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +2 -2
  24. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  25. package/dest/archiver/kv_archiver_store/contract_class_store.js +1 -1
  26. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +2 -2
  27. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
  28. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +40 -51
  29. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  30. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +65 -48
  31. package/dest/archiver/kv_archiver_store/log_store.d.ts +12 -16
  32. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  33. package/dest/archiver/kv_archiver_store/log_store.js +148 -84
  34. package/dest/archiver/kv_archiver_store/message_store.d.ts +6 -5
  35. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  36. package/dest/archiver/kv_archiver_store/message_store.js +15 -14
  37. package/dest/archiver/l1/bin/retrieve-calldata.d.ts +3 -0
  38. package/dest/archiver/l1/bin/retrieve-calldata.d.ts.map +1 -0
  39. package/dest/archiver/l1/bin/retrieve-calldata.js +149 -0
  40. package/dest/archiver/l1/calldata_retriever.d.ts +112 -0
  41. package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -0
  42. package/dest/archiver/l1/calldata_retriever.js +471 -0
  43. package/dest/archiver/l1/data_retrieval.d.ts +90 -0
  44. package/dest/archiver/l1/data_retrieval.d.ts.map +1 -0
  45. package/dest/archiver/{data_retrieval.js → l1/data_retrieval.js} +50 -106
  46. package/dest/archiver/l1/debug_tx.d.ts +19 -0
  47. package/dest/archiver/l1/debug_tx.d.ts.map +1 -0
  48. package/dest/archiver/l1/debug_tx.js +73 -0
  49. package/dest/archiver/l1/spire_proposer.d.ts +70 -0
  50. package/dest/archiver/l1/spire_proposer.d.ts.map +1 -0
  51. package/dest/archiver/l1/spire_proposer.js +157 -0
  52. package/dest/archiver/l1/trace_tx.d.ts +97 -0
  53. package/dest/archiver/l1/trace_tx.d.ts.map +1 -0
  54. package/dest/archiver/l1/trace_tx.js +91 -0
  55. package/dest/archiver/l1/types.d.ts +12 -0
  56. package/dest/archiver/l1/types.d.ts.map +1 -0
  57. package/dest/archiver/l1/types.js +3 -0
  58. package/dest/archiver/l1/validate_trace.d.ts +29 -0
  59. package/dest/archiver/l1/validate_trace.d.ts.map +1 -0
  60. package/dest/archiver/l1/validate_trace.js +150 -0
  61. package/dest/archiver/structs/inbox_message.d.ts +4 -4
  62. package/dest/archiver/structs/inbox_message.d.ts.map +1 -1
  63. package/dest/archiver/structs/inbox_message.js +6 -6
  64. package/dest/archiver/structs/published.d.ts +1 -2
  65. package/dest/archiver/structs/published.d.ts.map +1 -1
  66. package/dest/factory.d.ts +1 -1
  67. package/dest/factory.js +1 -1
  68. package/dest/index.d.ts +2 -2
  69. package/dest/index.d.ts.map +1 -1
  70. package/dest/index.js +1 -1
  71. package/dest/test/mock_archiver.d.ts +4 -5
  72. package/dest/test/mock_archiver.d.ts.map +1 -1
  73. package/dest/test/mock_archiver.js +5 -9
  74. package/dest/test/mock_l1_to_l2_message_source.d.ts +5 -6
  75. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  76. package/dest/test/mock_l1_to_l2_message_source.js +7 -11
  77. package/dest/test/mock_l2_block_source.d.ts +11 -4
  78. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  79. package/dest/test/mock_l2_block_source.js +18 -17
  80. package/dest/test/mock_structs.d.ts +3 -2
  81. package/dest/test/mock_structs.d.ts.map +1 -1
  82. package/dest/test/mock_structs.js +9 -9
  83. package/package.json +15 -14
  84. package/src/archiver/archiver.ts +567 -290
  85. package/src/archiver/archiver_store.ts +104 -42
  86. package/src/archiver/archiver_store_test_suite.ts +1895 -347
  87. package/src/archiver/config.ts +15 -10
  88. package/src/archiver/errors.ts +64 -0
  89. package/src/archiver/index.ts +1 -1
  90. package/src/archiver/instrumentation.ts +14 -0
  91. package/src/archiver/kv_archiver_store/block_store.ts +435 -95
  92. package/src/archiver/kv_archiver_store/contract_class_store.ts +1 -1
  93. package/src/archiver/kv_archiver_store/contract_instance_store.ts +1 -1
  94. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +81 -66
  95. package/src/archiver/kv_archiver_store/log_store.ts +208 -99
  96. package/src/archiver/kv_archiver_store/message_store.ts +21 -18
  97. package/src/archiver/l1/README.md +98 -0
  98. package/src/archiver/l1/bin/retrieve-calldata.ts +182 -0
  99. package/src/archiver/l1/calldata_retriever.ts +641 -0
  100. package/src/archiver/{data_retrieval.ts → l1/data_retrieval.ts} +96 -161
  101. package/src/archiver/l1/debug_tx.ts +99 -0
  102. package/src/archiver/l1/spire_proposer.ts +160 -0
  103. package/src/archiver/l1/trace_tx.ts +128 -0
  104. package/src/archiver/l1/types.ts +13 -0
  105. package/src/archiver/l1/validate_trace.ts +211 -0
  106. package/src/archiver/structs/inbox_message.ts +7 -8
  107. package/src/archiver/structs/published.ts +0 -1
  108. package/src/factory.ts +1 -1
  109. package/src/index.ts +1 -1
  110. package/src/test/fixtures/debug_traceTransaction-multicall3.json +88 -0
  111. package/src/test/fixtures/debug_traceTransaction-multiplePropose.json +153 -0
  112. package/src/test/fixtures/debug_traceTransaction-proxied.json +122 -0
  113. package/src/test/fixtures/trace_transaction-multicall3.json +65 -0
  114. package/src/test/fixtures/trace_transaction-multiplePropose.json +319 -0
  115. package/src/test/fixtures/trace_transaction-proxied.json +128 -0
  116. package/src/test/fixtures/trace_transaction-randomRevert.json +216 -0
  117. package/src/test/mock_archiver.ts +6 -11
  118. package/src/test/mock_l1_to_l2_message_source.ts +6 -11
  119. package/src/test/mock_l2_block_source.ts +22 -18
  120. package/src/test/mock_structs.ts +10 -10
  121. package/dest/archiver/data_retrieval.d.ts +0 -80
  122. package/dest/archiver/data_retrieval.d.ts.map +0 -1
@@ -1,25 +1,23 @@
1
- import type { BlobSinkClientInterface } from '@aztec/blob-sink/client';
1
+ import type { BlobClientInterface } from '@aztec/blob-client/client';
2
2
  import { GENESIS_BLOCK_HEADER_HASH } from '@aztec/constants';
3
3
  import { EpochCache } from '@aztec/epoch-cache';
4
- import {
5
- BlockTagTooOldError,
6
- InboxContract,
7
- type L1BlockId,
8
- RollupContract,
9
- type ViemPublicClient,
10
- createEthereumChain,
11
- } from '@aztec/ethereum';
4
+ import { createEthereumChain } from '@aztec/ethereum/chain';
5
+ import { BlockTagTooOldError, InboxContract, RollupContract } from '@aztec/ethereum/contracts';
6
+ import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
7
+ import type { L1BlockId } from '@aztec/ethereum/l1-types';
8
+ import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
12
9
  import { maxBigint } from '@aztec/foundation/bigint';
13
10
  import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
14
11
  import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
15
12
  import { merge, pick } from '@aztec/foundation/collection';
13
+ import { Fr } from '@aztec/foundation/curves/bn254';
16
14
  import type { EthAddress } from '@aztec/foundation/eth-address';
17
- import { Fr } from '@aztec/foundation/fields';
18
15
  import { type Logger, createLogger } from '@aztec/foundation/log';
19
16
  import { type PromiseWithResolvers, promiseWithResolvers } from '@aztec/foundation/promise';
20
17
  import { RunningPromise, makeLoggingErrorHandler } from '@aztec/foundation/running-promise';
21
18
  import { count } from '@aztec/foundation/string';
22
19
  import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
20
+ import { isDefined } from '@aztec/foundation/types';
23
21
  import type { CustomRange } from '@aztec/kv-store';
24
22
  import { RollupAbi } from '@aztec/l1-artifacts';
25
23
  import {
@@ -35,13 +33,17 @@ import type { FunctionSelector } from '@aztec/stdlib/abi';
35
33
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
36
34
  import {
37
35
  type ArchiverEmitter,
36
+ CheckpointedL2Block,
37
+ CommitteeAttestation,
38
38
  L2Block,
39
+ L2BlockNew,
40
+ type L2BlockSink,
39
41
  type L2BlockSource,
40
42
  L2BlockSourceEvents,
41
43
  type L2Tips,
42
44
  PublishedL2Block,
43
45
  } from '@aztec/stdlib/block';
44
- import type { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
46
+ import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
45
47
  import {
46
48
  type ContractClassPublic,
47
49
  type ContractDataSource,
@@ -62,8 +64,16 @@ import {
62
64
  } from '@aztec/stdlib/epoch-helpers';
63
65
  import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
64
66
  import type { L2LogsSource } from '@aztec/stdlib/interfaces/server';
65
- import { ContractClassLog, type LogFilter, type PrivateLog, type PublicLog, TxScopedL2Log } from '@aztec/stdlib/logs';
66
- import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
67
+ import {
68
+ ContractClassLog,
69
+ type LogFilter,
70
+ type PrivateLog,
71
+ type PublicLog,
72
+ type SiloedTag,
73
+ Tag,
74
+ TxScopedL2Log,
75
+ } from '@aztec/stdlib/logs';
76
+ import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
67
77
  import type { CheckpointHeader } from '@aztec/stdlib/rollup';
68
78
  import { type BlockHeader, type IndexedTxEffect, TxHash, TxReceipt } from '@aztec/stdlib/tx';
69
79
  import type { UInt64 } from '@aztec/stdlib/types';
@@ -81,14 +91,16 @@ import { type GetContractReturnType, type Hex, createPublicClient, fallback, htt
81
91
 
82
92
  import type { ArchiverDataStore, ArchiverL1SynchPoint } from './archiver_store.js';
83
93
  import type { ArchiverConfig } from './config.js';
94
+ import { InitialCheckpointNumberNotSequentialError, NoBlobBodiesFoundError } from './errors.js';
95
+ import { ArchiverInstrumentation } from './instrumentation.js';
96
+ import type { CheckpointData } from './kv_archiver_store/block_store.js';
84
97
  import {
85
98
  retrieveCheckpointsFromRollup,
86
99
  retrieveL1ToL2Message,
87
100
  retrieveL1ToL2Messages,
88
101
  retrievedToPublishedCheckpoint,
89
- } from './data_retrieval.js';
90
- import { InitialBlockNumberNotSequentialError, NoBlobBodiesFoundError } from './errors.js';
91
- import { ArchiverInstrumentation } from './instrumentation.js';
102
+ } from './l1/data_retrieval.js';
103
+ import { validateAndLogTraceAvailability } from './l1/validate_trace.js';
92
104
  import type { InboxMessage } from './structs/inbox_message.js';
93
105
  import { type ValidateBlockResult, validateCheckpointAttestations } from './validation.js';
94
106
 
@@ -97,9 +109,16 @@ import { type ValidateBlockResult, validateCheckpointAttestations } from './vali
97
109
  */
98
110
  export type ArchiveSource = L2BlockSource & L2LogsSource & ContractDataSource & L1ToL2MessageSource;
99
111
 
112
+ /** Request to add a block to the archiver, queued for processing by the sync loop. */
113
+ type AddBlockRequest = {
114
+ block: L2BlockNew;
115
+ resolve: () => void;
116
+ reject: (err: Error) => void;
117
+ };
118
+
100
119
  export type ArchiverDeps = {
101
120
  telemetry?: TelemetryClient;
102
- blobSinkClient: BlobSinkClientInterface;
121
+ blobClient: BlobClientInterface;
103
122
  epochCache?: EpochCache;
104
123
  dateProvider?: DateProvider;
105
124
  };
@@ -110,6 +129,7 @@ function mapArchiverConfig(config: Partial<ArchiverConfig>) {
110
129
  batchSize: config.archiverBatchSize,
111
130
  skipValidateBlockAttestations: config.skipValidateBlockAttestations,
112
131
  maxAllowedEthClientDriftSeconds: config.maxAllowedEthClientDriftSeconds,
132
+ ethereumAllowNoDebugHosts: config.ethereumAllowNoDebugHosts,
113
133
  };
114
134
  }
115
135
 
@@ -128,7 +148,10 @@ type RollupStatus = {
128
148
  * Responsible for handling robust L1 polling so that other components do not need to
129
149
  * concern themselves with it.
130
150
  */
131
- export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implements ArchiveSource, Traceable {
151
+ export class Archiver
152
+ extends (EventEmitter as new () => ArchiverEmitter)
153
+ implements ArchiveSource, L2BlockSink, Traceable
154
+ {
132
155
  /** A loop in which we will be continually fetching new checkpoints. */
133
156
  private runningPromise: RunningPromise;
134
157
 
@@ -142,11 +165,15 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
142
165
  private initialSyncComplete: boolean = false;
143
166
  private initialSyncPromise: PromiseWithResolvers<void>;
144
167
 
168
+ /** Queue of blocks to be added to the store, processed by the sync loop. */
169
+ private blockQueue: AddBlockRequest[] = [];
170
+
145
171
  public readonly tracer: Tracer;
146
172
 
147
173
  /**
148
174
  * Creates a new instance of the Archiver.
149
175
  * @param publicClient - A client for interacting with the Ethereum node.
176
+ * @param debugClient - A client for interacting with the Ethereum node for debug/trace methods.
150
177
  * @param rollupAddress - Ethereum address of the rollup contract.
151
178
  * @param inboxAddress - Ethereum address of the inbox contract.
152
179
  * @param registryAddress - Ethereum address of the registry contract.
@@ -156,15 +183,20 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
156
183
  */
157
184
  constructor(
158
185
  private readonly publicClient: ViemPublicClient,
159
- private readonly l1Addresses: { rollupAddress: EthAddress; inboxAddress: EthAddress; registryAddress: EthAddress },
186
+ private readonly debugClient: ViemPublicDebugClient,
187
+ private readonly l1Addresses: Pick<
188
+ L1ContractAddresses,
189
+ 'rollupAddress' | 'inboxAddress' | 'registryAddress' | 'governanceProposerAddress' | 'slashFactoryAddress'
190
+ > & { slashingProposerAddress: EthAddress },
160
191
  readonly dataStore: ArchiverDataStore,
161
192
  private config: {
162
193
  pollingIntervalMs: number;
163
194
  batchSize: number;
164
195
  skipValidateBlockAttestations?: boolean;
165
196
  maxAllowedEthClientDriftSeconds: number;
197
+ ethereumAllowNoDebugHosts?: boolean;
166
198
  },
167
- private readonly blobSinkClient: BlobSinkClientInterface,
199
+ private readonly blobClient: BlobClientInterface,
168
200
  private readonly epochCache: EpochCache,
169
201
  private readonly dateProvider: DateProvider,
170
202
  private readonly instrumentation: ArchiverInstrumentation,
@@ -206,18 +238,28 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
206
238
  const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
207
239
  const publicClient = createPublicClient({
208
240
  chain: chain.chainInfo,
209
- transport: fallback(config.l1RpcUrls.map(url => http(url))),
241
+ transport: fallback(config.l1RpcUrls.map(url => http(url, { batch: false }))),
210
242
  pollingInterval: config.viemPollingIntervalMS,
211
243
  });
212
244
 
245
+ // Create debug client using debug RPC URLs if available, otherwise fall back to regular RPC URLs
246
+ const debugRpcUrls = config.l1DebugRpcUrls.length > 0 ? config.l1DebugRpcUrls : config.l1RpcUrls;
247
+ const debugClient = createPublicClient({
248
+ chain: chain.chainInfo,
249
+ transport: fallback(debugRpcUrls.map(url => http(url, { batch: false }))),
250
+ pollingInterval: config.viemPollingIntervalMS,
251
+ }) as ViemPublicDebugClient;
252
+
213
253
  const rollup = new RollupContract(publicClient, config.l1Contracts.rollupAddress);
214
254
 
215
- const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, genesisArchiveRoot] = await Promise.all([
216
- rollup.getL1StartBlock(),
217
- rollup.getL1GenesisTime(),
218
- rollup.getProofSubmissionEpochs(),
219
- rollup.getGenesisArchiveTreeRoot(),
220
- ] as const);
255
+ const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, genesisArchiveRoot, slashingProposerAddress] =
256
+ await Promise.all([
257
+ rollup.getL1StartBlock(),
258
+ rollup.getL1GenesisTime(),
259
+ rollup.getProofSubmissionEpochs(),
260
+ rollup.getGenesisArchiveTreeRoot(),
261
+ rollup.getSlashingProposerAddress(),
262
+ ] as const);
221
263
 
222
264
  const l1StartBlockHash = await publicClient
223
265
  .getBlock({ blockNumber: l1StartBlock, includeTransactions: false })
@@ -237,7 +279,12 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
237
279
  };
238
280
 
239
281
  const opts = merge(
240
- { pollingIntervalMs: 10_000, batchSize: 100, maxAllowedEthClientDriftSeconds: 300 },
282
+ {
283
+ pollingIntervalMs: 10_000,
284
+ batchSize: 100,
285
+ maxAllowedEthClientDriftSeconds: 300,
286
+ ethereumAllowNoDebugHosts: false,
287
+ },
241
288
  mapArchiverConfig(config),
242
289
  );
243
290
 
@@ -246,10 +293,11 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
246
293
 
247
294
  const archiver = new Archiver(
248
295
  publicClient,
249
- config.l1Contracts,
296
+ debugClient,
297
+ { ...config.l1Contracts, slashingProposerAddress },
250
298
  archiverStore,
251
299
  opts,
252
- deps.blobSinkClient,
300
+ deps.blobClient,
253
301
  epochCache,
254
302
  deps.dateProvider ?? new DateProvider(),
255
303
  await ArchiverInstrumentation.new(telemetry, () => archiverStore.estimateSize()),
@@ -273,16 +321,17 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
273
321
  throw new Error('Archiver is already running');
274
322
  }
275
323
 
276
- await this.blobSinkClient.testSources();
324
+ await this.blobClient.testSources();
277
325
  await this.testEthereumNodeSynced();
326
+ await validateAndLogTraceAvailability(this.debugClient, this.config.ethereumAllowNoDebugHosts ?? false);
278
327
 
279
328
  // Log initial state for the archiver
280
329
  const { l1StartBlock } = this.l1constants;
281
330
  const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = l1StartBlock } = await this.store.getSynchPoint();
282
- const currentL2Block = await this.getBlockNumber();
331
+ const currentL2Checkpoint = await this.getSynchedCheckpointNumber();
283
332
  this.log.info(
284
- `Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo} and L2 block ${currentL2Block}`,
285
- { blocksSynchedTo, messagesSynchedTo, currentL2Block },
333
+ `Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo} and L2 checkpoint ${currentL2Checkpoint}`,
334
+ { blocksSynchedTo, messagesSynchedTo, currentL2Checkpoint },
286
335
  );
287
336
 
288
337
  // Start sync loop, and return the wait for initial sync if we are asked to block until synced
@@ -296,6 +345,51 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
296
345
  return this.runningPromise.trigger();
297
346
  }
298
347
 
348
+ /**
349
+ * Queues a block to be added to the archiver store and triggers processing.
350
+ * The block will be processed by the sync loop.
351
+ * Implements the L2BlockSink interface.
352
+ * @param block - The L2 block to add.
353
+ * @returns A promise that resolves when the block has been added to the store, or rejects on error.
354
+ */
355
+ public addBlock(block: L2BlockNew): Promise<void> {
356
+ return new Promise<void>((resolve, reject) => {
357
+ this.blockQueue.push({ block, resolve, reject });
358
+ this.log.debug(`Queued block ${block.number} for processing`);
359
+ // Trigger an immediate sync, but don't wait for it - the promise resolves when the block is processed
360
+ this.syncImmediate().catch(err => {
361
+ this.log.error(`Sync immediate call failed: ${err}`);
362
+ });
363
+ });
364
+ }
365
+
366
+ /**
367
+ * Processes all queued blocks, adding them to the store.
368
+ * Called at the beginning of each sync iteration.
369
+ * Blocks are processed in the order they were queued.
370
+ */
371
+ private async processQueuedBlocks(): Promise<void> {
372
+ if (this.blockQueue.length === 0) {
373
+ return;
374
+ }
375
+
376
+ // Take all blocks from the queue
377
+ const queuedItems = this.blockQueue.splice(0, this.blockQueue.length);
378
+ this.log.debug(`Processing ${queuedItems.length} queued block(s)`);
379
+
380
+ // Process each block individually to properly resolve/reject each promise
381
+ for (const { block, resolve, reject } of queuedItems) {
382
+ try {
383
+ await this.store.addBlocks([block]);
384
+ this.log.debug(`Added block ${block.number} to store`);
385
+ resolve();
386
+ } catch (err: any) {
387
+ this.log.error(`Failed to add block ${block.number} to store: ${err.message}`);
388
+ reject(err);
389
+ }
390
+ }
391
+ }
392
+
299
393
  public waitForInitialSync() {
300
394
  return this.initialSyncPromise.promise;
301
395
  }
@@ -315,11 +409,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
315
409
  }
316
410
  }
317
411
 
318
- /**
319
- * Fetches logs from L1 contracts and processes them.
320
- */
321
- @trackSpan('Archiver.sync')
322
- private async sync() {
412
+ private async syncFromL1() {
323
413
  /**
324
414
  * We keep track of three "pointers" to L1 blocks:
325
415
  * 1. the last L1 block that published an L2 block
@@ -405,7 +495,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
405
495
  // past it, since otherwise we'll keep downloading it and reprocessing it on every iteration until
406
496
  // we get a valid checkpoint to advance the syncpoint.
407
497
  if (!rollupStatus.validationResult?.valid && rollupStatus.lastL1BlockWithCheckpoint !== undefined) {
408
- await this.store.setBlockSynchedL1BlockNumber(rollupStatus.lastL1BlockWithCheckpoint);
498
+ await this.store.setCheckpointSynchedL1BlockNumber(rollupStatus.lastL1BlockWithCheckpoint);
409
499
  }
410
500
 
411
501
  // And lastly we check if we are missing any checkpoints behind us due to a possible L1 reorg.
@@ -439,6 +529,17 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
439
529
  }
440
530
  }
441
531
 
532
+ /**
533
+ * Fetches logs from L1 contracts and processes them.
534
+ */
535
+ @trackSpan('Archiver.sync')
536
+ private async sync() {
537
+ // Process any queued blocks first, before doing L1 sync
538
+ await this.processQueuedBlocks();
539
+ // Now perform L1 sync
540
+ await this.syncFromL1();
541
+ }
542
+
442
543
  /** Queries the rollup contract on whether a prune can be executed on the immediate next L1 block. */
443
544
  private async canPrune(currentL1BlockNumber: bigint, currentL1Timestamp: bigint) {
444
545
  const time = (currentL1Timestamp ?? 0n) + BigInt(this.l1constants.ethereumSlotDuration);
@@ -477,13 +578,26 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
477
578
 
478
579
  const checkpointsToUnwind = localPendingCheckpointNumber - provenCheckpointNumber;
479
580
 
480
- const checkpoints = await this.getCheckpoints(pruneFrom, checkpointsToUnwind);
581
+ const checkpointPromises = Array.from({ length: checkpointsToUnwind })
582
+ .fill(0)
583
+ .map((_, i) => this.store.getCheckpointData(CheckpointNumber(i + pruneFrom)));
584
+ const checkpoints = await Promise.all(checkpointPromises);
585
+
586
+ const blockPromises = await Promise.all(
587
+ checkpoints
588
+ .filter(isDefined)
589
+ .map(cp => this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber))),
590
+ );
591
+ const newBlocks = blockPromises.filter(isDefined).flat();
592
+
593
+ // TODO(pw/mbps): Don't convert to legacy blocks here
594
+ const blocks: L2Block[] = (await Promise.all(newBlocks.map(x => this.getBlock(x.number)))).filter(isDefined);
481
595
 
482
596
  // Emit an event for listening services to react to the chain prune
483
597
  this.emit(L2BlockSourceEvents.L2PruneDetected, {
484
598
  type: L2BlockSourceEvents.L2PruneDetected,
485
599
  epochNumber: pruneFromEpochNumber,
486
- blocks: checkpoints.flatMap(c => L2Block.fromCheckpoint(c)),
600
+ blocks,
487
601
  });
488
602
 
489
603
  this.log.debug(
@@ -594,7 +708,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
594
708
  // Log stats for messages retrieved (if any).
595
709
  if (messageCount > 0) {
596
710
  this.log.info(
597
- `Retrieved ${messageCount} new L1 to L2 messages up to message with index ${lastMessage?.index} for L2 block ${lastMessage?.l2BlockNumber}`,
711
+ `Retrieved ${messageCount} new L1 to L2 messages up to message with index ${lastMessage?.index} for checkpoint ${lastMessage?.checkpointNumber}`,
598
712
  { lastMessage, messageCount },
599
713
  );
600
714
  }
@@ -711,17 +825,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
711
825
  }
712
826
  }
713
827
 
714
- const localCheckpointForDestinationProvenCheckpointNumber = await this.getCheckpoint(provenCheckpointNumber);
828
+ const localCheckpointForDestinationProvenCheckpointNumber =
829
+ await this.store.getCheckpointData(provenCheckpointNumber);
715
830
 
716
831
  // Sanity check. I've hit what seems to be a state where the proven checkpoint is set to a value greater than the latest
717
832
  // synched checkpoint when requesting L2Tips from the archiver. This is the only place where the proven checkpoint is set.
718
833
  const synched = await this.getSynchedCheckpointNumber();
719
834
  if (
720
835
  localCheckpointForDestinationProvenCheckpointNumber &&
721
- synched < localCheckpointForDestinationProvenCheckpointNumber.number
836
+ synched < localCheckpointForDestinationProvenCheckpointNumber.checkpointNumber
722
837
  ) {
723
838
  this.log.error(
724
- `Hit local checkpoint greater than last synched checkpoint: ${localCheckpointForDestinationProvenCheckpointNumber.number} > ${synched}`,
839
+ `Hit local checkpoint greater than last synched checkpoint: ${localCheckpointForDestinationProvenCheckpointNumber.checkpointNumber} > ${synched}`,
725
840
  );
726
841
  }
727
842
 
@@ -731,7 +846,6 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
731
846
  }`,
732
847
  );
733
848
 
734
- const lastProvenBlockNumber = await this.getLastBlockNumberInCheckpoint(provenCheckpointNumber);
735
849
  if (
736
850
  localCheckpointForDestinationProvenCheckpointNumber &&
737
851
  provenArchive === localCheckpointForDestinationProvenCheckpointNumber.archive.root.toString()
@@ -744,25 +858,29 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
744
858
  });
745
859
  const provenSlotNumber = localCheckpointForDestinationProvenCheckpointNumber.header.slotNumber;
746
860
  const provenEpochNumber: EpochNumber = getEpochAtSlot(provenSlotNumber, this.l1constants);
861
+ const lastBlockNumberInCheckpoint =
862
+ localCheckpointForDestinationProvenCheckpointNumber.startBlock +
863
+ localCheckpointForDestinationProvenCheckpointNumber.numBlocks -
864
+ 1;
747
865
 
748
866
  this.emit(L2BlockSourceEvents.L2BlockProven, {
749
867
  type: L2BlockSourceEvents.L2BlockProven,
750
- blockNumber: lastProvenBlockNumber,
868
+ blockNumber: BlockNumber(lastBlockNumberInCheckpoint),
751
869
  slotNumber: provenSlotNumber,
752
870
  epochNumber: provenEpochNumber,
753
871
  });
872
+ this.instrumentation.updateLastProvenBlock(lastBlockNumberInCheckpoint);
754
873
  } else {
755
874
  this.log.trace(`Proven checkpoint ${provenCheckpointNumber} already stored.`);
756
875
  }
757
876
  }
758
- this.instrumentation.updateLastProvenBlock(lastProvenBlockNumber);
759
877
  };
760
878
 
761
879
  // This is an edge case that we only hit if there are no proposed checkpoints.
762
880
  // If we have 0 checkpoints locally and there are no checkpoints onchain there is nothing to do.
763
881
  const noCheckpoints = localPendingCheckpointNumber === 0 && pendingCheckpointNumber === 0;
764
882
  if (noCheckpoints) {
765
- await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
883
+ await this.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
766
884
  this.log.debug(
767
885
  `No checkpoints to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}, no checkpoints on chain`,
768
886
  );
@@ -774,7 +892,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
774
892
  // Related to the L2 reorgs of the pending chain. We are only interested in actually addressing a reorg if there
775
893
  // are any state that could be impacted by it. If we have no checkpoints, there is no impact.
776
894
  if (localPendingCheckpointNumber > 0) {
777
- const localPendingCheckpoint = await this.getCheckpoint(localPendingCheckpointNumber);
895
+ const localPendingCheckpoint = await this.store.getCheckpointData(localPendingCheckpointNumber);
778
896
  if (localPendingCheckpoint === undefined) {
779
897
  throw new Error(`Missing checkpoint ${localPendingCheckpointNumber}`);
780
898
  }
@@ -807,14 +925,14 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
807
925
 
808
926
  let tipAfterUnwind = localPendingCheckpointNumber;
809
927
  while (true) {
810
- const candidateCheckpoint = await this.getCheckpoint(tipAfterUnwind);
928
+ const candidateCheckpoint = await this.store.getCheckpointData(tipAfterUnwind);
811
929
  if (candidateCheckpoint === undefined) {
812
930
  break;
813
931
  }
814
932
 
815
- const archiveAtContract = await this.rollup.archiveAt(candidateCheckpoint.number);
933
+ const archiveAtContract = await this.rollup.archiveAt(candidateCheckpoint.checkpointNumber);
816
934
  this.log.trace(
817
- `Checking local checkpoint ${candidateCheckpoint.number} with archive ${candidateCheckpoint.archive.root}`,
935
+ `Checking local checkpoint ${candidateCheckpoint.checkpointNumber} with archive ${candidateCheckpoint.archive.root}`,
818
936
  {
819
937
  archiveAtContract,
820
938
  archiveLocal: candidateCheckpoint.archive.root.toString(),
@@ -849,14 +967,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
849
967
 
850
968
  this.log.trace(`Retrieving checkpoints from L1 block ${searchStartBlock} to ${searchEndBlock}`);
851
969
 
852
- // TODO(md): Retrieve from blob sink then from consensus client, then from peers
970
+ // TODO(md): Retrieve from blob client then from consensus client, then from peers
853
971
  const retrievedCheckpoints = await retrieveCheckpointsFromRollup(
854
972
  this.rollup.getContract() as GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
855
973
  this.publicClient,
856
- this.blobSinkClient,
974
+ this.debugClient,
975
+ this.blobClient,
857
976
  searchStartBlock, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
858
977
  searchEndBlock,
978
+ this.l1Addresses,
979
+ this.instrumentation,
859
980
  this.log,
981
+ !this.initialSyncComplete, // isHistoricalSync
860
982
  );
861
983
 
862
984
  if (retrievedCheckpoints.length === 0) {
@@ -916,6 +1038,25 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
916
1038
  continue;
917
1039
  }
918
1040
 
1041
+ // Check the inHash of the checkpoint against the l1->l2 messages.
1042
+ // The messages should've been synced up to the currentL1BlockNumber and must be available for the published
1043
+ // checkpoints we just retrieved.
1044
+ const l1ToL2Messages = await this.getL1ToL2Messages(published.checkpoint.number);
1045
+ const computedInHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
1046
+ const publishedInHash = published.checkpoint.header.contentCommitment.inHash;
1047
+ if (!computedInHash.equals(publishedInHash)) {
1048
+ this.log.fatal(`Mismatch inHash for checkpoint ${published.checkpoint.number}`, {
1049
+ checkpointHash: published.checkpoint.hash(),
1050
+ l1BlockNumber: published.l1.blockNumber,
1051
+ computedInHash,
1052
+ publishedInHash,
1053
+ });
1054
+ // Throwing an error since this is most likely caused by a bug.
1055
+ throw new Error(
1056
+ `Mismatch inHash for checkpoint ${published.checkpoint.number}. Expected ${computedInHash} but got ${publishedInHash}`,
1057
+ );
1058
+ }
1059
+
919
1060
  validCheckpoints.push(published);
920
1061
  this.log.debug(
921
1062
  `Ingesting new checkpoint ${published.checkpoint.number} with ${published.checkpoint.blocks.length} blocks`,
@@ -937,19 +1078,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
937
1078
  validCheckpoints.flatMap(c => c.checkpoint.blocks),
938
1079
  );
939
1080
  } catch (err) {
940
- if (err instanceof InitialBlockNumberNotSequentialError) {
941
- const { previousBlockNumber, newBlockNumber } = err;
942
- const previousBlock = previousBlockNumber
943
- ? await this.store.getPublishedBlock(BlockNumber(previousBlockNumber))
1081
+ if (err instanceof InitialCheckpointNumberNotSequentialError) {
1082
+ const { previousCheckpointNumber, newCheckpointNumber } = err;
1083
+ const previousCheckpoint = previousCheckpointNumber
1084
+ ? await this.store.getCheckpointData(CheckpointNumber(previousCheckpointNumber))
944
1085
  : undefined;
945
- const updatedL1SyncPoint = previousBlock?.l1.blockNumber ?? this.l1constants.l1StartBlock;
1086
+ const updatedL1SyncPoint = previousCheckpoint?.l1.blockNumber ?? this.l1constants.l1StartBlock;
946
1087
  await this.store.setBlockSynchedL1BlockNumber(updatedL1SyncPoint);
947
1088
  this.log.warn(
948
- `Attempting to insert block ${newBlockNumber} with previous block ${previousBlockNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`,
1089
+ `Attempting to insert checkpoint ${newCheckpointNumber} with previous block ${previousCheckpointNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`,
949
1090
  {
950
- previousBlockNumber,
951
- previousBlockHash: await previousBlock?.block.hash(),
952
- newBlockNumber,
1091
+ previousCheckpointNumber,
1092
+ newCheckpointNumber,
953
1093
  updatedL1SyncPoint,
954
1094
  },
955
1095
  );
@@ -969,7 +1109,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
969
1109
  });
970
1110
  }
971
1111
  lastRetrievedCheckpoint = validCheckpoints.at(-1) ?? lastRetrievedCheckpoint;
972
- lastL1BlockWithCheckpoint = publishedCheckpoints.at(-1)?.l1.blockNumber ?? lastL1BlockWithCheckpoint;
1112
+ lastL1BlockWithCheckpoint = retrievedCheckpoints.at(-1)?.l1.blockNumber ?? lastL1BlockWithCheckpoint;
973
1113
  } while (searchEndBlock < currentL1BlockNumber);
974
1114
 
975
1115
  // Important that we update AFTER inserting the blocks.
@@ -994,13 +1134,16 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
994
1134
  // We suspect an L1 reorg that added checkpoints *behind* us. If that is the case, it must have happened between
995
1135
  // the last checkpoint we saw and the current one, so we reset the last synched L1 block number. In the edge case
996
1136
  // we don't have one, we go back 2 L1 epochs, which is the deepest possible reorg (assuming Casper is working).
997
- const latestLocalCheckpoint =
998
- lastRetrievedCheckpoint ??
999
- (latestLocalCheckpointNumber > 0
1000
- ? await this.getPublishedCheckpoints(latestLocalCheckpointNumber, 1).then(([c]) => c)
1001
- : undefined);
1002
- const targetL1BlockNumber = latestLocalCheckpoint?.l1.blockNumber ?? maxBigint(currentL1BlockNumber - 64n, 0n);
1003
- const latestLocalCheckpointArchive = latestLocalCheckpoint?.checkpoint.archive.root.toString();
1137
+ let latestLocalCheckpointArchive: string | undefined = undefined;
1138
+ let targetL1BlockNumber = maxBigint(currentL1BlockNumber - 64n, 0n);
1139
+ if (lastRetrievedCheckpoint) {
1140
+ latestLocalCheckpointArchive = lastRetrievedCheckpoint.checkpoint.archive.root.toString();
1141
+ targetL1BlockNumber = lastRetrievedCheckpoint.l1.blockNumber;
1142
+ } else if (latestLocalCheckpointNumber > 0) {
1143
+ const checkpoint = await this.store.getRangeOfCheckpoints(latestLocalCheckpointNumber, 1).then(([c]) => c);
1144
+ latestLocalCheckpointArchive = checkpoint.archive.root.toString();
1145
+ targetL1BlockNumber = checkpoint.l1.blockNumber;
1146
+ }
1004
1147
  this.log.warn(
1005
1148
  `Failed to reach checkpoint ${pendingCheckpointNumber} at ${currentL1BlockNumber} (latest is ${latestLocalCheckpointNumber}). ` +
1006
1149
  `Rolling back last synched L1 block number to ${targetL1BlockNumber}.`,
@@ -1086,15 +1229,22 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1086
1229
  const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
1087
1230
  const blocks: L2Block[] = [];
1088
1231
 
1089
- // Walk the list of blocks backwards and filter by slots matching the requested epoch.
1090
- // We'll typically ask for blocks for a very recent epoch, so we shouldn't need an index here.
1091
- let block = await this.getBlock(await this.store.getSynchedL2BlockNumber());
1092
- const slot = (b: L2Block) => b.header.globalVariables.slotNumber;
1093
- while (block && slot(block) >= start) {
1094
- if (slot(block) <= end) {
1095
- blocks.push(block);
1232
+ // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
1233
+ // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
1234
+ let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
1235
+ const slot = (b: CheckpointData) => b.header.slotNumber;
1236
+ while (checkpoint && slot(checkpoint) >= start) {
1237
+ if (slot(checkpoint) <= end) {
1238
+ // push the blocks on backwards
1239
+ const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
1240
+ for (let i = endBlock; i >= checkpoint.startBlock; i--) {
1241
+ const block = await this.getBlock(BlockNumber(i));
1242
+ if (block) {
1243
+ blocks.push(block);
1244
+ }
1245
+ }
1096
1246
  }
1097
- block = await this.getBlock(BlockNumber(block.number - 1));
1247
+ checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
1098
1248
  }
1099
1249
 
1100
1250
  return blocks.reverse();
@@ -1104,17 +1254,22 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1104
1254
  const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
1105
1255
  const blocks: BlockHeader[] = [];
1106
1256
 
1107
- // Walk the list of blocks backwards and filter by slots matching the requested epoch.
1108
- // We'll typically ask for blocks for a very recent epoch, so we shouldn't need an index here.
1109
- let number = await this.store.getSynchedL2BlockNumber();
1110
- let header = await this.getBlockHeader(number);
1111
- const slot = (b: BlockHeader) => b.globalVariables.slotNumber;
1112
- while (header && slot(header) >= start) {
1113
- if (slot(header) <= end) {
1114
- blocks.push(header);
1257
+ // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
1258
+ // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
1259
+ let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
1260
+ const slot = (b: CheckpointData) => b.header.slotNumber;
1261
+ while (checkpoint && slot(checkpoint) >= start) {
1262
+ if (slot(checkpoint) <= end) {
1263
+ // push the blocks on backwards
1264
+ const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
1265
+ for (let i = endBlock; i >= checkpoint.startBlock; i--) {
1266
+ const block = await this.getBlockHeader(BlockNumber(i));
1267
+ if (block) {
1268
+ blocks.push(block);
1269
+ }
1270
+ }
1115
1271
  }
1116
- number = BlockNumber(number - 1);
1117
- header = await this.getBlockHeader(number);
1272
+ checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
1118
1273
  }
1119
1274
  return blocks.reverse();
1120
1275
  }
@@ -1152,37 +1307,6 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1152
1307
  return this.initialSyncComplete;
1153
1308
  }
1154
1309
 
1155
- public async getPublishedCheckpoints(
1156
- from: CheckpointNumber,
1157
- limit: number,
1158
- proven?: boolean,
1159
- ): Promise<PublishedCheckpoint[]> {
1160
- // TODO: Implement this properly. This only works when we have one block per checkpoint.
1161
- const blocks = await this.getPublishedBlocks(BlockNumber(from), limit, proven);
1162
- return blocks.map(b => b.toPublishedCheckpoint());
1163
- }
1164
-
1165
- public async getCheckpointByArchive(archive: Fr): Promise<Checkpoint | undefined> {
1166
- // TODO: Implement this properly. This only works when we have one block per checkpoint.
1167
- return (await this.getPublishedBlockByArchive(archive))?.block.toCheckpoint();
1168
- }
1169
-
1170
- public async getCheckpoints(from: CheckpointNumber, limit: number, proven?: boolean): Promise<Checkpoint[]> {
1171
- const published = await this.getPublishedCheckpoints(from, limit, proven);
1172
- return published.map(p => p.checkpoint);
1173
- }
1174
-
1175
- public async getCheckpoint(number: CheckpointNumber): Promise<Checkpoint | undefined> {
1176
- if (number < 0) {
1177
- number = await this.getSynchedCheckpointNumber();
1178
- }
1179
- if (number === 0) {
1180
- return undefined;
1181
- }
1182
- const published = await this.getPublishedCheckpoints(number, 1);
1183
- return published[0]?.checkpoint;
1184
- }
1185
-
1186
1310
  public async getCheckpointHeader(number: CheckpointNumber | 'latest'): Promise<CheckpointHeader | undefined> {
1187
1311
  if (number === 'latest') {
1188
1312
  number = await this.getSynchedCheckpointNumber();
@@ -1190,94 +1314,46 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1190
1314
  if (number === 0) {
1191
1315
  return undefined;
1192
1316
  }
1193
- const checkpoint = await this.getCheckpoint(number);
1194
- return checkpoint?.header;
1317
+ const checkpoint = await this.store.getCheckpointData(number);
1318
+ if (!checkpoint) {
1319
+ return undefined;
1320
+ }
1321
+ return checkpoint.header;
1195
1322
  }
1196
1323
 
1197
1324
  public getCheckpointNumber(): Promise<CheckpointNumber> {
1198
1325
  return this.getSynchedCheckpointNumber();
1199
1326
  }
1200
1327
 
1201
- public async getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
1202
- // TODO: Create store and apis for checkpoints.
1203
- // Checkpoint number will no longer be the same as the block number once we support multiple blocks per checkpoint.
1204
- return CheckpointNumber(await this.store.getSynchedL2BlockNumber());
1328
+ public getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
1329
+ return this.store.getSynchedCheckpointNumber();
1205
1330
  }
1206
1331
 
1207
- public async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
1208
- // TODO: Create store and apis for checkpoints.
1209
- // Proven checkpoint number will no longer be the same as the proven block number once we support multiple blocks per checkpoint.
1210
- return CheckpointNumber(await this.store.getProvenL2BlockNumber());
1332
+ public getProvenCheckpointNumber(): Promise<CheckpointNumber> {
1333
+ return this.store.getProvenCheckpointNumber();
1211
1334
  }
1212
1335
 
1213
1336
  public setProvenCheckpointNumber(checkpointNumber: CheckpointNumber): Promise<void> {
1214
- // TODO: Create store and apis for checkpoints.
1215
- // Proven checkpoint number will no longer be the same as the proven block number once we support multiple blocks per checkpoint.
1216
- return this.store.setProvenL2BlockNumber(BlockNumber.fromCheckpointNumber(checkpointNumber));
1337
+ return this.store.setProvenCheckpointNumber(checkpointNumber);
1217
1338
  }
1218
1339
 
1219
1340
  public unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number): Promise<boolean> {
1220
- // TODO: Create store and apis for checkpoints.
1221
- // This only works when we have one block per checkpoint.
1222
- return this.store.unwindBlocks(BlockNumber.fromCheckpointNumber(from), checkpointsToUnwind);
1341
+ return this.store.unwindCheckpoints(from, checkpointsToUnwind);
1223
1342
  }
1224
1343
 
1225
- public getLastBlockNumberInCheckpoint(checkpointNumber: CheckpointNumber): Promise<BlockNumber> {
1226
- // TODO: Create store and apis for checkpoints.
1227
- // Checkpoint number will no longer be the same as the block number once we support multiple blocks per checkpoint.
1228
- return Promise.resolve(BlockNumber.fromCheckpointNumber(checkpointNumber));
1344
+ public async getLastBlockNumberInCheckpoint(checkpointNumber: CheckpointNumber): Promise<BlockNumber | undefined> {
1345
+ const checkpointData = await this.store.getCheckpointData(checkpointNumber);
1346
+ if (!checkpointData) {
1347
+ return undefined;
1348
+ }
1349
+ return BlockNumber(checkpointData.startBlock + checkpointData.numBlocks - 1);
1229
1350
  }
1230
1351
 
1231
1352
  public addCheckpoints(
1232
1353
  checkpoints: PublishedCheckpoint[],
1233
1354
  pendingChainValidationStatus?: ValidateBlockResult,
1234
1355
  ): Promise<boolean> {
1235
- // TODO: Create store and apis for checkpoints.
1236
- // This only works when we have one block per checkpoint.
1237
- return this.store.addBlocks(
1238
- checkpoints.map(p => PublishedL2Block.fromPublishedCheckpoint(p)),
1239
- pendingChainValidationStatus,
1240
- );
1241
- }
1242
-
1243
- public async getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
1244
- // TODO: Create store and apis for checkpoints.
1245
- // This only works when we have one block per checkpoint.
1246
- const blocks = await this.getBlocksForEpoch(epochNumber);
1247
- return blocks.map(b => b.toCheckpoint());
1248
- }
1249
-
1250
- public getL1ToL2MessagesForCheckpoint(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
1251
- // TODO: Create dedicated api for checkpoints.
1252
- // This only works when we have one block per checkpoint.
1253
- return this.getL1ToL2Messages(BlockNumber.fromCheckpointNumber(checkpointNumber));
1254
- }
1255
-
1256
- /**
1257
- * Gets up to `limit` amount of L2 blocks starting from `from`.
1258
- * @param from - Number of the first block to return (inclusive).
1259
- * @param limit - The number of blocks to return.
1260
- * @param proven - If true, only return blocks that have been proven.
1261
- * @returns The requested L2 blocks.
1262
- */
1263
- public getBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<L2Block[]> {
1264
- return this.getPublishedBlocks(from, limit, proven).then(blocks => blocks.map(b => b.block));
1265
- }
1266
-
1267
- /** Equivalent to getBlocks but includes publish data. */
1268
- public async getPublishedBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<PublishedL2Block[]> {
1269
- const limitWithProven = proven
1270
- ? Math.min(limit, Math.max((await this.store.getProvenL2BlockNumber()) - from + 1, 0))
1271
- : limit;
1272
- return limitWithProven === 0 ? [] : await this.store.getPublishedBlocks(from, limitWithProven);
1273
- }
1274
-
1275
- public getPublishedBlockByHash(blockHash: Fr): Promise<PublishedL2Block | undefined> {
1276
- return this.store.getPublishedBlockByHash(blockHash);
1277
- }
1278
-
1279
- public getPublishedBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
1280
- return this.store.getPublishedBlockByArchive(archive);
1356
+ return this.store.addCheckpoints(checkpoints, pendingChainValidationStatus);
1281
1357
  }
1282
1358
 
1283
1359
  public getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
@@ -1293,7 +1369,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1293
1369
  * @param number - The block number to return.
1294
1370
  * @returns The requested L2 block.
1295
1371
  */
1296
- public async getBlock(number: BlockNumber): Promise<L2Block | undefined> {
1372
+ public async getL2BlockNew(number: BlockNumber): Promise<L2BlockNew | undefined> {
1297
1373
  // If the number provided is -ve, then return the latest block.
1298
1374
  if (number < 0) {
1299
1375
  number = await this.store.getSynchedL2BlockNumber();
@@ -1301,8 +1377,8 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1301
1377
  if (number === 0) {
1302
1378
  return undefined;
1303
1379
  }
1304
- const publishedBlock = await this.store.getPublishedBlock(number);
1305
- return publishedBlock?.block;
1380
+ const publishedBlock = await this.store.store.getBlock(number);
1381
+ return publishedBlock;
1306
1382
  }
1307
1383
 
1308
1384
  public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
@@ -1316,6 +1392,21 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1316
1392
  return headers.length === 0 ? undefined : headers[0];
1317
1393
  }
1318
1394
 
1395
+ getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
1396
+ return this.store.getCheckpointedBlock(number);
1397
+ }
1398
+
1399
+ getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
1400
+ return this.store.getCheckpointedBlockByHash(blockHash);
1401
+ }
1402
+
1403
+ getProvenBlockNumber(): Promise<BlockNumber> {
1404
+ return this.store.getProvenBlockNumber();
1405
+ }
1406
+ getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
1407
+ return this.store.getCheckpointedBlockByArchive(archive);
1408
+ }
1409
+
1319
1410
  public getTxEffect(txHash: TxHash) {
1320
1411
  return this.store.getTxEffect(txHash);
1321
1412
  }
@@ -1324,24 +1415,12 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1324
1415
  return this.store.getSettledTxReceipt(txHash);
1325
1416
  }
1326
1417
 
1327
- /**
1328
- * Retrieves all private logs from up to `limit` blocks, starting from the block number `from`.
1329
- * @param from - The block number from which to begin retrieving logs.
1330
- * @param limit - The maximum number of blocks to retrieve logs from.
1331
- * @returns An array of private logs from the specified range of blocks.
1332
- */
1333
- public getPrivateLogs(from: BlockNumber, limit: number): Promise<PrivateLog[]> {
1334
- return this.store.getPrivateLogs(from, limit);
1418
+ getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
1419
+ return this.store.getPrivateLogsByTags(tags);
1335
1420
  }
1336
1421
 
1337
- /**
1338
- * Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
1339
- * @param tags - The tags to filter the logs by.
1340
- * @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
1341
- * that tag.
1342
- */
1343
- getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
1344
- return this.store.getLogsByTags(tags);
1422
+ getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
1423
+ return this.store.getPublicLogsByTagsFromContract(contractAddress, tags);
1345
1424
  }
1346
1425
 
1347
1426
  /**
@@ -1364,19 +1443,11 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1364
1443
 
1365
1444
  /**
1366
1445
  * Gets the number of the latest L2 block processed by the block source implementation.
1446
+ * This includes both checkpointed and uncheckpointed blocks.
1367
1447
  * @returns The number of the latest L2 block processed by the block source implementation.
1368
1448
  */
1369
1449
  public getBlockNumber(): Promise<BlockNumber> {
1370
- return this.store.getSynchedL2BlockNumber();
1371
- }
1372
-
1373
- public getProvenBlockNumber(): Promise<BlockNumber> {
1374
- return this.store.getProvenL2BlockNumber();
1375
- }
1376
-
1377
- /** Forcefully updates the last proven block number. Use for testing. */
1378
- public setProvenBlockNumber(blockNumber: BlockNumber): Promise<void> {
1379
- return this.store.setProvenL2BlockNumber(blockNumber);
1450
+ return this.store.getLatestBlockNumber();
1380
1451
  }
1381
1452
 
1382
1453
  public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
@@ -1404,12 +1475,12 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1404
1475
  }
1405
1476
 
1406
1477
  /**
1407
- * Gets L1 to L2 message (to be) included in a given block.
1408
- * @param blockNumber - L2 block number to get messages for.
1478
+ * Gets L1 to L2 message (to be) included in a given checkpoint.
1479
+ * @param checkpointNumber - Checkpoint number to get messages for.
1409
1480
  * @returns The L1 to L2 messages/leaves of the messages subtree (throws if not found).
1410
1481
  */
1411
- getL1ToL2Messages(blockNumber: BlockNumber): Promise<Fr[]> {
1412
- return this.store.getL1ToL2Messages(blockNumber);
1482
+ getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
1483
+ return this.store.getL1ToL2Messages(checkpointNumber);
1413
1484
  }
1414
1485
 
1415
1486
  /**
@@ -1486,31 +1557,32 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1486
1557
  }
1487
1558
 
1488
1559
  public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
1560
+ // TODO(pw/mbps): This still assumes 1 block per checkpoint
1489
1561
  const currentBlocks = await this.getL2Tips();
1490
1562
  const currentL2Block = currentBlocks.latest.number;
1491
1563
  const currentProvenBlock = currentBlocks.proven.number;
1492
- // const currentFinalizedBlock = currentBlocks.finalized.number;
1493
1564
 
1494
1565
  if (targetL2BlockNumber >= currentL2Block) {
1495
1566
  throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
1496
1567
  }
1497
1568
  const blocksToUnwind = currentL2Block - targetL2BlockNumber;
1498
- const targetL2Block = await this.store.getPublishedBlock(targetL2BlockNumber);
1569
+ const targetL2Block = await this.store.getCheckpointedBlock(targetL2BlockNumber);
1499
1570
  if (!targetL2Block) {
1500
1571
  throw new Error(`Target L2 block ${targetL2BlockNumber} not found`);
1501
1572
  }
1502
1573
  const targetL1BlockNumber = targetL2Block.l1.blockNumber;
1574
+ const targetCheckpointNumber = CheckpointNumber.fromBlockNumber(targetL2BlockNumber);
1503
1575
  const targetL1BlockHash = await this.getL1BlockHash(targetL1BlockNumber);
1504
- this.log.info(`Unwinding ${blocksToUnwind} blocks from L2 block ${currentL2Block}`);
1505
- await this.store.unwindBlocks(BlockNumber(currentL2Block), blocksToUnwind);
1506
- this.log.info(`Unwinding L1 to L2 messages to ${targetL2BlockNumber}`);
1507
- await this.store.rollbackL1ToL2MessagesToL2Block(targetL2BlockNumber);
1576
+ this.log.info(`Unwinding ${blocksToUnwind} checkpoints from L2 block ${currentL2Block}`);
1577
+ await this.store.unwindCheckpoints(CheckpointNumber(currentL2Block), blocksToUnwind);
1578
+ this.log.info(`Unwinding L1 to L2 messages to checkpoint ${targetCheckpointNumber}`);
1579
+ await this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
1508
1580
  this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`);
1509
1581
  await this.store.setBlockSynchedL1BlockNumber(targetL1BlockNumber);
1510
1582
  await this.store.setMessageSynchedL1Block({ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash });
1511
1583
  if (targetL2BlockNumber < currentProvenBlock) {
1512
1584
  this.log.info(`Clearing proven L2 block number`);
1513
- await this.store.setProvenL2BlockNumber(BlockNumber.ZERO);
1585
+ await this.store.setProvenCheckpointNumber(CheckpointNumber.ZERO);
1514
1586
  }
1515
1587
  // TODO(palla/reorg): Set the finalized block when we add support for it.
1516
1588
  // if (targetL2BlockNumber < currentFinalizedBlock) {
@@ -1518,6 +1590,150 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
1518
1590
  // await this.store.setFinalizedL2BlockNumber(0);
1519
1591
  // }
1520
1592
  }
1593
+
1594
+ public async getPublishedCheckpoints(
1595
+ checkpointNumber: CheckpointNumber,
1596
+ limit: number,
1597
+ ): Promise<PublishedCheckpoint[]> {
1598
+ const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
1599
+ const blocks = (
1600
+ await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
1601
+ ).filter(isDefined);
1602
+
1603
+ const fullCheckpoints: PublishedCheckpoint[] = [];
1604
+ for (let i = 0; i < checkpoints.length; i++) {
1605
+ const blocksForCheckpoint = blocks[i];
1606
+ const checkpoint = checkpoints[i];
1607
+ const fullCheckpoint = new Checkpoint(
1608
+ checkpoint.archive,
1609
+ checkpoint.header,
1610
+ blocksForCheckpoint,
1611
+ checkpoint.checkpointNumber,
1612
+ );
1613
+ const publishedCheckpoint = new PublishedCheckpoint(
1614
+ fullCheckpoint,
1615
+ checkpoint.l1,
1616
+ checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
1617
+ );
1618
+ fullCheckpoints.push(publishedCheckpoint);
1619
+ }
1620
+ return fullCheckpoints;
1621
+ }
1622
+
1623
+ public async getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
1624
+ const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
1625
+ const checkpoints: Checkpoint[] = [];
1626
+
1627
+ // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
1628
+ // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
1629
+ let checkpointData = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
1630
+ const slot = (b: CheckpointData) => b.header.slotNumber;
1631
+ while (checkpointData && slot(checkpointData) >= start) {
1632
+ if (slot(checkpointData) <= end) {
1633
+ // push the checkpoints on backwards
1634
+ const [checkpoint] = await this.getPublishedCheckpoints(checkpointData.checkpointNumber, 1);
1635
+ checkpoints.push(checkpoint.checkpoint);
1636
+ }
1637
+ checkpointData = await this.store.getCheckpointData(CheckpointNumber(checkpointData.checkpointNumber - 1));
1638
+ }
1639
+
1640
+ return checkpoints.reverse();
1641
+ }
1642
+
1643
+ /* Legacy APIs */
1644
+
1645
+ public async getPublishedBlockByHash(blockHash: Fr): Promise<PublishedL2Block | undefined> {
1646
+ const checkpointedBlock = await this.store.getCheckpointedBlockByHash(blockHash);
1647
+ return this.buildOldBlockFromCheckpointedBlock(checkpointedBlock);
1648
+ }
1649
+ public async getPublishedBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
1650
+ const checkpointedBlock = await this.store.getCheckpointedBlockByArchive(archive);
1651
+ return this.buildOldBlockFromCheckpointedBlock(checkpointedBlock);
1652
+ }
1653
+
1654
+ /**
1655
+ * Gets up to `limit` amount of L2 blocks starting from `from`.
1656
+ * @param from - Number of the first block to return (inclusive).
1657
+ * @param limit - The number of blocks to return.
1658
+ * @param proven - If true, only return blocks that have been proven.
1659
+ * @returns The requested L2 blocks.
1660
+ */
1661
+ public async getBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<L2Block[]> {
1662
+ const publishedBlocks = await this.getPublishedBlocks(from, limit, proven);
1663
+ return publishedBlocks.map(x => x.block);
1664
+ }
1665
+
1666
+ public async getPublishedBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<PublishedL2Block[]> {
1667
+ const checkpoints = await this.store.getRangeOfCheckpoints(CheckpointNumber(from), limit);
1668
+ const provenCheckpointNumber = await this.getProvenCheckpointNumber();
1669
+ const blocks = (
1670
+ await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
1671
+ ).filter(isDefined);
1672
+
1673
+ const olbBlocks: PublishedL2Block[] = [];
1674
+ for (let i = 0; i < checkpoints.length; i++) {
1675
+ const blockForCheckpoint = blocks[i][0];
1676
+ const checkpoint = checkpoints[i];
1677
+ if (checkpoint.checkpointNumber > provenCheckpointNumber && proven === true) {
1678
+ // this checkpointisn't proven and we only want proven
1679
+ continue;
1680
+ }
1681
+ const oldCheckpoint = new Checkpoint(
1682
+ blockForCheckpoint.archive,
1683
+ checkpoint.header,
1684
+ [blockForCheckpoint],
1685
+ checkpoint.checkpointNumber,
1686
+ );
1687
+ const oldBlock = L2Block.fromCheckpoint(oldCheckpoint);
1688
+ const publishedBlock = new PublishedL2Block(
1689
+ oldBlock,
1690
+ checkpoint.l1,
1691
+ checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
1692
+ );
1693
+ olbBlocks.push(publishedBlock);
1694
+ }
1695
+ return olbBlocks;
1696
+ }
1697
+
1698
+ private async buildOldBlockFromCheckpointedBlock(
1699
+ checkpointedBlock: CheckpointedL2Block | undefined,
1700
+ ): Promise<PublishedL2Block | undefined> {
1701
+ if (!checkpointedBlock) {
1702
+ return undefined;
1703
+ }
1704
+ const checkpoint = await this.store.getCheckpointData(checkpointedBlock.checkpointNumber);
1705
+ if (!checkpoint) {
1706
+ return checkpoint;
1707
+ }
1708
+ const fullCheckpoint = new Checkpoint(
1709
+ checkpointedBlock?.block.archive,
1710
+ checkpoint?.header,
1711
+ [checkpointedBlock.block],
1712
+ checkpoint.checkpointNumber,
1713
+ );
1714
+ const oldBlock = L2Block.fromCheckpoint(fullCheckpoint);
1715
+ const published = new PublishedL2Block(
1716
+ oldBlock,
1717
+ checkpoint.l1,
1718
+ checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
1719
+ );
1720
+ return published;
1721
+ }
1722
+
1723
+ public async getBlock(number: BlockNumber): Promise<L2Block | undefined> {
1724
+ // If the number provided is -ve, then return the latest block.
1725
+ if (number < 0) {
1726
+ number = await this.store.getSynchedL2BlockNumber();
1727
+ }
1728
+ if (number === 0) {
1729
+ return undefined;
1730
+ }
1731
+ const publishedBlocks = await this.getPublishedBlocks(number, 1);
1732
+ if (publishedBlocks.length === 0) {
1733
+ return undefined;
1734
+ }
1735
+ return publishedBlocks[0].block;
1736
+ }
1521
1737
  }
1522
1738
 
1523
1739
  enum Operation {
@@ -1548,11 +1764,13 @@ export class ArchiverStoreHelper
1548
1764
  | 'close'
1549
1765
  | 'transactionAsync'
1550
1766
  | 'addBlocks'
1767
+ | 'getBlock'
1768
+ | 'getBlocks'
1551
1769
  >
1552
1770
  {
1553
1771
  #log = createLogger('archiver:block-helper');
1554
1772
 
1555
- constructor(protected readonly store: ArchiverDataStore) {}
1773
+ constructor(public readonly store: ArchiverDataStore) {}
1556
1774
 
1557
1775
  /**
1558
1776
  * Extracts and stores contract classes out of ContractClassPublished events emitted by the class registry contract.
@@ -1690,7 +1908,23 @@ export class ArchiverStoreHelper
1690
1908
  return true;
1691
1909
  }
1692
1910
 
1693
- public addBlocks(blocks: PublishedL2Block[], pendingChainValidationStatus?: ValidateBlockResult): Promise<boolean> {
1911
+ private async addBlockDataToDB(block: L2BlockNew) {
1912
+ const contractClassLogs = block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
1913
+ // ContractInstancePublished event logs are broadcast in privateLogs.
1914
+ const privateLogs = block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
1915
+ const publicLogs = block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
1916
+
1917
+ return (
1918
+ await Promise.all([
1919
+ this.#updatePublishedContractClasses(contractClassLogs, block.number, Operation.Store),
1920
+ this.#updateDeployedContractInstances(privateLogs, block.number, Operation.Store),
1921
+ this.#updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, Operation.Store),
1922
+ this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.number),
1923
+ ])
1924
+ ).every(Boolean);
1925
+ }
1926
+
1927
+ public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?: ValidateBlockResult): Promise<boolean> {
1694
1928
  // Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
1695
1929
  // or if the previous block is not in the store.
1696
1930
  return this.store.transactionAsync(async () => {
@@ -1700,25 +1934,10 @@ export class ArchiverStoreHelper
1700
1934
  // Update the pending chain validation status if provided
1701
1935
  pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
1702
1936
  // Add any logs emitted during the retrieved blocks
1703
- this.store.addLogs(blocks.map(block => block.block)),
1937
+ this.store.addLogs(blocks),
1704
1938
  // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
1705
- ...blocks.map(async block => {
1706
- const contractClassLogs = block.block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
1707
- // ContractInstancePublished event logs are broadcast in privateLogs.
1708
- const privateLogs = block.block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
1709
- const publicLogs = block.block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
1710
- return (
1711
- await Promise.all([
1712
- this.#updatePublishedContractClasses(contractClassLogs, block.block.number, Operation.Store),
1713
- this.#updateDeployedContractInstances(privateLogs, block.block.number, Operation.Store),
1714
- this.#updateUpdatedContractInstances(
1715
- publicLogs,
1716
- block.block.header.globalVariables.timestamp,
1717
- Operation.Store,
1718
- ),
1719
- this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.block.number),
1720
- ])
1721
- ).every(Boolean);
1939
+ ...blocks.map(block => {
1940
+ return this.addBlockDataToDB(block);
1722
1941
  }),
1723
1942
  ]);
1724
1943
 
@@ -1726,59 +1945,102 @@ export class ArchiverStoreHelper
1726
1945
  });
1727
1946
  }
1728
1947
 
1729
- public async unwindBlocks(from: BlockNumber, blocksToUnwind: number): Promise<boolean> {
1730
- const last = await this.getSynchedL2BlockNumber();
1731
- if (from != last) {
1732
- throw new Error(`Cannot unwind blocks from block ${from} when the last block is ${last}`);
1948
+ public addCheckpoints(
1949
+ checkpoints: PublishedCheckpoint[],
1950
+ pendingChainValidationStatus?: ValidateBlockResult,
1951
+ ): Promise<boolean> {
1952
+ // Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
1953
+ // or if the previous block is not in the store.
1954
+ return this.store.transactionAsync(async () => {
1955
+ await this.store.addCheckpoints(checkpoints);
1956
+ const allBlocks = checkpoints.flatMap((ch: PublishedCheckpoint) => ch.checkpoint.blocks);
1957
+
1958
+ const opResults = await Promise.all([
1959
+ // Update the pending chain validation status if provided
1960
+ pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
1961
+ // Add any logs emitted during the retrieved blocks
1962
+ this.store.addLogs(allBlocks),
1963
+ // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
1964
+ ...allBlocks.map(block => {
1965
+ return this.addBlockDataToDB(block);
1966
+ }),
1967
+ ]);
1968
+
1969
+ return opResults.every(Boolean);
1970
+ });
1971
+ }
1972
+
1973
+ public async unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number): Promise<boolean> {
1974
+ if (checkpointsToUnwind <= 0) {
1975
+ throw new Error(`Cannot unwind ${checkpointsToUnwind} blocks`);
1733
1976
  }
1734
- if (blocksToUnwind <= 0) {
1735
- throw new Error(`Cannot unwind ${blocksToUnwind} blocks`);
1977
+
1978
+ const last = await this.getSynchedCheckpointNumber();
1979
+ if (from != last) {
1980
+ throw new Error(`Cannot unwind checkpoints from checkpoint ${from} when the last checkpoint is ${last}`);
1736
1981
  }
1737
1982
 
1738
- // from - blocksToUnwind = the new head, so + 1 for what we need to remove
1739
- const blocks = await this.getPublishedBlocks(BlockNumber(from - blocksToUnwind + 1), blocksToUnwind);
1983
+ const blocks = [];
1984
+ const lastCheckpointNumber = from + checkpointsToUnwind - 1;
1985
+ for (let checkpointNumber = from; checkpointNumber <= lastCheckpointNumber; checkpointNumber++) {
1986
+ const blocksForCheckpoint = await this.store.getBlocksForCheckpoint(checkpointNumber);
1987
+ if (!blocksForCheckpoint) {
1988
+ continue;
1989
+ }
1990
+ blocks.push(...blocksForCheckpoint);
1991
+ }
1740
1992
 
1741
1993
  const opResults = await Promise.all([
1742
1994
  // Prune rolls back to the last proven block, which is by definition valid
1743
1995
  this.store.setPendingChainValidationStatus({ valid: true }),
1744
1996
  // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
1745
1997
  ...blocks.map(async block => {
1746
- const contractClassLogs = block.block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
1998
+ const contractClassLogs = block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
1747
1999
  // ContractInstancePublished event logs are broadcast in privateLogs.
1748
- const privateLogs = block.block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
1749
- const publicLogs = block.block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
2000
+ const privateLogs = block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
2001
+ const publicLogs = block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
1750
2002
 
1751
2003
  return (
1752
2004
  await Promise.all([
1753
- this.#updatePublishedContractClasses(contractClassLogs, block.block.number, Operation.Delete),
1754
- this.#updateDeployedContractInstances(privateLogs, block.block.number, Operation.Delete),
1755
- this.#updateUpdatedContractInstances(
1756
- publicLogs,
1757
- block.block.header.globalVariables.timestamp,
1758
- Operation.Delete,
1759
- ),
2005
+ this.#updatePublishedContractClasses(contractClassLogs, block.number, Operation.Delete),
2006
+ this.#updateDeployedContractInstances(privateLogs, block.number, Operation.Delete),
2007
+ this.#updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, Operation.Delete),
1760
2008
  ])
1761
2009
  ).every(Boolean);
1762
2010
  }),
1763
2011
 
1764
- this.store.deleteLogs(blocks.map(b => b.block)),
1765
- this.store.unwindBlocks(from, blocksToUnwind),
2012
+ this.store.deleteLogs(blocks),
2013
+ this.store.unwindCheckpoints(from, checkpointsToUnwind),
1766
2014
  ]);
1767
2015
 
1768
2016
  return opResults.every(Boolean);
1769
2017
  }
1770
2018
 
1771
- getPublishedBlocks(from: BlockNumber, limit: number): Promise<PublishedL2Block[]> {
1772
- return this.store.getPublishedBlocks(from, limit);
2019
+ getCheckpointData(checkpointNumber: CheckpointNumber): Promise<CheckpointData | undefined> {
2020
+ return this.store.getCheckpointData(checkpointNumber);
2021
+ }
2022
+
2023
+ getRangeOfCheckpoints(from: CheckpointNumber, limit: number): Promise<CheckpointData[]> {
2024
+ return this.store.getRangeOfCheckpoints(from, limit);
2025
+ }
2026
+
2027
+ getCheckpointedL2BlockNumber(): Promise<BlockNumber> {
2028
+ return this.store.getCheckpointedL2BlockNumber();
2029
+ }
2030
+ getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
2031
+ return this.store.getSynchedCheckpointNumber();
1773
2032
  }
1774
- getPublishedBlock(number: BlockNumber): Promise<PublishedL2Block | undefined> {
1775
- return this.store.getPublishedBlock(number);
2033
+ setCheckpointSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
2034
+ return this.store.setCheckpointSynchedL1BlockNumber(l1BlockNumber);
1776
2035
  }
1777
- getPublishedBlockByHash(blockHash: Fr): Promise<PublishedL2Block | undefined> {
1778
- return this.store.getPublishedBlockByHash(blockHash);
2036
+ getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
2037
+ return this.store.getCheckpointedBlock(number);
1779
2038
  }
1780
- getPublishedBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
1781
- return this.store.getPublishedBlockByArchive(archive);
2039
+ getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
2040
+ return this.store.getCheckpointedBlockByHash(blockHash);
2041
+ }
2042
+ getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
2043
+ return this.store.getCheckpointedBlockByArchive(archive);
1782
2044
  }
1783
2045
  getBlockHeaders(from: BlockNumber, limit: number): Promise<BlockHeader[]> {
1784
2046
  return this.store.getBlockHeaders(from, limit);
@@ -1789,6 +2051,18 @@ export class ArchiverStoreHelper
1789
2051
  getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
1790
2052
  return this.store.getBlockHeaderByArchive(archive);
1791
2053
  }
2054
+ getBlockByHash(blockHash: Fr): Promise<L2BlockNew | undefined> {
2055
+ return this.store.getBlockByHash(blockHash);
2056
+ }
2057
+ getBlockByArchive(archive: Fr): Promise<L2BlockNew | undefined> {
2058
+ return this.store.getBlockByArchive(archive);
2059
+ }
2060
+ getLatestBlockNumber(): Promise<BlockNumber> {
2061
+ return this.store.getLatestBlockNumber();
2062
+ }
2063
+ getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2BlockNew[] | undefined> {
2064
+ return this.store.getBlocksForCheckpoint(checkpointNumber);
2065
+ }
1792
2066
  getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
1793
2067
  return this.store.getTxEffect(txHash);
1794
2068
  }
@@ -1798,17 +2072,17 @@ export class ArchiverStoreHelper
1798
2072
  addL1ToL2Messages(messages: InboxMessage[]): Promise<void> {
1799
2073
  return this.store.addL1ToL2Messages(messages);
1800
2074
  }
1801
- getL1ToL2Messages(blockNumber: BlockNumber): Promise<Fr[]> {
1802
- return this.store.getL1ToL2Messages(blockNumber);
2075
+ getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
2076
+ return this.store.getL1ToL2Messages(checkpointNumber);
1803
2077
  }
1804
2078
  getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
1805
2079
  return this.store.getL1ToL2MessageIndex(l1ToL2Message);
1806
2080
  }
1807
- getPrivateLogs(from: BlockNumber, limit: number): Promise<PrivateLog[]> {
1808
- return this.store.getPrivateLogs(from, limit);
2081
+ getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
2082
+ return this.store.getPrivateLogsByTags(tags);
1809
2083
  }
1810
- getLogsByTags(tags: Fr[], logsPerTag?: number): Promise<TxScopedL2Log[][]> {
1811
- return this.store.getLogsByTags(tags, logsPerTag);
2084
+ getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
2085
+ return this.store.getPublicLogsByTagsFromContract(contractAddress, tags);
1812
2086
  }
1813
2087
  getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
1814
2088
  return this.store.getPublicLogs(filter);
@@ -1817,16 +2091,19 @@ export class ArchiverStoreHelper
1817
2091
  return this.store.getContractClassLogs(filter);
1818
2092
  }
1819
2093
  getSynchedL2BlockNumber(): Promise<BlockNumber> {
1820
- return this.store.getSynchedL2BlockNumber();
2094
+ return this.store.getCheckpointedL2BlockNumber();
2095
+ }
2096
+ getProvenCheckpointNumber(): Promise<CheckpointNumber> {
2097
+ return this.store.getProvenCheckpointNumber();
1821
2098
  }
1822
- getProvenL2BlockNumber(): Promise<BlockNumber> {
1823
- return this.store.getProvenL2BlockNumber();
2099
+ getProvenBlockNumber(): Promise<BlockNumber> {
2100
+ return this.store.getProvenBlockNumber();
1824
2101
  }
1825
- setProvenL2BlockNumber(l2BlockNumber: BlockNumber): Promise<void> {
1826
- return this.store.setProvenL2BlockNumber(l2BlockNumber);
2102
+ setProvenCheckpointNumber(checkpointNumber: CheckpointNumber): Promise<void> {
2103
+ return this.store.setProvenCheckpointNumber(checkpointNumber);
1827
2104
  }
1828
2105
  setBlockSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
1829
- return this.store.setBlockSynchedL1BlockNumber(l1BlockNumber);
2106
+ return this.store.setCheckpointSynchedL1BlockNumber(l1BlockNumber);
1830
2107
  }
1831
2108
  setMessageSynchedL1Block(l1Block: L1BlockId): Promise<void> {
1832
2109
  return this.store.setMessageSynchedL1Block(l1Block);
@@ -1858,8 +2135,8 @@ export class ArchiverStoreHelper
1858
2135
  estimateSize(): Promise<{ mappingSize: number; physicalFileSize: number; actualSize: number; numItems: number }> {
1859
2136
  return this.store.estimateSize();
1860
2137
  }
1861
- rollbackL1ToL2MessagesToL2Block(targetBlockNumber: BlockNumber): Promise<void> {
1862
- return this.store.rollbackL1ToL2MessagesToL2Block(targetBlockNumber);
2138
+ rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber: CheckpointNumber): Promise<void> {
2139
+ return this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
1863
2140
  }
1864
2141
  iterateL1ToL2Messages(range: CustomRange<bigint> = {}): AsyncIterableIterator<InboxMessage> {
1865
2142
  return this.store.iterateL1ToL2Messages(range);