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