@aztec/archiver 3.0.0-nightly.20251216 → 3.0.0-nightly.20251217
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 +60 -36
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +366 -180
- package/dest/archiver/archiver_store.d.ts +79 -23
- 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 +1624 -251
- package/dest/archiver/errors.d.ts +25 -1
- package/dest/archiver/errors.d.ts.map +1 -1
- package/dest/archiver/errors.js +37 -0
- package/dest/archiver/index.d.ts +2 -2
- package/dest/archiver/index.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.d.ts +49 -17
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +320 -83
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +29 -27
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +50 -26
- package/dest/archiver/kv_archiver_store/log_store.d.ts +4 -4
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/l1/data_retrieval.d.ts +11 -8
- package/dest/archiver/l1/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/l1/data_retrieval.js +25 -17
- package/dest/archiver/structs/published.d.ts +1 -2
- package/dest/archiver/structs/published.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.d.ts +3 -2
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +8 -15
- package/package.json +13 -13
- package/src/archiver/archiver.ts +464 -222
- package/src/archiver/archiver_store.ts +88 -22
- package/src/archiver/archiver_store_test_suite.ts +1626 -232
- package/src/archiver/errors.ts +64 -0
- package/src/archiver/index.ts +1 -1
- package/src/archiver/kv_archiver_store/block_store.ts +435 -94
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +62 -38
- package/src/archiver/kv_archiver_store/log_store.ts +4 -4
- package/src/archiver/l1/data_retrieval.ts +27 -13
- package/src/archiver/structs/published.ts +0 -1
- package/src/test/mock_l2_block_source.ts +9 -16
package/src/archiver/archiver.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { type PromiseWithResolvers, promiseWithResolvers } from '@aztec/foundati
|
|
|
17
17
|
import { RunningPromise, makeLoggingErrorHandler } from '@aztec/foundation/running-promise';
|
|
18
18
|
import { count } from '@aztec/foundation/string';
|
|
19
19
|
import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
|
|
20
|
+
import { isDefined } from '@aztec/foundation/types';
|
|
20
21
|
import type { CustomRange } from '@aztec/kv-store';
|
|
21
22
|
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
22
23
|
import {
|
|
@@ -32,13 +33,17 @@ import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
|
32
33
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
33
34
|
import {
|
|
34
35
|
type ArchiverEmitter,
|
|
36
|
+
CheckpointedL2Block,
|
|
37
|
+
CommitteeAttestation,
|
|
35
38
|
L2Block,
|
|
39
|
+
L2BlockNew,
|
|
40
|
+
type L2BlockSink,
|
|
36
41
|
type L2BlockSource,
|
|
37
42
|
L2BlockSourceEvents,
|
|
38
43
|
type L2Tips,
|
|
39
44
|
PublishedL2Block,
|
|
40
45
|
} from '@aztec/stdlib/block';
|
|
41
|
-
import
|
|
46
|
+
import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
42
47
|
import {
|
|
43
48
|
type ContractClassPublic,
|
|
44
49
|
type ContractDataSource,
|
|
@@ -78,8 +83,9 @@ import { type GetContractReturnType, type Hex, createPublicClient, fallback, htt
|
|
|
78
83
|
|
|
79
84
|
import type { ArchiverDataStore, ArchiverL1SynchPoint } from './archiver_store.js';
|
|
80
85
|
import type { ArchiverConfig } from './config.js';
|
|
81
|
-
import {
|
|
86
|
+
import { InitialCheckpointNumberNotSequentialError, NoBlobBodiesFoundError } from './errors.js';
|
|
82
87
|
import { ArchiverInstrumentation } from './instrumentation.js';
|
|
88
|
+
import type { CheckpointData } from './kv_archiver_store/block_store.js';
|
|
83
89
|
import {
|
|
84
90
|
retrieveCheckpointsFromRollup,
|
|
85
91
|
retrieveL1ToL2Message,
|
|
@@ -95,6 +101,13 @@ import { type ValidateBlockResult, validateCheckpointAttestations } from './vali
|
|
|
95
101
|
*/
|
|
96
102
|
export type ArchiveSource = L2BlockSource & L2LogsSource & ContractDataSource & L1ToL2MessageSource;
|
|
97
103
|
|
|
104
|
+
/** Request to add a block to the archiver, queued for processing by the sync loop. */
|
|
105
|
+
type AddBlockRequest = {
|
|
106
|
+
block: L2BlockNew;
|
|
107
|
+
resolve: () => void;
|
|
108
|
+
reject: (err: Error) => void;
|
|
109
|
+
};
|
|
110
|
+
|
|
98
111
|
export type ArchiverDeps = {
|
|
99
112
|
telemetry?: TelemetryClient;
|
|
100
113
|
blobSinkClient: BlobSinkClientInterface;
|
|
@@ -127,7 +140,10 @@ type RollupStatus = {
|
|
|
127
140
|
* Responsible for handling robust L1 polling so that other components do not need to
|
|
128
141
|
* concern themselves with it.
|
|
129
142
|
*/
|
|
130
|
-
export class Archiver
|
|
143
|
+
export class Archiver
|
|
144
|
+
extends (EventEmitter as new () => ArchiverEmitter)
|
|
145
|
+
implements ArchiveSource, L2BlockSink, Traceable
|
|
146
|
+
{
|
|
131
147
|
/** A loop in which we will be continually fetching new checkpoints. */
|
|
132
148
|
private runningPromise: RunningPromise;
|
|
133
149
|
|
|
@@ -141,6 +157,9 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
141
157
|
private initialSyncComplete: boolean = false;
|
|
142
158
|
private initialSyncPromise: PromiseWithResolvers<void>;
|
|
143
159
|
|
|
160
|
+
/** Queue of blocks to be added to the store, processed by the sync loop. */
|
|
161
|
+
private blockQueue: AddBlockRequest[] = [];
|
|
162
|
+
|
|
144
163
|
public readonly tracer: Tracer;
|
|
145
164
|
|
|
146
165
|
/**
|
|
@@ -301,10 +320,10 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
301
320
|
// Log initial state for the archiver
|
|
302
321
|
const { l1StartBlock } = this.l1constants;
|
|
303
322
|
const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = l1StartBlock } = await this.store.getSynchPoint();
|
|
304
|
-
const
|
|
323
|
+
const currentL2Checkpoint = await this.getSynchedCheckpointNumber();
|
|
305
324
|
this.log.info(
|
|
306
|
-
`Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo} and L2
|
|
307
|
-
{ blocksSynchedTo, messagesSynchedTo,
|
|
325
|
+
`Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo} and L2 checkpoint ${currentL2Checkpoint}`,
|
|
326
|
+
{ blocksSynchedTo, messagesSynchedTo, currentL2Checkpoint },
|
|
308
327
|
);
|
|
309
328
|
|
|
310
329
|
// Start sync loop, and return the wait for initial sync if we are asked to block until synced
|
|
@@ -318,6 +337,51 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
318
337
|
return this.runningPromise.trigger();
|
|
319
338
|
}
|
|
320
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Queues a block to be added to the archiver store and triggers processing.
|
|
342
|
+
* The block will be processed by the sync loop.
|
|
343
|
+
* Implements the L2BlockSink interface.
|
|
344
|
+
* @param block - The L2 block to add.
|
|
345
|
+
* @returns A promise that resolves when the block has been added to the store, or rejects on error.
|
|
346
|
+
*/
|
|
347
|
+
public addBlock(block: L2BlockNew): Promise<void> {
|
|
348
|
+
return new Promise<void>((resolve, reject) => {
|
|
349
|
+
this.blockQueue.push({ block, resolve, reject });
|
|
350
|
+
this.log.debug(`Queued block ${block.number} for processing`);
|
|
351
|
+
// Trigger an immediate sync, but don't wait for it - the promise resolves when the block is processed
|
|
352
|
+
this.syncImmediate().catch(err => {
|
|
353
|
+
this.log.error(`Sync immediate call failed: ${err}`);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Processes all queued blocks, adding them to the store.
|
|
360
|
+
* Called at the beginning of each sync iteration.
|
|
361
|
+
* Blocks are processed in the order they were queued.
|
|
362
|
+
*/
|
|
363
|
+
private async processQueuedBlocks(): Promise<void> {
|
|
364
|
+
if (this.blockQueue.length === 0) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Take all blocks from the queue
|
|
369
|
+
const queuedItems = this.blockQueue.splice(0, this.blockQueue.length);
|
|
370
|
+
this.log.debug(`Processing ${queuedItems.length} queued block(s)`);
|
|
371
|
+
|
|
372
|
+
// Process each block individually to properly resolve/reject each promise
|
|
373
|
+
for (const { block, resolve, reject } of queuedItems) {
|
|
374
|
+
try {
|
|
375
|
+
await this.store.addBlocks([block]);
|
|
376
|
+
this.log.debug(`Added block ${block.number} to store`);
|
|
377
|
+
resolve();
|
|
378
|
+
} catch (err: any) {
|
|
379
|
+
this.log.error(`Failed to add block ${block.number} to store: ${err.message}`);
|
|
380
|
+
reject(err);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
321
385
|
public waitForInitialSync() {
|
|
322
386
|
return this.initialSyncPromise.promise;
|
|
323
387
|
}
|
|
@@ -337,11 +401,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
337
401
|
}
|
|
338
402
|
}
|
|
339
403
|
|
|
340
|
-
|
|
341
|
-
* Fetches logs from L1 contracts and processes them.
|
|
342
|
-
*/
|
|
343
|
-
@trackSpan('Archiver.sync')
|
|
344
|
-
private async sync() {
|
|
404
|
+
private async syncFromL1() {
|
|
345
405
|
/**
|
|
346
406
|
* We keep track of three "pointers" to L1 blocks:
|
|
347
407
|
* 1. the last L1 block that published an L2 block
|
|
@@ -427,7 +487,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
427
487
|
// past it, since otherwise we'll keep downloading it and reprocessing it on every iteration until
|
|
428
488
|
// we get a valid checkpoint to advance the syncpoint.
|
|
429
489
|
if (!rollupStatus.validationResult?.valid && rollupStatus.lastL1BlockWithCheckpoint !== undefined) {
|
|
430
|
-
await this.store.
|
|
490
|
+
await this.store.setCheckpointSynchedL1BlockNumber(rollupStatus.lastL1BlockWithCheckpoint);
|
|
431
491
|
}
|
|
432
492
|
|
|
433
493
|
// And lastly we check if we are missing any checkpoints behind us due to a possible L1 reorg.
|
|
@@ -461,6 +521,17 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
461
521
|
}
|
|
462
522
|
}
|
|
463
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Fetches logs from L1 contracts and processes them.
|
|
526
|
+
*/
|
|
527
|
+
@trackSpan('Archiver.sync')
|
|
528
|
+
private async sync() {
|
|
529
|
+
// Process any queued blocks first, before doing L1 sync
|
|
530
|
+
await this.processQueuedBlocks();
|
|
531
|
+
// Now perform L1 sync
|
|
532
|
+
await this.syncFromL1();
|
|
533
|
+
}
|
|
534
|
+
|
|
464
535
|
/** Queries the rollup contract on whether a prune can be executed on the immediate next L1 block. */
|
|
465
536
|
private async canPrune(currentL1BlockNumber: bigint, currentL1Timestamp: bigint) {
|
|
466
537
|
const time = (currentL1Timestamp ?? 0n) + BigInt(this.l1constants.ethereumSlotDuration);
|
|
@@ -499,13 +570,26 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
499
570
|
|
|
500
571
|
const checkpointsToUnwind = localPendingCheckpointNumber - provenCheckpointNumber;
|
|
501
572
|
|
|
502
|
-
const
|
|
573
|
+
const checkpointPromises = Array.from({ length: checkpointsToUnwind })
|
|
574
|
+
.fill(0)
|
|
575
|
+
.map((_, i) => this.store.getCheckpointData(CheckpointNumber(i + pruneFrom)));
|
|
576
|
+
const checkpoints = await Promise.all(checkpointPromises);
|
|
577
|
+
|
|
578
|
+
const blockPromises = await Promise.all(
|
|
579
|
+
checkpoints
|
|
580
|
+
.filter(isDefined)
|
|
581
|
+
.map(cp => this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber))),
|
|
582
|
+
);
|
|
583
|
+
const newBlocks = blockPromises.filter(isDefined).flat();
|
|
584
|
+
|
|
585
|
+
// TODO(pw/mbps): Don't convert to legacy blocks here
|
|
586
|
+
const blocks: L2Block[] = (await Promise.all(newBlocks.map(x => this.getBlock(x.number)))).filter(isDefined);
|
|
503
587
|
|
|
504
588
|
// Emit an event for listening services to react to the chain prune
|
|
505
589
|
this.emit(L2BlockSourceEvents.L2PruneDetected, {
|
|
506
590
|
type: L2BlockSourceEvents.L2PruneDetected,
|
|
507
591
|
epochNumber: pruneFromEpochNumber,
|
|
508
|
-
blocks
|
|
592
|
+
blocks,
|
|
509
593
|
});
|
|
510
594
|
|
|
511
595
|
this.log.debug(
|
|
@@ -733,17 +817,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
733
817
|
}
|
|
734
818
|
}
|
|
735
819
|
|
|
736
|
-
const localCheckpointForDestinationProvenCheckpointNumber =
|
|
820
|
+
const localCheckpointForDestinationProvenCheckpointNumber =
|
|
821
|
+
await this.store.getCheckpointData(provenCheckpointNumber);
|
|
737
822
|
|
|
738
823
|
// Sanity check. I've hit what seems to be a state where the proven checkpoint is set to a value greater than the latest
|
|
739
824
|
// synched checkpoint when requesting L2Tips from the archiver. This is the only place where the proven checkpoint is set.
|
|
740
825
|
const synched = await this.getSynchedCheckpointNumber();
|
|
741
826
|
if (
|
|
742
827
|
localCheckpointForDestinationProvenCheckpointNumber &&
|
|
743
|
-
synched < localCheckpointForDestinationProvenCheckpointNumber.
|
|
828
|
+
synched < localCheckpointForDestinationProvenCheckpointNumber.checkpointNumber
|
|
744
829
|
) {
|
|
745
830
|
this.log.error(
|
|
746
|
-
`Hit local checkpoint greater than last synched checkpoint: ${localCheckpointForDestinationProvenCheckpointNumber.
|
|
831
|
+
`Hit local checkpoint greater than last synched checkpoint: ${localCheckpointForDestinationProvenCheckpointNumber.checkpointNumber} > ${synched}`,
|
|
747
832
|
);
|
|
748
833
|
}
|
|
749
834
|
|
|
@@ -753,7 +838,6 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
753
838
|
}`,
|
|
754
839
|
);
|
|
755
840
|
|
|
756
|
-
const lastProvenBlockNumber = await this.getLastBlockNumberInCheckpoint(provenCheckpointNumber);
|
|
757
841
|
if (
|
|
758
842
|
localCheckpointForDestinationProvenCheckpointNumber &&
|
|
759
843
|
provenArchive === localCheckpointForDestinationProvenCheckpointNumber.archive.root.toString()
|
|
@@ -766,25 +850,29 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
766
850
|
});
|
|
767
851
|
const provenSlotNumber = localCheckpointForDestinationProvenCheckpointNumber.header.slotNumber;
|
|
768
852
|
const provenEpochNumber: EpochNumber = getEpochAtSlot(provenSlotNumber, this.l1constants);
|
|
853
|
+
const lastBlockNumberInCheckpoint =
|
|
854
|
+
localCheckpointForDestinationProvenCheckpointNumber.startBlock +
|
|
855
|
+
localCheckpointForDestinationProvenCheckpointNumber.numBlocks -
|
|
856
|
+
1;
|
|
769
857
|
|
|
770
858
|
this.emit(L2BlockSourceEvents.L2BlockProven, {
|
|
771
859
|
type: L2BlockSourceEvents.L2BlockProven,
|
|
772
|
-
blockNumber:
|
|
860
|
+
blockNumber: BlockNumber(lastBlockNumberInCheckpoint),
|
|
773
861
|
slotNumber: provenSlotNumber,
|
|
774
862
|
epochNumber: provenEpochNumber,
|
|
775
863
|
});
|
|
864
|
+
this.instrumentation.updateLastProvenBlock(lastBlockNumberInCheckpoint);
|
|
776
865
|
} else {
|
|
777
866
|
this.log.trace(`Proven checkpoint ${provenCheckpointNumber} already stored.`);
|
|
778
867
|
}
|
|
779
868
|
}
|
|
780
|
-
this.instrumentation.updateLastProvenBlock(lastProvenBlockNumber);
|
|
781
869
|
};
|
|
782
870
|
|
|
783
871
|
// This is an edge case that we only hit if there are no proposed checkpoints.
|
|
784
872
|
// If we have 0 checkpoints locally and there are no checkpoints onchain there is nothing to do.
|
|
785
873
|
const noCheckpoints = localPendingCheckpointNumber === 0 && pendingCheckpointNumber === 0;
|
|
786
874
|
if (noCheckpoints) {
|
|
787
|
-
await this.store.
|
|
875
|
+
await this.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
|
|
788
876
|
this.log.debug(
|
|
789
877
|
`No checkpoints to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}, no checkpoints on chain`,
|
|
790
878
|
);
|
|
@@ -796,7 +884,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
796
884
|
// Related to the L2 reorgs of the pending chain. We are only interested in actually addressing a reorg if there
|
|
797
885
|
// are any state that could be impacted by it. If we have no checkpoints, there is no impact.
|
|
798
886
|
if (localPendingCheckpointNumber > 0) {
|
|
799
|
-
const localPendingCheckpoint = await this.
|
|
887
|
+
const localPendingCheckpoint = await this.store.getCheckpointData(localPendingCheckpointNumber);
|
|
800
888
|
if (localPendingCheckpoint === undefined) {
|
|
801
889
|
throw new Error(`Missing checkpoint ${localPendingCheckpointNumber}`);
|
|
802
890
|
}
|
|
@@ -829,14 +917,14 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
829
917
|
|
|
830
918
|
let tipAfterUnwind = localPendingCheckpointNumber;
|
|
831
919
|
while (true) {
|
|
832
|
-
const candidateCheckpoint = await this.
|
|
920
|
+
const candidateCheckpoint = await this.store.getCheckpointData(tipAfterUnwind);
|
|
833
921
|
if (candidateCheckpoint === undefined) {
|
|
834
922
|
break;
|
|
835
923
|
}
|
|
836
924
|
|
|
837
|
-
const archiveAtContract = await this.rollup.archiveAt(candidateCheckpoint.
|
|
925
|
+
const archiveAtContract = await this.rollup.archiveAt(candidateCheckpoint.checkpointNumber);
|
|
838
926
|
this.log.trace(
|
|
839
|
-
`Checking local checkpoint ${candidateCheckpoint.
|
|
927
|
+
`Checking local checkpoint ${candidateCheckpoint.checkpointNumber} with archive ${candidateCheckpoint.archive.root}`,
|
|
840
928
|
{
|
|
841
929
|
archiveAtContract,
|
|
842
930
|
archiveLocal: candidateCheckpoint.archive.root.toString(),
|
|
@@ -882,6 +970,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
882
970
|
this.l1Addresses,
|
|
883
971
|
this.instrumentation,
|
|
884
972
|
this.log,
|
|
973
|
+
!this.initialSyncComplete, // isHistoricalSync
|
|
885
974
|
);
|
|
886
975
|
|
|
887
976
|
if (retrievedCheckpoints.length === 0) {
|
|
@@ -981,19 +1070,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
981
1070
|
validCheckpoints.flatMap(c => c.checkpoint.blocks),
|
|
982
1071
|
);
|
|
983
1072
|
} catch (err) {
|
|
984
|
-
if (err instanceof
|
|
985
|
-
const {
|
|
986
|
-
const
|
|
987
|
-
? await this.store.
|
|
1073
|
+
if (err instanceof InitialCheckpointNumberNotSequentialError) {
|
|
1074
|
+
const { previousCheckpointNumber, newCheckpointNumber } = err;
|
|
1075
|
+
const previousCheckpoint = previousCheckpointNumber
|
|
1076
|
+
? await this.store.getCheckpointData(CheckpointNumber(previousCheckpointNumber))
|
|
988
1077
|
: undefined;
|
|
989
|
-
const updatedL1SyncPoint =
|
|
1078
|
+
const updatedL1SyncPoint = previousCheckpoint?.l1.blockNumber ?? this.l1constants.l1StartBlock;
|
|
990
1079
|
await this.store.setBlockSynchedL1BlockNumber(updatedL1SyncPoint);
|
|
991
1080
|
this.log.warn(
|
|
992
|
-
`Attempting to insert
|
|
1081
|
+
`Attempting to insert checkpoint ${newCheckpointNumber} with previous block ${previousCheckpointNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`,
|
|
993
1082
|
{
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
newBlockNumber,
|
|
1083
|
+
previousCheckpointNumber,
|
|
1084
|
+
newCheckpointNumber,
|
|
997
1085
|
updatedL1SyncPoint,
|
|
998
1086
|
},
|
|
999
1087
|
);
|
|
@@ -1038,13 +1126,16 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1038
1126
|
// We suspect an L1 reorg that added checkpoints *behind* us. If that is the case, it must have happened between
|
|
1039
1127
|
// the last checkpoint we saw and the current one, so we reset the last synched L1 block number. In the edge case
|
|
1040
1128
|
// we don't have one, we go back 2 L1 epochs, which is the deepest possible reorg (assuming Casper is working).
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1129
|
+
let latestLocalCheckpointArchive: string | undefined = undefined;
|
|
1130
|
+
let targetL1BlockNumber = maxBigint(currentL1BlockNumber - 64n, 0n);
|
|
1131
|
+
if (lastRetrievedCheckpoint) {
|
|
1132
|
+
latestLocalCheckpointArchive = lastRetrievedCheckpoint.checkpoint.archive.root.toString();
|
|
1133
|
+
targetL1BlockNumber = lastRetrievedCheckpoint.l1.blockNumber;
|
|
1134
|
+
} else if (latestLocalCheckpointNumber > 0) {
|
|
1135
|
+
const checkpoint = await this.store.getRangeOfCheckpoints(latestLocalCheckpointNumber, 1).then(([c]) => c);
|
|
1136
|
+
latestLocalCheckpointArchive = checkpoint.archive.root.toString();
|
|
1137
|
+
targetL1BlockNumber = checkpoint.l1.blockNumber;
|
|
1138
|
+
}
|
|
1048
1139
|
this.log.warn(
|
|
1049
1140
|
`Failed to reach checkpoint ${pendingCheckpointNumber} at ${currentL1BlockNumber} (latest is ${latestLocalCheckpointNumber}). ` +
|
|
1050
1141
|
`Rolling back last synched L1 block number to ${targetL1BlockNumber}.`,
|
|
@@ -1130,15 +1221,22 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1130
1221
|
const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
|
|
1131
1222
|
const blocks: L2Block[] = [];
|
|
1132
1223
|
|
|
1133
|
-
// Walk the list of
|
|
1134
|
-
// We'll typically ask for
|
|
1135
|
-
let
|
|
1136
|
-
const slot = (b:
|
|
1137
|
-
while (
|
|
1138
|
-
if (slot(
|
|
1139
|
-
blocks
|
|
1224
|
+
// Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
|
|
1225
|
+
// We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
|
|
1226
|
+
let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
|
|
1227
|
+
const slot = (b: CheckpointData) => b.header.slotNumber;
|
|
1228
|
+
while (checkpoint && slot(checkpoint) >= start) {
|
|
1229
|
+
if (slot(checkpoint) <= end) {
|
|
1230
|
+
// push the blocks on backwards
|
|
1231
|
+
const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
|
|
1232
|
+
for (let i = endBlock; i >= checkpoint.startBlock; i--) {
|
|
1233
|
+
const block = await this.getBlock(BlockNumber(i));
|
|
1234
|
+
if (block) {
|
|
1235
|
+
blocks.push(block);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1140
1238
|
}
|
|
1141
|
-
|
|
1239
|
+
checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
|
|
1142
1240
|
}
|
|
1143
1241
|
|
|
1144
1242
|
return blocks.reverse();
|
|
@@ -1148,17 +1246,22 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1148
1246
|
const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
|
|
1149
1247
|
const blocks: BlockHeader[] = [];
|
|
1150
1248
|
|
|
1151
|
-
// Walk the list of
|
|
1152
|
-
// We'll typically ask for
|
|
1153
|
-
let
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1249
|
+
// Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
|
|
1250
|
+
// We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
|
|
1251
|
+
let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
|
|
1252
|
+
const slot = (b: CheckpointData) => b.header.slotNumber;
|
|
1253
|
+
while (checkpoint && slot(checkpoint) >= start) {
|
|
1254
|
+
if (slot(checkpoint) <= end) {
|
|
1255
|
+
// push the blocks on backwards
|
|
1256
|
+
const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
|
|
1257
|
+
for (let i = endBlock; i >= checkpoint.startBlock; i--) {
|
|
1258
|
+
const block = await this.getBlockHeader(BlockNumber(i));
|
|
1259
|
+
if (block) {
|
|
1260
|
+
blocks.push(block);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1159
1263
|
}
|
|
1160
|
-
|
|
1161
|
-
header = await this.getBlockHeader(number);
|
|
1264
|
+
checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
|
|
1162
1265
|
}
|
|
1163
1266
|
return blocks.reverse();
|
|
1164
1267
|
}
|
|
@@ -1196,37 +1299,6 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1196
1299
|
return this.initialSyncComplete;
|
|
1197
1300
|
}
|
|
1198
1301
|
|
|
1199
|
-
public async getPublishedCheckpoints(
|
|
1200
|
-
from: CheckpointNumber,
|
|
1201
|
-
limit: number,
|
|
1202
|
-
proven?: boolean,
|
|
1203
|
-
): Promise<PublishedCheckpoint[]> {
|
|
1204
|
-
// TODO: Implement this properly. This only works when we have one block per checkpoint.
|
|
1205
|
-
const blocks = await this.getPublishedBlocks(BlockNumber(from), limit, proven);
|
|
1206
|
-
return blocks.map(b => b.toPublishedCheckpoint());
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
public async getCheckpointByArchive(archive: Fr): Promise<Checkpoint | undefined> {
|
|
1210
|
-
// TODO: Implement this properly. This only works when we have one block per checkpoint.
|
|
1211
|
-
return (await this.getPublishedBlockByArchive(archive))?.block.toCheckpoint();
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
public async getCheckpoints(from: CheckpointNumber, limit: number, proven?: boolean): Promise<Checkpoint[]> {
|
|
1215
|
-
const published = await this.getPublishedCheckpoints(from, limit, proven);
|
|
1216
|
-
return published.map(p => p.checkpoint);
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
public async getCheckpoint(number: CheckpointNumber): Promise<Checkpoint | undefined> {
|
|
1220
|
-
if (number < 0) {
|
|
1221
|
-
number = await this.getSynchedCheckpointNumber();
|
|
1222
|
-
}
|
|
1223
|
-
if (number === 0) {
|
|
1224
|
-
return undefined;
|
|
1225
|
-
}
|
|
1226
|
-
const published = await this.getPublishedCheckpoints(number, 1);
|
|
1227
|
-
return published[0]?.checkpoint;
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
1302
|
public async getCheckpointHeader(number: CheckpointNumber | 'latest'): Promise<CheckpointHeader | undefined> {
|
|
1231
1303
|
if (number === 'latest') {
|
|
1232
1304
|
number = await this.getSynchedCheckpointNumber();
|
|
@@ -1234,88 +1306,46 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1234
1306
|
if (number === 0) {
|
|
1235
1307
|
return undefined;
|
|
1236
1308
|
}
|
|
1237
|
-
const checkpoint = await this.
|
|
1238
|
-
|
|
1309
|
+
const checkpoint = await this.store.getCheckpointData(number);
|
|
1310
|
+
if (!checkpoint) {
|
|
1311
|
+
return undefined;
|
|
1312
|
+
}
|
|
1313
|
+
return checkpoint.header;
|
|
1239
1314
|
}
|
|
1240
1315
|
|
|
1241
1316
|
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
1242
1317
|
return this.getSynchedCheckpointNumber();
|
|
1243
1318
|
}
|
|
1244
1319
|
|
|
1245
|
-
public
|
|
1246
|
-
|
|
1247
|
-
// Checkpoint number will no longer be the same as the block number once we support multiple blocks per checkpoint.
|
|
1248
|
-
return CheckpointNumber(await this.store.getSynchedL2BlockNumber());
|
|
1320
|
+
public getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
1321
|
+
return this.store.getSynchedCheckpointNumber();
|
|
1249
1322
|
}
|
|
1250
1323
|
|
|
1251
|
-
public
|
|
1252
|
-
|
|
1253
|
-
// Proven checkpoint number will no longer be the same as the proven block number once we support multiple blocks per checkpoint.
|
|
1254
|
-
return CheckpointNumber(await this.store.getProvenL2BlockNumber());
|
|
1324
|
+
public getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
1325
|
+
return this.store.getProvenCheckpointNumber();
|
|
1255
1326
|
}
|
|
1256
1327
|
|
|
1257
1328
|
public setProvenCheckpointNumber(checkpointNumber: CheckpointNumber): Promise<void> {
|
|
1258
|
-
|
|
1259
|
-
// Proven checkpoint number will no longer be the same as the proven block number once we support multiple blocks per checkpoint.
|
|
1260
|
-
return this.store.setProvenL2BlockNumber(BlockNumber.fromCheckpointNumber(checkpointNumber));
|
|
1329
|
+
return this.store.setProvenCheckpointNumber(checkpointNumber);
|
|
1261
1330
|
}
|
|
1262
1331
|
|
|
1263
1332
|
public unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number): Promise<boolean> {
|
|
1264
|
-
|
|
1265
|
-
// This only works when we have one block per checkpoint.
|
|
1266
|
-
return this.store.unwindBlocks(BlockNumber.fromCheckpointNumber(from), checkpointsToUnwind);
|
|
1333
|
+
return this.store.unwindCheckpoints(from, checkpointsToUnwind);
|
|
1267
1334
|
}
|
|
1268
1335
|
|
|
1269
|
-
public getLastBlockNumberInCheckpoint(checkpointNumber: CheckpointNumber): Promise<BlockNumber> {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1336
|
+
public async getLastBlockNumberInCheckpoint(checkpointNumber: CheckpointNumber): Promise<BlockNumber | undefined> {
|
|
1337
|
+
const checkpointData = await this.store.getCheckpointData(checkpointNumber);
|
|
1338
|
+
if (!checkpointData) {
|
|
1339
|
+
return undefined;
|
|
1340
|
+
}
|
|
1341
|
+
return BlockNumber(checkpointData.startBlock + checkpointData.numBlocks - 1);
|
|
1273
1342
|
}
|
|
1274
1343
|
|
|
1275
1344
|
public addCheckpoints(
|
|
1276
1345
|
checkpoints: PublishedCheckpoint[],
|
|
1277
1346
|
pendingChainValidationStatus?: ValidateBlockResult,
|
|
1278
1347
|
): Promise<boolean> {
|
|
1279
|
-
|
|
1280
|
-
// This only works when we have one block per checkpoint.
|
|
1281
|
-
return this.store.addBlocks(
|
|
1282
|
-
checkpoints.map(p => PublishedL2Block.fromPublishedCheckpoint(p)),
|
|
1283
|
-
pendingChainValidationStatus,
|
|
1284
|
-
);
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
public async getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
|
|
1288
|
-
// TODO: Create store and apis for checkpoints.
|
|
1289
|
-
// This only works when we have one block per checkpoint.
|
|
1290
|
-
const blocks = await this.getBlocksForEpoch(epochNumber);
|
|
1291
|
-
return blocks.map(b => b.toCheckpoint());
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
/**
|
|
1295
|
-
* Gets up to `limit` amount of L2 blocks starting from `from`.
|
|
1296
|
-
* @param from - Number of the first block to return (inclusive).
|
|
1297
|
-
* @param limit - The number of blocks to return.
|
|
1298
|
-
* @param proven - If true, only return blocks that have been proven.
|
|
1299
|
-
* @returns The requested L2 blocks.
|
|
1300
|
-
*/
|
|
1301
|
-
public getBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<L2Block[]> {
|
|
1302
|
-
return this.getPublishedBlocks(from, limit, proven).then(blocks => blocks.map(b => b.block));
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
/** Equivalent to getBlocks but includes publish data. */
|
|
1306
|
-
public async getPublishedBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<PublishedL2Block[]> {
|
|
1307
|
-
const limitWithProven = proven
|
|
1308
|
-
? Math.min(limit, Math.max((await this.store.getProvenL2BlockNumber()) - from + 1, 0))
|
|
1309
|
-
: limit;
|
|
1310
|
-
return limitWithProven === 0 ? [] : await this.store.getPublishedBlocks(from, limitWithProven);
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
public getPublishedBlockByHash(blockHash: Fr): Promise<PublishedL2Block | undefined> {
|
|
1314
|
-
return this.store.getPublishedBlockByHash(blockHash);
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
public getPublishedBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
|
|
1318
|
-
return this.store.getPublishedBlockByArchive(archive);
|
|
1348
|
+
return this.store.addCheckpoints(checkpoints, pendingChainValidationStatus);
|
|
1319
1349
|
}
|
|
1320
1350
|
|
|
1321
1351
|
public getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
|
|
@@ -1331,7 +1361,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1331
1361
|
* @param number - The block number to return.
|
|
1332
1362
|
* @returns The requested L2 block.
|
|
1333
1363
|
*/
|
|
1334
|
-
public async
|
|
1364
|
+
public async getL2BlockNew(number: BlockNumber): Promise<L2BlockNew | undefined> {
|
|
1335
1365
|
// If the number provided is -ve, then return the latest block.
|
|
1336
1366
|
if (number < 0) {
|
|
1337
1367
|
number = await this.store.getSynchedL2BlockNumber();
|
|
@@ -1339,8 +1369,8 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1339
1369
|
if (number === 0) {
|
|
1340
1370
|
return undefined;
|
|
1341
1371
|
}
|
|
1342
|
-
const publishedBlock = await this.store.
|
|
1343
|
-
return publishedBlock
|
|
1372
|
+
const publishedBlock = await this.store.store.getBlock(number);
|
|
1373
|
+
return publishedBlock;
|
|
1344
1374
|
}
|
|
1345
1375
|
|
|
1346
1376
|
public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
|
|
@@ -1354,6 +1384,21 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1354
1384
|
return headers.length === 0 ? undefined : headers[0];
|
|
1355
1385
|
}
|
|
1356
1386
|
|
|
1387
|
+
getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
|
|
1388
|
+
return this.store.getCheckpointedBlock(number);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1392
|
+
return this.store.getCheckpointedBlockByHash(blockHash);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
getProvenBlockNumber(): Promise<BlockNumber> {
|
|
1396
|
+
return this.store.getProvenBlockNumber();
|
|
1397
|
+
}
|
|
1398
|
+
getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1399
|
+
return this.store.getCheckpointedBlockByArchive(archive);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1357
1402
|
public getTxEffect(txHash: TxHash) {
|
|
1358
1403
|
return this.store.getTxEffect(txHash);
|
|
1359
1404
|
}
|
|
@@ -1392,19 +1437,11 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1392
1437
|
|
|
1393
1438
|
/**
|
|
1394
1439
|
* Gets the number of the latest L2 block processed by the block source implementation.
|
|
1440
|
+
* This includes both checkpointed and uncheckpointed blocks.
|
|
1395
1441
|
* @returns The number of the latest L2 block processed by the block source implementation.
|
|
1396
1442
|
*/
|
|
1397
1443
|
public getBlockNumber(): Promise<BlockNumber> {
|
|
1398
|
-
return this.store.
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
public getProvenBlockNumber(): Promise<BlockNumber> {
|
|
1402
|
-
return this.store.getProvenL2BlockNumber();
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
/** Forcefully updates the last proven block number. Use for testing. */
|
|
1406
|
-
public setProvenBlockNumber(blockNumber: BlockNumber): Promise<void> {
|
|
1407
|
-
return this.store.setProvenL2BlockNumber(blockNumber);
|
|
1444
|
+
return this.store.getLatestBlockNumber();
|
|
1408
1445
|
}
|
|
1409
1446
|
|
|
1410
1447
|
public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
|
|
@@ -1514,24 +1551,24 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1514
1551
|
}
|
|
1515
1552
|
|
|
1516
1553
|
public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
|
|
1554
|
+
// TODO(pw/mbps): This still assumes 1 block per checkpoint
|
|
1517
1555
|
const currentBlocks = await this.getL2Tips();
|
|
1518
1556
|
const currentL2Block = currentBlocks.latest.number;
|
|
1519
1557
|
const currentProvenBlock = currentBlocks.proven.number;
|
|
1520
|
-
// const currentFinalizedBlock = currentBlocks.finalized.number;
|
|
1521
1558
|
|
|
1522
1559
|
if (targetL2BlockNumber >= currentL2Block) {
|
|
1523
1560
|
throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
|
|
1524
1561
|
}
|
|
1525
1562
|
const blocksToUnwind = currentL2Block - targetL2BlockNumber;
|
|
1526
|
-
const targetL2Block = await this.store.
|
|
1563
|
+
const targetL2Block = await this.store.getCheckpointedBlock(targetL2BlockNumber);
|
|
1527
1564
|
if (!targetL2Block) {
|
|
1528
1565
|
throw new Error(`Target L2 block ${targetL2BlockNumber} not found`);
|
|
1529
1566
|
}
|
|
1530
1567
|
const targetL1BlockNumber = targetL2Block.l1.blockNumber;
|
|
1531
1568
|
const targetCheckpointNumber = CheckpointNumber.fromBlockNumber(targetL2BlockNumber);
|
|
1532
1569
|
const targetL1BlockHash = await this.getL1BlockHash(targetL1BlockNumber);
|
|
1533
|
-
this.log.info(`Unwinding ${blocksToUnwind}
|
|
1534
|
-
await this.store.
|
|
1570
|
+
this.log.info(`Unwinding ${blocksToUnwind} checkpoints from L2 block ${currentL2Block}`);
|
|
1571
|
+
await this.store.unwindCheckpoints(CheckpointNumber(currentL2Block), blocksToUnwind);
|
|
1535
1572
|
this.log.info(`Unwinding L1 to L2 messages to checkpoint ${targetCheckpointNumber}`);
|
|
1536
1573
|
await this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
1537
1574
|
this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`);
|
|
@@ -1539,7 +1576,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1539
1576
|
await this.store.setMessageSynchedL1Block({ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash });
|
|
1540
1577
|
if (targetL2BlockNumber < currentProvenBlock) {
|
|
1541
1578
|
this.log.info(`Clearing proven L2 block number`);
|
|
1542
|
-
await this.store.
|
|
1579
|
+
await this.store.setProvenCheckpointNumber(CheckpointNumber.ZERO);
|
|
1543
1580
|
}
|
|
1544
1581
|
// TODO(palla/reorg): Set the finalized block when we add support for it.
|
|
1545
1582
|
// if (targetL2BlockNumber < currentFinalizedBlock) {
|
|
@@ -1547,6 +1584,150 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1547
1584
|
// await this.store.setFinalizedL2BlockNumber(0);
|
|
1548
1585
|
// }
|
|
1549
1586
|
}
|
|
1587
|
+
|
|
1588
|
+
public async getPublishedCheckpoints(
|
|
1589
|
+
checkpointNumber: CheckpointNumber,
|
|
1590
|
+
limit: number,
|
|
1591
|
+
): Promise<PublishedCheckpoint[]> {
|
|
1592
|
+
const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
|
|
1593
|
+
const blocks = (
|
|
1594
|
+
await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
|
|
1595
|
+
).filter(isDefined);
|
|
1596
|
+
|
|
1597
|
+
const fullCheckpoints: PublishedCheckpoint[] = [];
|
|
1598
|
+
for (let i = 0; i < checkpoints.length; i++) {
|
|
1599
|
+
const blocksForCheckpoint = blocks[i];
|
|
1600
|
+
const checkpoint = checkpoints[i];
|
|
1601
|
+
const fullCheckpoint = new Checkpoint(
|
|
1602
|
+
checkpoint.archive,
|
|
1603
|
+
checkpoint.header,
|
|
1604
|
+
blocksForCheckpoint,
|
|
1605
|
+
checkpoint.checkpointNumber,
|
|
1606
|
+
);
|
|
1607
|
+
const publishedCheckpoint = new PublishedCheckpoint(
|
|
1608
|
+
fullCheckpoint,
|
|
1609
|
+
checkpoint.l1,
|
|
1610
|
+
checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
|
|
1611
|
+
);
|
|
1612
|
+
fullCheckpoints.push(publishedCheckpoint);
|
|
1613
|
+
}
|
|
1614
|
+
return fullCheckpoints;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
public async getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
|
|
1618
|
+
const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
|
|
1619
|
+
const checkpoints: Checkpoint[] = [];
|
|
1620
|
+
|
|
1621
|
+
// Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
|
|
1622
|
+
// We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
|
|
1623
|
+
let checkpointData = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
|
|
1624
|
+
const slot = (b: CheckpointData) => b.header.slotNumber;
|
|
1625
|
+
while (checkpointData && slot(checkpointData) >= start) {
|
|
1626
|
+
if (slot(checkpointData) <= end) {
|
|
1627
|
+
// push the checkpoints on backwards
|
|
1628
|
+
const [checkpoint] = await this.getPublishedCheckpoints(checkpointData.checkpointNumber, 1);
|
|
1629
|
+
checkpoints.push(checkpoint.checkpoint);
|
|
1630
|
+
}
|
|
1631
|
+
checkpointData = await this.store.getCheckpointData(CheckpointNumber(checkpointData.checkpointNumber - 1));
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
return checkpoints.reverse();
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
/* Legacy APIs */
|
|
1638
|
+
|
|
1639
|
+
public async getPublishedBlockByHash(blockHash: Fr): Promise<PublishedL2Block | undefined> {
|
|
1640
|
+
const checkpointedBlock = await this.store.getCheckpointedBlockByHash(blockHash);
|
|
1641
|
+
return this.buildOldBlockFromCheckpointedBlock(checkpointedBlock);
|
|
1642
|
+
}
|
|
1643
|
+
public async getPublishedBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
|
|
1644
|
+
const checkpointedBlock = await this.store.getCheckpointedBlockByArchive(archive);
|
|
1645
|
+
return this.buildOldBlockFromCheckpointedBlock(checkpointedBlock);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Gets up to `limit` amount of L2 blocks starting from `from`.
|
|
1650
|
+
* @param from - Number of the first block to return (inclusive).
|
|
1651
|
+
* @param limit - The number of blocks to return.
|
|
1652
|
+
* @param proven - If true, only return blocks that have been proven.
|
|
1653
|
+
* @returns The requested L2 blocks.
|
|
1654
|
+
*/
|
|
1655
|
+
public async getBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<L2Block[]> {
|
|
1656
|
+
const publishedBlocks = await this.getPublishedBlocks(from, limit, proven);
|
|
1657
|
+
return publishedBlocks.map(x => x.block);
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
public async getPublishedBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<PublishedL2Block[]> {
|
|
1661
|
+
const checkpoints = await this.store.getRangeOfCheckpoints(CheckpointNumber(from), limit);
|
|
1662
|
+
const provenCheckpointNumber = await this.getProvenCheckpointNumber();
|
|
1663
|
+
const blocks = (
|
|
1664
|
+
await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
|
|
1665
|
+
).filter(isDefined);
|
|
1666
|
+
|
|
1667
|
+
const olbBlocks: PublishedL2Block[] = [];
|
|
1668
|
+
for (let i = 0; i < checkpoints.length; i++) {
|
|
1669
|
+
const blockForCheckpoint = blocks[i][0];
|
|
1670
|
+
const checkpoint = checkpoints[i];
|
|
1671
|
+
if (checkpoint.checkpointNumber > provenCheckpointNumber && proven === true) {
|
|
1672
|
+
// this checkpointisn't proven and we only want proven
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1675
|
+
const oldCheckpoint = new Checkpoint(
|
|
1676
|
+
blockForCheckpoint.archive,
|
|
1677
|
+
checkpoint.header,
|
|
1678
|
+
[blockForCheckpoint],
|
|
1679
|
+
checkpoint.checkpointNumber,
|
|
1680
|
+
);
|
|
1681
|
+
const oldBlock = L2Block.fromCheckpoint(oldCheckpoint);
|
|
1682
|
+
const publishedBlock = new PublishedL2Block(
|
|
1683
|
+
oldBlock,
|
|
1684
|
+
checkpoint.l1,
|
|
1685
|
+
checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
|
|
1686
|
+
);
|
|
1687
|
+
olbBlocks.push(publishedBlock);
|
|
1688
|
+
}
|
|
1689
|
+
return olbBlocks;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
private async buildOldBlockFromCheckpointedBlock(
|
|
1693
|
+
checkpointedBlock: CheckpointedL2Block | undefined,
|
|
1694
|
+
): Promise<PublishedL2Block | undefined> {
|
|
1695
|
+
if (!checkpointedBlock) {
|
|
1696
|
+
return undefined;
|
|
1697
|
+
}
|
|
1698
|
+
const checkpoint = await this.store.getCheckpointData(checkpointedBlock.checkpointNumber);
|
|
1699
|
+
if (!checkpoint) {
|
|
1700
|
+
return checkpoint;
|
|
1701
|
+
}
|
|
1702
|
+
const fullCheckpoint = new Checkpoint(
|
|
1703
|
+
checkpointedBlock?.block.archive,
|
|
1704
|
+
checkpoint?.header,
|
|
1705
|
+
[checkpointedBlock.block],
|
|
1706
|
+
checkpoint.checkpointNumber,
|
|
1707
|
+
);
|
|
1708
|
+
const oldBlock = L2Block.fromCheckpoint(fullCheckpoint);
|
|
1709
|
+
const published = new PublishedL2Block(
|
|
1710
|
+
oldBlock,
|
|
1711
|
+
checkpoint.l1,
|
|
1712
|
+
checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
|
|
1713
|
+
);
|
|
1714
|
+
return published;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
public async getBlock(number: BlockNumber): Promise<L2Block | undefined> {
|
|
1718
|
+
// If the number provided is -ve, then return the latest block.
|
|
1719
|
+
if (number < 0) {
|
|
1720
|
+
number = await this.store.getSynchedL2BlockNumber();
|
|
1721
|
+
}
|
|
1722
|
+
if (number === 0) {
|
|
1723
|
+
return undefined;
|
|
1724
|
+
}
|
|
1725
|
+
const publishedBlocks = await this.getPublishedBlocks(number, 1);
|
|
1726
|
+
if (publishedBlocks.length === 0) {
|
|
1727
|
+
return undefined;
|
|
1728
|
+
}
|
|
1729
|
+
return publishedBlocks[0].block;
|
|
1730
|
+
}
|
|
1550
1731
|
}
|
|
1551
1732
|
|
|
1552
1733
|
enum Operation {
|
|
@@ -1577,11 +1758,13 @@ export class ArchiverStoreHelper
|
|
|
1577
1758
|
| 'close'
|
|
1578
1759
|
| 'transactionAsync'
|
|
1579
1760
|
| 'addBlocks'
|
|
1761
|
+
| 'getBlock'
|
|
1762
|
+
| 'getBlocks'
|
|
1580
1763
|
>
|
|
1581
1764
|
{
|
|
1582
1765
|
#log = createLogger('archiver:block-helper');
|
|
1583
1766
|
|
|
1584
|
-
constructor(
|
|
1767
|
+
constructor(public readonly store: ArchiverDataStore) {}
|
|
1585
1768
|
|
|
1586
1769
|
/**
|
|
1587
1770
|
* Extracts and stores contract classes out of ContractClassPublished events emitted by the class registry contract.
|
|
@@ -1719,7 +1902,23 @@ export class ArchiverStoreHelper
|
|
|
1719
1902
|
return true;
|
|
1720
1903
|
}
|
|
1721
1904
|
|
|
1722
|
-
|
|
1905
|
+
private async addBlockDataToDB(block: L2BlockNew) {
|
|
1906
|
+
const contractClassLogs = block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
|
|
1907
|
+
// ContractInstancePublished event logs are broadcast in privateLogs.
|
|
1908
|
+
const privateLogs = block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
|
|
1909
|
+
const publicLogs = block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
|
|
1910
|
+
|
|
1911
|
+
return (
|
|
1912
|
+
await Promise.all([
|
|
1913
|
+
this.#updatePublishedContractClasses(contractClassLogs, block.number, Operation.Store),
|
|
1914
|
+
this.#updateDeployedContractInstances(privateLogs, block.number, Operation.Store),
|
|
1915
|
+
this.#updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, Operation.Store),
|
|
1916
|
+
this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.number),
|
|
1917
|
+
])
|
|
1918
|
+
).every(Boolean);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?: ValidateBlockResult): Promise<boolean> {
|
|
1723
1922
|
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1724
1923
|
// or if the previous block is not in the store.
|
|
1725
1924
|
return this.store.transactionAsync(async () => {
|
|
@@ -1729,25 +1928,10 @@ export class ArchiverStoreHelper
|
|
|
1729
1928
|
// Update the pending chain validation status if provided
|
|
1730
1929
|
pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
|
|
1731
1930
|
// Add any logs emitted during the retrieved blocks
|
|
1732
|
-
this.store.addLogs(blocks
|
|
1931
|
+
this.store.addLogs(blocks),
|
|
1733
1932
|
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
1734
|
-
...blocks.map(
|
|
1735
|
-
|
|
1736
|
-
// ContractInstancePublished event logs are broadcast in privateLogs.
|
|
1737
|
-
const privateLogs = block.block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
|
|
1738
|
-
const publicLogs = block.block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
|
|
1739
|
-
return (
|
|
1740
|
-
await Promise.all([
|
|
1741
|
-
this.#updatePublishedContractClasses(contractClassLogs, block.block.number, Operation.Store),
|
|
1742
|
-
this.#updateDeployedContractInstances(privateLogs, block.block.number, Operation.Store),
|
|
1743
|
-
this.#updateUpdatedContractInstances(
|
|
1744
|
-
publicLogs,
|
|
1745
|
-
block.block.header.globalVariables.timestamp,
|
|
1746
|
-
Operation.Store,
|
|
1747
|
-
),
|
|
1748
|
-
this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.block.number),
|
|
1749
|
-
])
|
|
1750
|
-
).every(Boolean);
|
|
1933
|
+
...blocks.map(block => {
|
|
1934
|
+
return this.addBlockDataToDB(block);
|
|
1751
1935
|
}),
|
|
1752
1936
|
]);
|
|
1753
1937
|
|
|
@@ -1755,59 +1939,102 @@ export class ArchiverStoreHelper
|
|
|
1755
1939
|
});
|
|
1756
1940
|
}
|
|
1757
1941
|
|
|
1758
|
-
public
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1942
|
+
public addCheckpoints(
|
|
1943
|
+
checkpoints: PublishedCheckpoint[],
|
|
1944
|
+
pendingChainValidationStatus?: ValidateBlockResult,
|
|
1945
|
+
): Promise<boolean> {
|
|
1946
|
+
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1947
|
+
// or if the previous block is not in the store.
|
|
1948
|
+
return this.store.transactionAsync(async () => {
|
|
1949
|
+
await this.store.addCheckpoints(checkpoints);
|
|
1950
|
+
const allBlocks = checkpoints.flatMap((ch: PublishedCheckpoint) => ch.checkpoint.blocks);
|
|
1951
|
+
|
|
1952
|
+
const opResults = await Promise.all([
|
|
1953
|
+
// Update the pending chain validation status if provided
|
|
1954
|
+
pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
|
|
1955
|
+
// Add any logs emitted during the retrieved blocks
|
|
1956
|
+
this.store.addLogs(allBlocks),
|
|
1957
|
+
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
1958
|
+
...allBlocks.map(block => {
|
|
1959
|
+
return this.addBlockDataToDB(block);
|
|
1960
|
+
}),
|
|
1961
|
+
]);
|
|
1962
|
+
|
|
1963
|
+
return opResults.every(Boolean);
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
public async unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number): Promise<boolean> {
|
|
1968
|
+
if (checkpointsToUnwind <= 0) {
|
|
1969
|
+
throw new Error(`Cannot unwind ${checkpointsToUnwind} blocks`);
|
|
1762
1970
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1971
|
+
|
|
1972
|
+
const last = await this.getSynchedCheckpointNumber();
|
|
1973
|
+
if (from != last) {
|
|
1974
|
+
throw new Error(`Cannot unwind checkpoints from checkpoint ${from} when the last checkpoint is ${last}`);
|
|
1765
1975
|
}
|
|
1766
1976
|
|
|
1767
|
-
|
|
1768
|
-
const
|
|
1977
|
+
const blocks = [];
|
|
1978
|
+
const lastCheckpointNumber = from + checkpointsToUnwind - 1;
|
|
1979
|
+
for (let checkpointNumber = from; checkpointNumber <= lastCheckpointNumber; checkpointNumber++) {
|
|
1980
|
+
const blocksForCheckpoint = await this.store.getBlocksForCheckpoint(checkpointNumber);
|
|
1981
|
+
if (!blocksForCheckpoint) {
|
|
1982
|
+
continue;
|
|
1983
|
+
}
|
|
1984
|
+
blocks.push(...blocksForCheckpoint);
|
|
1985
|
+
}
|
|
1769
1986
|
|
|
1770
1987
|
const opResults = await Promise.all([
|
|
1771
1988
|
// Prune rolls back to the last proven block, which is by definition valid
|
|
1772
1989
|
this.store.setPendingChainValidationStatus({ valid: true }),
|
|
1773
1990
|
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
1774
1991
|
...blocks.map(async block => {
|
|
1775
|
-
const contractClassLogs = block.
|
|
1992
|
+
const contractClassLogs = block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
|
|
1776
1993
|
// ContractInstancePublished event logs are broadcast in privateLogs.
|
|
1777
|
-
const privateLogs = block.
|
|
1778
|
-
const publicLogs = block.
|
|
1994
|
+
const privateLogs = block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
|
|
1995
|
+
const publicLogs = block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
|
|
1779
1996
|
|
|
1780
1997
|
return (
|
|
1781
1998
|
await Promise.all([
|
|
1782
|
-
this.#updatePublishedContractClasses(contractClassLogs, block.
|
|
1783
|
-
this.#updateDeployedContractInstances(privateLogs, block.
|
|
1784
|
-
this.#updateUpdatedContractInstances(
|
|
1785
|
-
publicLogs,
|
|
1786
|
-
block.block.header.globalVariables.timestamp,
|
|
1787
|
-
Operation.Delete,
|
|
1788
|
-
),
|
|
1999
|
+
this.#updatePublishedContractClasses(contractClassLogs, block.number, Operation.Delete),
|
|
2000
|
+
this.#updateDeployedContractInstances(privateLogs, block.number, Operation.Delete),
|
|
2001
|
+
this.#updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, Operation.Delete),
|
|
1789
2002
|
])
|
|
1790
2003
|
).every(Boolean);
|
|
1791
2004
|
}),
|
|
1792
2005
|
|
|
1793
|
-
this.store.deleteLogs(blocks
|
|
1794
|
-
this.store.
|
|
2006
|
+
this.store.deleteLogs(blocks),
|
|
2007
|
+
this.store.unwindCheckpoints(from, checkpointsToUnwind),
|
|
1795
2008
|
]);
|
|
1796
2009
|
|
|
1797
2010
|
return opResults.every(Boolean);
|
|
1798
2011
|
}
|
|
1799
2012
|
|
|
1800
|
-
|
|
1801
|
-
return this.store.
|
|
2013
|
+
getCheckpointData(checkpointNumber: CheckpointNumber): Promise<CheckpointData | undefined> {
|
|
2014
|
+
return this.store.getCheckpointData(checkpointNumber);
|
|
1802
2015
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
2016
|
+
|
|
2017
|
+
getRangeOfCheckpoints(from: CheckpointNumber, limit: number): Promise<CheckpointData[]> {
|
|
2018
|
+
return this.store.getRangeOfCheckpoints(from, limit);
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
getCheckpointedL2BlockNumber(): Promise<BlockNumber> {
|
|
2022
|
+
return this.store.getCheckpointedL2BlockNumber();
|
|
2023
|
+
}
|
|
2024
|
+
getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
2025
|
+
return this.store.getSynchedCheckpointNumber();
|
|
2026
|
+
}
|
|
2027
|
+
setCheckpointSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
|
|
2028
|
+
return this.store.setCheckpointSynchedL1BlockNumber(l1BlockNumber);
|
|
2029
|
+
}
|
|
2030
|
+
getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
|
|
2031
|
+
return this.store.getCheckpointedBlock(number);
|
|
1805
2032
|
}
|
|
1806
|
-
|
|
1807
|
-
return this.store.
|
|
2033
|
+
getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
2034
|
+
return this.store.getCheckpointedBlockByHash(blockHash);
|
|
1808
2035
|
}
|
|
1809
|
-
|
|
1810
|
-
return this.store.
|
|
2036
|
+
getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
2037
|
+
return this.store.getCheckpointedBlockByArchive(archive);
|
|
1811
2038
|
}
|
|
1812
2039
|
getBlockHeaders(from: BlockNumber, limit: number): Promise<BlockHeader[]> {
|
|
1813
2040
|
return this.store.getBlockHeaders(from, limit);
|
|
@@ -1818,6 +2045,18 @@ export class ArchiverStoreHelper
|
|
|
1818
2045
|
getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
1819
2046
|
return this.store.getBlockHeaderByArchive(archive);
|
|
1820
2047
|
}
|
|
2048
|
+
getBlockByHash(blockHash: Fr): Promise<L2BlockNew | undefined> {
|
|
2049
|
+
return this.store.getBlockByHash(blockHash);
|
|
2050
|
+
}
|
|
2051
|
+
getBlockByArchive(archive: Fr): Promise<L2BlockNew | undefined> {
|
|
2052
|
+
return this.store.getBlockByArchive(archive);
|
|
2053
|
+
}
|
|
2054
|
+
getLatestBlockNumber(): Promise<BlockNumber> {
|
|
2055
|
+
return this.store.getLatestBlockNumber();
|
|
2056
|
+
}
|
|
2057
|
+
getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2BlockNew[] | undefined> {
|
|
2058
|
+
return this.store.getBlocksForCheckpoint(checkpointNumber);
|
|
2059
|
+
}
|
|
1821
2060
|
getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
|
|
1822
2061
|
return this.store.getTxEffect(txHash);
|
|
1823
2062
|
}
|
|
@@ -1843,16 +2082,19 @@ export class ArchiverStoreHelper
|
|
|
1843
2082
|
return this.store.getContractClassLogs(filter);
|
|
1844
2083
|
}
|
|
1845
2084
|
getSynchedL2BlockNumber(): Promise<BlockNumber> {
|
|
1846
|
-
return this.store.
|
|
2085
|
+
return this.store.getCheckpointedL2BlockNumber();
|
|
2086
|
+
}
|
|
2087
|
+
getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
2088
|
+
return this.store.getProvenCheckpointNumber();
|
|
1847
2089
|
}
|
|
1848
|
-
|
|
1849
|
-
return this.store.
|
|
2090
|
+
getProvenBlockNumber(): Promise<BlockNumber> {
|
|
2091
|
+
return this.store.getProvenBlockNumber();
|
|
1850
2092
|
}
|
|
1851
|
-
|
|
1852
|
-
return this.store.
|
|
2093
|
+
setProvenCheckpointNumber(checkpointNumber: CheckpointNumber): Promise<void> {
|
|
2094
|
+
return this.store.setProvenCheckpointNumber(checkpointNumber);
|
|
1853
2095
|
}
|
|
1854
2096
|
setBlockSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
|
|
1855
|
-
return this.store.
|
|
2097
|
+
return this.store.setCheckpointSynchedL1BlockNumber(l1BlockNumber);
|
|
1856
2098
|
}
|
|
1857
2099
|
setMessageSynchedL1Block(l1Block: L1BlockId): Promise<void> {
|
|
1858
2100
|
return this.store.setMessageSynchedL1Block(l1Block);
|