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