@aztec/archiver 0.0.0-test.0 → 0.0.1-commit.24de95ac
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 +126 -46
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +683 -261
- package/dest/archiver/archiver_store.d.ts +84 -49
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +707 -213
- package/dest/archiver/config.d.ts +4 -20
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +16 -12
- package/dest/archiver/data_retrieval.d.ts +25 -20
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +147 -68
- package/dest/archiver/errors.d.ts +8 -0
- package/dest/archiver/errors.d.ts.map +1 -1
- package/dest/archiver/errors.js +12 -0
- package/dest/archiver/index.d.ts +2 -3
- package/dest/archiver/index.d.ts.map +1 -1
- package/dest/archiver/index.js +1 -2
- package/dest/archiver/instrumentation.d.ts +9 -3
- 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 +47 -10
- 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 +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 +12 -18
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +10 -7
- 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 +49 -34
- 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 +1 -1
- 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 +22 -16
- 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/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 +1 -10
- package/dest/archiver/structs/published.d.ts.map +1 -1
- package/dest/archiver/structs/published.js +1 -1
- package/dest/archiver/validation.d.ts +11 -0
- package/dest/archiver/validation.d.ts.map +1 -0
- package/dest/archiver/validation.js +90 -0
- package/dest/factory.d.ts +7 -12
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +18 -49
- package/dest/rpc/index.d.ts +1 -2
- package/dest/rpc/index.d.ts.map +1 -1
- package/dest/rpc/index.js +1 -4
- package/dest/test/mock_archiver.d.ts +1 -1
- package/dest/test/mock_l1_to_l2_message_source.d.ts +4 -2
- 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 +32 -5
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +118 -7
- 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 +25 -27
- package/src/archiver/archiver.ts +858 -317
- package/src/archiver/archiver_store.ts +97 -55
- package/src/archiver/archiver_store_test_suite.ts +663 -210
- package/src/archiver/config.ts +23 -41
- package/src/archiver/data_retrieval.ts +215 -92
- package/src/archiver/errors.ts +21 -0
- package/src/archiver/index.ts +2 -3
- package/src/archiver/instrumentation.ts +75 -20
- 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 +1 -11
- package/src/archiver/validation.ts +99 -0
- package/src/factory.ts +24 -66
- 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 +152 -8
- 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
package/src/archiver/config.ts
CHANGED
|
@@ -1,62 +1,31 @@
|
|
|
1
1
|
import { type BlobSinkConfig, blobSinkConfigMapping } from '@aztec/blob-sink/client';
|
|
2
2
|
import {
|
|
3
|
-
type L1ContractAddresses,
|
|
4
3
|
type L1ContractsConfig,
|
|
5
4
|
type L1ReaderConfig,
|
|
5
|
+
l1ContractAddressesMapping,
|
|
6
6
|
l1ContractsConfigMappings,
|
|
7
7
|
l1ReaderConfigMappings,
|
|
8
8
|
} from '@aztec/ethereum';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
type ConfigMappingsType,
|
|
11
|
+
booleanConfigHelper,
|
|
12
|
+
getConfigFromMappings,
|
|
13
|
+
numberConfigHelper,
|
|
14
|
+
} from '@aztec/foundation/config';
|
|
10
15
|
import { type ChainConfig, chainConfigMappings } from '@aztec/stdlib/config';
|
|
16
|
+
import type { ArchiverSpecificConfig } from '@aztec/stdlib/interfaces/server';
|
|
11
17
|
|
|
12
18
|
/**
|
|
19
|
+
* The archiver configuration.
|
|
13
20
|
* There are 2 polling intervals used in this configuration. The first is the archiver polling interval, archiverPollingIntervalMS.
|
|
14
21
|
* This is the interval between successive calls to eth_blockNumber via viem.
|
|
15
22
|
* Results of calls to eth_blockNumber are cached by viem with this cache being updated periodically at the interval specified by viemPollingIntervalMS.
|
|
16
23
|
* As a result the maximum observed polling time for new blocks will be viemPollingIntervalMS + archiverPollingIntervalMS.
|
|
17
24
|
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The archiver configuration.
|
|
21
|
-
*/
|
|
22
|
-
export type ArchiverConfig = {
|
|
23
|
-
/** URL for an archiver service. If set, will return an archiver client as opposed to starting a new one. */
|
|
24
|
-
archiverUrl?: string;
|
|
25
|
-
|
|
26
|
-
/** URL for an L1 consensus client */
|
|
27
|
-
l1ConsensusHostUrl?: string;
|
|
28
|
-
|
|
29
|
-
/** The polling interval in ms for retrieving new L2 blocks and encrypted logs. */
|
|
30
|
-
archiverPollingIntervalMS?: number;
|
|
31
|
-
|
|
32
|
-
/** The number of L2 blocks the archiver will attempt to download at a time. */
|
|
33
|
-
archiverBatchSize?: number;
|
|
34
|
-
|
|
35
|
-
/** The polling interval viem uses in ms */
|
|
36
|
-
viemPollingIntervalMS?: number;
|
|
37
|
-
|
|
38
|
-
/** The deployed L1 contract addresses */
|
|
39
|
-
l1Contracts: L1ContractAddresses;
|
|
40
|
-
|
|
41
|
-
/** The max number of logs that can be obtained in 1 "getPublicLogs" call. */
|
|
42
|
-
maxLogs?: number;
|
|
43
|
-
} & L1ReaderConfig &
|
|
44
|
-
L1ContractsConfig &
|
|
45
|
-
BlobSinkConfig &
|
|
46
|
-
ChainConfig;
|
|
25
|
+
export type ArchiverConfig = ArchiverSpecificConfig & L1ReaderConfig & L1ContractsConfig & BlobSinkConfig & ChainConfig;
|
|
47
26
|
|
|
48
27
|
export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
49
28
|
...blobSinkConfigMapping,
|
|
50
|
-
archiverUrl: {
|
|
51
|
-
env: 'ARCHIVER_URL',
|
|
52
|
-
description:
|
|
53
|
-
'URL for an archiver service. If set, will return an archiver client as opposed to starting a new one.',
|
|
54
|
-
},
|
|
55
|
-
l1ConsensusHostUrl: {
|
|
56
|
-
env: 'L1_CONSENSUS_HOST_URL',
|
|
57
|
-
description: 'URL for an L1 consensus client.',
|
|
58
|
-
parseEnv: (val: string) => val,
|
|
59
|
-
},
|
|
60
29
|
archiverPollingIntervalMS: {
|
|
61
30
|
env: 'ARCHIVER_POLLING_INTERVAL_MS',
|
|
62
31
|
description: 'The polling interval in ms for retrieving new L2 blocks and encrypted logs.',
|
|
@@ -72,6 +41,15 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
72
41
|
description: 'The max number of logs that can be obtained in 1 "getPublicLogs" call.',
|
|
73
42
|
...numberConfigHelper(1_000),
|
|
74
43
|
},
|
|
44
|
+
archiverStoreMapSizeKb: {
|
|
45
|
+
env: 'ARCHIVER_STORE_MAP_SIZE_KB',
|
|
46
|
+
parseEnv: (val: string | undefined) => (val ? +val : undefined),
|
|
47
|
+
description: 'The maximum possible size of the archiver DB in KB. Overwrites the general dataStoreMapSizeKb.',
|
|
48
|
+
},
|
|
49
|
+
skipValidateBlockAttestations: {
|
|
50
|
+
description: 'Whether to skip validating block attestations (use only for testing).',
|
|
51
|
+
...booleanConfigHelper(false),
|
|
52
|
+
},
|
|
75
53
|
...chainConfigMappings,
|
|
76
54
|
...l1ReaderConfigMappings,
|
|
77
55
|
viemPollingIntervalMS: {
|
|
@@ -80,6 +58,10 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
80
58
|
...numberConfigHelper(1000),
|
|
81
59
|
},
|
|
82
60
|
...l1ContractsConfigMappings,
|
|
61
|
+
l1Contracts: {
|
|
62
|
+
description: 'The deployed L1 contract addresses',
|
|
63
|
+
nested: l1ContractAddressesMapping,
|
|
64
|
+
},
|
|
83
65
|
};
|
|
84
66
|
|
|
85
67
|
/**
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BlobDeserializationError, SpongeBlob, getBlobFieldsInCheckpoint } from '@aztec/blob-lib';
|
|
2
2
|
import type { BlobSinkClientInterface } from '@aztec/blob-sink/client';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
EpochProofPublicInputArgs,
|
|
5
|
+
ViemClient,
|
|
6
|
+
ViemCommitteeAttestations,
|
|
7
|
+
ViemHeader,
|
|
8
|
+
ViemPublicClient,
|
|
9
|
+
ViemStateReference,
|
|
10
|
+
} from '@aztec/ethereum';
|
|
4
11
|
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
12
|
+
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
5
13
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
14
|
import type { ViemSignature } from '@aztec/foundation/eth-signature';
|
|
7
15
|
import { Fr } from '@aztec/foundation/fields';
|
|
8
16
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { Body, L2Block } from '@aztec/stdlib/block';
|
|
12
|
-
import { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
17
|
+
import { type InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
18
|
+
import { Body, CommitteeAttestation, L2Block, L2BlockHeader, PublishedL2Block } from '@aztec/stdlib/block';
|
|
13
19
|
import { Proof } from '@aztec/stdlib/proofs';
|
|
20
|
+
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
14
21
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
15
|
-
import {
|
|
22
|
+
import { GlobalVariables, StateReference } from '@aztec/stdlib/tx';
|
|
16
23
|
|
|
17
24
|
import {
|
|
18
25
|
type GetContractEventsReturnType,
|
|
@@ -21,11 +28,80 @@ import {
|
|
|
21
28
|
decodeFunctionData,
|
|
22
29
|
getAbiItem,
|
|
23
30
|
hexToBytes,
|
|
31
|
+
multicall3Abi,
|
|
24
32
|
} from 'viem';
|
|
25
33
|
|
|
26
34
|
import { NoBlobBodiesFoundError } from './errors.js';
|
|
27
35
|
import type { DataRetrieval } from './structs/data_retrieval.js';
|
|
28
|
-
import type {
|
|
36
|
+
import type { InboxMessage } from './structs/inbox_message.js';
|
|
37
|
+
import type { L1PublishedData } from './structs/published.js';
|
|
38
|
+
|
|
39
|
+
export type RetrievedL2Block = {
|
|
40
|
+
l2BlockNumber: number;
|
|
41
|
+
archiveRoot: Fr;
|
|
42
|
+
stateReference: StateReference;
|
|
43
|
+
header: CheckpointHeader;
|
|
44
|
+
blobFields: Fr[];
|
|
45
|
+
l1: L1PublishedData;
|
|
46
|
+
chainId: Fr;
|
|
47
|
+
version: Fr;
|
|
48
|
+
attestations: CommitteeAttestation[];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export async function retrievedBlockToPublishedL2Block(retrievedBlock: RetrievedL2Block): Promise<PublishedL2Block> {
|
|
52
|
+
const {
|
|
53
|
+
l2BlockNumber,
|
|
54
|
+
archiveRoot,
|
|
55
|
+
stateReference,
|
|
56
|
+
header: checkpointHeader,
|
|
57
|
+
blobFields,
|
|
58
|
+
l1,
|
|
59
|
+
chainId,
|
|
60
|
+
version,
|
|
61
|
+
attestations,
|
|
62
|
+
} = retrievedBlock;
|
|
63
|
+
|
|
64
|
+
const archive = new AppendOnlyTreeSnapshot(
|
|
65
|
+
archiveRoot,
|
|
66
|
+
l2BlockNumber + 1, // nextAvailableLeafIndex
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const globalVariables = GlobalVariables.from({
|
|
70
|
+
chainId,
|
|
71
|
+
version,
|
|
72
|
+
blockNumber: l2BlockNumber,
|
|
73
|
+
slotNumber: checkpointHeader.slotNumber,
|
|
74
|
+
timestamp: checkpointHeader.timestamp,
|
|
75
|
+
coinbase: checkpointHeader.coinbase,
|
|
76
|
+
feeRecipient: checkpointHeader.feeRecipient,
|
|
77
|
+
gasFees: checkpointHeader.gasFees,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// TODO(#17027)
|
|
81
|
+
// This works when there's only one block in the checkpoint.
|
|
82
|
+
// If there's more than one block, we need to build the spongeBlob from the endSpongeBlob of the previous block.
|
|
83
|
+
const spongeBlob = await SpongeBlob.init(blobFields.length);
|
|
84
|
+
// Skip the first field which is the checkpoint prefix indicating the number of total blob fields in a checkpoint.
|
|
85
|
+
const blockBlobFields = blobFields.slice(1);
|
|
86
|
+
await spongeBlob.absorb(blockBlobFields);
|
|
87
|
+
const spongeBlobHash = await spongeBlob.squeeze();
|
|
88
|
+
|
|
89
|
+
const body = Body.fromBlobFields(blockBlobFields);
|
|
90
|
+
|
|
91
|
+
const header = L2BlockHeader.from({
|
|
92
|
+
lastArchive: new AppendOnlyTreeSnapshot(checkpointHeader.lastArchiveRoot, l2BlockNumber),
|
|
93
|
+
contentCommitment: checkpointHeader.contentCommitment,
|
|
94
|
+
state: stateReference,
|
|
95
|
+
globalVariables,
|
|
96
|
+
totalFees: body.txEffects.reduce((accum, txEffect) => accum.add(txEffect.transactionFee), Fr.ZERO),
|
|
97
|
+
totalManaUsed: checkpointHeader.totalManaUsed,
|
|
98
|
+
spongeBlobHash,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const block = new L2Block(archive, header, body);
|
|
102
|
+
|
|
103
|
+
return PublishedL2Block.fromFields({ block, l1, attestations });
|
|
104
|
+
}
|
|
29
105
|
|
|
30
106
|
/**
|
|
31
107
|
* Fetches new L2 blocks.
|
|
@@ -43,8 +119,11 @@ export async function retrieveBlocksFromRollup(
|
|
|
43
119
|
searchStartBlock: bigint,
|
|
44
120
|
searchEndBlock: bigint,
|
|
45
121
|
logger: Logger = createLogger('archiver'),
|
|
46
|
-
): Promise<
|
|
47
|
-
const retrievedBlocks:
|
|
122
|
+
): Promise<RetrievedL2Block[]> {
|
|
123
|
+
const retrievedBlocks: RetrievedL2Block[] = [];
|
|
124
|
+
|
|
125
|
+
let rollupConstants: { chainId: Fr; version: Fr; targetCommitteeSize: number } | undefined;
|
|
126
|
+
|
|
48
127
|
do {
|
|
49
128
|
if (searchStartBlock > searchEndBlock) {
|
|
50
129
|
break;
|
|
@@ -68,11 +147,25 @@ export async function retrieveBlocksFromRollup(
|
|
|
68
147
|
`Got ${l2BlockProposedLogs.length} L2 block processed logs for L2 blocks ${l2BlockProposedLogs[0].args.blockNumber}-${lastLog.args.blockNumber} between L1 blocks ${searchStartBlock}-${searchEndBlock}`,
|
|
69
148
|
);
|
|
70
149
|
|
|
150
|
+
if (rollupConstants === undefined) {
|
|
151
|
+
const [chainId, version, targetCommitteeSize] = await Promise.all([
|
|
152
|
+
publicClient.getChainId(),
|
|
153
|
+
rollup.read.getVersion(),
|
|
154
|
+
rollup.read.getTargetCommitteeSize(),
|
|
155
|
+
]);
|
|
156
|
+
rollupConstants = {
|
|
157
|
+
chainId: new Fr(chainId),
|
|
158
|
+
version: new Fr(version),
|
|
159
|
+
targetCommitteeSize: Number(targetCommitteeSize),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
71
163
|
const newBlocks = await processL2BlockProposedLogs(
|
|
72
164
|
rollup,
|
|
73
165
|
publicClient,
|
|
74
166
|
blobSinkClient,
|
|
75
167
|
l2BlockProposedLogs,
|
|
168
|
+
rollupConstants,
|
|
76
169
|
logger,
|
|
77
170
|
);
|
|
78
171
|
retrievedBlocks.push(...newBlocks);
|
|
@@ -90,18 +183,19 @@ export async function retrieveBlocksFromRollup(
|
|
|
90
183
|
* @param logs - L2BlockProposed logs.
|
|
91
184
|
* @returns - An array blocks.
|
|
92
185
|
*/
|
|
93
|
-
|
|
186
|
+
async function processL2BlockProposedLogs(
|
|
94
187
|
rollup: GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
|
|
95
188
|
publicClient: ViemPublicClient,
|
|
96
189
|
blobSinkClient: BlobSinkClientInterface,
|
|
97
190
|
logs: GetContractEventsReturnType<typeof RollupAbi, 'L2BlockProposed'>,
|
|
191
|
+
{ chainId, version, targetCommitteeSize }: { chainId: Fr; version: Fr; targetCommitteeSize: number },
|
|
98
192
|
logger: Logger,
|
|
99
|
-
): Promise<
|
|
100
|
-
const retrievedBlocks:
|
|
193
|
+
): Promise<RetrievedL2Block[]> {
|
|
194
|
+
const retrievedBlocks: RetrievedL2Block[] = [];
|
|
101
195
|
await asyncPool(10, logs, async log => {
|
|
102
|
-
const l2BlockNumber = log.args.blockNumber
|
|
196
|
+
const l2BlockNumber = Number(log.args.blockNumber!);
|
|
103
197
|
const archive = log.args.archive!;
|
|
104
|
-
const archiveFromChain = await rollup.read.archiveAt([l2BlockNumber]);
|
|
198
|
+
const archiveFromChain = await rollup.read.archiveAt([BigInt(l2BlockNumber)]);
|
|
105
199
|
const blobHashes = log.args.versionedBlobHashes!.map(blobHash => Buffer.from(blobHash.slice(2), 'hex'));
|
|
106
200
|
|
|
107
201
|
// The value from the event and contract will match only if the block is in the chain.
|
|
@@ -113,6 +207,7 @@ export async function processL2BlockProposedLogs(
|
|
|
113
207
|
blobHashes,
|
|
114
208
|
l2BlockNumber,
|
|
115
209
|
rollup.address,
|
|
210
|
+
targetCommitteeSize,
|
|
116
211
|
logger,
|
|
117
212
|
);
|
|
118
213
|
|
|
@@ -122,7 +217,13 @@ export async function processL2BlockProposedLogs(
|
|
|
122
217
|
timestamp: await getL1BlockTime(publicClient, log.blockNumber),
|
|
123
218
|
};
|
|
124
219
|
|
|
125
|
-
retrievedBlocks.push({
|
|
220
|
+
retrievedBlocks.push({ ...block, l1, chainId, version });
|
|
221
|
+
logger.trace(`Retrieved L2 block ${l2BlockNumber} from L1 tx ${log.transactionHash}`, {
|
|
222
|
+
l1BlockNumber: log.blockNumber,
|
|
223
|
+
l2BlockNumber,
|
|
224
|
+
archive: archive.toString(),
|
|
225
|
+
attestations: block.attestations,
|
|
226
|
+
});
|
|
126
227
|
} else {
|
|
127
228
|
logger.warn(`Ignoring L2 block ${l2BlockNumber} due to archive root mismatch`, {
|
|
128
229
|
actual: archive,
|
|
@@ -140,37 +241,36 @@ export async function getL1BlockTime(publicClient: ViemPublicClient, blockNumber
|
|
|
140
241
|
}
|
|
141
242
|
|
|
142
243
|
/**
|
|
143
|
-
* Extracts the first 'propose' method calldata from a
|
|
144
|
-
* @param
|
|
244
|
+
* Extracts the first 'propose' method calldata from a multicall3 transaction's data.
|
|
245
|
+
* @param multicall3Data - The multicall3 transaction input data
|
|
145
246
|
* @param rollupAddress - The address of the rollup contract
|
|
146
247
|
* @returns The calldata for the first 'propose' method call to the rollup contract
|
|
147
248
|
*/
|
|
148
|
-
function extractRollupProposeCalldata(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
data: forwarderData,
|
|
249
|
+
function extractRollupProposeCalldata(multicall3Data: Hex, rollupAddress: Hex): Hex {
|
|
250
|
+
const { functionName: multicall3FunctionName, args: multicall3Args } = decodeFunctionData({
|
|
251
|
+
abi: multicall3Abi,
|
|
252
|
+
data: multicall3Data,
|
|
153
253
|
});
|
|
154
254
|
|
|
155
|
-
if (
|
|
156
|
-
throw new Error(`Unexpected
|
|
255
|
+
if (multicall3FunctionName !== 'aggregate3') {
|
|
256
|
+
throw new Error(`Unexpected multicall3 method called ${multicall3FunctionName}`);
|
|
157
257
|
}
|
|
158
258
|
|
|
159
|
-
if (
|
|
160
|
-
throw new Error(`Unexpected number of arguments for
|
|
259
|
+
if (multicall3Args.length !== 1) {
|
|
260
|
+
throw new Error(`Unexpected number of arguments for multicall3`);
|
|
161
261
|
}
|
|
162
262
|
|
|
163
|
-
const [
|
|
263
|
+
const [calls] = multicall3Args;
|
|
164
264
|
|
|
165
265
|
// Find all rollup calls
|
|
166
266
|
const rollupAddressLower = rollupAddress.toLowerCase();
|
|
167
267
|
|
|
168
|
-
for (let i = 0; i <
|
|
169
|
-
const addr =
|
|
268
|
+
for (let i = 0; i < calls.length; i++) {
|
|
269
|
+
const addr = calls[i].target;
|
|
170
270
|
if (addr.toLowerCase() !== rollupAddressLower) {
|
|
171
271
|
continue;
|
|
172
272
|
}
|
|
173
|
-
const callData =
|
|
273
|
+
const callData = calls[i].callData;
|
|
174
274
|
|
|
175
275
|
try {
|
|
176
276
|
const { functionName: rollupFunctionName } = decodeFunctionData({
|
|
@@ -181,13 +281,13 @@ function extractRollupProposeCalldata(forwarderData: Hex, rollupAddress: Hex): H
|
|
|
181
281
|
if (rollupFunctionName === 'propose') {
|
|
182
282
|
return callData;
|
|
183
283
|
}
|
|
184
|
-
} catch
|
|
284
|
+
} catch {
|
|
185
285
|
// Skip invalid function data
|
|
186
286
|
continue;
|
|
187
287
|
}
|
|
188
288
|
}
|
|
189
289
|
|
|
190
|
-
throw new Error(`Rollup address not found in
|
|
290
|
+
throw new Error(`Rollup address not found in multicall3 args`);
|
|
191
291
|
}
|
|
192
292
|
|
|
193
293
|
/**
|
|
@@ -196,18 +296,19 @@ function extractRollupProposeCalldata(forwarderData: Hex, rollupAddress: Hex): H
|
|
|
196
296
|
* TODO: Add retries and error management.
|
|
197
297
|
* @param publicClient - The viem public client to use for transaction retrieval.
|
|
198
298
|
* @param txHash - Hash of the tx that published it.
|
|
199
|
-
* @param
|
|
299
|
+
* @param l2BlockNumber - L2 block number.
|
|
200
300
|
* @returns L2 block from the calldata, deserialized
|
|
201
301
|
*/
|
|
202
302
|
async function getBlockFromRollupTx(
|
|
203
303
|
publicClient: ViemPublicClient,
|
|
204
304
|
blobSinkClient: BlobSinkClientInterface,
|
|
205
305
|
txHash: `0x${string}`,
|
|
206
|
-
blobHashes: Buffer[], //
|
|
207
|
-
|
|
306
|
+
blobHashes: Buffer[], // TODO(md): buffer32?
|
|
307
|
+
l2BlockNumber: number,
|
|
208
308
|
rollupAddress: Hex,
|
|
309
|
+
targetCommitteeSize: number,
|
|
209
310
|
logger: Logger,
|
|
210
|
-
): Promise<
|
|
311
|
+
): Promise<Omit<RetrievedL2Block, 'l1' | 'chainId' | 'version'>> {
|
|
211
312
|
const { input: forwarderData, blockHash } = await publicClient.getTransaction({ hash: txHash });
|
|
212
313
|
|
|
213
314
|
const rollupData = extractRollupProposeCalldata(forwarderData, rollupAddress);
|
|
@@ -220,29 +321,48 @@ async function getBlockFromRollupTx(
|
|
|
220
321
|
throw new Error(`Unexpected rollup method called ${rollupFunctionName}`);
|
|
221
322
|
}
|
|
222
323
|
|
|
223
|
-
const [decodedArgs, ,] = rollupArgs! as readonly [
|
|
324
|
+
const [decodedArgs, packedAttestations, _signers, _blobInput] = rollupArgs! as readonly [
|
|
224
325
|
{
|
|
225
|
-
header: Hex;
|
|
226
326
|
archive: Hex;
|
|
227
|
-
|
|
327
|
+
stateReference: ViemStateReference;
|
|
228
328
|
oracleInput: {
|
|
229
329
|
feeAssetPriceModifier: bigint;
|
|
230
330
|
};
|
|
231
|
-
|
|
331
|
+
header: ViemHeader;
|
|
332
|
+
txHashes: readonly Hex[];
|
|
232
333
|
},
|
|
233
|
-
|
|
334
|
+
ViemCommitteeAttestations,
|
|
335
|
+
Hex[],
|
|
336
|
+
ViemSignature,
|
|
234
337
|
Hex,
|
|
235
338
|
];
|
|
236
339
|
|
|
237
|
-
const
|
|
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);
|
|
238
354
|
const blobBodies = await blobSinkClient.getBlobSidecar(blockHash, blobHashes);
|
|
239
355
|
if (blobBodies.length === 0) {
|
|
240
|
-
throw new NoBlobBodiesFoundError(
|
|
356
|
+
throw new NoBlobBodiesFoundError(l2BlockNumber);
|
|
241
357
|
}
|
|
242
358
|
|
|
243
|
-
let
|
|
359
|
+
let blobFields: Fr[];
|
|
244
360
|
try {
|
|
245
|
-
|
|
361
|
+
// Get the fields that were actually added in the checkpoint. And check the encoding of the fields.
|
|
362
|
+
blobFields = getBlobFieldsInCheckpoint(
|
|
363
|
+
blobBodies.map(b => b.blob),
|
|
364
|
+
true /* checkEncoding */,
|
|
365
|
+
);
|
|
246
366
|
} catch (err: any) {
|
|
247
367
|
if (err instanceof BlobDeserializationError) {
|
|
248
368
|
logger.fatal(err.message);
|
|
@@ -252,23 +372,31 @@ async function getBlockFromRollupTx(
|
|
|
252
372
|
throw err;
|
|
253
373
|
}
|
|
254
374
|
|
|
255
|
-
|
|
256
|
-
const blockBody = Body.fromBlobFields(blockFields);
|
|
375
|
+
const archiveRoot = new Fr(Buffer.from(hexToBytes(decodedArgs.archive)));
|
|
257
376
|
|
|
258
|
-
const
|
|
377
|
+
const stateReference = StateReference.fromViem(decodedArgs.stateReference);
|
|
259
378
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
);
|
|
379
|
+
return {
|
|
380
|
+
l2BlockNumber,
|
|
381
|
+
archiveRoot,
|
|
382
|
+
stateReference,
|
|
383
|
+
header,
|
|
384
|
+
blobFields,
|
|
385
|
+
attestations,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
270
388
|
|
|
271
|
-
|
|
389
|
+
/** Given an L1 to L2 message, retrieves its corresponding event from the Inbox within a specific block range. */
|
|
390
|
+
export async function retrieveL1ToL2Message(
|
|
391
|
+
inbox: GetContractReturnType<typeof InboxAbi, ViemClient>,
|
|
392
|
+
leaf: Fr,
|
|
393
|
+
fromBlock: bigint,
|
|
394
|
+
toBlock: bigint,
|
|
395
|
+
): Promise<InboxMessage | undefined> {
|
|
396
|
+
const logs = await inbox.getEvents.MessageSent({ hash: leaf.toString() }, { fromBlock, toBlock });
|
|
397
|
+
|
|
398
|
+
const messages = mapLogsInboxMessage(logs);
|
|
399
|
+
return messages.length > 0 ? messages[0] : undefined;
|
|
272
400
|
}
|
|
273
401
|
|
|
274
402
|
/**
|
|
@@ -281,39 +409,39 @@ async function getBlockFromRollupTx(
|
|
|
281
409
|
* @returns An array of InboxLeaf and next eth block to search from.
|
|
282
410
|
*/
|
|
283
411
|
export async function retrieveL1ToL2Messages(
|
|
284
|
-
inbox: GetContractReturnType<typeof InboxAbi,
|
|
412
|
+
inbox: GetContractReturnType<typeof InboxAbi, ViemClient>,
|
|
285
413
|
searchStartBlock: bigint,
|
|
286
414
|
searchEndBlock: bigint,
|
|
287
|
-
): Promise<
|
|
288
|
-
const retrievedL1ToL2Messages:
|
|
289
|
-
|
|
290
|
-
if (searchStartBlock > searchEndBlock) {
|
|
291
|
-
break;
|
|
292
|
-
}
|
|
293
|
-
|
|
415
|
+
): Promise<InboxMessage[]> {
|
|
416
|
+
const retrievedL1ToL2Messages: InboxMessage[] = [];
|
|
417
|
+
while (searchStartBlock <= searchEndBlock) {
|
|
294
418
|
const messageSentLogs = (
|
|
295
|
-
await inbox.getEvents.MessageSent(
|
|
296
|
-
{},
|
|
297
|
-
{
|
|
298
|
-
fromBlock: searchStartBlock,
|
|
299
|
-
toBlock: searchEndBlock,
|
|
300
|
-
},
|
|
301
|
-
)
|
|
419
|
+
await inbox.getEvents.MessageSent({}, { fromBlock: searchStartBlock, toBlock: searchEndBlock })
|
|
302
420
|
).filter(log => log.blockNumber! >= searchStartBlock && log.blockNumber! <= searchEndBlock);
|
|
303
421
|
|
|
304
422
|
if (messageSentLogs.length === 0) {
|
|
305
423
|
break;
|
|
306
424
|
}
|
|
307
425
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
426
|
+
retrievedL1ToL2Messages.push(...mapLogsInboxMessage(messageSentLogs));
|
|
427
|
+
searchStartBlock = messageSentLogs.at(-1)!.blockNumber + 1n;
|
|
428
|
+
}
|
|
312
429
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
430
|
+
return retrievedL1ToL2Messages;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function mapLogsInboxMessage(logs: GetContractEventsReturnType<typeof InboxAbi, 'MessageSent'>): InboxMessage[] {
|
|
434
|
+
return logs.map(log => {
|
|
435
|
+
const { index, hash, l2BlockNumber, rollingHash } = log.args;
|
|
436
|
+
return {
|
|
437
|
+
index: index!,
|
|
438
|
+
leaf: Fr.fromHexString(hash!),
|
|
439
|
+
l1BlockNumber: log.blockNumber,
|
|
440
|
+
l1BlockHash: Buffer32.fromString(log.blockHash),
|
|
441
|
+
l2BlockNumber: Number(l2BlockNumber!),
|
|
442
|
+
rollingHash: Buffer16.fromString(rollingHash!),
|
|
443
|
+
};
|
|
444
|
+
});
|
|
317
445
|
}
|
|
318
446
|
|
|
319
447
|
/** Retrieves L2ProofVerified events from the rollup contract. */
|
|
@@ -322,7 +450,7 @@ export async function retrieveL2ProofVerifiedEvents(
|
|
|
322
450
|
rollupAddress: EthAddress,
|
|
323
451
|
searchStartBlock: bigint,
|
|
324
452
|
searchEndBlock?: bigint,
|
|
325
|
-
): Promise<{ l1BlockNumber: bigint; l2BlockNumber:
|
|
453
|
+
): Promise<{ l1BlockNumber: bigint; l2BlockNumber: number; proverId: Fr; txHash: Hex }[]> {
|
|
326
454
|
const logs = await publicClient.getLogs({
|
|
327
455
|
address: rollupAddress.toString(),
|
|
328
456
|
fromBlock: searchStartBlock,
|
|
@@ -333,7 +461,7 @@ export async function retrieveL2ProofVerifiedEvents(
|
|
|
333
461
|
|
|
334
462
|
return logs.map(log => ({
|
|
335
463
|
l1BlockNumber: log.blockNumber,
|
|
336
|
-
l2BlockNumber: log.args.blockNumber,
|
|
464
|
+
l2BlockNumber: Number(log.args.blockNumber),
|
|
337
465
|
proverId: Fr.fromHexString(log.args.proverId),
|
|
338
466
|
txHash: log.transactionHash,
|
|
339
467
|
}));
|
|
@@ -345,9 +473,9 @@ export async function retrieveL2ProofsFromRollup(
|
|
|
345
473
|
rollupAddress: EthAddress,
|
|
346
474
|
searchStartBlock: bigint,
|
|
347
475
|
searchEndBlock?: bigint,
|
|
348
|
-
): Promise<DataRetrieval<{ proof: Proof; proverId: Fr; l2BlockNumber:
|
|
476
|
+
): Promise<DataRetrieval<{ proof: Proof; proverId: Fr; l2BlockNumber: number; txHash: `0x${string}` }>> {
|
|
349
477
|
const logs = await retrieveL2ProofVerifiedEvents(publicClient, rollupAddress, searchStartBlock, searchEndBlock);
|
|
350
|
-
const retrievedData: { proof: Proof; proverId: Fr; l2BlockNumber:
|
|
478
|
+
const retrievedData: { proof: Proof; proverId: Fr; l2BlockNumber: number; txHash: `0x${string}` }[] = [];
|
|
351
479
|
const lastProcessedL1BlockNumber = logs.length > 0 ? logs.at(-1)!.l1BlockNumber : searchStartBlock - 1n;
|
|
352
480
|
|
|
353
481
|
for (const { txHash, proverId, l2BlockNumber } of logs) {
|
|
@@ -363,7 +491,6 @@ export async function retrieveL2ProofsFromRollup(
|
|
|
363
491
|
export type SubmitBlockProof = {
|
|
364
492
|
archiveRoot: Fr;
|
|
365
493
|
proverId: Fr;
|
|
366
|
-
aggregationObject: Buffer;
|
|
367
494
|
proof: Proof;
|
|
368
495
|
};
|
|
369
496
|
|
|
@@ -386,7 +513,6 @@ export async function getProofFromSubmitProofTx(
|
|
|
386
513
|
|
|
387
514
|
let proverId: Fr;
|
|
388
515
|
let archiveRoot: Fr;
|
|
389
|
-
let aggregationObject: Buffer;
|
|
390
516
|
let proof: Proof;
|
|
391
517
|
|
|
392
518
|
if (functionName === 'submitEpochRootProof') {
|
|
@@ -396,12 +522,10 @@ export async function getProofFromSubmitProofTx(
|
|
|
396
522
|
end: bigint;
|
|
397
523
|
args: EpochProofPublicInputArgs;
|
|
398
524
|
fees: readonly Hex[];
|
|
399
|
-
aggregationObject: Hex;
|
|
400
525
|
proof: Hex;
|
|
401
526
|
},
|
|
402
527
|
];
|
|
403
528
|
|
|
404
|
-
aggregationObject = Buffer.from(hexToBytes(decodedArgs.aggregationObject));
|
|
405
529
|
proverId = Fr.fromHexString(decodedArgs.args.proverId);
|
|
406
530
|
archiveRoot = Fr.fromHexString(decodedArgs.args.endArchive);
|
|
407
531
|
proof = Proof.fromBuffer(Buffer.from(hexToBytes(decodedArgs.proof)));
|
|
@@ -415,7 +539,6 @@ export async function getProofFromSubmitProofTx(
|
|
|
415
539
|
|
|
416
540
|
return {
|
|
417
541
|
proverId,
|
|
418
|
-
aggregationObject,
|
|
419
542
|
archiveRoot,
|
|
420
543
|
proof,
|
|
421
544
|
};
|
package/src/archiver/errors.ts
CHANGED
|
@@ -3,3 +3,24 @@ export class NoBlobBodiesFoundError extends Error {
|
|
|
3
3
|
super(`No blob bodies found for block ${l2BlockNum}`);
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
|
+
|
|
7
|
+
export class InitialBlockNumberNotSequentialError extends Error {
|
|
8
|
+
constructor(
|
|
9
|
+
public readonly newBlockNumber: number,
|
|
10
|
+
public readonly previousBlockNumber: number | undefined,
|
|
11
|
+
) {
|
|
12
|
+
super(
|
|
13
|
+
`Cannot insert new block ${newBlockNumber} given previous block number in store is ${
|
|
14
|
+
previousBlockNumber ?? 'undefined'
|
|
15
|
+
}`,
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class BlockNumberNotSequentialError extends Error {
|
|
21
|
+
constructor(newBlockNumber: number, previous: number | undefined) {
|
|
22
|
+
super(
|
|
23
|
+
`Cannot insert new block ${newBlockNumber} given previous block number in batch is ${previous ?? 'undefined'}`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/archiver/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export * from './archiver.js';
|
|
2
2
|
export * from './config.js';
|
|
3
|
-
export { type
|
|
4
|
-
export { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js';
|
|
3
|
+
export { type PublishedL2Block, type L1PublishedData } from './structs/published.js';
|
|
5
4
|
export type { ArchiverDataStore } from './archiver_store.js';
|
|
6
|
-
export { KVArchiverDataStore } from './kv_archiver_store/kv_archiver_store.js';
|
|
5
|
+
export { KVArchiverDataStore, ARCHIVER_DB_VERSION } from './kv_archiver_store/kv_archiver_store.js';
|
|
7
6
|
export { ContractInstanceStore } from './kv_archiver_store/contract_instance_store.js';
|