@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.
- package/README.md +27 -6
- package/dest/archiver/archiver.d.ts +87 -64
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +463 -278
- package/dest/archiver/archiver_store.d.ts +46 -28
- 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 +316 -143
- package/dest/archiver/config.d.ts +6 -23
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +19 -12
- package/dest/archiver/errors.d.ts +1 -1
- package/dest/archiver/errors.d.ts.map +1 -1
- package/dest/archiver/index.d.ts +1 -1
- package/dest/archiver/instrumentation.d.ts +5 -3
- package/dest/archiver/instrumentation.d.ts.map +1 -1
- package/dest/archiver/instrumentation.js +14 -0
- package/dest/archiver/kv_archiver_store/block_store.d.ts +45 -9
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +99 -12
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +30 -30
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +26 -15
- package/dest/archiver/kv_archiver_store/log_store.d.ts +3 -10
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +4 -26
- package/dest/archiver/kv_archiver_store/message_store.d.ts +6 -5
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +15 -14
- package/dest/archiver/l1/bin/retrieve-calldata.d.ts +3 -0
- package/dest/archiver/l1/bin/retrieve-calldata.d.ts.map +1 -0
- package/dest/archiver/l1/bin/retrieve-calldata.js +147 -0
- package/dest/archiver/l1/calldata_retriever.d.ts +98 -0
- package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -0
- package/dest/archiver/l1/calldata_retriever.js +403 -0
- package/dest/archiver/l1/data_retrieval.d.ts +87 -0
- package/dest/archiver/l1/data_retrieval.d.ts.map +1 -0
- package/dest/archiver/{data_retrieval.js → l1/data_retrieval.js} +118 -154
- package/dest/archiver/l1/debug_tx.d.ts +19 -0
- package/dest/archiver/l1/debug_tx.d.ts.map +1 -0
- package/dest/archiver/l1/debug_tx.js +73 -0
- package/dest/archiver/l1/spire_proposer.d.ts +70 -0
- package/dest/archiver/l1/spire_proposer.d.ts.map +1 -0
- package/dest/archiver/l1/spire_proposer.js +157 -0
- package/dest/archiver/l1/trace_tx.d.ts +97 -0
- package/dest/archiver/l1/trace_tx.d.ts.map +1 -0
- package/dest/archiver/l1/trace_tx.js +91 -0
- package/dest/archiver/l1/types.d.ts +12 -0
- package/dest/archiver/l1/types.d.ts.map +1 -0
- package/dest/archiver/l1/types.js +3 -0
- package/dest/archiver/l1/validate_trace.d.ts +29 -0
- package/dest/archiver/l1/validate_trace.d.ts.map +1 -0
- package/dest/archiver/l1/validate_trace.js +150 -0
- package/dest/archiver/structs/data_retrieval.d.ts +1 -1
- package/dest/archiver/structs/inbox_message.d.ts +4 -4
- package/dest/archiver/structs/inbox_message.d.ts.map +1 -1
- package/dest/archiver/structs/inbox_message.js +6 -5
- package/dest/archiver/structs/published.d.ts +3 -2
- package/dest/archiver/structs/published.d.ts.map +1 -1
- package/dest/archiver/validation.d.ts +10 -4
- package/dest/archiver/validation.d.ts.map +1 -1
- package/dest/archiver/validation.js +66 -44
- package/dest/factory.d.ts +3 -11
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +5 -17
- 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 -2
- package/dest/test/index.d.ts +1 -1
- package/dest/test/mock_archiver.d.ts +16 -8
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +19 -14
- package/dest/test/mock_l1_to_l2_message_source.d.ts +7 -6
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +10 -9
- package/dest/test/mock_l2_block_source.d.ts +24 -20
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +79 -13
- package/dest/test/mock_structs.d.ts +3 -2
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +9 -8
- package/package.json +18 -17
- package/src/archiver/archiver.ts +610 -363
- package/src/archiver/archiver_store.ts +55 -28
- package/src/archiver/archiver_store_test_suite.ts +369 -143
- package/src/archiver/config.ts +26 -51
- package/src/archiver/instrumentation.ts +19 -2
- package/src/archiver/kv_archiver_store/block_store.ts +139 -21
- package/src/archiver/kv_archiver_store/contract_class_store.ts +1 -1
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +1 -1
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +48 -33
- package/src/archiver/kv_archiver_store/log_store.ts +4 -30
- package/src/archiver/kv_archiver_store/message_store.ts +21 -18
- package/src/archiver/l1/README.md +98 -0
- package/src/archiver/l1/bin/retrieve-calldata.ts +182 -0
- package/src/archiver/l1/calldata_retriever.ts +531 -0
- package/src/archiver/{data_retrieval.ts → l1/data_retrieval.ts} +198 -242
- package/src/archiver/l1/debug_tx.ts +99 -0
- package/src/archiver/l1/spire_proposer.ts +160 -0
- package/src/archiver/l1/trace_tx.ts +128 -0
- package/src/archiver/l1/types.ts +13 -0
- package/src/archiver/l1/validate_trace.ts +211 -0
- package/src/archiver/structs/inbox_message.ts +8 -8
- package/src/archiver/structs/published.ts +2 -1
- package/src/archiver/validation.ts +86 -32
- package/src/factory.ts +6 -26
- package/src/index.ts +1 -1
- package/src/test/fixtures/debug_traceTransaction-multicall3.json +88 -0
- package/src/test/fixtures/debug_traceTransaction-multiplePropose.json +153 -0
- package/src/test/fixtures/debug_traceTransaction-proxied.json +122 -0
- package/src/test/fixtures/trace_transaction-multicall3.json +65 -0
- package/src/test/fixtures/trace_transaction-multiplePropose.json +319 -0
- package/src/test/fixtures/trace_transaction-proxied.json +128 -0
- package/src/test/fixtures/trace_transaction-randomRevert.json +216 -0
- package/src/test/mock_archiver.ts +22 -16
- package/src/test/mock_l1_to_l2_message_source.ts +10 -9
- package/src/test/mock_l2_block_source.ts +110 -27
- package/src/test/mock_structs.ts +10 -9
- package/dest/archiver/data_retrieval.d.ts +0 -78
- package/dest/archiver/data_retrieval.d.ts.map +0 -1
|
@@ -1,23 +1,26 @@
|
|
|
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 {
|
|
4
|
-
|
|
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
|
|
14
|
-
import {
|
|
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,
|
|
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,
|
|
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 '
|
|
33
|
-
import type {
|
|
34
|
-
import type {
|
|
35
|
-
import type {
|
|
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
|
|
38
|
-
|
|
41
|
+
export type RetrievedCheckpoint = {
|
|
42
|
+
checkpointNumber: CheckpointNumber;
|
|
39
43
|
archiveRoot: Fr;
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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<
|
|
113
|
-
const
|
|
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
|
|
122
|
-
await rollup.getEvents.
|
|
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 (
|
|
182
|
+
if (checkpointProposedLogs.length === 0) {
|
|
132
183
|
break;
|
|
133
184
|
}
|
|
134
185
|
|
|
135
|
-
const lastLog =
|
|
186
|
+
const lastLog = checkpointProposedLogs.at(-1)!;
|
|
136
187
|
logger.debug(
|
|
137
|
-
`Got ${
|
|
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
|
|
204
|
+
const newCheckpoints = await processCheckpointProposedLogs(
|
|
154
205
|
rollup,
|
|
155
206
|
publicClient,
|
|
207
|
+
debugClient,
|
|
156
208
|
blobSinkClient,
|
|
157
|
-
|
|
209
|
+
checkpointProposedLogs,
|
|
158
210
|
rollupConstants,
|
|
211
|
+
contractAddresses,
|
|
212
|
+
instrumentation,
|
|
159
213
|
logger,
|
|
160
214
|
);
|
|
161
|
-
|
|
215
|
+
retrievedCheckpoints.push(...newCheckpoints);
|
|
162
216
|
searchStartBlock = lastLog.blockNumber! + 1n;
|
|
163
217
|
} while (searchStartBlock <= searchEndBlock);
|
|
164
218
|
|
|
165
|
-
// The
|
|
166
|
-
return
|
|
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
|
|
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
|
|
174
|
-
* @
|
|
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
|
|
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, '
|
|
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<
|
|
184
|
-
const
|
|
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
|
|
257
|
+
const checkpointNumber = CheckpointNumber.fromBigInt(log.args.checkpointNumber!);
|
|
187
258
|
const archive = log.args.archive!;
|
|
188
|
-
const archiveFromChain = await rollup.read.archiveAt([BigInt(
|
|
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
|
|
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
|
|
194
|
-
publicClient,
|
|
195
|
-
blobSinkClient,
|
|
264
|
+
const checkpoint = await calldataRetriever.getCheckpointFromRollupTx(
|
|
196
265
|
log.transactionHash!,
|
|
197
266
|
blobHashes,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
211
|
-
logger.trace(`Retrieved
|
|
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
|
-
|
|
286
|
+
checkpointNumber,
|
|
214
287
|
archive: archive.toString(),
|
|
215
|
-
attestations:
|
|
288
|
+
attestations: checkpoint.attestations,
|
|
216
289
|
});
|
|
217
290
|
} else {
|
|
218
|
-
logger.warn(`Ignoring
|
|
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
|
|
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
|
-
|
|
296
|
-
blobHashes: Buffer[],
|
|
297
|
-
|
|
298
|
-
rollupAddress: Hex,
|
|
299
|
-
targetCommitteeSize: number,
|
|
308
|
+
blockHash: string,
|
|
309
|
+
blobHashes: Buffer<ArrayBufferLike>[],
|
|
310
|
+
checkpointNumber: CheckpointNumber,
|
|
300
311
|
logger: Logger,
|
|
301
|
-
): Promise<
|
|
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(
|
|
315
|
+
throw new NoBlobBodiesFoundError(checkpointNumber);
|
|
348
316
|
}
|
|
349
317
|
|
|
350
|
-
let
|
|
318
|
+
let checkpointBlobData: CheckpointBlobData;
|
|
351
319
|
try {
|
|
352
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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;
|
|
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;
|
|
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,
|
|
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,
|
|
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
|
|
437
|
+
export type SubmitEpochProof = {
|
|
482
438
|
archiveRoot: Fr;
|
|
483
439
|
proverId: Fr;
|
|
484
440
|
proof: Proof;
|
|
485
441
|
};
|
|
486
442
|
|
|
487
443
|
/**
|
|
488
|
-
* Gets
|
|
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
|
|
494
|
-
* @returns
|
|
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<
|
|
456
|
+
): Promise<SubmitEpochProof> {
|
|
501
457
|
const { input: data } = await publicClient.getTransaction({ hash: txHash });
|
|
502
458
|
const { functionName, args } = decodeFunctionData({ abi: RollupAbi, data });
|
|
503
459
|
|