@aztec/archiver 3.0.0-canary.a9708bd → 3.0.0-devnet.2-patch.1

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 (127) hide show
  1. package/README.md +27 -6
  2. package/dest/archiver/archiver.d.ts +87 -64
  3. package/dest/archiver/archiver.d.ts.map +1 -1
  4. package/dest/archiver/archiver.js +463 -278
  5. package/dest/archiver/archiver_store.d.ts +46 -28
  6. package/dest/archiver/archiver_store.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
  8. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  9. package/dest/archiver/archiver_store_test_suite.js +316 -143
  10. package/dest/archiver/config.d.ts +6 -23
  11. package/dest/archiver/config.d.ts.map +1 -1
  12. package/dest/archiver/config.js +19 -12
  13. package/dest/archiver/errors.d.ts +1 -1
  14. package/dest/archiver/errors.d.ts.map +1 -1
  15. package/dest/archiver/index.d.ts +1 -1
  16. package/dest/archiver/instrumentation.d.ts +5 -3
  17. package/dest/archiver/instrumentation.d.ts.map +1 -1
  18. package/dest/archiver/instrumentation.js +14 -0
  19. package/dest/archiver/kv_archiver_store/block_store.d.ts +45 -9
  20. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  21. package/dest/archiver/kv_archiver_store/block_store.js +99 -12
  22. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +2 -2
  23. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  24. package/dest/archiver/kv_archiver_store/contract_class_store.js +1 -1
  25. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +2 -2
  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 +30 -30
  28. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  29. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +26 -15
  30. package/dest/archiver/kv_archiver_store/log_store.d.ts +3 -10
  31. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  32. package/dest/archiver/kv_archiver_store/log_store.js +4 -26
  33. package/dest/archiver/kv_archiver_store/message_store.d.ts +6 -5
  34. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  35. package/dest/archiver/kv_archiver_store/message_store.js +15 -14
  36. package/dest/archiver/l1/bin/retrieve-calldata.d.ts +3 -0
  37. package/dest/archiver/l1/bin/retrieve-calldata.d.ts.map +1 -0
  38. package/dest/archiver/l1/bin/retrieve-calldata.js +147 -0
  39. package/dest/archiver/l1/calldata_retriever.d.ts +98 -0
  40. package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -0
  41. package/dest/archiver/l1/calldata_retriever.js +403 -0
  42. package/dest/archiver/l1/data_retrieval.d.ts +87 -0
  43. package/dest/archiver/l1/data_retrieval.d.ts.map +1 -0
  44. package/dest/archiver/{data_retrieval.js → l1/data_retrieval.js} +118 -154
  45. package/dest/archiver/l1/debug_tx.d.ts +19 -0
  46. package/dest/archiver/l1/debug_tx.d.ts.map +1 -0
  47. package/dest/archiver/l1/debug_tx.js +73 -0
  48. package/dest/archiver/l1/spire_proposer.d.ts +70 -0
  49. package/dest/archiver/l1/spire_proposer.d.ts.map +1 -0
  50. package/dest/archiver/l1/spire_proposer.js +157 -0
  51. package/dest/archiver/l1/trace_tx.d.ts +97 -0
  52. package/dest/archiver/l1/trace_tx.d.ts.map +1 -0
  53. package/dest/archiver/l1/trace_tx.js +91 -0
  54. package/dest/archiver/l1/types.d.ts +12 -0
  55. package/dest/archiver/l1/types.d.ts.map +1 -0
  56. package/dest/archiver/l1/types.js +3 -0
  57. package/dest/archiver/l1/validate_trace.d.ts +29 -0
  58. package/dest/archiver/l1/validate_trace.d.ts.map +1 -0
  59. package/dest/archiver/l1/validate_trace.js +150 -0
  60. package/dest/archiver/structs/data_retrieval.d.ts +1 -1
  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 -5
  64. package/dest/archiver/structs/published.d.ts +3 -2
  65. package/dest/archiver/structs/published.d.ts.map +1 -1
  66. package/dest/archiver/validation.d.ts +10 -4
  67. package/dest/archiver/validation.d.ts.map +1 -1
  68. package/dest/archiver/validation.js +66 -44
  69. package/dest/factory.d.ts +3 -11
  70. package/dest/factory.d.ts.map +1 -1
  71. package/dest/factory.js +5 -17
  72. package/dest/index.d.ts +2 -2
  73. package/dest/index.d.ts.map +1 -1
  74. package/dest/index.js +1 -1
  75. package/dest/rpc/index.d.ts +2 -2
  76. package/dest/test/index.d.ts +1 -1
  77. package/dest/test/mock_archiver.d.ts +16 -8
  78. package/dest/test/mock_archiver.d.ts.map +1 -1
  79. package/dest/test/mock_archiver.js +19 -14
  80. package/dest/test/mock_l1_to_l2_message_source.d.ts +7 -6
  81. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  82. package/dest/test/mock_l1_to_l2_message_source.js +10 -9
  83. package/dest/test/mock_l2_block_source.d.ts +24 -20
  84. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  85. package/dest/test/mock_l2_block_source.js +79 -13
  86. package/dest/test/mock_structs.d.ts +3 -2
  87. package/dest/test/mock_structs.d.ts.map +1 -1
  88. package/dest/test/mock_structs.js +9 -8
  89. package/package.json +18 -17
  90. package/src/archiver/archiver.ts +610 -363
  91. package/src/archiver/archiver_store.ts +55 -28
  92. package/src/archiver/archiver_store_test_suite.ts +369 -143
  93. package/src/archiver/config.ts +26 -51
  94. package/src/archiver/instrumentation.ts +19 -2
  95. package/src/archiver/kv_archiver_store/block_store.ts +139 -21
  96. package/src/archiver/kv_archiver_store/contract_class_store.ts +1 -1
  97. package/src/archiver/kv_archiver_store/contract_instance_store.ts +1 -1
  98. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +48 -33
  99. package/src/archiver/kv_archiver_store/log_store.ts +4 -30
  100. package/src/archiver/kv_archiver_store/message_store.ts +21 -18
  101. package/src/archiver/l1/README.md +98 -0
  102. package/src/archiver/l1/bin/retrieve-calldata.ts +182 -0
  103. package/src/archiver/l1/calldata_retriever.ts +531 -0
  104. package/src/archiver/{data_retrieval.ts → l1/data_retrieval.ts} +198 -242
  105. package/src/archiver/l1/debug_tx.ts +99 -0
  106. package/src/archiver/l1/spire_proposer.ts +160 -0
  107. package/src/archiver/l1/trace_tx.ts +128 -0
  108. package/src/archiver/l1/types.ts +13 -0
  109. package/src/archiver/l1/validate_trace.ts +211 -0
  110. package/src/archiver/structs/inbox_message.ts +8 -8
  111. package/src/archiver/structs/published.ts +2 -1
  112. package/src/archiver/validation.ts +86 -32
  113. package/src/factory.ts +6 -26
  114. package/src/index.ts +1 -1
  115. package/src/test/fixtures/debug_traceTransaction-multicall3.json +88 -0
  116. package/src/test/fixtures/debug_traceTransaction-multiplePropose.json +153 -0
  117. package/src/test/fixtures/debug_traceTransaction-proxied.json +122 -0
  118. package/src/test/fixtures/trace_transaction-multicall3.json +65 -0
  119. package/src/test/fixtures/trace_transaction-multiplePropose.json +319 -0
  120. package/src/test/fixtures/trace_transaction-proxied.json +128 -0
  121. package/src/test/fixtures/trace_transaction-randomRevert.json +216 -0
  122. package/src/test/mock_archiver.ts +22 -16
  123. package/src/test/mock_l1_to_l2_message_source.ts +10 -9
  124. package/src/test/mock_l2_block_source.ts +110 -27
  125. package/src/test/mock_structs.ts +10 -9
  126. package/dest/archiver/data_retrieval.d.ts +0 -78
  127. package/dest/archiver/data_retrieval.d.ts.map +0 -1
