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

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 (81) hide show
  1. package/dest/archiver/archiver.d.ts +53 -40
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +333 -221
  4. package/dest/archiver/archiver_store.d.ts +16 -15
  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 +85 -84
  9. package/dest/archiver/config.d.ts +1 -1
  10. package/dest/archiver/config.d.ts.map +1 -1
  11. package/dest/archiver/config.js +5 -0
  12. package/dest/archiver/data_retrieval.d.ts +18 -17
  13. package/dest/archiver/data_retrieval.d.ts.map +1 -1
  14. package/dest/archiver/data_retrieval.js +111 -86
  15. package/dest/archiver/errors.d.ts +1 -1
  16. package/dest/archiver/errors.d.ts.map +1 -1
  17. package/dest/archiver/index.d.ts +1 -1
  18. package/dest/archiver/instrumentation.d.ts +3 -3
  19. package/dest/archiver/instrumentation.d.ts.map +1 -1
  20. package/dest/archiver/kv_archiver_store/block_store.d.ts +9 -8
  21. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  22. package/dest/archiver/kv_archiver_store/block_store.js +8 -7
  23. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +1 -1
  24. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  25. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +1 -1
  26. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
  27. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +18 -17
  28. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  29. package/dest/archiver/kv_archiver_store/log_store.d.ts +1 -1
  30. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  31. package/dest/archiver/kv_archiver_store/log_store.js +3 -2
  32. package/dest/archiver/kv_archiver_store/message_store.d.ts +1 -1
  33. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  34. package/dest/archiver/structs/data_retrieval.d.ts +1 -1
  35. package/dest/archiver/structs/inbox_message.d.ts +3 -3
  36. package/dest/archiver/structs/inbox_message.d.ts.map +1 -1
  37. package/dest/archiver/structs/inbox_message.js +2 -1
  38. package/dest/archiver/structs/published.d.ts +3 -2
  39. package/dest/archiver/structs/published.d.ts.map +1 -1
  40. package/dest/archiver/validation.d.ts +10 -4
  41. package/dest/archiver/validation.d.ts.map +1 -1
  42. package/dest/archiver/validation.js +29 -21
  43. package/dest/factory.d.ts +1 -1
  44. package/dest/factory.d.ts.map +1 -1
  45. package/dest/factory.js +3 -2
  46. package/dest/index.d.ts +2 -2
  47. package/dest/index.d.ts.map +1 -1
  48. package/dest/index.js +1 -1
  49. package/dest/rpc/index.d.ts +2 -2
  50. package/dest/test/index.d.ts +1 -1
  51. package/dest/test/mock_archiver.d.ts +14 -5
  52. package/dest/test/mock_archiver.d.ts.map +1 -1
  53. package/dest/test/mock_archiver.js +19 -10
  54. package/dest/test/mock_l1_to_l2_message_source.d.ts +4 -2
  55. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  56. package/dest/test/mock_l1_to_l2_message_source.js +7 -2
  57. package/dest/test/mock_l2_block_source.d.ts +14 -9
  58. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  59. package/dest/test/mock_l2_block_source.js +20 -7
  60. package/dest/test/mock_structs.d.ts +1 -1
  61. package/dest/test/mock_structs.d.ts.map +1 -1
  62. package/dest/test/mock_structs.js +3 -2
  63. package/package.json +17 -17
  64. package/src/archiver/archiver.ts +448 -290
  65. package/src/archiver/archiver_store.ts +19 -14
  66. package/src/archiver/archiver_store_test_suite.ts +111 -79
  67. package/src/archiver/config.ts +5 -0
  68. package/src/archiver/data_retrieval.ts +157 -125
  69. package/src/archiver/instrumentation.ts +2 -2
  70. package/src/archiver/kv_archiver_store/block_store.ts +17 -16
  71. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +16 -15
  72. package/src/archiver/kv_archiver_store/log_store.ts +3 -2
  73. package/src/archiver/structs/inbox_message.ts +5 -4
  74. package/src/archiver/structs/published.ts +2 -1
  75. package/src/archiver/validation.ts +52 -27
  76. package/src/factory.ts +3 -2
  77. package/src/index.ts +1 -1
  78. package/src/test/mock_archiver.ts +22 -11
  79. package/src/test/mock_l1_to_l2_message_source.ts +9 -3
  80. package/src/test/mock_l2_block_source.ts +30 -13
  81. package/src/test/mock_structs.ts +3 -2
@@ -1,4 +1,10 @@
1
- import { BlobDeserializationError, SpongeBlob, getBlobFieldsInCheckpoint } from '@aztec/blob-lib';
1
+ import {
2
+ BlobDeserializationError,
3
+ type CheckpointBlobData,
4
+ SpongeBlob,
5
+ decodeCheckpointBlobDataFromBlobs,
6
+ encodeBlockBlobData,
7
+ } from '@aztec/blob-lib';
2
8
  import type { BlobSinkClientInterface } from '@aztec/blob-sink/client';
