@aztec/archiver 3.0.0-rc.5 → 4.0.0-nightly.20260107
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 +69 -49
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +777 -214
- package/dest/archiver/archiver_store.d.ts +89 -30
- 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 +1785 -288
- package/dest/archiver/config.d.ts +3 -3
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +2 -2
- 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 -84
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +33 -37
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +60 -35
- package/dest/archiver/kv_archiver_store/log_store.d.ts +14 -11
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +149 -62
- package/dest/archiver/l1/bin/retrieve-calldata.js +5 -3
- package/dest/archiver/l1/calldata_retriever.d.ts +17 -3
- package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/archiver/l1/calldata_retriever.js +75 -7
- package/dest/archiver/l1/data_retrieval.d.ts +13 -10
- package/dest/archiver/l1/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/l1/data_retrieval.js +31 -18
- package/dest/archiver/structs/published.d.ts +1 -2
- package/dest/archiver/structs/published.d.ts.map +1 -1
- package/dest/factory.d.ts +1 -1
- package/dest/factory.js +1 -1
- package/dest/test/mock_l2_block_source.d.ts +10 -3
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +16 -15
- package/package.json +13 -13
- package/src/archiver/archiver.ts +509 -260
- package/src/archiver/archiver_store.ts +99 -29
- package/src/archiver/archiver_store_test_suite.ts +1831 -274
- package/src/archiver/config.ts +7 -3
- package/src/archiver/errors.ts +64 -0
- package/src/archiver/index.ts +1 -1
- package/src/archiver/kv_archiver_store/block_store.ts +434 -94
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +74 -49
- package/src/archiver/kv_archiver_store/log_store.ts +213 -77
- package/src/archiver/l1/bin/retrieve-calldata.ts +3 -3
- package/src/archiver/l1/calldata_retriever.ts +116 -6
- package/src/archiver/l1/data_retrieval.ts +41 -20
- package/src/archiver/structs/published.ts +0 -1
- package/src/factory.ts +1 -1
- package/src/test/mock_l2_block_source.ts +20 -16
package/src/archiver/archiver.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
2
|
import { GENESIS_BLOCK_HEADER_HASH } from '@aztec/constants';
|
|
3
3
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
4
4
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
@@ -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,
|
|
@@ -59,7 +64,15 @@ import {
|
|
|
59
64
|
} from '@aztec/stdlib/epoch-helpers';
|
|
60
65
|
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
61
66
|
import type { L2LogsSource } from '@aztec/stdlib/interfaces/server';
|
|
62
|
-
import {
|
|
67
|
+
import {
|
|
68
|
+
ContractClassLog,
|
|
69
|
+
type LogFilter,
|
|
70
|
+
type PrivateLog,
|
|
71
|
+
type PublicLog,
|
|
72
|
+
type SiloedTag,
|
|
73
|
+
Tag,
|
|
74
|
+
TxScopedL2Log,
|
|
75
|
+
} from '@aztec/stdlib/logs';
|
|
63
76
|
import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
64
77
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
65
78
|
import { type BlockHeader, type IndexedTxEffect, TxHash, TxReceipt } from '@aztec/stdlib/tx';
|
|
@@ -78,8 +91,9 @@ import { type GetContractReturnType, type Hex, createPublicClient, fallback, htt
|
|
|
78
91
|
|
|
79
92
|
import type { ArchiverDataStore, ArchiverL1SynchPoint } from './archiver_store.js';
|
|
80
93
|
import type { ArchiverConfig } from './config.js';
|
|
81
|
-
import {
|
|
94
|
+
import { InitialCheckpointNumberNotSequentialError, NoBlobBodiesFoundError } from './errors.js';
|
|
82
95
|
import { ArchiverInstrumentation } from './instrumentation.js';
|
|
96
|
+
import type { CheckpointData } from './kv_archiver_store/block_store.js';
|
|
83
97
|
import {
|
|
84
98
|
retrieveCheckpointsFromRollup,
|
|
85
99
|
retrieveL1ToL2Message,
|
|
@@ -95,9 +109,16 @@ import { type ValidateBlockResult, validateCheckpointAttestations } from './vali
|
|
|
95
109
|
*/
|
|
96
110
|
export type ArchiveSource = L2BlockSource & L2LogsSource & ContractDataSource & L1ToL2MessageSource;
|
|
97
111
|
|
|
112
|
+
/** Request to add a block to the archiver, queued for processing by the sync loop. */
|
|
113
|
+
type AddBlockRequest = {
|
|
114
|
+
block: L2BlockNew;
|
|
115
|
+
resolve: () => void;
|
|
116
|
+
reject: (err: Error) => void;
|
|
117
|
+
};
|
|
118
|
+
|
|
98
119
|
export type ArchiverDeps = {
|
|
99
120
|
telemetry?: TelemetryClient;
|
|
100
|
-
|
|
121
|
+
blobClient: BlobClientInterface;
|
|
101
122
|
epochCache?: EpochCache;
|
|
102
123
|
dateProvider?: DateProvider;
|
|
103
124
|
};
|
|
@@ -127,7 +148,10 @@ type RollupStatus = {
|
|
|
127
148
|
* Responsible for handling robust L1 polling so that other components do not need to
|
|
128
149
|
* concern themselves with it.
|
|
129
150
|
*/
|
|
130
|
-
export class Archiver
|
|
151
|
+
export class Archiver
|
|
152
|
+
extends (EventEmitter as new () => ArchiverEmitter)
|
|
153
|
+
implements ArchiveSource, L2BlockSink, Traceable
|
|
154
|
+
{
|
|
131
155
|
/** A loop in which we will be continually fetching new checkpoints. */
|
|
132
156
|
private runningPromise: RunningPromise;
|
|
133
157
|
|
|
@@ -141,6 +165,9 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
141
165
|
private initialSyncComplete: boolean = false;
|
|
142
166
|
private initialSyncPromise: PromiseWithResolvers<void>;
|
|
143
167
|
|
|
168
|
+
/** Queue of blocks to be added to the store, processed by the sync loop. */
|
|
169
|
+
private blockQueue: AddBlockRequest[] = [];
|
|
170
|
+
|
|
144
171
|
public readonly tracer: Tracer;
|
|
145
172
|
|
|
146
173
|
/**
|
|
@@ -169,7 +196,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
169
196
|
maxAllowedEthClientDriftSeconds: number;
|
|
170
197
|
ethereumAllowNoDebugHosts?: boolean;
|
|
171
198
|
},
|
|
172
|
-
private readonly
|
|
199
|
+
private readonly blobClient: BlobClientInterface,
|
|
173
200
|
private readonly epochCache: EpochCache,
|
|
174
201
|
private readonly dateProvider: DateProvider,
|
|
175
202
|
private readonly instrumentation: ArchiverInstrumentation,
|
|
@@ -211,7 +238,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
211
238
|
const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
212
239
|
const publicClient = createPublicClient({
|
|
213
240
|
chain: chain.chainInfo,
|
|
214
|
-
transport: fallback(config.l1RpcUrls.map(url => http(url))),
|
|
241
|
+
transport: fallback(config.l1RpcUrls.map(url => http(url, { batch: false }))),
|
|
215
242
|
pollingInterval: config.viemPollingIntervalMS,
|
|
216
243
|
});
|
|
217
244
|
|
|
@@ -219,7 +246,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
219
246
|
const debugRpcUrls = config.l1DebugRpcUrls.length > 0 ? config.l1DebugRpcUrls : config.l1RpcUrls;
|
|
220
247
|
const debugClient = createPublicClient({
|
|
221
248
|
chain: chain.chainInfo,
|
|
222
|
-
transport: fallback(debugRpcUrls.map(url => http(url))),
|
|
249
|
+
transport: fallback(debugRpcUrls.map(url => http(url, { batch: false }))),
|
|
223
250
|
pollingInterval: config.viemPollingIntervalMS,
|
|
224
251
|
}) as ViemPublicDebugClient;
|
|
225
252
|
|
|
@@ -248,7 +275,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
248
275
|
slotDuration,
|
|
249
276
|
ethereumSlotDuration,
|
|
250
277
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
251
|
-
genesisArchiveRoot: Fr.
|
|
278
|
+
genesisArchiveRoot: Fr.fromString(genesisArchiveRoot.toString()),
|
|
252
279
|
};
|
|
253
280
|
|
|
254
281
|
const opts = merge(
|
|
@@ -270,7 +297,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
270
297
|
{ ...config.l1Contracts, slashingProposerAddress },
|
|
271
298
|
archiverStore,
|
|
272
299
|
opts,
|
|
273
|
-
deps.
|
|
300
|
+
deps.blobClient,
|
|
274
301
|
epochCache,
|
|
275
302
|
deps.dateProvider ?? new DateProvider(),
|
|
276
303
|
await ArchiverInstrumentation.new(telemetry, () => archiverStore.estimateSize()),
|
|
@@ -294,17 +321,17 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
294
321
|
throw new Error('Archiver is already running');
|
|
295
322
|
}
|
|
296
323
|
|
|
297
|
-
await this.
|
|
324
|
+
await this.blobClient.testSources();
|
|
298
325
|
await this.testEthereumNodeSynced();
|
|
299
326
|
await validateAndLogTraceAvailability(this.debugClient, this.config.ethereumAllowNoDebugHosts ?? false);
|
|
300
327
|
|
|
301
328
|
// Log initial state for the archiver
|
|
302
329
|
const { l1StartBlock } = this.l1constants;
|
|
303
330
|
const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = l1StartBlock } = await this.store.getSynchPoint();
|
|
304
|
-
const
|
|
331
|
+
const currentL2Checkpoint = await this.getSynchedCheckpointNumber();
|
|
305
332
|
this.log.info(
|
|
306
|
-
`Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo} and L2
|
|
307
|
-
{ blocksSynchedTo, messagesSynchedTo,
|
|
333
|
+
`Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo} and L2 checkpoint ${currentL2Checkpoint}`,
|
|
334
|
+
{ blocksSynchedTo, messagesSynchedTo, currentL2Checkpoint },
|
|
308
335
|
);
|
|
309
336
|
|
|
310
337
|
// Start sync loop, and return the wait for initial sync if we are asked to block until synced
|
|
@@ -318,6 +345,51 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
318
345
|
return this.runningPromise.trigger();
|
|
319
346
|
}
|
|
320
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Queues a block to be added to the archiver store and triggers processing.
|
|
350
|
+
* The block will be processed by the sync loop.
|
|
351
|
+
* Implements the L2BlockSink interface.
|
|
352
|
+
* @param block - The L2 block to add.
|
|
353
|
+
* @returns A promise that resolves when the block has been added to the store, or rejects on error.
|
|
354
|
+
*/
|
|
355
|
+
public addBlock(block: L2BlockNew): Promise<void> {
|
|
356
|
+
return new Promise<void>((resolve, reject) => {
|
|
357
|
+
this.blockQueue.push({ block, resolve, reject });
|
|
358
|
+
this.log.debug(`Queued block ${block.number} for processing`);
|
|
359
|
+
// Trigger an immediate sync, but don't wait for it - the promise resolves when the block is processed
|
|
360
|
+
this.syncImmediate().catch(err => {
|
|
361
|
+
this.log.error(`Sync immediate call failed: ${err}`);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Processes all queued blocks, adding them to the store.
|
|
368
|
+
* Called at the beginning of each sync iteration.
|
|
369
|
+
* Blocks are processed in the order they were queued.
|
|
370
|
+
*/
|
|
371
|
+
private async processQueuedBlocks(): Promise<void> {
|
|
372
|
+
if (this.blockQueue.length === 0) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Take all blocks from the queue
|
|
377
|
+
const queuedItems = this.blockQueue.splice(0, this.blockQueue.length);
|
|
378
|
+
this.log.debug(`Processing ${queuedItems.length} queued block(s)`);
|
|
379
|
+
|
|
380
|
+
// Process each block individually to properly resolve/reject each promise
|
|
381
|
+
for (const { block, resolve, reject } of queuedItems) {
|
|
382
|
+
try {
|
|
383
|
+
await this.store.addBlocks([block]);
|
|
384
|
+
this.log.debug(`Added block ${block.number} to store`);
|
|
385
|
+
resolve();
|
|
386
|
+
} catch (err: any) {
|
|
387
|
+
this.log.error(`Failed to add block ${block.number} to store: ${err.message}`);
|
|
388
|
+
reject(err);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
321
393
|
public waitForInitialSync() {
|
|
322
394
|
return this.initialSyncPromise.promise;
|
|
323
395
|
}
|
|
@@ -337,11 +409,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
337
409
|
}
|
|
338
410
|
}
|
|
339
411
|
|
|
340
|
-
|
|
341
|
-
* Fetches logs from L1 contracts and processes them.
|
|
342
|
-
*/
|
|
343
|
-
@trackSpan('Archiver.sync')
|
|
344
|
-
private async sync() {
|
|
412
|
+
private async syncFromL1() {
|
|
345
413
|
/**
|
|
346
414
|
* We keep track of three "pointers" to L1 blocks:
|
|
347
415
|
* 1. the last L1 block that published an L2 block
|
|
@@ -427,7 +495,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
427
495
|
// past it, since otherwise we'll keep downloading it and reprocessing it on every iteration until
|
|
428
496
|
// we get a valid checkpoint to advance the syncpoint.
|
|
429
497
|
if (!rollupStatus.validationResult?.valid && rollupStatus.lastL1BlockWithCheckpoint !== undefined) {
|
|
430
|
-
await this.store.
|
|
498
|
+
await this.store.setCheckpointSynchedL1BlockNumber(rollupStatus.lastL1BlockWithCheckpoint);
|
|
431
499
|
}
|
|
432
500
|
|
|
433
501
|
// And lastly we check if we are missing any checkpoints behind us due to a possible L1 reorg.
|
|
@@ -461,6 +529,17 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
461
529
|
}
|
|
462
530
|
}
|
|
463
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Fetches logs from L1 contracts and processes them.
|
|
534
|
+
*/
|
|
535
|
+
@trackSpan('Archiver.sync')
|
|
536
|
+
private async sync() {
|
|
537
|
+
// Process any queued blocks first, before doing L1 sync
|
|
538
|
+
await this.processQueuedBlocks();
|
|
539
|
+
// Now perform L1 sync
|
|
540
|
+
await this.syncFromL1();
|
|
541
|
+
}
|
|
542
|
+
|
|
464
543
|
/** Queries the rollup contract on whether a prune can be executed on the immediate next L1 block. */
|
|
465
544
|
private async canPrune(currentL1BlockNumber: bigint, currentL1Timestamp: bigint) {
|
|
466
545
|
const time = (currentL1Timestamp ?? 0n) + BigInt(this.l1constants.ethereumSlotDuration);
|
|
@@ -499,13 +578,26 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
499
578
|
|
|
500
579
|
const checkpointsToUnwind = localPendingCheckpointNumber - provenCheckpointNumber;
|
|
501
580
|
|
|
502
|
-
const
|
|
581
|
+
const checkpointPromises = Array.from({ length: checkpointsToUnwind })
|
|
582
|
+
.fill(0)
|
|
583
|
+
.map((_, i) => this.store.getCheckpointData(CheckpointNumber(i + pruneFrom)));
|
|
584
|
+
const checkpoints = await Promise.all(checkpointPromises);
|
|
585
|
+
|
|
586
|
+
const blockPromises = await Promise.all(
|
|
587
|
+
checkpoints
|
|
588
|
+
.filter(isDefined)
|
|
589
|
+
.map(cp => this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber))),
|
|
590
|
+
);
|
|
591
|
+
const newBlocks = blockPromises.filter(isDefined).flat();
|
|
592
|
+
|
|
593
|
+
// TODO(pw/mbps): Don't convert to legacy blocks here
|
|
594
|
+
const blocks: L2Block[] = (await Promise.all(newBlocks.map(x => this.getBlock(x.number)))).filter(isDefined);
|
|
503
595
|
|
|
504
596
|
// Emit an event for listening services to react to the chain prune
|
|
505
597
|
this.emit(L2BlockSourceEvents.L2PruneDetected, {
|
|
506
598
|
type: L2BlockSourceEvents.L2PruneDetected,
|
|
507
599
|
epochNumber: pruneFromEpochNumber,
|
|
508
|
-
blocks
|
|
600
|
+
blocks,
|
|
509
601
|
});
|
|
510
602
|
|
|
511
603
|
this.log.debug(
|
|
@@ -697,20 +789,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
697
789
|
private async handleCheckpoints(blocksSynchedTo: bigint, currentL1BlockNumber: bigint): Promise<RollupStatus> {
|
|
698
790
|
const localPendingCheckpointNumber = await this.getSynchedCheckpointNumber();
|
|
699
791
|
const initialValidationResult: ValidateBlockResult | undefined = await this.store.getPendingChainValidationStatus();
|
|
700
|
-
const
|
|
701
|
-
rollupProvenCheckpointNumber,
|
|
702
|
-
provenArchive,
|
|
703
|
-
rollupPendingCheckpointNumber,
|
|
704
|
-
pendingArchive,
|
|
705
|
-
archiveForLocalPendingCheckpointNumber,
|
|
706
|
-
] = await this.rollup.status(localPendingCheckpointNumber, { blockNumber: currentL1BlockNumber });
|
|
707
|
-
const provenCheckpointNumber = CheckpointNumber.fromBigInt(rollupProvenCheckpointNumber);
|
|
708
|
-
const pendingCheckpointNumber = CheckpointNumber.fromBigInt(rollupPendingCheckpointNumber);
|
|
709
|
-
const rollupStatus = {
|
|
792
|
+
const {
|
|
710
793
|
provenCheckpointNumber,
|
|
711
794
|
provenArchive,
|
|
712
795
|
pendingCheckpointNumber,
|
|
713
796
|
pendingArchive,
|
|
797
|
+
archiveOfMyCheckpoint: archiveForLocalPendingCheckpointNumber,
|
|
798
|
+
} = await this.rollup.status(localPendingCheckpointNumber, { blockNumber: currentL1BlockNumber });
|
|
799
|
+
const rollupStatus: RollupStatus = {
|
|
800
|
+
provenCheckpointNumber,
|
|
801
|
+
provenArchive: provenArchive.toString(),
|
|
802
|
+
pendingCheckpointNumber,
|
|
803
|
+
pendingArchive: pendingArchive.toString(),
|
|
714
804
|
validationResult: initialValidationResult,
|
|
715
805
|
};
|
|
716
806
|
this.log.trace(`Retrieved rollup status at current L1 block ${currentL1BlockNumber}.`, {
|
|
@@ -733,17 +823,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
733
823
|
}
|
|
734
824
|
}
|
|
735
825
|
|
|
736
|
-
const localCheckpointForDestinationProvenCheckpointNumber =
|
|
826
|
+
const localCheckpointForDestinationProvenCheckpointNumber =
|
|
827
|
+
await this.store.getCheckpointData(provenCheckpointNumber);
|
|
737
828
|
|
|
738
829
|
// 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
830
|
// synched checkpoint when requesting L2Tips from the archiver. This is the only place where the proven checkpoint is set.
|
|
740
831
|
const synched = await this.getSynchedCheckpointNumber();
|
|
741
832
|
if (
|
|
742
833
|
localCheckpointForDestinationProvenCheckpointNumber &&
|
|
743
|
-
synched < localCheckpointForDestinationProvenCheckpointNumber.
|
|
834
|
+
synched < localCheckpointForDestinationProvenCheckpointNumber.checkpointNumber
|
|
744
835
|
) {
|
|
745
836
|
this.log.error(
|
|
746
|
-
`Hit local checkpoint greater than last synched checkpoint: ${localCheckpointForDestinationProvenCheckpointNumber.
|
|
837
|
+
`Hit local checkpoint greater than last synched checkpoint: ${localCheckpointForDestinationProvenCheckpointNumber.checkpointNumber} > ${synched}`,
|
|
747
838
|
);
|
|
748
839
|
}
|
|
749
840
|
|
|
@@ -753,38 +844,39 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
753
844
|
}`,
|
|
754
845
|
);
|
|
755
846
|
|
|
756
|
-
const lastProvenBlockNumber = await this.getLastBlockNumberInCheckpoint(provenCheckpointNumber);
|
|
757
847
|
if (
|
|
758
848
|
localCheckpointForDestinationProvenCheckpointNumber &&
|
|
759
|
-
provenArchive
|
|
849
|
+
provenArchive.equals(localCheckpointForDestinationProvenCheckpointNumber.archive.root)
|
|
760
850
|
) {
|
|
761
851
|
const localProvenCheckpointNumber = await this.getProvenCheckpointNumber();
|
|
762
852
|
if (localProvenCheckpointNumber !== provenCheckpointNumber) {
|
|
763
853
|
await this.setProvenCheckpointNumber(provenCheckpointNumber);
|
|
764
|
-
this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, {
|
|
765
|
-
provenCheckpointNumber,
|
|
766
|
-
});
|
|
854
|
+
this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
|
|
767
855
|
const provenSlotNumber = localCheckpointForDestinationProvenCheckpointNumber.header.slotNumber;
|
|
768
856
|
const provenEpochNumber: EpochNumber = getEpochAtSlot(provenSlotNumber, this.l1constants);
|
|
857
|
+
const lastBlockNumberInCheckpoint =
|
|
858
|
+
localCheckpointForDestinationProvenCheckpointNumber.startBlock +
|
|
859
|
+
localCheckpointForDestinationProvenCheckpointNumber.numBlocks -
|
|
860
|
+
1;
|
|
769
861
|
|
|
770
862
|
this.emit(L2BlockSourceEvents.L2BlockProven, {
|
|
771
863
|
type: L2BlockSourceEvents.L2BlockProven,
|
|
772
|
-
blockNumber:
|
|
864
|
+
blockNumber: BlockNumber(lastBlockNumberInCheckpoint),
|
|
773
865
|
slotNumber: provenSlotNumber,
|
|
774
866
|
epochNumber: provenEpochNumber,
|
|
775
867
|
});
|
|
868
|
+
this.instrumentation.updateLastProvenBlock(lastBlockNumberInCheckpoint);
|
|
776
869
|
} else {
|
|
777
870
|
this.log.trace(`Proven checkpoint ${provenCheckpointNumber} already stored.`);
|
|
778
871
|
}
|
|
779
872
|
}
|
|
780
|
-
this.instrumentation.updateLastProvenBlock(lastProvenBlockNumber);
|
|
781
873
|
};
|
|
782
874
|
|
|
783
875
|
// This is an edge case that we only hit if there are no proposed checkpoints.
|
|
784
876
|
// If we have 0 checkpoints locally and there are no checkpoints onchain there is nothing to do.
|
|
785
877
|
const noCheckpoints = localPendingCheckpointNumber === 0 && pendingCheckpointNumber === 0;
|
|
786
878
|
if (noCheckpoints) {
|
|
787
|
-
await this.store.
|
|
879
|
+
await this.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
|
|
788
880
|
this.log.debug(
|
|
789
881
|
`No checkpoints to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}, no checkpoints on chain`,
|
|
790
882
|
);
|
|
@@ -796,13 +888,13 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
796
888
|
// Related to the L2 reorgs of the pending chain. We are only interested in actually addressing a reorg if there
|
|
797
889
|
// are any state that could be impacted by it. If we have no checkpoints, there is no impact.
|
|
798
890
|
if (localPendingCheckpointNumber > 0) {
|
|
799
|
-
const localPendingCheckpoint = await this.
|
|
891
|
+
const localPendingCheckpoint = await this.store.getCheckpointData(localPendingCheckpointNumber);
|
|
800
892
|
if (localPendingCheckpoint === undefined) {
|
|
801
893
|
throw new Error(`Missing checkpoint ${localPendingCheckpointNumber}`);
|
|
802
894
|
}
|
|
803
895
|
|
|
804
896
|
const localPendingArchiveRoot = localPendingCheckpoint.archive.root.toString();
|
|
805
|
-
const noCheckpointSinceLast = localPendingCheckpoint && pendingArchive === localPendingArchiveRoot;
|
|
897
|
+
const noCheckpointSinceLast = localPendingCheckpoint && pendingArchive.toString() === localPendingArchiveRoot;
|
|
806
898
|
if (noCheckpointSinceLast) {
|
|
807
899
|
// We believe the following line causes a problem when we encounter L1 re-orgs.
|
|
808
900
|
// Basically, by setting the synched L1 block number here, we are saying that we have
|
|
@@ -816,7 +908,9 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
816
908
|
return rollupStatus;
|
|
817
909
|
}
|
|
818
910
|
|
|
819
|
-
const localPendingCheckpointInChain = archiveForLocalPendingCheckpointNumber
|
|
911
|
+
const localPendingCheckpointInChain = archiveForLocalPendingCheckpointNumber.equals(
|
|
912
|
+
localPendingCheckpoint.archive.root,
|
|
913
|
+
);
|
|
820
914
|
if (!localPendingCheckpointInChain) {
|
|
821
915
|
// If our local pending checkpoint tip is not in the chain on L1 a "prune" must have happened
|
|
822
916
|
// or the L1 have reorged.
|
|
@@ -829,20 +923,20 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
829
923
|
|
|
830
924
|
let tipAfterUnwind = localPendingCheckpointNumber;
|
|
831
925
|
while (true) {
|
|
832
|
-
const candidateCheckpoint = await this.
|
|
926
|
+
const candidateCheckpoint = await this.store.getCheckpointData(tipAfterUnwind);
|
|
833
927
|
if (candidateCheckpoint === undefined) {
|
|
834
928
|
break;
|
|
835
929
|
}
|
|
836
930
|
|
|
837
|
-
const archiveAtContract = await this.rollup.archiveAt(candidateCheckpoint.
|
|
931
|
+
const archiveAtContract = await this.rollup.archiveAt(candidateCheckpoint.checkpointNumber);
|
|
838
932
|
this.log.trace(
|
|
839
|
-
`Checking local checkpoint ${candidateCheckpoint.
|
|
933
|
+
`Checking local checkpoint ${candidateCheckpoint.checkpointNumber} with archive ${candidateCheckpoint.archive.root}`,
|
|
840
934
|
{
|
|
841
935
|
archiveAtContract,
|
|
842
936
|
archiveLocal: candidateCheckpoint.archive.root.toString(),
|
|
843
937
|
},
|
|
844
938
|
);
|
|
845
|
-
if (archiveAtContract
|
|
939
|
+
if (archiveAtContract.equals(candidateCheckpoint.archive.root)) {
|
|
846
940
|
break;
|
|
847
941
|
}
|
|
848
942
|
tipAfterUnwind--;
|
|
@@ -871,17 +965,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
871
965
|
|
|
872
966
|
this.log.trace(`Retrieving checkpoints from L1 block ${searchStartBlock} to ${searchEndBlock}`);
|
|
873
967
|
|
|
874
|
-
// TODO(md): Retrieve from blob
|
|
968
|
+
// TODO(md): Retrieve from blob client then from consensus client, then from peers
|
|
875
969
|
const retrievedCheckpoints = await retrieveCheckpointsFromRollup(
|
|
876
970
|
this.rollup.getContract() as GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
|
|
877
971
|
this.publicClient,
|
|
878
972
|
this.debugClient,
|
|
879
|
-
this.
|
|
973
|
+
this.blobClient,
|
|
880
974
|
searchStartBlock, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
|
|
881
975
|
searchEndBlock,
|
|
882
976
|
this.l1Addresses,
|
|
883
977
|
this.instrumentation,
|
|
884
978
|
this.log,
|
|
979
|
+
!this.initialSyncComplete, // isHistoricalSync
|
|
885
980
|
);
|
|
886
981
|
|
|
887
982
|
if (retrievedCheckpoints.length === 0) {
|
|
@@ -981,19 +1076,18 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
981
1076
|
validCheckpoints.flatMap(c => c.checkpoint.blocks),
|
|
982
1077
|
);
|
|
983
1078
|
} catch (err) {
|
|
984
|
-
if (err instanceof
|
|
985
|
-
const {
|
|
986
|
-
const
|
|
987
|
-
? await this.store.
|
|
1079
|
+
if (err instanceof InitialCheckpointNumberNotSequentialError) {
|
|
1080
|
+
const { previousCheckpointNumber, newCheckpointNumber } = err;
|
|
1081
|
+
const previousCheckpoint = previousCheckpointNumber
|
|
1082
|
+
? await this.store.getCheckpointData(CheckpointNumber(previousCheckpointNumber))
|
|
988
1083
|
: undefined;
|
|
989
|
-
const updatedL1SyncPoint =
|
|
1084
|
+
const updatedL1SyncPoint = previousCheckpoint?.l1.blockNumber ?? this.l1constants.l1StartBlock;
|
|
990
1085
|
await this.store.setBlockSynchedL1BlockNumber(updatedL1SyncPoint);
|
|
991
1086
|
this.log.warn(
|
|
992
|
-
`Attempting to insert
|
|
1087
|
+
`Attempting to insert checkpoint ${newCheckpointNumber} with previous block ${previousCheckpointNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`,
|
|
993
1088
|
{
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
newBlockNumber,
|
|
1089
|
+
previousCheckpointNumber,
|
|
1090
|
+
newCheckpointNumber,
|
|
997
1091
|
updatedL1SyncPoint,
|
|
998
1092
|
},
|
|
999
1093
|
);
|
|
@@ -1038,13 +1132,16 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1038
1132
|
// We suspect an L1 reorg that added checkpoints *behind* us. If that is the case, it must have happened between
|
|
1039
1133
|
// the last checkpoint we saw and the current one, so we reset the last synched L1 block number. In the edge case
|
|
1040
1134
|
// 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
|
-
|
|
1135
|
+
let latestLocalCheckpointArchive: string | undefined = undefined;
|
|
1136
|
+
let targetL1BlockNumber = maxBigint(currentL1BlockNumber - 64n, 0n);
|
|
1137
|
+
if (lastRetrievedCheckpoint) {
|
|
1138
|
+
latestLocalCheckpointArchive = lastRetrievedCheckpoint.checkpoint.archive.root.toString();
|
|
1139
|
+
targetL1BlockNumber = lastRetrievedCheckpoint.l1.blockNumber;
|
|
1140
|
+
} else if (latestLocalCheckpointNumber > 0) {
|
|
1141
|
+
const checkpoint = await this.store.getRangeOfCheckpoints(latestLocalCheckpointNumber, 1).then(([c]) => c);
|
|
1142
|
+
latestLocalCheckpointArchive = checkpoint.archive.root.toString();
|
|
1143
|
+
targetL1BlockNumber = checkpoint.l1.blockNumber;
|
|
1144
|
+
}
|
|
1048
1145
|
this.log.warn(
|
|
1049
1146
|
`Failed to reach checkpoint ${pendingCheckpointNumber} at ${currentL1BlockNumber} (latest is ${latestLocalCheckpointNumber}). ` +
|
|
1050
1147
|
`Rolling back last synched L1 block number to ${targetL1BlockNumber}.`,
|
|
@@ -1130,15 +1227,22 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1130
1227
|
const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
|
|
1131
1228
|
const blocks: L2Block[] = [];
|
|
1132
1229
|
|
|
1133
|
-
// Walk the list of
|
|
1134
|
-
// We'll typically ask for
|
|
1135
|
-
let
|
|
1136
|
-
const slot = (b:
|
|
1137
|
-
while (
|
|
1138
|
-
if (slot(
|
|
1139
|
-
blocks
|
|
1230
|
+
// Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
|
|
1231
|
+
// We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
|
|
1232
|
+
let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
|
|
1233
|
+
const slot = (b: CheckpointData) => b.header.slotNumber;
|
|
1234
|
+
while (checkpoint && slot(checkpoint) >= start) {
|
|
1235
|
+
if (slot(checkpoint) <= end) {
|
|
1236
|
+
// push the blocks on backwards
|
|
1237
|
+
const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
|
|
1238
|
+
for (let i = endBlock; i >= checkpoint.startBlock; i--) {
|
|
1239
|
+
const block = await this.getBlock(BlockNumber(i));
|
|
1240
|
+
if (block) {
|
|
1241
|
+
blocks.push(block);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1140
1244
|
}
|
|
1141
|
-
|
|
1245
|
+
checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
|
|
1142
1246
|
}
|
|
1143
1247
|
|
|
1144
1248
|
return blocks.reverse();
|
|
@@ -1148,17 +1252,22 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1148
1252
|
const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
|
|
1149
1253
|
const blocks: BlockHeader[] = [];
|
|
1150
1254
|
|
|
1151
|
-
// Walk the list of
|
|
1152
|
-
// We'll typically ask for
|
|
1153
|
-
let
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1255
|
+
// Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
|
|
1256
|
+
// We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
|
|
1257
|
+
let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
|
|
1258
|
+
const slot = (b: CheckpointData) => b.header.slotNumber;
|
|
1259
|
+
while (checkpoint && slot(checkpoint) >= start) {
|
|
1260
|
+
if (slot(checkpoint) <= end) {
|
|
1261
|
+
// push the blocks on backwards
|
|
1262
|
+
const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
|
|
1263
|
+
for (let i = endBlock; i >= checkpoint.startBlock; i--) {
|
|
1264
|
+
const block = await this.getBlockHeader(BlockNumber(i));
|
|
1265
|
+
if (block) {
|
|
1266
|
+
blocks.push(block);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1159
1269
|
}
|
|
1160
|
-
|
|
1161
|
-
header = await this.getBlockHeader(number);
|
|
1270
|
+
checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
|
|
1162
1271
|
}
|
|
1163
1272
|
return blocks.reverse();
|
|
1164
1273
|
}
|
|
@@ -1196,37 +1305,6 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1196
1305
|
return this.initialSyncComplete;
|
|
1197
1306
|
}
|
|
1198
1307
|
|
|
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
1308
|
public async getCheckpointHeader(number: CheckpointNumber | 'latest'): Promise<CheckpointHeader | undefined> {
|
|
1231
1309
|
if (number === 'latest') {
|
|
1232
1310
|
number = await this.getSynchedCheckpointNumber();
|
|
@@ -1234,88 +1312,46 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1234
1312
|
if (number === 0) {
|
|
1235
1313
|
return undefined;
|
|
1236
1314
|
}
|
|
1237
|
-
const checkpoint = await this.
|
|
1238
|
-
|
|
1315
|
+
const checkpoint = await this.store.getCheckpointData(number);
|
|
1316
|
+
if (!checkpoint) {
|
|
1317
|
+
return undefined;
|
|
1318
|
+
}
|
|
1319
|
+
return checkpoint.header;
|
|
1239
1320
|
}
|
|
1240
1321
|
|
|
1241
1322
|
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
1242
1323
|
return this.getSynchedCheckpointNumber();
|
|
1243
1324
|
}
|
|
1244
1325
|
|
|
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());
|
|
1326
|
+
public getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
1327
|
+
return this.store.getSynchedCheckpointNumber();
|
|
1249
1328
|
}
|
|
1250
1329
|
|
|
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());
|
|
1330
|
+
public getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
1331
|
+
return this.store.getProvenCheckpointNumber();
|
|
1255
1332
|
}
|
|
1256
1333
|
|
|
1257
1334
|
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));
|
|
1335
|
+
return this.store.setProvenCheckpointNumber(checkpointNumber);
|
|
1261
1336
|
}
|
|
1262
1337
|
|
|
1263
1338
|
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);
|
|
1339
|
+
return this.store.unwindCheckpoints(from, checkpointsToUnwind);
|
|
1267
1340
|
}
|
|
1268
1341
|
|
|
1269
|
-
public getLastBlockNumberInCheckpoint(checkpointNumber: CheckpointNumber): Promise<BlockNumber> {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1342
|
+
public async getLastBlockNumberInCheckpoint(checkpointNumber: CheckpointNumber): Promise<BlockNumber | undefined> {
|
|
1343
|
+
const checkpointData = await this.store.getCheckpointData(checkpointNumber);
|
|
1344
|
+
if (!checkpointData) {
|
|
1345
|
+
return undefined;
|
|
1346
|
+
}
|
|
1347
|
+
return BlockNumber(checkpointData.startBlock + checkpointData.numBlocks - 1);
|
|
1273
1348
|
}
|
|
1274
1349
|
|
|
1275
1350
|
public addCheckpoints(
|
|
1276
1351
|
checkpoints: PublishedCheckpoint[],
|
|
1277
1352
|
pendingChainValidationStatus?: ValidateBlockResult,
|
|
1278
1353
|
): 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);
|
|
1354
|
+
return this.store.addCheckpoints(checkpoints, pendingChainValidationStatus);
|
|
1319
1355
|
}
|
|
1320
1356
|
|
|
1321
1357
|
public getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
|
|
@@ -1331,7 +1367,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1331
1367
|
* @param number - The block number to return.
|
|
1332
1368
|
* @returns The requested L2 block.
|
|
1333
1369
|
*/
|
|
1334
|
-
public async
|
|
1370
|
+
public async getL2BlockNew(number: BlockNumber): Promise<L2BlockNew | undefined> {
|
|
1335
1371
|
// If the number provided is -ve, then return the latest block.
|
|
1336
1372
|
if (number < 0) {
|
|
1337
1373
|
number = await this.store.getSynchedL2BlockNumber();
|
|
@@ -1339,8 +1375,8 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1339
1375
|
if (number === 0) {
|
|
1340
1376
|
return undefined;
|
|
1341
1377
|
}
|
|
1342
|
-
const publishedBlock = await this.store.
|
|
1343
|
-
return publishedBlock
|
|
1378
|
+
const publishedBlock = await this.store.store.getBlock(number);
|
|
1379
|
+
return publishedBlock;
|
|
1344
1380
|
}
|
|
1345
1381
|
|
|
1346
1382
|
public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
|
|
@@ -1354,6 +1390,21 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1354
1390
|
return headers.length === 0 ? undefined : headers[0];
|
|
1355
1391
|
}
|
|
1356
1392
|
|
|
1393
|
+
getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
|
|
1394
|
+
return this.store.getCheckpointedBlock(number);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1398
|
+
return this.store.getCheckpointedBlockByHash(blockHash);
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
getProvenBlockNumber(): Promise<BlockNumber> {
|
|
1402
|
+
return this.store.getProvenBlockNumber();
|
|
1403
|
+
}
|
|
1404
|
+
getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1405
|
+
return this.store.getCheckpointedBlockByArchive(archive);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1357
1408
|
public getTxEffect(txHash: TxHash) {
|
|
1358
1409
|
return this.store.getTxEffect(txHash);
|
|
1359
1410
|
}
|
|
@@ -1362,14 +1413,12 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1362
1413
|
return this.store.getSettledTxReceipt(txHash);
|
|
1363
1414
|
}
|
|
1364
1415
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
getLogsByTags(tags: Fr[]): Promise<TxScopedL2Log[][]> {
|
|
1372
|
-
return this.store.getLogsByTags(tags);
|
|
1416
|
+
getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
|
|
1417
|
+
return this.store.getPrivateLogsByTags(tags);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
|
|
1421
|
+
return this.store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
1373
1422
|
}
|
|
1374
1423
|
|
|
1375
1424
|
/**
|
|
@@ -1392,19 +1441,11 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1392
1441
|
|
|
1393
1442
|
/**
|
|
1394
1443
|
* Gets the number of the latest L2 block processed by the block source implementation.
|
|
1444
|
+
* This includes both checkpointed and uncheckpointed blocks.
|
|
1395
1445
|
* @returns The number of the latest L2 block processed by the block source implementation.
|
|
1396
1446
|
*/
|
|
1397
1447
|
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);
|
|
1448
|
+
return this.store.getLatestBlockNumber();
|
|
1408
1449
|
}
|
|
1409
1450
|
|
|
1410
1451
|
public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
|
|
@@ -1514,24 +1555,24 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1514
1555
|
}
|
|
1515
1556
|
|
|
1516
1557
|
public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
|
|
1558
|
+
// TODO(pw/mbps): This still assumes 1 block per checkpoint
|
|
1517
1559
|
const currentBlocks = await this.getL2Tips();
|
|
1518
1560
|
const currentL2Block = currentBlocks.latest.number;
|
|
1519
1561
|
const currentProvenBlock = currentBlocks.proven.number;
|
|
1520
|
-
// const currentFinalizedBlock = currentBlocks.finalized.number;
|
|
1521
1562
|
|
|
1522
1563
|
if (targetL2BlockNumber >= currentL2Block) {
|
|
1523
1564
|
throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
|
|
1524
1565
|
}
|
|
1525
1566
|
const blocksToUnwind = currentL2Block - targetL2BlockNumber;
|
|
1526
|
-
const targetL2Block = await this.store.
|
|
1567
|
+
const targetL2Block = await this.store.getCheckpointedBlock(targetL2BlockNumber);
|
|
1527
1568
|
if (!targetL2Block) {
|
|
1528
1569
|
throw new Error(`Target L2 block ${targetL2BlockNumber} not found`);
|
|
1529
1570
|
}
|
|
1530
1571
|
const targetL1BlockNumber = targetL2Block.l1.blockNumber;
|
|
1531
1572
|
const targetCheckpointNumber = CheckpointNumber.fromBlockNumber(targetL2BlockNumber);
|
|
1532
1573
|
const targetL1BlockHash = await this.getL1BlockHash(targetL1BlockNumber);
|
|
1533
|
-
this.log.info(`Unwinding ${blocksToUnwind}
|
|
1534
|
-
await this.store.
|
|
1574
|
+
this.log.info(`Unwinding ${blocksToUnwind} checkpoints from L2 block ${currentL2Block}`);
|
|
1575
|
+
await this.store.unwindCheckpoints(CheckpointNumber(currentL2Block), blocksToUnwind);
|
|
1535
1576
|
this.log.info(`Unwinding L1 to L2 messages to checkpoint ${targetCheckpointNumber}`);
|
|
1536
1577
|
await this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
1537
1578
|
this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`);
|
|
@@ -1539,7 +1580,7 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1539
1580
|
await this.store.setMessageSynchedL1Block({ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash });
|
|
1540
1581
|
if (targetL2BlockNumber < currentProvenBlock) {
|
|
1541
1582
|
this.log.info(`Clearing proven L2 block number`);
|
|
1542
|
-
await this.store.
|
|
1583
|
+
await this.store.setProvenCheckpointNumber(CheckpointNumber.ZERO);
|
|
1543
1584
|
}
|
|
1544
1585
|
// TODO(palla/reorg): Set the finalized block when we add support for it.
|
|
1545
1586
|
// if (targetL2BlockNumber < currentFinalizedBlock) {
|
|
@@ -1547,6 +1588,150 @@ export class Archiver extends (EventEmitter as new () => ArchiverEmitter) implem
|
|
|
1547
1588
|
// await this.store.setFinalizedL2BlockNumber(0);
|
|
1548
1589
|
// }
|
|
1549
1590
|
}
|
|
1591
|
+
|
|
1592
|
+
public async getPublishedCheckpoints(
|
|
1593
|
+
checkpointNumber: CheckpointNumber,
|
|
1594
|
+
limit: number,
|
|
1595
|
+
): Promise<PublishedCheckpoint[]> {
|
|
1596
|
+
const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
|
|
1597
|
+
const blocks = (
|
|
1598
|
+
await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
|
|
1599
|
+
).filter(isDefined);
|
|
1600
|
+
|
|
1601
|
+
const fullCheckpoints: PublishedCheckpoint[] = [];
|
|
1602
|
+
for (let i = 0; i < checkpoints.length; i++) {
|
|
1603
|
+
const blocksForCheckpoint = blocks[i];
|
|
1604
|
+
const checkpoint = checkpoints[i];
|
|
1605
|
+
const fullCheckpoint = new Checkpoint(
|
|
1606
|
+
checkpoint.archive,
|
|
1607
|
+
checkpoint.header,
|
|
1608
|
+
blocksForCheckpoint,
|
|
1609
|
+
checkpoint.checkpointNumber,
|
|
1610
|
+
);
|
|
1611
|
+
const publishedCheckpoint = new PublishedCheckpoint(
|
|
1612
|
+
fullCheckpoint,
|
|
1613
|
+
checkpoint.l1,
|
|
1614
|
+
checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
|
|
1615
|
+
);
|
|
1616
|
+
fullCheckpoints.push(publishedCheckpoint);
|
|
1617
|
+
}
|
|
1618
|
+
return fullCheckpoints;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
public async getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
|
|
1622
|
+
const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
|
|
1623
|
+
const checkpoints: Checkpoint[] = [];
|
|
1624
|
+
|
|
1625
|
+
// Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
|
|
1626
|
+
// We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
|
|
1627
|
+
let checkpointData = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
|
|
1628
|
+
const slot = (b: CheckpointData) => b.header.slotNumber;
|
|
1629
|
+
while (checkpointData && slot(checkpointData) >= start) {
|
|
1630
|
+
if (slot(checkpointData) <= end) {
|
|
1631
|
+
// push the checkpoints on backwards
|
|
1632
|
+
const [checkpoint] = await this.getPublishedCheckpoints(checkpointData.checkpointNumber, 1);
|
|
1633
|
+
checkpoints.push(checkpoint.checkpoint);
|
|
1634
|
+
}
|
|
1635
|
+
checkpointData = await this.store.getCheckpointData(CheckpointNumber(checkpointData.checkpointNumber - 1));
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
return checkpoints.reverse();
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/* Legacy APIs */
|
|
1642
|
+
|
|
1643
|
+
public async getPublishedBlockByHash(blockHash: Fr): Promise<PublishedL2Block | undefined> {
|
|
1644
|
+
const checkpointedBlock = await this.store.getCheckpointedBlockByHash(blockHash);
|
|
1645
|
+
return this.buildOldBlockFromCheckpointedBlock(checkpointedBlock);
|
|
1646
|
+
}
|
|
1647
|
+
public async getPublishedBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
|
|
1648
|
+
const checkpointedBlock = await this.store.getCheckpointedBlockByArchive(archive);
|
|
1649
|
+
return this.buildOldBlockFromCheckpointedBlock(checkpointedBlock);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
/**
|
|
1653
|
+
* Gets up to `limit` amount of L2 blocks starting from `from`.
|
|
1654
|
+
* @param from - Number of the first block to return (inclusive).
|
|
1655
|
+
* @param limit - The number of blocks to return.
|
|
1656
|
+
* @param proven - If true, only return blocks that have been proven.
|
|
1657
|
+
* @returns The requested L2 blocks.
|
|
1658
|
+
*/
|
|
1659
|
+
public async getBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<L2Block[]> {
|
|
1660
|
+
const publishedBlocks = await this.getPublishedBlocks(from, limit, proven);
|
|
1661
|
+
return publishedBlocks.map(x => x.block);
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
public async getPublishedBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<PublishedL2Block[]> {
|
|
1665
|
+
const checkpoints = await this.store.getRangeOfCheckpoints(CheckpointNumber(from), limit);
|
|
1666
|
+
const provenCheckpointNumber = await this.getProvenCheckpointNumber();
|
|
1667
|
+
const blocks = (
|
|
1668
|
+
await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
|
|
1669
|
+
).filter(isDefined);
|
|
1670
|
+
|
|
1671
|
+
const olbBlocks: PublishedL2Block[] = [];
|
|
1672
|
+
for (let i = 0; i < checkpoints.length; i++) {
|
|
1673
|
+
const blockForCheckpoint = blocks[i][0];
|
|
1674
|
+
const checkpoint = checkpoints[i];
|
|
1675
|
+
if (checkpoint.checkpointNumber > provenCheckpointNumber && proven === true) {
|
|
1676
|
+
// this checkpointisn't proven and we only want proven
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1679
|
+
const oldCheckpoint = new Checkpoint(
|
|
1680
|
+
blockForCheckpoint.archive,
|
|
1681
|
+
checkpoint.header,
|
|
1682
|
+
[blockForCheckpoint],
|
|
1683
|
+
checkpoint.checkpointNumber,
|
|
1684
|
+
);
|
|
1685
|
+
const oldBlock = L2Block.fromCheckpoint(oldCheckpoint);
|
|
1686
|
+
const publishedBlock = new PublishedL2Block(
|
|
1687
|
+
oldBlock,
|
|
1688
|
+
checkpoint.l1,
|
|
1689
|
+
checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
|
|
1690
|
+
);
|
|
1691
|
+
olbBlocks.push(publishedBlock);
|
|
1692
|
+
}
|
|
1693
|
+
return olbBlocks;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
private async buildOldBlockFromCheckpointedBlock(
|
|
1697
|
+
checkpointedBlock: CheckpointedL2Block | undefined,
|
|
1698
|
+
): Promise<PublishedL2Block | undefined> {
|
|
1699
|
+
if (!checkpointedBlock) {
|
|
1700
|
+
return undefined;
|
|
1701
|
+
}
|
|
1702
|
+
const checkpoint = await this.store.getCheckpointData(checkpointedBlock.checkpointNumber);
|
|
1703
|
+
if (!checkpoint) {
|
|
1704
|
+
return checkpoint;
|
|
1705
|
+
}
|
|
1706
|
+
const fullCheckpoint = new Checkpoint(
|
|
1707
|
+
checkpointedBlock?.block.archive,
|
|
1708
|
+
checkpoint?.header,
|
|
1709
|
+
[checkpointedBlock.block],
|
|
1710
|
+
checkpoint.checkpointNumber,
|
|
1711
|
+
);
|
|
1712
|
+
const oldBlock = L2Block.fromCheckpoint(fullCheckpoint);
|
|
1713
|
+
const published = new PublishedL2Block(
|
|
1714
|
+
oldBlock,
|
|
1715
|
+
checkpoint.l1,
|
|
1716
|
+
checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
|
|
1717
|
+
);
|
|
1718
|
+
return published;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
public async getBlock(number: BlockNumber): Promise<L2Block | undefined> {
|
|
1722
|
+
// If the number provided is -ve, then return the latest block.
|
|
1723
|
+
if (number < 0) {
|
|
1724
|
+
number = await this.store.getSynchedL2BlockNumber();
|
|
1725
|
+
}
|
|
1726
|
+
if (number === 0) {
|
|
1727
|
+
return undefined;
|
|
1728
|
+
}
|
|
1729
|
+
const publishedBlocks = await this.getPublishedBlocks(number, 1);
|
|
1730
|
+
if (publishedBlocks.length === 0) {
|
|
1731
|
+
return undefined;
|
|
1732
|
+
}
|
|
1733
|
+
return publishedBlocks[0].block;
|
|
1734
|
+
}
|
|
1550
1735
|
}
|
|
1551
1736
|
|
|
1552
1737
|
enum Operation {
|
|
@@ -1577,11 +1762,13 @@ export class ArchiverStoreHelper
|
|
|
1577
1762
|
| 'close'
|
|
1578
1763
|
| 'transactionAsync'
|
|
1579
1764
|
| 'addBlocks'
|
|
1765
|
+
| 'getBlock'
|
|
1766
|
+
| 'getBlocks'
|
|
1580
1767
|
>
|
|
1581
1768
|
{
|
|
1582
1769
|
#log = createLogger('archiver:block-helper');
|
|
1583
1770
|
|
|
1584
|
-
constructor(
|
|
1771
|
+
constructor(public readonly store: ArchiverDataStore) {}
|
|
1585
1772
|
|
|
1586
1773
|
/**
|
|
1587
1774
|
* Extracts and stores contract classes out of ContractClassPublished events emitted by the class registry contract.
|
|
@@ -1719,7 +1906,23 @@ export class ArchiverStoreHelper
|
|
|
1719
1906
|
return true;
|
|
1720
1907
|
}
|
|
1721
1908
|
|
|
1722
|
-
|
|
1909
|
+
private async addBlockDataToDB(block: L2BlockNew) {
|
|
1910
|
+
const contractClassLogs = block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
|
|
1911
|
+
// ContractInstancePublished event logs are broadcast in privateLogs.
|
|
1912
|
+
const privateLogs = block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
|
|
1913
|
+
const publicLogs = block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
|
|
1914
|
+
|
|
1915
|
+
return (
|
|
1916
|
+
await Promise.all([
|
|
1917
|
+
this.#updatePublishedContractClasses(contractClassLogs, block.number, Operation.Store),
|
|
1918
|
+
this.#updateDeployedContractInstances(privateLogs, block.number, Operation.Store),
|
|
1919
|
+
this.#updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, Operation.Store),
|
|
1920
|
+
this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.number),
|
|
1921
|
+
])
|
|
1922
|
+
).every(Boolean);
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?: ValidateBlockResult): Promise<boolean> {
|
|
1723
1926
|
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1724
1927
|
// or if the previous block is not in the store.
|
|
1725
1928
|
return this.store.transactionAsync(async () => {
|
|
@@ -1729,25 +1932,10 @@ export class ArchiverStoreHelper
|
|
|
1729
1932
|
// Update the pending chain validation status if provided
|
|
1730
1933
|
pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
|
|
1731
1934
|
// Add any logs emitted during the retrieved blocks
|
|
1732
|
-
this.store.addLogs(blocks
|
|
1935
|
+
this.store.addLogs(blocks),
|
|
1733
1936
|
// 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);
|
|
1937
|
+
...blocks.map(block => {
|
|
1938
|
+
return this.addBlockDataToDB(block);
|
|
1751
1939
|
}),
|
|
1752
1940
|
]);
|
|
1753
1941
|
|
|
@@ -1755,59 +1943,102 @@ export class ArchiverStoreHelper
|
|
|
1755
1943
|
});
|
|
1756
1944
|
}
|
|
1757
1945
|
|
|
1758
|
-
public
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1946
|
+
public addCheckpoints(
|
|
1947
|
+
checkpoints: PublishedCheckpoint[],
|
|
1948
|
+
pendingChainValidationStatus?: ValidateBlockResult,
|
|
1949
|
+
): Promise<boolean> {
|
|
1950
|
+
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1951
|
+
// or if the previous block is not in the store.
|
|
1952
|
+
return this.store.transactionAsync(async () => {
|
|
1953
|
+
await this.store.addCheckpoints(checkpoints);
|
|
1954
|
+
const allBlocks = checkpoints.flatMap((ch: PublishedCheckpoint) => ch.checkpoint.blocks);
|
|
1955
|
+
|
|
1956
|
+
const opResults = await Promise.all([
|
|
1957
|
+
// Update the pending chain validation status if provided
|
|
1958
|
+
pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
|
|
1959
|
+
// Add any logs emitted during the retrieved blocks
|
|
1960
|
+
this.store.addLogs(allBlocks),
|
|
1961
|
+
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
1962
|
+
...allBlocks.map(block => {
|
|
1963
|
+
return this.addBlockDataToDB(block);
|
|
1964
|
+
}),
|
|
1965
|
+
]);
|
|
1966
|
+
|
|
1967
|
+
return opResults.every(Boolean);
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
public async unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number): Promise<boolean> {
|
|
1972
|
+
if (checkpointsToUnwind <= 0) {
|
|
1973
|
+
throw new Error(`Cannot unwind ${checkpointsToUnwind} blocks`);
|
|
1762
1974
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1975
|
+
|
|
1976
|
+
const last = await this.getSynchedCheckpointNumber();
|
|
1977
|
+
if (from != last) {
|
|
1978
|
+
throw new Error(`Cannot unwind checkpoints from checkpoint ${from} when the last checkpoint is ${last}`);
|
|
1765
1979
|
}
|
|
1766
1980
|
|
|
1767
|
-
|
|
1768
|
-
const
|
|
1981
|
+
const blocks = [];
|
|
1982
|
+
const lastCheckpointNumber = from + checkpointsToUnwind - 1;
|
|
1983
|
+
for (let checkpointNumber = from; checkpointNumber <= lastCheckpointNumber; checkpointNumber++) {
|
|
1984
|
+
const blocksForCheckpoint = await this.store.getBlocksForCheckpoint(checkpointNumber);
|
|
1985
|
+
if (!blocksForCheckpoint) {
|
|
1986
|
+
continue;
|
|
1987
|
+
}
|
|
1988
|
+
blocks.push(...blocksForCheckpoint);
|
|
1989
|
+
}
|
|
1769
1990
|
|
|
1770
1991
|
const opResults = await Promise.all([
|
|
1771
1992
|
// Prune rolls back to the last proven block, which is by definition valid
|
|
1772
1993
|
this.store.setPendingChainValidationStatus({ valid: true }),
|
|
1773
1994
|
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
1774
1995
|
...blocks.map(async block => {
|
|
1775
|
-
const contractClassLogs = block.
|
|
1996
|
+
const contractClassLogs = block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
|
|
1776
1997
|
// ContractInstancePublished event logs are broadcast in privateLogs.
|
|
1777
|
-
const privateLogs = block.
|
|
1778
|
-
const publicLogs = block.
|
|
1998
|
+
const privateLogs = block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
|
|
1999
|
+
const publicLogs = block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
|
|
1779
2000
|
|
|
1780
2001
|
return (
|
|
1781
2002
|
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
|
-
),
|
|
2003
|
+
this.#updatePublishedContractClasses(contractClassLogs, block.number, Operation.Delete),
|
|
2004
|
+
this.#updateDeployedContractInstances(privateLogs, block.number, Operation.Delete),
|
|
2005
|
+
this.#updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, Operation.Delete),
|
|
1789
2006
|
])
|
|
1790
2007
|
).every(Boolean);
|
|
1791
2008
|
}),
|
|
1792
2009
|
|
|
1793
|
-
this.store.deleteLogs(blocks
|
|
1794
|
-
this.store.
|
|
2010
|
+
this.store.deleteLogs(blocks),
|
|
2011
|
+
this.store.unwindCheckpoints(from, checkpointsToUnwind),
|
|
1795
2012
|
]);
|
|
1796
2013
|
|
|
1797
2014
|
return opResults.every(Boolean);
|
|
1798
2015
|
}
|
|
1799
2016
|
|
|
1800
|
-
|
|
1801
|
-
return this.store.
|
|
2017
|
+
getCheckpointData(checkpointNumber: CheckpointNumber): Promise<CheckpointData | undefined> {
|
|
2018
|
+
return this.store.getCheckpointData(checkpointNumber);
|
|
1802
2019
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
2020
|
+
|
|
2021
|
+
getRangeOfCheckpoints(from: CheckpointNumber, limit: number): Promise<CheckpointData[]> {
|
|
2022
|
+
return this.store.getRangeOfCheckpoints(from, limit);
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
getCheckpointedL2BlockNumber(): Promise<BlockNumber> {
|
|
2026
|
+
return this.store.getCheckpointedL2BlockNumber();
|
|
2027
|
+
}
|
|
2028
|
+
getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
2029
|
+
return this.store.getSynchedCheckpointNumber();
|
|
2030
|
+
}
|
|
2031
|
+
setCheckpointSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
|
|
2032
|
+
return this.store.setCheckpointSynchedL1BlockNumber(l1BlockNumber);
|
|
2033
|
+
}
|
|
2034
|
+
getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
|
|
2035
|
+
return this.store.getCheckpointedBlock(number);
|
|
1805
2036
|
}
|
|
1806
|
-
|
|
1807
|
-
return this.store.
|
|
2037
|
+
getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
2038
|
+
return this.store.getCheckpointedBlockByHash(blockHash);
|
|
1808
2039
|
}
|
|
1809
|
-
|
|
1810
|
-
return this.store.
|
|
2040
|
+
getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
2041
|
+
return this.store.getCheckpointedBlockByArchive(archive);
|
|
1811
2042
|
}
|
|
1812
2043
|
getBlockHeaders(from: BlockNumber, limit: number): Promise<BlockHeader[]> {
|
|
1813
2044
|
return this.store.getBlockHeaders(from, limit);
|
|
@@ -1818,6 +2049,18 @@ export class ArchiverStoreHelper
|
|
|
1818
2049
|
getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
1819
2050
|
return this.store.getBlockHeaderByArchive(archive);
|
|
1820
2051
|
}
|
|
2052
|
+
getBlockByHash(blockHash: Fr): Promise<L2BlockNew | undefined> {
|
|
2053
|
+
return this.store.getBlockByHash(blockHash);
|
|
2054
|
+
}
|
|
2055
|
+
getBlockByArchive(archive: Fr): Promise<L2BlockNew | undefined> {
|
|
2056
|
+
return this.store.getBlockByArchive(archive);
|
|
2057
|
+
}
|
|
2058
|
+
getLatestBlockNumber(): Promise<BlockNumber> {
|
|
2059
|
+
return this.store.getLatestBlockNumber();
|
|
2060
|
+
}
|
|
2061
|
+
getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2BlockNew[] | undefined> {
|
|
2062
|
+
return this.store.getBlocksForCheckpoint(checkpointNumber);
|
|
2063
|
+
}
|
|
1821
2064
|
getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
|
|
1822
2065
|
return this.store.getTxEffect(txHash);
|
|
1823
2066
|
}
|
|
@@ -1833,8 +2076,11 @@ export class ArchiverStoreHelper
|
|
|
1833
2076
|
getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
|
|
1834
2077
|
return this.store.getL1ToL2MessageIndex(l1ToL2Message);
|
|
1835
2078
|
}
|
|
1836
|
-
|
|
1837
|
-
return this.store.
|
|
2079
|
+
getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
|
|
2080
|
+
return this.store.getPrivateLogsByTags(tags);
|
|
2081
|
+
}
|
|
2082
|
+
getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
|
|
2083
|
+
return this.store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
1838
2084
|
}
|
|
1839
2085
|
getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
1840
2086
|
return this.store.getPublicLogs(filter);
|
|
@@ -1843,16 +2089,19 @@ export class ArchiverStoreHelper
|
|
|
1843
2089
|
return this.store.getContractClassLogs(filter);
|
|
1844
2090
|
}
|
|
1845
2091
|
getSynchedL2BlockNumber(): Promise<BlockNumber> {
|
|
1846
|
-
return this.store.
|
|
2092
|
+
return this.store.getCheckpointedL2BlockNumber();
|
|
2093
|
+
}
|
|
2094
|
+
getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
2095
|
+
return this.store.getProvenCheckpointNumber();
|
|
1847
2096
|
}
|
|
1848
|
-
|
|
1849
|
-
return this.store.
|
|
2097
|
+
getProvenBlockNumber(): Promise<BlockNumber> {
|
|
2098
|
+
return this.store.getProvenBlockNumber();
|
|
1850
2099
|
}
|
|
1851
|
-
|
|
1852
|
-
return this.store.
|
|
2100
|
+
setProvenCheckpointNumber(checkpointNumber: CheckpointNumber): Promise<void> {
|
|
2101
|
+
return this.store.setProvenCheckpointNumber(checkpointNumber);
|
|
1853
2102
|
}
|
|
1854
2103
|
setBlockSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
|
|
1855
|
-
return this.store.
|
|
2104
|
+
return this.store.setCheckpointSynchedL1BlockNumber(l1BlockNumber);
|
|
1856
2105
|
}
|
|
1857
2106
|
setMessageSynchedL1Block(l1Block: L1BlockId): Promise<void> {
|
|
1858
2107
|
return this.store.setMessageSynchedL1Block(l1Block);
|