@@ -1,23 +1,26 @@
1
- import { Blob, BlobDeserializationError } 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
- import type {
4
- EpochProofPublicInputArgs,
5
- ViemClient,
6
- ViemCommitteeAttestations,
7
- ViemHeader,
8
- ViemPublicClient,
9
- ViemStateReference,
10
- } from '@aztec/ethereum';
9
+ import type { EpochProofPublicInputArgs } from '@aztec/ethereum/contracts';
10
+ import type { ViemClient, ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
11
11
  import { asyncPool } from '@aztec/foundation/async-pool';
12
+ import { CheckpointNumber } from '@aztec/foundation/branded-types';
12
13
  import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
13
- import type { EthAddress } from '@aztec/foundation/eth-address';
14
- import { Fr } from '@aztec/foundation/fields';
14
+ import { Fr } from '@aztec/foundation/curves/bn254';
15
+ import { EthAddress } from '@aztec/foundation/eth-address';
15
16
  import { type Logger, createLogger } from '@aztec/foundation/log';
16
17
  import { type InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
17
- import { Body, CommitteeAttestation, L2Block } from '@aztec/stdlib/block';
18
+ import { Body, CommitteeAttestation, L2BlockNew } from '@aztec/stdlib/block';
19
+ import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
18
20
  import { Proof } from '@aztec/stdlib/proofs';
21
+ import { CheckpointHeader } from '@aztec/stdlib/rollup';
19
22
  import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
20
- import { BlockHeader, GlobalVariables, ProposedBlockHeader, StateReference } from '@aztec/stdlib/tx';
23
+ import { BlockHeader, GlobalVariables, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
21
24
 
22
25
  import {
23
26
  type GetContractEventsReturnType,
@@ -26,91 +29,139 @@ import {
26
29
  decodeFunctionData,
27
30
  getAbiItem,
28
31
  hexToBytes,
29
- multicall3Abi,
30
32
  } from 'viem';
31
33
 
32
- import { NoBlobBodiesFoundError } from './errors.js';
33
- import type { DataRetrieval } from './structs/data_retrieval.js';
34
- import type { InboxMessage } from './structs/inbox_message.js';
35
- import type { L1PublishedData, PublishedL2Block } from './structs/published.js';
34
+ import { NoBlobBodiesFoundError } from '../errors.js';
35
+ import type { ArchiverInstrumentation } from '../instrumentation.js';
36
+ import type { DataRetrieval } from '../structs/data_retrieval.js';
37
+ import type { InboxMessage } from '../structs/inbox_message.js';
38
+ import type { L1PublishedData } from '../structs/published.js';
39
+ import { CalldataRetriever } from './calldata_retriever.js';
36
40
 
37
- export type RetrievedL2Block = {
38
- l2BlockNumber: number;
41
+ export type RetrievedCheckpoint = {
42
+ checkpointNumber: CheckpointNumber;
39
43
  archiveRoot: Fr;
40
- stateReference: StateReference;
41
- header: ProposedBlockHeader;
42
- body: Body;
44
+ header: CheckpointHeader;
45
+ checkpointBlobData: CheckpointBlobData;
43
46
  l1: L1PublishedData;
44
47
  chainId: Fr;
45
48
  version: Fr;
46
49
  attestations: CommitteeAttestation[];
47
50
  };
48
51
 
49
- export function retrievedBlockToPublishedL2Block(retrievedBlock: RetrievedL2Block): PublishedL2Block {
50
- const {
51
- l2BlockNumber,
52
- archiveRoot,
53
- stateReference,
54
- header: proposedHeader,
55
- body,
56
- l1,
57
- chainId,
58
- version,
59
- attestations,
60
- } = retrievedBlock;
61
-
62
- const archive = new AppendOnlyTreeSnapshot(
63
- archiveRoot,
64
- l2BlockNumber + 1, // nextAvailableLeafIndex
65
- );
66
-
67
- const globalVariables = GlobalVariables.from({
68
- chainId,
69
- version,
70
- blockNumber: l2BlockNumber,
71
- slotNumber: proposedHeader.slotNumber,
72
- timestamp: proposedHeader.timestamp,
73
- coinbase: proposedHeader.coinbase,
74
- feeRecipient: proposedHeader.feeRecipient,
75
- gasFees: proposedHeader.gasFees,
76
- });
52
+ export async function retrievedToPublishedCheckpoint({
53
+ checkpointNumber,
54
+ archiveRoot,
55
+ header: checkpointHeader,
56
+ checkpointBlobData,
57
+ l1,
58
+ chainId,
59
+ version,
60
+ attestations,
61
+ }: RetrievedCheckpoint): Promise<PublishedCheckpoint> {
62
+ const { blocks: blocksBlobData } = checkpointBlobData;
63
+
64
+ // The lastArchiveRoot of a block is the new archive for the previous block.
65
+ const newArchiveRoots = blocksBlobData
66
+ .map(b => b.lastArchiveRoot)
67
+ .slice(1)
68
+ .concat([archiveRoot]);
69
+
70
+ // An error will be thrown from `decodeCheckpointBlobDataFromBlobs` if it can't read a field for the
71
+ // `l1ToL2MessageRoot` of the first block. So below we can safely assume it exists:
72
+ const l1toL2MessageTreeRoot = blocksBlobData[0].l1ToL2MessageRoot!;
73
+
74
+ const spongeBlob = SpongeBlob.init();
75
+ const l2Blocks: L2BlockNew[] = [];
76
+ for (let i = 0; i < blocksBlobData.length; i++) {
77
+ const blockBlobData = blocksBlobData[i];
78
+ const { blockEndMarker, blockEndStateField, lastArchiveRoot, noteHashRoot, nullifierRoot, publicDataRoot } =
79
+ blockBlobData;
80
+
81
+ const l2BlockNumber = blockEndMarker.blockNumber;
82
+
83
+ const globalVariables = GlobalVariables.from({
84
+ chainId,
85
+ version,
86
+ blockNumber: l2BlockNumber,
87
+ slotNumber: checkpointHeader.slotNumber,
88
+ timestamp: blockEndMarker.timestamp,
89
+ coinbase: checkpointHeader.coinbase,
90
+ feeRecipient: checkpointHeader.feeRecipient,
91
+ gasFees: checkpointHeader.gasFees,
92
+ });
93
+
94
+ const state = StateReference.from({
95
+ l1ToL2MessageTree: new AppendOnlyTreeSnapshot(
96
+ l1toL2MessageTreeRoot,
97
+ blockEndStateField.l1ToL2MessageNextAvailableLeafIndex,
98
+ ),
99
+ partial: PartialStateReference.from({
100
+ noteHashTree: new AppendOnlyTreeSnapshot(noteHashRoot, blockEndStateField.noteHashNextAvailableLeafIndex),
101
+ nullifierTree: new AppendOnlyTreeSnapshot(nullifierRoot, blockEndStateField.nullifierNextAvailableLeafIndex),
102
+ publicDataTree: new AppendOnlyTreeSnapshot(publicDataRoot, blockEndStateField.publicDataNextAvailableLeafIndex),
103
+ }),
104
+ });
105
+
106
+ const body = Body.fromTxBlobData(checkpointBlobData.blocks[0].txs);
107
+
108
+ const blobFields = encodeBlockBlobData(blockBlobData);
109
+ await spongeBlob.absorb(blobFields);
110
+
111
+ const clonedSpongeBlob = spongeBlob.clone();
112
+ const spongeBlobHash = await clonedSpongeBlob.squeeze();
113
+
114
+ const header = BlockHeader.from({
115
+ lastArchive: new AppendOnlyTreeSnapshot(lastArchiveRoot, l2BlockNumber),
116
+ state,
117
+ spongeBlobHash,
118
+ globalVariables,
119
+ totalFees: body.txEffects.reduce((accum, txEffect) => accum.add(txEffect.transactionFee), Fr.ZERO),
120
+ totalManaUsed: new Fr(blockEndStateField.totalManaUsed),
121
+ });
122
+
123
+ const newArchive = new AppendOnlyTreeSnapshot(newArchiveRoots[i], l2BlockNumber + 1);
124
+
125
+ l2Blocks.push(new L2BlockNew(newArchive, header, body, checkpointNumber, i));
126
+ }
77
127
 
78
- const header = BlockHeader.from({
79
- lastArchive: new AppendOnlyTreeSnapshot(proposedHeader.lastArchiveRoot, l2BlockNumber),
80
- contentCommitment: proposedHeader.contentCommitment,
81
- state: stateReference,
82
- globalVariables,
83
- totalFees: body.txEffects.reduce((accum, txEffect) => accum.add(txEffect.transactionFee), Fr.ZERO),
84
- totalManaUsed: proposedHeader.totalManaUsed,
128
+ const lastBlock = l2Blocks.at(-1)!;
129
+ const checkpoint = Checkpoint.from({
130
+ archive: new AppendOnlyTreeSnapshot(archiveRoot, lastBlock.number + 1),
131
+ header: checkpointHeader,
132
+ blocks: l2Blocks,
133
+ number: checkpointNumber,
85
134
  });
86
135
 
87
- const block = new L2Block(archive, header, body);
88
-
89
- return {
90
- block,
91
- l1,
92
- attestations,
93
- };
136
+ return PublishedCheckpoint.from({ checkpoint, l1, attestations });
94
137
  }
95
138
 
96
139
  /**
97
- * Fetches new L2 blocks.
140
+ * Fetches new checkpoints.
98
141
  * @param publicClient - The viem public client to use for transaction retrieval.
142
+ * @param debugClient - The viem debug client to use for trace/debug RPC methods (optional).
99
143
  * @param rollupAddress - The address of the rollup contract.
100
144
  * @param searchStartBlock - The block number to use for starting the search.
101
145
  * @param searchEndBlock - The highest block number that we should search up to.
102
146
  * @param expectedNextL2BlockNum - The next L2 block number that we expect to find.
103
147
  * @returns An array of block; as well as the next eth block to search from.
104
148
  */
105
- export async function retrieveBlocksFromRollup(
149
+ export async function retrieveCheckpointsFromRollup(
106
150
  rollup: GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
107
151
  publicClient: ViemPublicClient,
152
+ debugClient: ViemPublicDebugClient,
108
153
  blobSinkClient: BlobSinkClientInterface,
109
154
  searchStartBlock: bigint,
110
155
  searchEndBlock: bigint,
156
+ contractAddresses: {
157
+ governanceProposerAddress: EthAddress;
158
+ slashFactoryAddress?: EthAddress;
159
+ slashingProposerAddress: EthAddress;
160
+ },
161
+ instrumentation: ArchiverInstrumentation,
111
162
  logger: Logger = createLogger('archiver'),
112
- ): Promise<RetrievedL2Block[]> {
113
- const retrievedBlocks: RetrievedL2Block[] = [];
163
+ ): Promise<RetrievedCheckpoint[]> {
164
+ const retrievedCheckpoints: RetrievedCheckpoint[] = [];
114
165
 
115
166
  let rollupConstants: { chainId: Fr; version: Fr; targetCommitteeSize: number } | undefined;
116
167
 
@@ -118,8 +169,8 @@ export async function retrieveBlocksFromRollup(
118
169
  if (searchStartBlock > searchEndBlock) {
119
170
  break;
120
171
  }
121
- const l2BlockProposedLogs = (
122
- await rollup.getEvents.L2BlockProposed(
172
+ const checkpointProposedLogs = (
173
+ await rollup.getEvents.CheckpointProposed(
123
174
  {},
124
175
  {
125
176
  fromBlock: searchStartBlock,
@@ -128,13 +179,13 @@ export async function retrieveBlocksFromRollup(
128
179
  )
129
180
  ).filter(log => log.blockNumber! >= searchStartBlock && log.blockNumber! <= searchEndBlock);
130
181
 
131
- if (l2BlockProposedLogs.length === 0) {
182
+ if (checkpointProposedLogs.length === 0) {
132
183
  break;
133
184
  }
134
185
 
135
- const lastLog = l2BlockProposedLogs[l2BlockProposedLogs.length - 1];
186
+ const lastLog = checkpointProposedLogs.at(-1)!;
136
187
  logger.debug(
137
- `Got ${l2BlockProposedLogs.length} L2 block processed logs for L2 blocks ${l2BlockProposedLogs[0].args.blockNumber}-${lastLog.args.blockNumber} between L1 blocks ${searchStartBlock}-${searchEndBlock}`,
188
+ `Got ${checkpointProposedLogs.length} processed logs for checkpoints ${checkpointProposedLogs[0].args.checkpointNumber}-${lastLog.args.checkpointNumber} between L1 blocks ${searchStartBlock}-${searchEndBlock}`,
138
189
  );
139
190
 
140
191
  if (rollupConstants === undefined) {
@@ -150,54 +201,76 @@ export async function retrieveBlocksFromRollup(
150
201
  };
151
202
  }
152
203
 
153
- const newBlocks = await processL2BlockProposedLogs(
204
+ const newCheckpoints = await processCheckpointProposedLogs(
154
205
  rollup,
155
206
  publicClient,
207
+ debugClient,
156
208
  blobSinkClient,
157
- l2BlockProposedLogs,
209
+ checkpointProposedLogs,
158
210
  rollupConstants,
211
+ contractAddresses,
212
+ instrumentation,
159
213
  logger,
160
214
  );
161
- retrievedBlocks.push(...newBlocks);
215
+ retrievedCheckpoints.push(...newCheckpoints);
162
216
  searchStartBlock = lastLog.blockNumber! + 1n;
163
217
  } while (searchStartBlock <= searchEndBlock);
164
218
 
165
- // The asyncpool from processL2BlockProposedLogs will not necessarily return the blocks in order, so we sort them before returning.
166
- return retrievedBlocks.sort((a, b) => Number(a.l1.blockNumber - b.l1.blockNumber));
219
+ // The asyncPool from processCheckpointProposedLogs will not necessarily return the checkpoints in order, so we sort them before returning.
220
+ return retrievedCheckpoints.sort((a, b) => Number(a.l1.blockNumber - b.l1.blockNumber));
167
221
  }
168
222
 
169
223
  /**
170
- * Processes newly received L2BlockProposed logs.
224
+ * Processes newly received CheckpointProposed logs.
171
225
  * @param rollup - The rollup contract
172
226
  * @param publicClient - The viem public client to use for transaction retrieval.
173
- * @param logs - L2BlockProposed logs.
174
- * @returns - An array blocks.
227
+ * @param debugClient - The viem debug client to use for trace/debug RPC methods (optional).
228
+ * @param logs - CheckpointProposed logs.
229
+ * @returns - An array of checkpoints.
175
230
  */
176
- async function processL2BlockProposedLogs(
231
+ async function processCheckpointProposedLogs(
177
232
  rollup: GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
178
233
  publicClient: ViemPublicClient,
234
+ debugClient: ViemPublicDebugClient,
179
235
  blobSinkClient: BlobSinkClientInterface,
180
- logs: GetContractEventsReturnType<typeof RollupAbi, 'L2BlockProposed'>,
236
+ logs: GetContractEventsReturnType<typeof RollupAbi, 'CheckpointProposed'>,
181
237
  { chainId, version, targetCommitteeSize }: { chainId: Fr; version: Fr; targetCommitteeSize: number },
238
+ contractAddresses: {
239
+ governanceProposerAddress: EthAddress;
240
+ slashFactoryAddress?: EthAddress;
241
+ slashingProposerAddress: EthAddress;
242
+ },
243
+ instrumentation: ArchiverInstrumentation,
182
244
  logger: Logger,
183
- ): Promise<RetrievedL2Block[]> {
184
- const retrievedBlocks: RetrievedL2Block[] = [];
245
+ ): Promise<RetrievedCheckpoint[]> {
246
+ const retrievedCheckpoints: RetrievedCheckpoint[] = [];
247
+ const calldataRetriever = new CalldataRetriever(
248
+ publicClient,
249
+ debugClient,
250
+ targetCommitteeSize,
251
+ instrumentation,
252
+ logger,
253
+ { ...contractAddresses, rollupAddress: EthAddress.fromString(rollup.address) },
254
+ );
255
+
185
256
  await asyncPool(10, logs, async log => {
186
- const l2BlockNumber = Number(log.args.blockNumber!);
257
+ const checkpointNumber = CheckpointNumber.fromBigInt(log.args.checkpointNumber!);
187
258
  const archive = log.args.archive!;
188
- const archiveFromChain = await rollup.read.archiveAt([BigInt(l2BlockNumber)]);
259
+ const archiveFromChain = await rollup.read.archiveAt([BigInt(checkpointNumber)]);
189
260
  const blobHashes = log.args.versionedBlobHashes!.map(blobHash => Buffer.from(blobHash.slice(2), 'hex'));
190
261
 
191
- // The value from the event and contract will match only if the block is in the chain.
262
+ // The value from the event and contract will match only if the checkpoint is in the chain.
192
263
  if (archive === archiveFromChain) {
193
- const block = await getBlockFromRollupTx(
194
- publicClient,
195
- blobSinkClient,
264
+ const checkpoint = await calldataRetriever.getCheckpointFromRollupTx(
196
265
  log.transactionHash!,
197
266
  blobHashes,
198
- l2BlockNumber,
199
- rollup.address,
200
- targetCommitteeSize,
267
+ checkpointNumber,
268
+ );
269
+ const checkpointBlobData = await getCheckpointBlobDataFromBlobs(
270
+ blobSinkClient,
271
+ checkpoint.blockHash,
272
+ blobHashes,
273
+ checkpointNumber,
201
274
  logger,
202
275
  );
203
276
 
@@ -207,22 +280,22 @@ async function processL2BlockProposedLogs(
207
280
  timestamp: await getL1BlockTime(publicClient, log.blockNumber),
208
281
  };
209
282
 
210
- retrievedBlocks.push({ ...block, l1, chainId, version });
211
- logger.trace(`Retrieved L2 block ${l2BlockNumber} from L1 tx ${log.transactionHash}`, {
283
+ retrievedCheckpoints.push({ ...checkpoint, checkpointBlobData, l1, chainId, version });
284
+ logger.trace(`Retrieved checkpoint ${checkpointNumber} from L1 tx ${log.transactionHash}`, {
212
285
  l1BlockNumber: log.blockNumber,
213
- l2BlockNumber,
286
+ checkpointNumber,
214
287
  archive: archive.toString(),
215
- attestations: block.attestations,
288
+ attestations: checkpoint.attestations,
216
289
  });
217
290
  } else {
218
- logger.warn(`Ignoring L2 block ${l2BlockNumber} due to archive root mismatch`, {
291
+ logger.warn(`Ignoring checkpoint ${checkpointNumber} due to archive root mismatch`, {
219
292
  actual: archive,
220
293
  expected: archiveFromChain,
221
294
  });
222
295
  }
223
296
  });
224
297
 
225
- return retrievedBlocks;
298
+ return retrievedCheckpoints;
226
299
  }
227
300
 
228
301
  export async function getL1BlockTime(publicClient: ViemPublicClient, blockNumber: bigint): Promise<bigint> {
@@ -230,150 +303,33 @@ export async function getL1BlockTime(publicClient: ViemPublicClient, blockNumber
230
303
  return block.timestamp;
231
304
  }
232
305
 
233
- /**
234
- * Extracts the first 'propose' method calldata from a multicall3 transaction's data.
235
- * @param multicall3Data - The multicall3 transaction input data
236
- * @param rollupAddress - The address of the rollup contract
237
- * @returns The calldata for the first 'propose' method call to the rollup contract
238
- */
239
- function extractRollupProposeCalldata(multicall3Data: Hex, rollupAddress: Hex): Hex {
240
- const { functionName: multicall3FunctionName, args: multicall3Args } = decodeFunctionData({
241
- abi: multicall3Abi,
242
- data: multicall3Data,
243
- });
244
-
245
- if (multicall3FunctionName !== 'aggregate3') {
246
- throw new Error(`Unexpected multicall3 method called ${multicall3FunctionName}`);
247
- }
248
-
249
- if (multicall3Args.length !== 1) {
250
- throw new Error(`Unexpected number of arguments for multicall3`);
251
- }
252
-
253
- const [calls] = multicall3Args;
254
-
255
- // Find all rollup calls
256
- const rollupAddressLower = rollupAddress.toLowerCase();
257
-
258
- for (let i = 0; i < calls.length; i++) {
259
- const addr = calls[i].target;
260
- if (addr.toLowerCase() !== rollupAddressLower) {
261
- continue;
262
- }
263
- const callData = calls[i].callData;
264
-
265
- try {
266
- const { functionName: rollupFunctionName } = decodeFunctionData({
267
- abi: RollupAbi,
268
- data: callData,
269
- });
270
-
271
- if (rollupFunctionName === 'propose') {
272
- return callData;
273
- }
274
- } catch {
275
- // Skip invalid function data
276
- continue;
277
- }
278
- }
279
-
280
- throw new Error(`Rollup address not found in multicall3 args`);
281
- }
282
-
283
- /**
284
- * Gets block from the calldata of an L1 transaction.
285
- * Assumes that the block was published from an EOA.
286
- * TODO: Add retries and error management.
287
- * @param publicClient - The viem public client to use for transaction retrieval.
288
- * @param txHash - Hash of the tx that published it.
289
- * @param l2BlockNumber - L2 block number.
290
- * @returns L2 block from the calldata, deserialized
291
- */
292
- async function getBlockFromRollupTx(
293
- publicClient: ViemPublicClient,
306
+ export async function getCheckpointBlobDataFromBlobs(
294
307
  blobSinkClient: BlobSinkClientInterface,
295
- txHash: `0x${string}`,
296
- blobHashes: Buffer[], // TODO(md): buffer32?
297
- l2BlockNumber: number,
298
- rollupAddress: Hex,
299
- targetCommitteeSize: number,
308
+ blockHash: string,
309
+ blobHashes: Buffer<ArrayBufferLike>[],
310
+ checkpointNumber: CheckpointNumber,
300
311
  logger: Logger,
301
- ): Promise<Omit<RetrievedL2Block, 'l1' | 'chainId' | 'version'>> {
302
- const { input: forwarderData, blockHash } = await publicClient.getTransaction({ hash: txHash });
303
-
304
- const rollupData = extractRollupProposeCalldata(forwarderData, rollupAddress);
305
- const { functionName: rollupFunctionName, args: rollupArgs } = decodeFunctionData({
306
- abi: RollupAbi,
307
- data: rollupData,
308
- });
309
-
310
- if (rollupFunctionName !== 'propose') {
311
- throw new Error(`Unexpected rollup method called ${rollupFunctionName}`);
312
- }
313
-
314
- const [decodedArgs, packedAttestations, _signers, _blobInput] = rollupArgs! as readonly [
315
- {
316
- archive: Hex;
317
- stateReference: ViemStateReference;
318
- oracleInput: {
319
- feeAssetPriceModifier: bigint;
320
- };
321
- header: ViemHeader;
322
- txHashes: readonly Hex[];
323
- },
324
- ViemCommitteeAttestations,
325
- Hex[],
326
- Hex,
327
- ];
328
-
329
- const attestations = CommitteeAttestation.fromPacked(packedAttestations, targetCommitteeSize);
330
-
331
- logger.trace(`Recovered propose calldata from tx ${txHash}`, {
332
- l2BlockNumber,
333
- archive: decodedArgs.archive,
334
- stateReference: decodedArgs.stateReference,
335
- header: decodedArgs.header,
336
- blobHashes,
337
- attestations,
338
- packedAttestations,
339
- targetCommitteeSize,
340
- });
341
-
342
- // TODO(md): why is the proposed block header different to the actual block header?
343
- // This is likely going to be a footgun
344
- const header = ProposedBlockHeader.fromViem(decodedArgs.header);
312
+ ): Promise<CheckpointBlobData> {
345
313
  const blobBodies = await blobSinkClient.getBlobSidecar(blockHash, blobHashes);
346
314
  if (blobBodies.length === 0) {
347
- throw new NoBlobBodiesFoundError(l2BlockNumber);
315
+ throw new NoBlobBodiesFoundError(checkpointNumber);
348
316
  }
349
317
 
350
- let blockFields: Fr[];
318
+ let checkpointBlobData: CheckpointBlobData;
351
319
  try {
352
- blockFields = Blob.toEncodedFields(blobBodies.map(b => b.blob));
320
+ // Attempt to decode the checkpoint blob data.
321
+ checkpointBlobData = decodeCheckpointBlobDataFromBlobs(blobBodies.map(b => b.blob));
353
322
  } catch (err: any) {
354
323
  if (err instanceof BlobDeserializationError) {
355
324
  logger.fatal(err.message);
356
325
  } else {
357
326
  logger.fatal('Unable to sync: failed to decode fetched blob, this blob was likely not created by us');
358
327
  }
328
+ // Throwing an error since this is most likely caused by a bug.
359
329
  throw err;
360
330
  }
361
331
 
362
- // The blob source gives us blockFields, and we must construct the body from them:
363
- const body = Body.fromBlobFields(blockFields);
364
-
365
- const archiveRoot = new Fr(Buffer.from(hexToBytes(decodedArgs.archive)));
366
-
367
- const stateReference = StateReference.fromViem(decodedArgs.stateReference);
368
-
369
- return {
370
- l2BlockNumber,
371
- archiveRoot,
372
- stateReference,
373
- header,
374
- body,
375
- attestations,
376
- };
332
+ return checkpointBlobData;
377
333
  }
378
334
 
379
335
  /** Given an L1 to L2 message, retrieves its corresponding event from the Inbox within a specific block range. */
@@ -422,13 +378,13 @@ export async function retrieveL1ToL2Messages(
422
378
 
423
379
  function mapLogsInboxMessage(logs: GetContractEventsReturnType<typeof InboxAbi, 'MessageSent'>): InboxMessage[] {
424
380
  return logs.map(log => {
425
- const { index, hash, l2BlockNumber, rollingHash } = log.args;
381
+ const { index, hash, checkpointNumber, rollingHash } = log.args;
426
382
  return {
427
383
  index: index!,
428
384
  leaf: Fr.fromHexString(hash!),
429
385
  l1BlockNumber: log.blockNumber,
430
386
  l1BlockHash: Buffer32.fromString(log.blockHash),
431
- l2BlockNumber: Number(l2BlockNumber!),
387
+ checkpointNumber: CheckpointNumber.fromBigInt(checkpointNumber!),
432
388
  rollingHash: Buffer16.fromString(rollingHash!),
433
389
  };
434
390
  });
@@ -440,7 +396,7 @@ export async function retrieveL2ProofVerifiedEvents(
440
396
  rollupAddress: EthAddress,
441
397
  searchStartBlock: bigint,
442
398
  searchEndBlock?: bigint,
443
- ): Promise<{ l1BlockNumber: bigint; l2BlockNumber: number; proverId: Fr; txHash: Hex }[]> {
399
+ ): Promise<{ l1BlockNumber: bigint; checkpointNumber: CheckpointNumber; proverId: Fr; txHash: Hex }[]> {
444
400
  const logs = await publicClient.getLogs({
445
401
  address: rollupAddress.toString(),
446
402
  fromBlock: searchStartBlock,
@@ -451,7 +407,7 @@ export async function retrieveL2ProofVerifiedEvents(
451
407
 
452
408
  return logs.map(log => ({
453
409
  l1BlockNumber: log.blockNumber,
454
- l2BlockNumber: Number(log.args.blockNumber),
410
+ checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber),
455
411
  proverId: Fr.fromHexString(log.args.proverId),
456
412
  txHash: log.transactionHash,
457
413
  }));
@@ -463,14 +419,14 @@ export async function retrieveL2ProofsFromRollup(
463
419
  rollupAddress: EthAddress,
464
420
  searchStartBlock: bigint,
465
421
  searchEndBlock?: bigint,
466
- ): Promise<DataRetrieval<{ proof: Proof; proverId: Fr; l2BlockNumber: number; txHash: `0x${string}` }>> {
422
+ ): Promise<DataRetrieval<{ proof: Proof; proverId: Fr; checkpointNumber: number; txHash: `0x${string}` }>> {
467
423
  const logs = await retrieveL2ProofVerifiedEvents(publicClient, rollupAddress, searchStartBlock, searchEndBlock);
468
- const retrievedData: { proof: Proof; proverId: Fr; l2BlockNumber: number; txHash: `0x${string}` }[] = [];
424
+ const retrievedData: { proof: Proof; proverId: Fr; checkpointNumber: number; txHash: `0x${string}` }[] = [];
469
425
  const lastProcessedL1BlockNumber = logs.length > 0 ? logs.at(-1)!.l1BlockNumber : searchStartBlock - 1n;
470
426
 
471
- for (const { txHash, proverId, l2BlockNumber } of logs) {
427
+ for (const { txHash, proverId, checkpointNumber } of logs) {
472
428
  const proofData = await getProofFromSubmitProofTx(publicClient, txHash, proverId);
473
- retrievedData.push({ proof: proofData.proof, proverId: proofData.proverId, l2BlockNumber, txHash });
429
+ retrievedData.push({ proof: proofData.proof, proverId: proofData.proverId, checkpointNumber, txHash });
474
430
  }
475
431
  return {
476
432
  retrievedData,
@@ -478,26 +434,26 @@ export async function retrieveL2ProofsFromRollup(
478
434
  };
479
435
  }
480
436
 
481
- export type SubmitBlockProof = {
437
+ export type SubmitEpochProof = {
482
438
  archiveRoot: Fr;
483
439
  proverId: Fr;
484
440
  proof: Proof;
485
441
  };
486
442
 
487
443
  /**
488
- * Gets block metadata (header and archive snapshot) from the calldata of an L1 transaction.
444
+ * Gets epoch proof metadata (archive root and proof) from the calldata of an L1 transaction.
489
445
  * Assumes that the block was published from an EOA.
490
446
  * TODO: Add retries and error management.
491
447
  * @param publicClient - The viem public client to use for transaction retrieval.
492
448
  * @param txHash - Hash of the tx that published it.
493
- * @param l2BlockNum - L2 block number.
494
- * @returns L2 block metadata (header and archive) from the calldata, deserialized
449
+ * @param expectedProverId - Expected prover ID.
450
+ * @returns Epoch proof metadata from the calldata, deserialized.
495
451
  */
496
452
  export async function getProofFromSubmitProofTx(
497
453
  publicClient: ViemPublicClient,
498
454
  txHash: `0x${string}`,
499
455
  expectedProverId: Fr,
500
- ): Promise<SubmitBlockProof> {
456
+ ): Promise<SubmitEpochProof> {
501
457
  const { input: data } = await publicClient.getTransaction({ hash: txHash });
502
458
  const { functionName, args } = decodeFunctionData({ abi: RollupAbi, data });
503
459