@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
package/src/archiver/config.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { type BlobSinkConfig, blobSinkConfigMapping } from '@aztec/blob-sink/client';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
l1ContractAddressesMapping,
|
|
6
|
-
l1ContractsConfigMappings,
|
|
7
|
-
l1ReaderConfigMappings,
|
|
8
|
-
} from '@aztec/ethereum';
|
|
2
|
+
import { type L1ContractsConfig, l1ContractsConfigMappings } from '@aztec/ethereum/config';
|
|
3
|
+
import { l1ContractAddressesMapping } from '@aztec/ethereum/l1-contract-addresses';
|
|
4
|
+
import { type L1ReaderConfig, l1ReaderConfigMappings } from '@aztec/ethereum/l1-reader';
|
|
9
5
|
import {
|
|
10
6
|
type ConfigMappingsType,
|
|
11
7
|
booleanConfigHelper,
|
|
@@ -50,6 +46,16 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
50
46
|
description: 'Whether to skip validating block attestations (use only for testing).',
|
|
51
47
|
...booleanConfigHelper(false),
|
|
52
48
|
},
|
|
49
|
+
maxAllowedEthClientDriftSeconds: {
|
|
50
|
+
env: 'MAX_ALLOWED_ETH_CLIENT_DRIFT_SECONDS',
|
|
51
|
+
description: 'Maximum allowed drift in seconds between the Ethereum client and current time.',
|
|
52
|
+
...numberConfigHelper(300),
|
|
53
|
+
},
|
|
54
|
+
ethereumAllowNoDebugHosts: {
|
|
55
|
+
env: 'ETHEREUM_ALLOW_NO_DEBUG_HOSTS',
|
|
56
|
+
description: 'Whether to allow starting the archiver without debug/trace method support on Ethereum hosts',
|
|
57
|
+
...booleanConfigHelper(true),
|
|
58
|
+
},
|
|
53
59
|
...chainConfigMappings,
|
|
54
60
|
...l1ReaderConfigMappings,
|
|
55
61
|
viemPollingIntervalMS: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
-
import type {
|
|
2
|
+
import type { L2BlockNew } from '@aztec/stdlib/block';
|
|
3
3
|
import {
|
|
4
4
|
Attributes,
|
|
5
5
|
type Gauge,
|
|
@@ -34,6 +34,8 @@ export class ArchiverInstrumentation {
|
|
|
34
34
|
private syncDurationPerMessage: Histogram;
|
|
35
35
|
private syncMessageCount: UpDownCounter;
|
|
36
36
|
|
|
37
|
+
private blockProposalTxTargetCount: UpDownCounter;
|
|
38
|
+
|
|
37
39
|
private log = createLogger('archiver:instrumentation');
|
|
38
40
|
|
|
39
41
|
private constructor(
|
|
@@ -114,6 +116,11 @@ export class ArchiverInstrumentation {
|
|
|
114
116
|
valueType: ValueType.INT,
|
|
115
117
|
});
|
|
116
118
|
|
|
119
|
+
this.blockProposalTxTargetCount = meter.createUpDownCounter(Metrics.ARCHIVER_BLOCK_PROPOSAL_TX_TARGET_COUNT, {
|
|
120
|
+
description: 'Number of block proposals by tx target',
|
|
121
|
+
valueType: ValueType.INT,
|
|
122
|
+
});
|
|
123
|
+
|
|
117
124
|
this.dbMetrics = new LmdbMetrics(
|
|
118
125
|
meter,
|
|
119
126
|
{
|
|
@@ -139,7 +146,7 @@ export class ArchiverInstrumentation {
|
|
|
139
146
|
return this.telemetry.isEnabled();
|
|
140
147
|
}
|
|
141
148
|
|
|
142
|
-
public processNewBlocks(syncTimePerBlock: number, blocks:
|
|
149
|
+
public processNewBlocks(syncTimePerBlock: number, blocks: L2BlockNew[]) {
|
|
143
150
|
this.syncDurationPerBlock.record(Math.ceil(syncTimePerBlock));
|
|
144
151
|
this.blockHeight.record(Math.max(...blocks.map(b => b.number)));
|
|
145
152
|
this.syncBlockCount.add(blocks.length);
|
|
@@ -184,4 +191,11 @@ export class ArchiverInstrumentation {
|
|
|
184
191
|
public updateL1BlockHeight(blockNumber: bigint) {
|
|
185
192
|
this.l1BlockHeight.record(Number(blockNumber));
|
|
186
193
|
}
|
|
194
|
+
|
|
195
|
+
public recordBlockProposalTxTarget(target: string, usedTrace: boolean) {
|
|
196
|
+
this.blockProposalTxTargetCount.add(1, {
|
|
197
|
+
[Attributes.L1_BLOCK_PROPOSAL_TX_TARGET]: target.toLowerCase(),
|
|
198
|
+
[Attributes.L1_BLOCK_PROPOSAL_USED_TRACE]: usedTrace,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
187
201
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
|
-
import {
|
|
2
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
4
|
import { toArray } from '@aztec/foundation/iterable';
|
|
4
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
6
|
import { BufferReader } from '@aztec/foundation/serialize';
|
|
@@ -158,7 +159,7 @@ export class BlockStore {
|
|
|
158
159
|
* @param blocksToUnwind - The number of blocks we are to unwind
|
|
159
160
|
* @returns True if the operation is successful
|
|
160
161
|
*/
|
|
161
|
-
async unwindBlocks(from:
|
|
162
|
+
async unwindBlocks(from: BlockNumber, blocksToUnwind: number) {
|
|
162
163
|
return await this.db.transactionAsync(async () => {
|
|
163
164
|
const last = await this.getSynchedL2BlockNumber();
|
|
164
165
|
if (from !== last) {
|
|
@@ -167,12 +168,12 @@ export class BlockStore {
|
|
|
167
168
|
|
|
168
169
|
const proven = await this.getProvenL2BlockNumber();
|
|
169
170
|
if (from - blocksToUnwind < proven) {
|
|
170
|
-
await this.setProvenL2BlockNumber(from - blocksToUnwind);
|
|
171
|
+
await this.setProvenL2BlockNumber(BlockNumber(from - blocksToUnwind));
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
for (let i = 0; i < blocksToUnwind; i++) {
|
|
174
175
|
const blockNumber = from - i;
|
|
175
|
-
const block = await this.getBlock(blockNumber);
|
|
176
|
+
const block = await this.getBlock(BlockNumber(blockNumber));
|
|
176
177
|
|
|
177
178
|
if (block === undefined) {
|
|
178
179
|
this.#log.warn(`Cannot remove block ${blockNumber} from the store since we don't have it`);
|
|
@@ -200,7 +201,7 @@ export class BlockStore {
|
|
|
200
201
|
* @param limit - The number of blocks to return.
|
|
201
202
|
* @returns The requested L2 blocks
|
|
202
203
|
*/
|
|
203
|
-
async *getBlocks(start:
|
|
204
|
+
async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<PublishedL2Block> {
|
|
204
205
|
for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
|
|
205
206
|
const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
|
|
206
207
|
if (block) {
|
|
@@ -214,7 +215,7 @@ export class BlockStore {
|
|
|
214
215
|
* @param blockNumber - The number of the block to return.
|
|
215
216
|
* @returns The requested L2 block.
|
|
216
217
|
*/
|
|
217
|
-
async getBlock(blockNumber:
|
|
218
|
+
async getBlock(blockNumber: BlockNumber): Promise<PublishedL2Block | undefined> {
|
|
218
219
|
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
219
220
|
if (!blockStorage || !blockStorage.header) {
|
|
220
221
|
return Promise.resolve(undefined);
|
|
@@ -232,7 +233,7 @@ export class BlockStore {
|
|
|
232
233
|
if (blockNumber === undefined) {
|
|
233
234
|
return undefined;
|
|
234
235
|
}
|
|
235
|
-
return this.getBlock(blockNumber);
|
|
236
|
+
return this.getBlock(BlockNumber(blockNumber));
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
/**
|
|
@@ -245,7 +246,7 @@ export class BlockStore {
|
|
|
245
246
|
if (blockNumber === undefined) {
|
|
246
247
|
return undefined;
|
|
247
248
|
}
|
|
248
|
-
return this.getBlock(blockNumber);
|
|
249
|
+
return this.getBlock(BlockNumber(blockNumber));
|
|
249
250
|
}
|
|
250
251
|
|
|
251
252
|
/**
|
|
@@ -288,7 +289,7 @@ export class BlockStore {
|
|
|
288
289
|
* @param limit - The number of blocks to return.
|
|
289
290
|
* @returns The requested L2 block headers
|
|
290
291
|
*/
|
|
291
|
-
async *getBlockHeaders(start:
|
|
292
|
+
async *getBlockHeaders(start: BlockNumber, limit: number): AsyncIterableIterator<BlockHeader> {
|
|
292
293
|
for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
|
|
293
294
|
const header = L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
|
|
294
295
|
if (header.getBlockNumber() !== blockNumber) {
|
|
@@ -300,7 +301,7 @@ export class BlockStore {
|
|
|
300
301
|
}
|
|
301
302
|
}
|
|
302
303
|
|
|
303
|
-
private async *getBlockStorages(start:
|
|
304
|
+
private async *getBlockStorages(start: BlockNumber, limit: number) {
|
|
304
305
|
let expectedBlockNumber = start;
|
|
305
306
|
for await (const [blockNumber, blockStorage] of this.#blocks.entriesAsync(this.#computeBlockRange(start, limit))) {
|
|
306
307
|
if (blockNumber !== expectedBlockNumber) {
|
|
@@ -382,7 +383,7 @@ export class BlockStore {
|
|
|
382
383
|
'',
|
|
383
384
|
txEffect.data.transactionFee.toBigInt(),
|
|
384
385
|
txEffect.l2BlockHash,
|
|
385
|
-
txEffect.l2BlockNumber,
|
|
386
|
+
BlockNumber(txEffect.l2BlockNumber),
|
|
386
387
|
);
|
|
387
388
|
}
|
|
388
389
|
|
|
@@ -413,9 +414,9 @@ export class BlockStore {
|
|
|
413
414
|
* Gets the number of the latest L2 block processed.
|
|
414
415
|
* @returns The number of the latest L2 block processed.
|
|
415
416
|
*/
|
|
416
|
-
async getSynchedL2BlockNumber(): Promise<
|
|
417
|
+
async getSynchedL2BlockNumber(): Promise<BlockNumber> {
|
|
417
418
|
const [lastBlockNumber] = await toArray(this.#blocks.keysAsync({ reverse: true, limit: 1 }));
|
|
418
|
-
return typeof lastBlockNumber === 'number' ? lastBlockNumber : INITIAL_L2_BLOCK_NUM - 1;
|
|
419
|
+
return typeof lastBlockNumber === 'number' ? BlockNumber(lastBlockNumber) : BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
419
420
|
}
|
|
420
421
|
|
|
421
422
|
/**
|
|
@@ -430,19 +431,19 @@ export class BlockStore {
|
|
|
430
431
|
return this.#lastSynchedL1Block.set(l1BlockNumber);
|
|
431
432
|
}
|
|
432
433
|
|
|
433
|
-
async getProvenL2BlockNumber(): Promise<
|
|
434
|
+
async getProvenL2BlockNumber(): Promise<BlockNumber> {
|
|
434
435
|
const [latestBlockNumber, provenBlockNumber] = await Promise.all([
|
|
435
436
|
this.getSynchedL2BlockNumber(),
|
|
436
437
|
this.#lastProvenL2Block.getAsync(),
|
|
437
438
|
]);
|
|
438
|
-
return (provenBlockNumber ?? 0) > latestBlockNumber ? latestBlockNumber : (provenBlockNumber ?? 0);
|
|
439
|
+
return (provenBlockNumber ?? 0) > latestBlockNumber ? latestBlockNumber : BlockNumber(provenBlockNumber ?? 0);
|
|
439
440
|
}
|
|
440
441
|
|
|
441
|
-
setProvenL2BlockNumber(blockNumber:
|
|
442
|
+
setProvenL2BlockNumber(blockNumber: BlockNumber) {
|
|
442
443
|
return this.#lastProvenL2Block.set(blockNumber);
|
|
443
444
|
}
|
|
444
445
|
|
|
445
|
-
#computeBlockRange(start:
|
|
446
|
+
#computeBlockRange(start: BlockNumber, limit: number): Required<Pick<Range<number>, 'start' | 'limit'>> {
|
|
446
447
|
if (limit < 1) {
|
|
447
448
|
throw new Error(`Invalid limit: ${limit}`);
|
|
448
449
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fr } from '@aztec/foundation/
|
|
1
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
2
|
import { toArray } from '@aztec/foundation/iterable';
|
|
3
3
|
import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
4
4
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { L1BlockId } from '@aztec/ethereum';
|
|
2
|
-
import type {
|
|
1
|
+
import type { L1BlockId } from '@aztec/ethereum/l1-types';
|
|
2
|
+
import type { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
4
|
import { toArray } from '@aztec/foundation/iterable';
|
|
4
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
6
|
import type { AztecAsyncKVStore, CustomRange, StoreSize } from '@aztec/kv-store';
|
|
@@ -15,7 +16,7 @@ import type {
|
|
|
15
16
|
UtilityFunctionWithMembershipProof,
|
|
16
17
|
} from '@aztec/stdlib/contract';
|
|
17
18
|
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
18
|
-
import {
|
|
19
|
+
import type { LogFilter, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
19
20
|
import type { BlockHeader, TxHash, TxReceipt } from '@aztec/stdlib/tx';
|
|
20
21
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
21
22
|
|
|
@@ -30,7 +31,7 @@ import { ContractInstanceStore } from './contract_instance_store.js';
|
|
|
30
31
|
import { LogStore } from './log_store.js';
|
|
31
32
|
import { MessageStore } from './message_store.js';
|
|
32
33
|
|
|
33
|
-
export const ARCHIVER_DB_VERSION =
|
|
34
|
+
export const ARCHIVER_DB_VERSION = 4;
|
|
34
35
|
export const MAX_FUNCTION_SIGNATURES = 1000;
|
|
35
36
|
export const MAX_FUNCTION_NAME_LEN = 256;
|
|
36
37
|
|
|
@@ -65,7 +66,7 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
65
66
|
return this.db.transactionAsync(callback);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
public getBlockNumber(): Promise<
|
|
69
|
+
public getBlockNumber(): Promise<BlockNumber> {
|
|
69
70
|
return this.getSynchedL2BlockNumber();
|
|
70
71
|
}
|
|
71
72
|
|
|
@@ -124,7 +125,7 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
124
125
|
async addContractClasses(
|
|
125
126
|
data: ContractClassPublic[],
|
|
126
127
|
bytecodeCommitments: Fr[],
|
|
127
|
-
blockNumber:
|
|
128
|
+
blockNumber: BlockNumber,
|
|
128
129
|
): Promise<boolean> {
|
|
129
130
|
return (
|
|
130
131
|
await Promise.all(
|
|
@@ -133,7 +134,7 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
133
134
|
).every(Boolean);
|
|
134
135
|
}
|
|
135
136
|
|
|
136
|
-
async deleteContractClasses(data: ContractClassPublic[], blockNumber:
|
|
137
|
+
async deleteContractClasses(data: ContractClassPublic[], blockNumber: BlockNumber): Promise<boolean> {
|
|
137
138
|
return (await Promise.all(data.map(c => this.#contractClassStore.deleteContractClasses(c, blockNumber)))).every(
|
|
138
139
|
Boolean,
|
|
139
140
|
);
|
|
@@ -151,13 +152,13 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
151
152
|
return this.#contractClassStore.addFunctions(contractClassId, privateFunctions, utilityFunctions);
|
|
152
153
|
}
|
|
153
154
|
|
|
154
|
-
async addContractInstances(data: ContractInstanceWithAddress[], blockNumber:
|
|
155
|
+
async addContractInstances(data: ContractInstanceWithAddress[], blockNumber: BlockNumber): Promise<boolean> {
|
|
155
156
|
return (await Promise.all(data.map(c => this.#contractInstanceStore.addContractInstance(c, blockNumber)))).every(
|
|
156
157
|
Boolean,
|
|
157
158
|
);
|
|
158
159
|
}
|
|
159
160
|
|
|
160
|
-
async deleteContractInstances(data: ContractInstanceWithAddress[], _blockNumber:
|
|
161
|
+
async deleteContractInstances(data: ContractInstanceWithAddress[], _blockNumber: BlockNumber): Promise<boolean> {
|
|
161
162
|
return (await Promise.all(data.map(c => this.#contractInstanceStore.deleteContractInstance(c)))).every(Boolean);
|
|
162
163
|
}
|
|
163
164
|
|
|
@@ -196,11 +197,11 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
196
197
|
* @param blocksToUnwind - The number of blocks we are to unwind
|
|
197
198
|
* @returns True if the operation is successful
|
|
198
199
|
*/
|
|
199
|
-
unwindBlocks(from:
|
|
200
|
+
unwindBlocks(from: BlockNumber, blocksToUnwind: number): Promise<boolean> {
|
|
200
201
|
return this.#blockStore.unwindBlocks(from, blocksToUnwind);
|
|
201
202
|
}
|
|
202
203
|
|
|
203
|
-
getPublishedBlock(number:
|
|
204
|
+
getPublishedBlock(number: BlockNumber): Promise<PublishedL2Block | undefined> {
|
|
204
205
|
return this.#blockStore.getBlock(number);
|
|
205
206
|
}
|
|
206
207
|
|
|
@@ -219,7 +220,7 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
219
220
|
* @param limit - The number of blocks to return.
|
|
220
221
|
* @returns The requested L2 blocks
|
|
221
222
|
*/
|
|
222
|
-
getPublishedBlocks(start:
|
|
223
|
+
getPublishedBlocks(start: BlockNumber, limit: number): Promise<PublishedL2Block[]> {
|
|
223
224
|
return toArray(this.#blockStore.getBlocks(start, limit));
|
|
224
225
|
}
|
|
225
226
|
|
|
@@ -230,7 +231,7 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
230
231
|
* @param limit - The number of blocks to return.
|
|
231
232
|
* @returns The requested L2 blocks
|
|
232
233
|
*/
|
|
233
|
-
getBlockHeaders(start:
|
|
234
|
+
getBlockHeaders(start: BlockNumber, limit: number): Promise<BlockHeader[]> {
|
|
234
235
|
return toArray(this.#blockStore.getBlockHeaders(start, limit));
|
|
235
236
|
}
|
|
236
237
|
|
|
@@ -299,22 +300,12 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
299
300
|
}
|
|
300
301
|
|
|
301
302
|
/**
|
|
302
|
-
* Gets L1 to L2 message (to be) included in a given
|
|
303
|
-
* @param
|
|
303
|
+
* Gets L1 to L2 message (to be) included in a given checkpoint.
|
|
304
|
+
* @param checkpointNumber - Checkpoint number to get messages for.
|
|
304
305
|
* @returns The L1 to L2 messages/leaves of the messages subtree (throws if not found).
|
|
305
306
|
*/
|
|
306
|
-
getL1ToL2Messages(
|
|
307
|
-
return this.#messageStore.getL1ToL2Messages(
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Retrieves all private logs from up to `limit` blocks, starting from the block number `from`.
|
|
312
|
-
* @param from - The block number from which to begin retrieving logs.
|
|
313
|
-
* @param limit - The maximum number of blocks to retrieve logs from.
|
|
314
|
-
* @returns An array of private logs from the specified range of blocks.
|
|
315
|
-
*/
|
|
316
|
-
getPrivateLogs(from: number, limit: number): Promise<PrivateLog[]> {
|
|
317
|
-
return this.#logStore.getPrivateLogs(from, limit);
|
|
307
|
+
getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
|
|
308
|
+
return this.#messageStore.getL1ToL2Messages(checkpointNumber);
|
|
318
309
|
}
|
|
319
310
|
|
|
320
311
|
/**
|
|
@@ -362,15 +353,15 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
362
353
|
* Gets the number of the latest L2 block processed.
|
|
363
354
|
* @returns The number of the latest L2 block processed.
|
|
364
355
|
*/
|
|
365
|
-
getSynchedL2BlockNumber(): Promise<
|
|
356
|
+
getSynchedL2BlockNumber(): Promise<BlockNumber> {
|
|
366
357
|
return this.#blockStore.getSynchedL2BlockNumber();
|
|
367
358
|
}
|
|
368
359
|
|
|
369
|
-
getProvenL2BlockNumber(): Promise<
|
|
360
|
+
getProvenL2BlockNumber(): Promise<BlockNumber> {
|
|
370
361
|
return this.#blockStore.getProvenL2BlockNumber();
|
|
371
362
|
}
|
|
372
363
|
|
|
373
|
-
async setProvenL2BlockNumber(blockNumber:
|
|
364
|
+
async setProvenL2BlockNumber(blockNumber: BlockNumber) {
|
|
374
365
|
await this.#blockStore.setProvenL2BlockNumber(blockNumber);
|
|
375
366
|
}
|
|
376
367
|
|
|
@@ -400,8 +391,8 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
|
|
|
400
391
|
return this.db.estimateSize();
|
|
401
392
|
}
|
|
402
393
|
|
|
403
|
-
public
|
|
404
|
-
return this.#messageStore.
|
|
394
|
+
public rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber: CheckpointNumber): Promise<void> {
|
|
395
|
+
return this.#messageStore.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
405
396
|
}
|
|
406
397
|
|
|
407
398
|
public iterateL1ToL2Messages(range: CustomRange<bigint> = {}): AsyncIterableIterator<InboxMessage> {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX } from '@aztec/constants';
|
|
2
|
-
import
|
|
2
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
5
|
import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
|
|
5
6
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
@@ -11,7 +12,6 @@ import {
|
|
|
11
12
|
ExtendedPublicLog,
|
|
12
13
|
type LogFilter,
|
|
13
14
|
LogId,
|
|
14
|
-
PrivateLog,
|
|
15
15
|
PublicLog,
|
|
16
16
|
TxScopedL2Log,
|
|
17
17
|
} from '@aztec/stdlib/logs';
|
|
@@ -24,7 +24,6 @@ import type { BlockStore } from './block_store.js';
|
|
|
24
24
|
export class LogStore {
|
|
25
25
|
#logsByTag: AztecAsyncMap<string, Buffer[]>;
|
|
26
26
|
#logTagsByBlock: AztecAsyncMap<number, string[]>;
|
|
27
|
-
#privateLogsByBlock: AztecAsyncMap<number, Buffer>;
|
|
28
27
|
#publicLogsByBlock: AztecAsyncMap<number, Buffer>;
|
|
29
28
|
#contractClassLogsByBlock: AztecAsyncMap<number, Buffer>;
|
|
30
29
|
#logsMaxPageSize: number;
|
|
@@ -37,7 +36,6 @@ export class LogStore {
|
|
|
37
36
|
) {
|
|
38
37
|
this.#logsByTag = db.openMap('archiver_tagged_logs_by_tag');
|
|
39
38
|
this.#logTagsByBlock = db.openMap('archiver_log_tags_by_block');
|
|
40
|
-
this.#privateLogsByBlock = db.openMap('archiver_private_logs_by_block');
|
|
41
39
|
this.#publicLogsByBlock = db.openMap('archiver_public_logs_by_block');
|
|
42
40
|
this.#contractClassLogsByBlock = db.openMap('archiver_contract_class_logs_by_block');
|
|
43
41
|
|
|
@@ -111,12 +109,6 @@ export class LogStore {
|
|
|
111
109
|
}
|
|
112
110
|
await this.#logTagsByBlock.set(block.number, tagsInBlock);
|
|
113
111
|
|
|
114
|
-
const privateLogsInBlock = block.body.txEffects
|
|
115
|
-
.map(txEffect => txEffect.privateLogs)
|
|
116
|
-
.flat()
|
|
117
|
-
.map(log => log.toBuffer());
|
|
118
|
-
await this.#privateLogsByBlock.set(block.number, Buffer.concat(privateLogsInBlock));
|
|
119
|
-
|
|
120
112
|
const publicLogsInBlock = block.body.txEffects
|
|
121
113
|
.map((txEffect, txIndex) =>
|
|
122
114
|
[
|
|
@@ -159,7 +151,6 @@ export class LogStore {
|
|
|
159
151
|
await Promise.all(
|
|
160
152
|
blocks.map(block =>
|
|
161
153
|
Promise.all([
|
|
162
|
-
this.#privateLogsByBlock.delete(block.number),
|
|
163
154
|
this.#publicLogsByBlock.delete(block.number),
|
|
164
155
|
this.#logTagsByBlock.delete(block.number),
|
|
165
156
|
this.#contractClassLogsByBlock.delete(block.number),
|
|
@@ -172,23 +163,6 @@ export class LogStore {
|
|
|
172
163
|
});
|
|
173
164
|
}
|
|
174
165
|
|
|
175
|
-
/**
|
|
176
|
-
* Retrieves all private logs from up to `limit` blocks, starting from the block number `start`.
|
|
177
|
-
* @param start - The block number from which to begin retrieving logs.
|
|
178
|
-
* @param limit - The maximum number of blocks to retrieve logs from.
|
|
179
|
-
* @returns An array of private logs from the specified range of blocks.
|
|
180
|
-
*/
|
|
181
|
-
async getPrivateLogs(start: number, limit: number): Promise<PrivateLog[]> {
|
|
182
|
-
const logs = [];
|
|
183
|
-
for await (const buffer of this.#privateLogsByBlock.valuesAsync({ start, limit })) {
|
|
184
|
-
const reader = new BufferReader(buffer);
|
|
185
|
-
while (reader.remainingBytes() > 0) {
|
|
186
|
-
logs.push(reader.readObject(PrivateLog));
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return logs;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
166
|
/**
|
|
193
167
|
* Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
|
|
194
168
|
* @param tags - The tags to filter the logs by.
|
|
@@ -389,9 +363,9 @@ export class LogStore {
|
|
|
389
363
|
const log = txLogs[logIndex];
|
|
390
364
|
if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) {
|
|
391
365
|
if (log instanceof ContractClassLog) {
|
|
392
|
-
results.push(new ExtendedContractClassLog(new LogId(blockNumber, txIndex, logIndex), log));
|
|
366
|
+
results.push(new ExtendedContractClassLog(new LogId(BlockNumber(blockNumber), txIndex, logIndex), log));
|
|
393
367
|
} else {
|
|
394
|
-
results.push(new ExtendedPublicLog(new LogId(blockNumber, txIndex, logIndex), log));
|
|
368
|
+
results.push(new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), txIndex, logIndex), log));
|
|
395
369
|
}
|
|
396
370
|
|
|
397
371
|
if (results.length >= this.#logsMaxPageSize) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { L1BlockId } from '@aztec/ethereum';
|
|
1
|
+
import type { L1BlockId } from '@aztec/ethereum/l1-types';
|
|
2
|
+
import { CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
3
|
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
3
|
-
import { Fr } from '@aztec/foundation/
|
|
4
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
5
|
import { toArray } from '@aztec/foundation/iterable';
|
|
5
6
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
7
|
import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
@@ -113,20 +114,20 @@ export class MessageStore {
|
|
|
113
114
|
);
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
// Check index corresponds to the
|
|
117
|
-
const [expectedStart, expectedEnd] = InboxLeaf.
|
|
117
|
+
// Check index corresponds to the checkpoint number.
|
|
118
|
+
const [expectedStart, expectedEnd] = InboxLeaf.indexRangeForCheckpoint(message.checkpointNumber);
|
|
118
119
|
if (message.index < expectedStart || message.index >= expectedEnd) {
|
|
119
120
|
throw new MessageStoreError(
|
|
120
121
|
`Invalid index ${message.index} for incoming L1 to L2 message ${message.leaf.toString()} ` +
|
|
121
|
-
`at
|
|
122
|
+
`at checkpoint ${message.checkpointNumber} (expected value in range [${expectedStart}, ${expectedEnd}))`,
|
|
122
123
|
message,
|
|
123
124
|
);
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
// Check there are no gaps in the indices within the same
|
|
127
|
+
// Check there are no gaps in the indices within the same checkpoint.
|
|
127
128
|
if (
|
|
128
129
|
lastMessage &&
|
|
129
|
-
message.
|
|
130
|
+
message.checkpointNumber === lastMessage.checkpointNumber &&
|
|
130
131
|
message.index !== lastMessage.index + 1n
|
|
131
132
|
) {
|
|
132
133
|
throw new MessageStoreError(
|
|
@@ -138,12 +139,12 @@ export class MessageStore {
|
|
|
138
139
|
|
|
139
140
|
// Check the first message in a block has the correct index.
|
|
140
141
|
if (
|
|
141
|
-
(!lastMessage || message.
|
|
142
|
-
message.index !==
|
|
142
|
+
(!lastMessage || message.checkpointNumber > lastMessage.checkpointNumber) &&
|
|
143
|
+
message.index !== expectedStart
|
|
143
144
|
) {
|
|
144
145
|
throw new MessageStoreError(
|
|
145
|
-
`Message ${message.leaf.toString()} for
|
|
146
|
-
`${message.index} (expected ${
|
|
146
|
+
`Message ${message.leaf.toString()} for checkpoint ${message.checkpointNumber} has wrong index ` +
|
|
147
|
+
`${message.index} (expected ${expectedStart})`,
|
|
147
148
|
message,
|
|
148
149
|
);
|
|
149
150
|
}
|
|
@@ -184,10 +185,10 @@ export class MessageStore {
|
|
|
184
185
|
return msg ? deserializeInboxMessage(msg) : undefined;
|
|
185
186
|
}
|
|
186
187
|
|
|
187
|
-
public async getL1ToL2Messages(
|
|
188
|
+
public async getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
|
|
188
189
|
const messages: Fr[] = [];
|
|
189
190
|
|
|
190
|
-
const [startIndex, endIndex] = InboxLeaf.
|
|
191
|
+
const [startIndex, endIndex] = InboxLeaf.indexRangeForCheckpoint(checkpointNumber);
|
|
191
192
|
let lastIndex = startIndex - 1n;
|
|
192
193
|
|
|
193
194
|
for await (const msgBuffer of this.#l1ToL2Messages.valuesAsync({
|
|
@@ -195,8 +196,10 @@ export class MessageStore {
|
|
|
195
196
|
end: this.indexToKey(endIndex),
|
|
196
197
|
})) {
|
|
197
198
|
const msg = deserializeInboxMessage(msgBuffer);
|
|
198
|
-
if (msg.
|
|
199
|
-
throw new Error(
|
|
199
|
+
if (msg.checkpointNumber !== checkpointNumber) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`L1 to L2 message with index ${msg.index} has invalid checkpoint number ${msg.checkpointNumber}`,
|
|
202
|
+
);
|
|
200
203
|
} else if (msg.index !== lastIndex + 1n) {
|
|
201
204
|
throw new Error(`Expected L1 to L2 message with index ${lastIndex + 1n} but got ${msg.index}`);
|
|
202
205
|
}
|
|
@@ -232,9 +235,9 @@ export class MessageStore {
|
|
|
232
235
|
});
|
|
233
236
|
}
|
|
234
237
|
|
|
235
|
-
public
|
|
236
|
-
this.#log.debug(`Deleting L1 to L2 messages up to target
|
|
237
|
-
const startIndex = InboxLeaf.
|
|
238
|
+
public rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber: CheckpointNumber): Promise<void> {
|
|
239
|
+
this.#log.debug(`Deleting L1 to L2 messages up to target checkpoint ${targetCheckpointNumber}`);
|
|
240
|
+
const startIndex = InboxLeaf.smallestIndexForCheckpoint(CheckpointNumber(targetCheckpointNumber + 1));
|
|
238
241
|
return this.removeL1ToL2Messages(startIndex);
|
|
239
242
|
}
|
|
240
243
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Archiver L1 Data Retrieval
|
|
2
|
+
|
|
3
|
+
Modules and classes to handle data retrieval from L1 for the archiver.
|
|
4
|
+
|
|
5
|
+
## Calldata Retriever
|
|
6
|
+
|
|
7
|
+
The sequencer publisher bundles multiple operations into a single multicall3 transaction for gas
|
|
8
|
+
efficiency. A typical transaction includes:
|
|
9
|
+
|
|
10
|
+
1. Attestation invalidations (if needed): `invalidateBadAttestation`, `invalidateInsufficientAttestations`
|
|
11
|
+
2. Block proposal: `propose` (exactly one per transaction to the rollup contract)
|
|
12
|
+
3. Governance and slashing (if needed): votes, payload creation/execution
|
|
13
|
+
|
|
14
|
+
The archiver needs to extract the `propose` calldata from these bundled transactions to reconstruct
|
|
15
|
+
L2 blocks. This class needs to handle scenarios where the transaction was submitted via multicall3,
|
|
16
|
+
as well as alternative ways for submitting the `propose` call that other clients might use.
|
|
17
|
+
|
|
18
|
+
### Multicall3 Validation and Decoding
|
|
19
|
+
|
|
20
|
+
First attempt to decode the transaction as a multicall3 `aggregate3` call with validation:
|
|
21
|
+
|
|
22
|
+
- Check if transaction is to multicall3 address (`0xcA11bde05977b3631167028862bE2a173976CA11`)
|
|
23
|
+
- Decode as `aggregate3(Call3[] calldata calls)`
|
|
24
|
+
- Allow calls to known addresses and methods (rollup, governance, slashing contracts, etc.)
|
|
25
|
+
- Find the single `propose` call to the rollup contract
|
|
26
|
+
- Verify exactly one `propose` call exists
|
|
27
|
+
- Extract and return the propose calldata
|
|
28
|
+
|
|
29
|
+
This step handles the common case efficiently without requiring expensive trace or debug RPC calls.
|
|
30
|
+
Any validation failure triggers fallback to the next step.
|
|
31
|
+
|
|
32
|
+
### Direct Propose Call
|
|
33
|
+
|
|
34
|
+
Second attempt to decode the transaction as a direct `propose` call to the rollup contract:
|
|
35
|
+
|
|
36
|
+
- Check if transaction is to the rollup address
|
|
37
|
+
- Decode as `propose` function call
|
|
38
|
+
- Verify the function is indeed `propose`
|
|
39
|
+
- Return the transaction input as the propose calldata
|
|
40
|
+
|
|
41
|
+
This handles scenarios where clients submit transactions directly to the rollup contract without
|
|
42
|
+
using multicall3 for bundling. Any validation failure triggers fallback to the next step.
|
|
43
|
+
|
|
44
|
+
### Spire Proposer Call
|
|
45
|
+
|
|
46
|
+
Given existing attempts to route the call via the Spire proposer, we also check if the tx is `to` the
|
|
47
|
+
proposer known address, and if so, we try decoding it as either a multicall3 or a direct call to the
|
|
48
|
+
rollup contract.
|
|
49
|
+
|
|
50
|
+
Similar as with the multicall3 check, we check that there are no other calls in the Spire proposer, so
|
|
51
|
+
we are absolutely sure that the only call is the successful one to the rollup. Any extraneous call would
|
|
52
|
+
imply an unexpected path to calling `propose` in the rollup contract, and since we cannot verify if the
|
|
53
|
+
calldata arguments we extracted are the correct ones (see the section below), we cannot know for sure which
|
|
54
|
+
one is the call that succeeded, so we don't know which calldata to process.
|
|
55
|
+
|
|
56
|
+
Furthermore, since the Spire proposer is upgradeable, we check if the implementation has not changed in
|
|
57
|
+
order to decode. As usual, any validation failure triggers fallback to the next step.
|
|
58
|
+
|
|
59
|
+
### Verifying Multicall3 Arguments
|
|
60
|
+
|
|
61
|
+
**This is NOT implemented for simplicity's sake**
|
|
62
|
+
|
|
63
|
+
If the checks above don't hold, such as when there are multiple calls to `propose`, then we cannot
|
|
64
|
+
reliably extract the `propose` calldata from the multicall3 arguments alone. We can try a best-effort
|
|
65
|
+
where we try all `propose` calls we see and validate them against on-chain data. Note that we can use these
|
|
66
|
+
same strategies if we were to obtain the calldata from another source.
|
|
67
|
+
|
|
68
|
+
#### TempBlockLog Verification
|
|
69
|
+
|
|
70
|
+
Read the stored `TempBlockLog` for the L2 block number from L1 and verify it matches our decoded header hash,
|
|
71
|
+
since the `TempBlockLog` stores the hash of the proposed block header, the payload commitment, and the attestations.
|
|
72
|
+
|
|
73
|
+
However, `TempBlockLog` is only stored temporarily and deleted after proven, so this method only works for recent
|
|
74
|
+
blocks, not for historical data syncing.
|
|
75
|
+
|
|
76
|
+
#### Archive Verification
|
|
77
|
+
|
|
78
|
+
Verify that the archive root in the decoded propose is correct with regard to the block header. This requires
|
|
79
|
+
hashing the block header we have retrieved, inserting it into the archive tree, and checking the resulting root
|
|
80
|
+
against the one we got from L1.
|
|
81
|
+
|
|
82
|
+
However, this requires that the archive keeps a reference to world-state, which is not the case in the current
|
|
83
|
+
system.
|
|
84
|
+
|
|
85
|
+
#### Emit Commitments in Rollup Contract
|
|
86
|
+
|
|
87
|
+
Modify rollup contract to emit commitments to the block header in the `L2BlockProposed` event, allowing us to easily
|
|
88
|
+
verify the calldata we obtained vs the emitted event.
|
|
89
|
+
|
|
90
|
+
However, modifying the rollup contract is out of scope for this change. But we can implement this approach in `v2`.
|
|
91
|
+
|
|
92
|
+
### Debug and Trace Transaction Fallback
|
|
93
|
+
|
|
94
|
+
Last, we use L1 node's trace/debug RPC methods to definitively identify the one successful `propose` call within the tx.
|
|
95
|
+
We can then extract the exact calldata that hit the `propose` function in the rollup contract.
|
|
96
|
+
|
|
97
|
+
This approach requires access to a debug-enabled L1 node, which may be more resource-intensive, so we only
|
|
98
|
+
use it as a fallback when the first step fails, which should be rare in practice.
|