3
9
  import type {
4
10
  EpochProofPublicInputArgs,
@@ -6,20 +12,21 @@ import type {
6
12
  ViemCommitteeAttestations,
7
13
  ViemHeader,
8
14
  ViemPublicClient,
9
- ViemStateReference,
10
15
  } from '@aztec/ethereum';
11
16
  import { asyncPool } from '@aztec/foundation/async-pool';
17
+ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
12
18
  import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
13
19
  import type { EthAddress } from '@aztec/foundation/eth-address';
14
20
  import type { ViemSignature } from '@aztec/foundation/eth-signature';
15
21
  import { Fr } from '@aztec/foundation/fields';
16
22
  import { type Logger, createLogger } from '@aztec/foundation/log';
17
23
  import { type InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
18
- import { Body, CommitteeAttestation, L2Block, L2BlockHeader, PublishedL2Block } from '@aztec/stdlib/block';
24
+ import { Body, CommitteeAttestation, L2BlockNew } from '@aztec/stdlib/block';
25
+ import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
19
26
  import { Proof } from '@aztec/stdlib/proofs';
20
27
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
21
28
  import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
22
- import { GlobalVariables, StateReference } from '@aztec/stdlib/tx';
29
+ import { BlockHeader, GlobalVariables, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
23
30
 
24
31
  import {
25
32
  type GetContractEventsReturnType,
@@ -36,75 +43,106 @@ import type { DataRetrieval } from './structs/data_retrieval.js';
36
43
  import type { InboxMessage } from './structs/inbox_message.js';
37
44
  import type { L1PublishedData } from './structs/published.js';
38
45
 
39
- export type RetrievedL2Block = {
40
- l2BlockNumber: number;
46
+ export type RetrievedCheckpoint = {
47
+ checkpointNumber: CheckpointNumber;
41
48
  archiveRoot: Fr;
42
- stateReference: StateReference;
43
49
  header: CheckpointHeader;
44
- blobFields: Fr[];
50
+ checkpointBlobData: CheckpointBlobData;
45
51
  l1: L1PublishedData;
46
52
  chainId: Fr;
47
53
  version: Fr;
48
54
  attestations: CommitteeAttestation[];
49
55
  };
50
56
 
51
- export async function retrievedBlockToPublishedL2Block(retrievedBlock: RetrievedL2Block): Promise<PublishedL2Block> {
52
- const {
53
- l2BlockNumber,
54
- archiveRoot,
55
- stateReference,
56
- header: checkpointHeader,
57
- blobFields,
58
- l1,
59
- chainId,
60
- version,
61
- attestations,
62
- } = retrievedBlock;
63
-
64
- const archive = new AppendOnlyTreeSnapshot(
65
- archiveRoot,
66
- l2BlockNumber + 1, // nextAvailableLeafIndex
67
- );
68
-
69
- const globalVariables = GlobalVariables.from({
70
- chainId,
71
- version,
72
- blockNumber: l2BlockNumber,
73
- slotNumber: checkpointHeader.slotNumber,
74
- timestamp: checkpointHeader.timestamp,
75
- coinbase: checkpointHeader.coinbase,
76
- feeRecipient: checkpointHeader.feeRecipient,
77
- gasFees: checkpointHeader.gasFees,
78
- });
57
+ export async function retrievedToPublishedCheckpoint({
58
+ checkpointNumber,
59
+ archiveRoot,
60
+ header: checkpointHeader,
61
+ checkpointBlobData,
62
+ l1,
63
+ chainId,
64
+ version,
65
+ attestations,
66
+ }: RetrievedCheckpoint): Promise<PublishedCheckpoint> {
67
+ const { blocks: blocksBlobData } = checkpointBlobData;
68
+
69
+ // The lastArchiveRoot of a block is the new archive for the previous block.
70
+ const newArchiveRoots = blocksBlobData
71
+ .map(b => b.lastArchiveRoot)
72
+ .slice(1)
73
+ .concat([archiveRoot]);
74
+
75
+ // `blocksBlobData` is created from `decodeCheckpointBlobDataFromBlobs`. An error will be thrown if it can't read a
76
+ // field for the `l1ToL2MessageRoot` of the first block. So below we can safely assume it exists:
77
+ const l1toL2MessageTreeRoot = blocksBlobData[0].l1ToL2MessageRoot!;
78
+
79
+ const spongeBlob = SpongeBlob.init();
80
+ const l2Blocks: L2BlockNew[] = [];
81
+ for (let i = 0; i < blocksBlobData.length; i++) {
82
+ const blockBlobData = blocksBlobData[i];
83
+ const { blockEndMarker, blockEndStateField, lastArchiveRoot, noteHashRoot, nullifierRoot, publicDataRoot } =
84
+ blockBlobData;
85
+
86
+ const l2BlockNumber = blockEndMarker.blockNumber;
87
+
88
+ const globalVariables = GlobalVariables.from({
89
+ chainId,
90
+ version,
91
+ blockNumber: l2BlockNumber,
92
+ slotNumber: checkpointHeader.slotNumber,
93
+ timestamp: blockEndMarker.timestamp,
94
+ coinbase: checkpointHeader.coinbase,
95
+ feeRecipient: checkpointHeader.feeRecipient,
96
+ gasFees: checkpointHeader.gasFees,
97
+ });
98
+
99
+ const state = StateReference.from({
100
+ l1ToL2MessageTree: new AppendOnlyTreeSnapshot(
101
+ l1toL2MessageTreeRoot,
102
+ blockEndStateField.l1ToL2MessageNextAvailableLeafIndex,
103
+ ),
104
+ partial: PartialStateReference.from({
105
+ noteHashTree: new AppendOnlyTreeSnapshot(noteHashRoot, blockEndStateField.noteHashNextAvailableLeafIndex),
106
+ nullifierTree: new AppendOnlyTreeSnapshot(nullifierRoot, blockEndStateField.nullifierNextAvailableLeafIndex),
107
+ publicDataTree: new AppendOnlyTreeSnapshot(publicDataRoot, blockEndStateField.publicDataNextAvailableLeafIndex),
108
+ }),
109
+ });
110
+
111
+ const body = Body.fromTxBlobData(checkpointBlobData.blocks[0].txs);
112
+
113
+ const blobFields = encodeBlockBlobData(blockBlobData);
114
+ await spongeBlob.absorb(blobFields);
115
+
116
+ const clonedSpongeBlob = spongeBlob.clone();
117
+ const spongeBlobHash = await clonedSpongeBlob.squeeze();
118
+
119
+ const header = BlockHeader.from({
120
+ lastArchive: new AppendOnlyTreeSnapshot(lastArchiveRoot, l2BlockNumber),
121
+ state,
122
+ spongeBlobHash,
123
+ globalVariables,
124
+ totalFees: body.txEffects.reduce((accum, txEffect) => accum.add(txEffect.transactionFee), Fr.ZERO),
125
+ totalManaUsed: new Fr(blockEndStateField.totalManaUsed),
126
+ });
127
+
128
+ const newArchive = new AppendOnlyTreeSnapshot(newArchiveRoots[i], l2BlockNumber + 1);
129
+
130
+ l2Blocks.push(new L2BlockNew(newArchive, header, body));
131
+ }
79
132
 
80
- // TODO(#17027)
81
- // This works when there's only one block in the checkpoint.
82
- // If there's more than one block, we need to build the spongeBlob from the endSpongeBlob of the previous block.
83
- const spongeBlob = await SpongeBlob.init(blobFields.length);
84
- // Skip the first field which is the checkpoint prefix indicating the number of total blob fields in a checkpoint.
85
- const blockBlobFields = blobFields.slice(1);
86
- await spongeBlob.absorb(blockBlobFields);
87
- const spongeBlobHash = await spongeBlob.squeeze();
88
-
89
- const body = Body.fromBlobFields(blockBlobFields);
90
-
91
- const header = L2BlockHeader.from({
92
- lastArchive: new AppendOnlyTreeSnapshot(checkpointHeader.lastArchiveRoot, l2BlockNumber),
93
- contentCommitment: checkpointHeader.contentCommitment,
94
- state: stateReference,
95
- globalVariables,
96
- totalFees: body.txEffects.reduce((accum, txEffect) => accum.add(txEffect.transactionFee), Fr.ZERO),
97
- totalManaUsed: checkpointHeader.totalManaUsed,
98
- spongeBlobHash,
133
+ const lastBlock = l2Blocks.at(-1)!;
134
+ const checkpoint = Checkpoint.from({
135
+ archive: new AppendOnlyTreeSnapshot(archiveRoot, lastBlock.number + 1),
136
+ header: checkpointHeader,
137
+ blocks: l2Blocks,
138
+ number: checkpointNumber,
99
139
  });
100
140
 
101
- const block = new L2Block(archive, header, body);
102
-
103
- return PublishedL2Block.fromFields({ block, l1, attestations });
141
+ return PublishedCheckpoint.from({ checkpoint, l1, attestations });
104
142
  }
105
143
 
106
144
  /**
107
- * Fetches new L2 blocks.
145
+ * Fetches new checkpoints.
108
146
  * @param publicClient - The viem public client to use for transaction retrieval.
109
147
  * @param rollupAddress - The address of the rollup contract.
110
148
  * @param searchStartBlock - The block number to use for starting the search.
@@ -112,15 +150,15 @@ export async function retrievedBlockToPublishedL2Block(retrievedBlock: Retrieved
112
150
  * @param expectedNextL2BlockNum - The next L2 block number that we expect to find.
113
151
  * @returns An array of block; as well as the next eth block to search from.
114
152
  */
115
- export async function retrieveBlocksFromRollup(
153
+ export async function retrieveCheckpointsFromRollup(
116
154
  rollup: GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
117
155
  publicClient: ViemPublicClient,
118
156
  blobSinkClient: BlobSinkClientInterface,
119
157
  searchStartBlock: bigint,
120
158
  searchEndBlock: bigint,
121
159
  logger: Logger = createLogger('archiver'),
122
- ): Promise<RetrievedL2Block[]> {
123
- const retrievedBlocks: RetrievedL2Block[] = [];
160
+ ): Promise<RetrievedCheckpoint[]> {
161
+ const retrievedCheckpoints: RetrievedCheckpoint[] = [];
124
162
 
125
163
  let rollupConstants: { chainId: Fr; version: Fr; targetCommitteeSize: number } | undefined;
126
164
 
@@ -128,8 +166,8 @@ export async function retrieveBlocksFromRollup(
128
166
  if (searchStartBlock > searchEndBlock) {
129
167
  break;
130
168
  }
131
- const l2BlockProposedLogs = (
132
- await rollup.getEvents.L2BlockProposed(
169
+ const checkpointProposedLogs = (
170
+ await rollup.getEvents.CheckpointProposed(
133
171
  {},
134
172
  {
135
173
  fromBlock: searchStartBlock,
@@ -138,13 +176,13 @@ export async function retrieveBlocksFromRollup(
138
176
  )
139
177
  ).filter(log => log.blockNumber! >= searchStartBlock && log.blockNumber! <= searchEndBlock);
140
178
 
141
- if (l2BlockProposedLogs.length === 0) {
179
+ if (checkpointProposedLogs.length === 0) {
142
180
  break;
143
181
  }
144
182
 
145
- const lastLog = l2BlockProposedLogs[l2BlockProposedLogs.length - 1];
183
+ const lastLog = checkpointProposedLogs.at(-1)!;
146
184
  logger.debug(
147
- `Got ${l2BlockProposedLogs.length} L2 block processed logs for L2 blocks ${l2BlockProposedLogs[0].args.blockNumber}-${lastLog.args.blockNumber} between L1 blocks ${searchStartBlock}-${searchEndBlock}`,
185
+ `Got ${checkpointProposedLogs.length} processed logs for checkpoints ${checkpointProposedLogs[0].args.checkpointNumber}-${lastLog.args.checkpointNumber} between L1 blocks ${searchStartBlock}-${searchEndBlock}`,
148
186
  );
149
187
 
150
188
  if (rollupConstants === undefined) {
@@ -160,52 +198,52 @@ export async function retrieveBlocksFromRollup(
160
198
  };
161
199
  }
162
200
 
163
- const newBlocks = await processL2BlockProposedLogs(
201
+ const newCheckpoints = await processCheckpointProposedLogs(
164
202
  rollup,
165
203
  publicClient,
166
204
  blobSinkClient,
167
- l2BlockProposedLogs,
205
+ checkpointProposedLogs,
168
206
  rollupConstants,
169
207
  logger,
170
208
  );
171
- retrievedBlocks.push(...newBlocks);
209
+ retrievedCheckpoints.push(...newCheckpoints);
172
210
  searchStartBlock = lastLog.blockNumber! + 1n;
173
211
  } while (searchStartBlock <= searchEndBlock);
174
212
 
175
- // The asyncpool from processL2BlockProposedLogs will not necessarily return the blocks in order, so we sort them before returning.
176
- return retrievedBlocks.sort((a, b) => Number(a.l1.blockNumber - b.l1.blockNumber));
213
+ // The asyncPool from processCheckpointProposedLogs will not necessarily return the checkpoints in order, so we sort them before returning.
214
+ return retrievedCheckpoints.sort((a, b) => Number(a.l1.blockNumber - b.l1.blockNumber));
177
215
  }
178
216
 
179
217
  /**
180
- * Processes newly received L2BlockProposed logs.
218
+ * Processes newly received CheckpointProposed logs.
181
219
  * @param rollup - The rollup contract
182
220
  * @param publicClient - The viem public client to use for transaction retrieval.
183
- * @param logs - L2BlockProposed logs.
184
- * @returns - An array blocks.
221
+ * @param logs - CheckpointProposed logs.
222
+ * @returns - An array of checkpoints.
185
223
  */
186
- async function processL2BlockProposedLogs(
224
+ async function processCheckpointProposedLogs(
187
225
  rollup: GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
188
226
  publicClient: ViemPublicClient,
189
227
  blobSinkClient: BlobSinkClientInterface,
190
- logs: GetContractEventsReturnType<typeof RollupAbi, 'L2BlockProposed'>,
228
+ logs: GetContractEventsReturnType<typeof RollupAbi, 'CheckpointProposed'>,
191
229
  { chainId, version, targetCommitteeSize }: { chainId: Fr; version: Fr; targetCommitteeSize: number },
192
230
  logger: Logger,
193
- ): Promise<RetrievedL2Block[]> {
194
- const retrievedBlocks: RetrievedL2Block[] = [];
231
+ ): Promise<RetrievedCheckpoint[]> {
232
+ const retrievedCheckpoints: RetrievedCheckpoint[] = [];
195
233
  await asyncPool(10, logs, async log => {
196
- const l2BlockNumber = Number(log.args.blockNumber!);
234
+ const checkpointNumber = CheckpointNumber.fromBigInt(log.args.checkpointNumber!);
197
235
  const archive = log.args.archive!;
198
- const archiveFromChain = await rollup.read.archiveAt([BigInt(l2BlockNumber)]);
236
+ const archiveFromChain = await rollup.read.archiveAt([BigInt(checkpointNumber)]);
199
237
  const blobHashes = log.args.versionedBlobHashes!.map(blobHash => Buffer.from(blobHash.slice(2), 'hex'));
200
238
 
201
- // The value from the event and contract will match only if the block is in the chain.
239
+ // The value from the event and contract will match only if the checkpoint is in the chain.
202
240
  if (archive === archiveFromChain) {
203
- const block = await getBlockFromRollupTx(
241
+ const checkpoint = await getCheckpointFromRollupTx(
204
242
  publicClient,
205
243
  blobSinkClient,
206
244
  log.transactionHash!,
207
245
  blobHashes,
208
- l2BlockNumber,
246
+ checkpointNumber,
209
247
  rollup.address,
210
248
  targetCommitteeSize,
211
249
  logger,
@@ -217,22 +255,22 @@ async function processL2BlockProposedLogs(
217
255
  timestamp: await getL1BlockTime(publicClient, log.blockNumber),
218
256
  };
219
257
 
220
- retrievedBlocks.push({ ...block, l1, chainId, version });
221
- logger.trace(`Retrieved L2 block ${l2BlockNumber} from L1 tx ${log.transactionHash}`, {
258
+ retrievedCheckpoints.push({ ...checkpoint, l1, chainId, version });
259
+ logger.trace(`Retrieved checkpoint ${checkpointNumber} from L1 tx ${log.transactionHash}`, {
222
260
  l1BlockNumber: log.blockNumber,
223
- l2BlockNumber,
261
+ checkpointNumber,
224
262
  archive: archive.toString(),
225
- attestations: block.attestations,
263
+ attestations: checkpoint.attestations,
226
264
  });
227
265
  } else {
228
- logger.warn(`Ignoring L2 block ${l2BlockNumber} due to archive root mismatch`, {
266
+ logger.warn(`Ignoring checkpoint ${checkpointNumber} due to archive root mismatch`, {
229
267
  actual: archive,
230
268
  expected: archiveFromChain,
231
269
  });
232
270
  }
233
271
  });
234
272
 
235
- return retrievedBlocks;
273
+ return retrievedCheckpoints;
236
274
  }
237
275
 
238
276
  export async function getL1BlockTime(publicClient: ViemPublicClient, blockNumber: bigint): Promise<bigint> {
@@ -291,24 +329,25 @@ function extractRollupProposeCalldata(multicall3Data: Hex, rollupAddress: Hex):
291
329
  }
292
330
 
293
331
  /**
294
- * Gets block from the calldata of an L1 transaction.
295
- * Assumes that the block was published from an EOA.
332
+ * Gets checkpoint from the calldata of an L1 transaction.
333
+ * Assumes that the checkpoint was published from an EOA.
296
334
  * TODO: Add retries and error management.
297
335
  * @param publicClient - The viem public client to use for transaction retrieval.
298
336
  * @param txHash - Hash of the tx that published it.
299
- * @param l2BlockNumber - L2 block number.
300
- * @returns L2 block from the calldata, deserialized
337
+ * @param checkpointNumber - Checkpoint number.
338
+ * @returns Checkpoint from the calldata, deserialized
301
339
  */
302
- async function getBlockFromRollupTx(
340
+ async function getCheckpointFromRollupTx(
303
341
  publicClient: ViemPublicClient,
304
342
  blobSinkClient: BlobSinkClientInterface,
305
343
  txHash: `0x${string}`,
306
344
  blobHashes: Buffer[], // TODO(md): buffer32?
307
- l2BlockNumber: number,
345
+ checkpointNumber: CheckpointNumber,
308
346
  rollupAddress: Hex,
309
347
  targetCommitteeSize: number,
310
348
  logger: Logger,
311
- ): Promise<Omit<RetrievedL2Block, 'l1' | 'chainId' | 'version'>> {
349
+ ): Promise<Omit<RetrievedCheckpoint, 'l1' | 'chainId' | 'version'>> {
350
+ logger.trace(`Fetching checkpoint ${checkpointNumber} from rollup tx ${txHash}`);
312
351
  const { input: forwarderData, blockHash } = await publicClient.getTransaction({ hash: txHash });
313
352
 
314
353
  const rollupData = extractRollupProposeCalldata(forwarderData, rollupAddress);
@@ -324,7 +363,6 @@ async function getBlockFromRollupTx(
324
363
  const [decodedArgs, packedAttestations, _signers, _blobInput] = rollupArgs! as readonly [
325
364
  {
326
365
  archive: Hex;
327
- stateReference: ViemStateReference;
328
366
  oracleInput: {
329
367
  feeAssetPriceModifier: bigint;
330
368
  };
@@ -340,10 +378,10 @@ async function getBlockFromRollupTx(
340
378
  const attestations = CommitteeAttestation.fromPacked(packedAttestations, targetCommitteeSize);
341
379
 
342
380
  logger.trace(`Recovered propose calldata from tx ${txHash}`, {
343
- l2BlockNumber,
381
+ checkpointNumber,
344
382
  archive: decodedArgs.archive,
345
- stateReference: decodedArgs.stateReference,
346
383
  header: decodedArgs.header,
384
+ l1BlockHash: blockHash,
347
385
  blobHashes,
348
386
  attestations,
349
387
  packedAttestations,
@@ -353,16 +391,13 @@ async function getBlockFromRollupTx(
353
391
  const header = CheckpointHeader.fromViem(decodedArgs.header);
354
392
  const blobBodies = await blobSinkClient.getBlobSidecar(blockHash, blobHashes);
355
393
  if (blobBodies.length === 0) {
356
- throw new NoBlobBodiesFoundError(l2BlockNumber);
394
+ throw new NoBlobBodiesFoundError(checkpointNumber);
357
395
  }
358
396
 
359
- let blobFields: Fr[];
397
+ let checkpointBlobData: CheckpointBlobData;
360
398
  try {
361
- // Get the fields that were actually added in the checkpoint. And check the encoding of the fields.
362
- blobFields = getBlobFieldsInCheckpoint(
363
- blobBodies.map(b => b.blob),
364
- true /* checkEncoding */,
365
- );
399
+ // Attempt to decode the checkpoint blob data.
400
+ checkpointBlobData = decodeCheckpointBlobDataFromBlobs(blobBodies.map(b => b.blob));
366
401
  } catch (err: any) {
367
402
  if (err instanceof BlobDeserializationError) {
368
403
  logger.fatal(err.message);
@@ -374,14 +409,11 @@ async function getBlockFromRollupTx(
374
409
 
375
410
  const archiveRoot = new Fr(Buffer.from(hexToBytes(decodedArgs.archive)));
376
411
 
377
- const stateReference = StateReference.fromViem(decodedArgs.stateReference);
378
-
379
412
  return {
380
- l2BlockNumber,
413
+ checkpointNumber,
381
414
  archiveRoot,
382
- stateReference,
383
415
  header,
384
- blobFields,
416
+ checkpointBlobData,
385
417
  attestations,
386
418
  };
387
419
  }
@@ -432,13 +464,13 @@ export async function retrieveL1ToL2Messages(
432
464
 
433
465
  function mapLogsInboxMessage(logs: GetContractEventsReturnType<typeof InboxAbi, 'MessageSent'>): InboxMessage[] {
434
466
  return logs.map(log => {
435
- const { index, hash, l2BlockNumber, rollingHash } = log.args;
467
+ const { index, hash, checkpointNumber, rollingHash } = log.args;
436
468
  return {
437
469
  index: index!,
438
470
  leaf: Fr.fromHexString(hash!),
439
471
  l1BlockNumber: log.blockNumber,
440
472
  l1BlockHash: Buffer32.fromString(log.blockHash),
441
- l2BlockNumber: Number(l2BlockNumber!),
473
+ l2BlockNumber: BlockNumber(Number(checkpointNumber!)),
442
474
  rollingHash: Buffer16.fromString(rollingHash!),
443
475
  };
444
476
  });
@@ -450,7 +482,7 @@ export async function retrieveL2ProofVerifiedEvents(
450
482
  rollupAddress: EthAddress,
451
483
  searchStartBlock: bigint,
452
484
  searchEndBlock?: bigint,
453
- ): Promise<{ l1BlockNumber: bigint; l2BlockNumber: number; proverId: Fr; txHash: Hex }[]> {
485
+ ): Promise<{ l1BlockNumber: bigint; checkpointNumber: CheckpointNumber; proverId: Fr; txHash: Hex }[]> {
454
486
  const logs = await publicClient.getLogs({
455
487
  address: rollupAddress.toString(),
456
488
  fromBlock: searchStartBlock,
@@ -461,7 +493,7 @@ export async function retrieveL2ProofVerifiedEvents(
461
493
 
462
494
  return logs.map(log => ({
463
495
  l1BlockNumber: log.blockNumber,
464
- l2BlockNumber: Number(log.args.blockNumber),
496
+ checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber),
465
497
  proverId: Fr.fromHexString(log.args.proverId),
466
498
  txHash: log.transactionHash,
467
499
  }));
@@ -473,14 +505,14 @@ export async function retrieveL2ProofsFromRollup(
473
505
  rollupAddress: EthAddress,
474
506
  searchStartBlock: bigint,
475
507
  searchEndBlock?: bigint,
476
- ): Promise<DataRetrieval<{ proof: Proof; proverId: Fr; l2BlockNumber: number; txHash: `0x${string}` }>> {
508
+ ): Promise<DataRetrieval<{ proof: Proof; proverId: Fr; checkpointNumber: number; txHash: `0x${string}` }>> {
477
509
  const logs = await retrieveL2ProofVerifiedEvents(publicClient, rollupAddress, searchStartBlock, searchEndBlock);
478
- const retrievedData: { proof: Proof; proverId: Fr; l2BlockNumber: number; txHash: `0x${string}` }[] = [];
510
+ const retrievedData: { proof: Proof; proverId: Fr; checkpointNumber: number; txHash: `0x${string}` }[] = [];
479
511
  const lastProcessedL1BlockNumber = logs.length > 0 ? logs.at(-1)!.l1BlockNumber : searchStartBlock - 1n;
480
512
 
481
- for (const { txHash, proverId, l2BlockNumber } of logs) {
513
+ for (const { txHash, proverId, checkpointNumber } of logs) {
482
514
  const proofData = await getProofFromSubmitProofTx(publicClient, txHash, proverId);
483
- retrievedData.push({ proof: proofData.proof, proverId: proofData.proverId, l2BlockNumber, txHash });
515
+ retrievedData.push({ proof: proofData.proof, proverId: proofData.proverId, checkpointNumber, txHash });
484
516
  }
485
517
  return {
486
518
  retrievedData,
@@ -488,26 +520,26 @@ export async function retrieveL2ProofsFromRollup(
488
520
  };
489
521
  }
490
522
 
491
- export type SubmitBlockProof = {
523
+ export type SubmitEpochProof = {
492
524
  archiveRoot: Fr;
493
525
  proverId: Fr;
494
526
  proof: Proof;
495
527
  };
496
528
 
497
529
  /**
498
- * Gets block metadata (header and archive snapshot) from the calldata of an L1 transaction.
530
+ * Gets epoch proof metadata (archive root and proof) from the calldata of an L1 transaction.
499
531
  * Assumes that the block was published from an EOA.
500
532
  * TODO: Add retries and error management.
501
533
  * @param publicClient - The viem public client to use for transaction retrieval.
502
534
  * @param txHash - Hash of the tx that published it.
503
- * @param l2BlockNum - L2 block number.
504
- * @returns L2 block metadata (header and archive) from the calldata, deserialized
535
+ * @param expectedProverId - Expected prover ID.
536
+ * @returns Epoch proof metadata from the calldata, deserialized.
505
537
  */
506
538
  export async function getProofFromSubmitProofTx(
507
539
  publicClient: ViemPublicClient,
508
540
  txHash: `0x${string}`,
509
541
  expectedProverId: Fr,
510
- ): Promise<SubmitBlockProof> {
542
+ ): Promise<SubmitEpochProof> {
511
543
  const { input: data } = await publicClient.getTransaction({ hash: txHash });
512
544
  const { functionName, args } = decodeFunctionData({ abi: RollupAbi, data });
513
545
 
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from '@aztec/foundation/log';
2
- import type { L2Block } from '@aztec/stdlib/block';
2
+ import type { L2BlockNew } from '@aztec/stdlib/block';
3
3
  import {
4
4
  Attributes,
5
5
  type Gauge,
@@ -139,7 +139,7 @@ export class ArchiverInstrumentation {
139
139
  return this.telemetry.isEnabled();
140
140
  }
141
141
 
142
- public processNewBlocks(syncTimePerBlock: number, blocks: L2Block[]) {
142
+ public processNewBlocks(syncTimePerBlock: number, blocks: L2BlockNew[]) {
143
143
  this.syncDurationPerBlock.record(Math.ceil(syncTimePerBlock));
144
144
  this.blockHeight.record(Math.max(...blocks.map(b => b.number)));
145
145
  this.syncBlockCount.add(blocks.length);
@@ -1,4 +1,5 @@
1
1
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
+ import { BlockNumber } from '@aztec/foundation/branded-types';
2
3
  import { Fr } from '@aztec/foundation/fields';
3
4
  import { toArray } from '@aztec/foundation/iterable';
4
5
  import { createLogger } from '@aztec/foundation/log';
@@ -158,7 +159,7 @@ export class BlockStore {
158
159
  * @param blocksToUnwind - The number of blocks we are to unwind
159
160
  * @returns True if the operation is successful
160
161
  */
161
- async unwindBlocks(from: number, blocksToUnwind: number) {
162
+ async unwindBlocks(from: BlockNumber, blocksToUnwind: number) {
162
163
  return await this.db.transactionAsync(async () => {
163
164
  const last = await this.getSynchedL2BlockNumber();
164
165
  if (from !== last) {
@@ -167,12 +168,12 @@ export class BlockStore {
167
168
 
168
169
  const proven = await this.getProvenL2BlockNumber();
169
170
  if (from - blocksToUnwind < proven) {
170
- await this.setProvenL2BlockNumber(from - blocksToUnwind);
171
+ await this.setProvenL2BlockNumber(BlockNumber(from - blocksToUnwind));
171
172
  }
172
173
 
173
174
  for (let i = 0; i < blocksToUnwind; i++) {
174
175
  const blockNumber = from - i;
175
- const block = await this.getBlock(blockNumber);
176
+ const block = await this.getBlock(BlockNumber(blockNumber));
176
177
 
177
178
  if (block === undefined) {
178
179
  this.#log.warn(`Cannot remove block ${blockNumber} from the store since we don't have it`);
@@ -200,7 +201,7 @@ export class BlockStore {
200
201
  * @param limit - The number of blocks to return.
201
202
  * @returns The requested L2 blocks
202
203
  */
203
- async *getBlocks(start: number, limit: number): AsyncIterableIterator<PublishedL2Block> {
204
+ async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<PublishedL2Block> {
204
205
  for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
205
206
  const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
206
207
  if (block) {
@@ -214,7 +215,7 @@ export class BlockStore {
214
215
  * @param blockNumber - The number of the block to return.
215
216
  * @returns The requested L2 block.
216
217
  */
217
- async getBlock(blockNumber: number): Promise<PublishedL2Block | undefined> {
218
+ async getBlock(blockNumber: BlockNumber): Promise<PublishedL2Block | undefined> {
218
219
  const blockStorage = await this.#blocks.getAsync(blockNumber);
219
220
  if (!blockStorage || !blockStorage.header) {
220
221
  return Promise.resolve(undefined);
@@ -232,7 +233,7 @@ export class BlockStore {
232
233
  if (blockNumber === undefined) {
233
234
  return undefined;
234
235
  }
235
- return this.getBlock(blockNumber);
236
+ return this.getBlock(BlockNumber(blockNumber));
236
237
  }
237
238
 
238
239
  /**
@@ -245,7 +246,7 @@ export class BlockStore {
245
246
  if (blockNumber === undefined) {
246
247
  return undefined;
247
248
  }
248
- return this.getBlock(blockNumber);
249
+ return this.getBlock(BlockNumber(blockNumber));
249
250
  }
250
251
 
251
252
  /**
@@ -288,7 +289,7 @@ export class BlockStore {
288
289
  * @param limit - The number of blocks to return.
289
290
  * @returns The requested L2 block headers
290
291
  */
291
- async *getBlockHeaders(start: number, limit: number): AsyncIterableIterator<BlockHeader> {
292
+ async *getBlockHeaders(start: BlockNumber, limit: number): AsyncIterableIterator<BlockHeader> {
292
293
  for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
293
294
  const header = L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
294
295
  if (header.getBlockNumber() !== blockNumber) {
@@ -300,7 +301,7 @@ export class BlockStore {
300
301
  }
301
302
  }
302
303
 
303
- private async *getBlockStorages(start: number, limit: number) {
304
+ private async *getBlockStorages(start: BlockNumber, limit: number) {
304
305
  let expectedBlockNumber = start;
305
306
  for await (const [blockNumber, blockStorage] of this.#blocks.entriesAsync(this.#computeBlockRange(start, limit))) {
306
307
  if (blockNumber !== expectedBlockNumber) {
@@ -382,7 +383,7 @@ export class BlockStore {
382
383
  '',
383
384
  txEffect.data.transactionFee.toBigInt(),
384
385
  txEffect.l2BlockHash,
385
- txEffect.l2BlockNumber,
386
+ BlockNumber(txEffect.l2BlockNumber),
386
387
  );
387
388
  }
388
389
 
@@ -413,9 +414,9 @@ export class BlockStore {
413
414
  * Gets the number of the latest L2 block processed.
414
415
  * @returns The number of the latest L2 block processed.
415
416
  */
416
- async getSynchedL2BlockNumber(): Promise<number> {
417
+ async getSynchedL2BlockNumber(): Promise<BlockNumber> {
417
418
  const [lastBlockNumber] = await toArray(this.#blocks.keysAsync({ reverse: true, limit: 1 }));
418
- return typeof lastBlockNumber === 'number' ? lastBlockNumber : INITIAL_L2_BLOCK_NUM - 1;
419
+ return typeof lastBlockNumber === 'number' ? BlockNumber(lastBlockNumber) : BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
419
420
  }
420
421
 
421
422
  /**
@@ -430,19 +431,19 @@ export class BlockStore {
430
431
  return this.#lastSynchedL1Block.set(l1BlockNumber);
431
432
  }
432
433
 
433
- async getProvenL2BlockNumber(): Promise<number> {
434
+ async getProvenL2BlockNumber(): Promise<BlockNumber> {
434
435
  const [latestBlockNumber, provenBlockNumber] = await Promise.all([
435
436
  this.getSynchedL2BlockNumber(),
436
437
  this.#lastProvenL2Block.getAsync(),
437
438
  ]);
438
- return (provenBlockNumber ?? 0) > latestBlockNumber ? latestBlockNumber : (provenBlockNumber ?? 0);
439
+ return (provenBlockNumber ?? 0) > latestBlockNumber ? latestBlockNumber : BlockNumber(provenBlockNumber ?? 0);
439
440
  }
440
441
 
441
- setProvenL2BlockNumber(blockNumber: number) {
442
+ setProvenL2BlockNumber(blockNumber: BlockNumber) {
442
443
  return this.#lastProvenL2Block.set(blockNumber);
443
444
  }
444
445
 
445
- #computeBlockRange(start: number, limit: number): Required<Pick<Range<number>, 'start' | 'limit'>> {
446
+ #computeBlockRange(start: BlockNumber, limit: number): Required<Pick<Range<number>, 'start' | 'limit'>> {
446
447
  if (limit < 1) {
447
448
  throw new Error(`Invalid limit: ${limit}`);
448
449
  }