@aztec/archiver 3.0.0-devnet.6 → 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.
- package/dest/archiver/archiver.d.ts +67 -60
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +391 -262
- package/dest/archiver/archiver_store.d.ts +21 -27
- 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 +125 -140
- package/dest/archiver/config.d.ts +3 -2
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +13 -1
- 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 +11 -0
- package/dest/archiver/kv_archiver_store/block_store.d.ts +10 -9
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +9 -8
- 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 +23 -29
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +7 -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/l1/data_retrieval.js +318 -0
- 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 +29 -21
- package/dest/factory.d.ts +3 -5
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +3 -2
- 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 +15 -10
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +22 -9
- 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 +531 -345
- package/src/archiver/archiver_store.ts +24 -27
- package/src/archiver/archiver_store_test_suite.ts +151 -128
- package/src/archiver/config.ts +13 -7
- package/src/archiver/instrumentation.ts +16 -2
- package/src/archiver/kv_archiver_store/block_store.ts +18 -17
- 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 +23 -32
- 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} +196 -250
- 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 +52 -27
- package/src/factory.ts +4 -5
- 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 +32 -15
- package/src/test/mock_structs.ts +10 -9
- package/dest/archiver/data_retrieval.d.ts +0 -79
- package/dest/archiver/data_retrieval.d.ts.map +0 -1
- package/dest/archiver/data_retrieval.js +0 -362
|
@@ -1,25 +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
|
|
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,
|
|
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 '
|
|
35
|
-
import type {
|
|
36
|
-
import type {
|
|
37
|
-
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';
|
|
38
40
|
|
|
39
|
-
export type
|
|
40
|
-
|
|
41
|
+
export type RetrievedCheckpoint = {
|
|
42
|
+
checkpointNumber: CheckpointNumber;
|
|
41
43
|
archiveRoot: Fr;
|
|
42
|
-
stateReference: StateReference;
|
|
43
44
|
header: CheckpointHeader;
|
|
44
|
-
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
return PublishedL2Block.fromFields({ block, l1, attestations });
|
|
136
|
+
return PublishedCheckpoint.from({ checkpoint, l1, attestations });
|
|
104
137
|
}
|
|
105
138
|
|
|
106
139
|
/**
|
|
107
|
-
* Fetches new
|
|
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
|
|
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<
|
|
123
|
-
const
|
|
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
|
|
132
|
-
await rollup.getEvents.
|
|
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 (
|
|
182
|
+
if (checkpointProposedLogs.length === 0) {
|
|
142
183
|
break;
|
|
143
184
|
}
|
|
144
185
|
|
|
145
|
-
const lastLog =
|
|
186
|
+
const lastLog = checkpointProposedLogs.at(-1)!;
|
|
146
187
|
logger.debug(
|
|
147
|
-
`Got ${
|
|
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
|
|
204
|
+
const newCheckpoints = await processCheckpointProposedLogs(
|
|
164
205
|
rollup,
|
|
165
206
|
publicClient,
|
|
207
|
+
debugClient,
|
|
166
208
|
blobSinkClient,
|
|
167
|
-
|
|
209
|
+
checkpointProposedLogs,
|
|
168
210
|
rollupConstants,
|
|
211
|
+
contractAddresses,
|
|
212
|
+
instrumentation,
|
|
169
213
|
logger,
|
|
170
214
|
);
|
|
171
|
-
|
|
215
|
+
retrievedCheckpoints.push(...newCheckpoints);
|
|
172
216
|
searchStartBlock = lastLog.blockNumber! + 1n;
|
|
173
217
|
} while (searchStartBlock <= searchEndBlock);
|
|
174
218
|
|
|
175
|
-
// The
|
|
176
|
-
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));
|
|
177
221
|
}
|
|
178
222
|
|
|
179
223
|
/**
|
|
180
|
-
* Processes newly received
|
|
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
|
|
184
|
-
* @
|
|
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
|
|
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, '
|
|
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<
|
|
194
|
-
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
|
+
|
|
195
256
|
await asyncPool(10, logs, async log => {
|
|
196
|
-
const
|
|
257
|
+
const checkpointNumber = CheckpointNumber.fromBigInt(log.args.checkpointNumber!);
|
|
197
258
|
const archive = log.args.archive!;
|
|
198
|
-
const archiveFromChain = await rollup.read.archiveAt([BigInt(
|
|
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
|
|
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
|
|
204
|
-
publicClient,
|
|
205
|
-
blobSinkClient,
|
|
264
|
+
const checkpoint = await calldataRetriever.getCheckpointFromRollupTx(
|
|
206
265
|
log.transactionHash!,
|
|
207
266
|
blobHashes,
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
221
|
-
logger.trace(`Retrieved
|
|
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
|
-
|
|
286
|
+
checkpointNumber,
|
|
224
287
|
archive: archive.toString(),
|
|
225
|
-
attestations:
|
|
288
|
+
attestations: checkpoint.attestations,
|
|
226
289
|
});
|
|
227
290
|
} else {
|
|
228
|
-
logger.warn(`Ignoring
|
|
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
|
|
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
|
-
|
|
306
|
-
blobHashes: Buffer[],
|
|
307
|
-
|
|
308
|
-
rollupAddress: Hex,
|
|
309
|
-
targetCommitteeSize: number,
|
|
308
|
+
blockHash: string,
|
|
309
|
+
blobHashes: Buffer<ArrayBufferLike>[],
|
|
310
|
+
checkpointNumber: CheckpointNumber,
|
|
310
311
|
logger: Logger,
|
|
311
|
-
): Promise<
|
|
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(
|
|
315
|
+
throw new NoBlobBodiesFoundError(checkpointNumber);
|
|
357
316
|
}
|
|
358
317
|
|
|
359
|
-
let
|
|
318
|
+
let checkpointBlobData: CheckpointBlobData;
|
|
360
319
|
try {
|
|
361
|
-
//
|
|
362
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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;
|
|
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;
|
|
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,
|
|
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,
|
|
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
|
|
437
|
+
export type SubmitEpochProof = {
|
|
492
438
|
archiveRoot: Fr;
|
|
493
439
|
proverId: Fr;
|
|
494
440
|
proof: Proof;
|
|
495
441
|
};
|
|
496
442
|
|
|
497
443
|
/**
|
|
498
|
-
* Gets
|
|
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
|
|
504
|
-
* @returns
|
|
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<
|
|
456
|
+
): Promise<SubmitEpochProof> {
|
|
511
457
|
const { input: data } = await publicClient.getTransaction({ hash: txHash });
|
|
512
458
|
const { functionName, args } = decodeFunctionData({ abi: RollupAbi, data });
|
|
513
459
|
|