@aztec/archiver 4.0.0-nightly.20260112 → 4.0.0-nightly.20260114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -22
- package/dest/archiver/archive_source_base.d.ts +75 -0
- package/dest/archiver/archive_source_base.d.ts.map +1 -0
- package/dest/archiver/archive_source_base.js +202 -0
- package/dest/archiver/archiver.d.ts +32 -168
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +123 -613
- package/dest/archiver/archiver_store_updates.d.ts +38 -0
- package/dest/archiver/archiver_store_updates.d.ts.map +1 -0
- package/dest/archiver/archiver_store_updates.js +212 -0
- package/dest/archiver/config.js +2 -2
- package/dest/archiver/index.d.ts +3 -2
- package/dest/archiver/index.d.ts.map +1 -1
- package/dest/archiver/index.js +2 -0
- package/dest/archiver/kv_archiver_store/block_store.d.ts +12 -5
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +23 -4
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +173 -12
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +159 -48
- package/dest/archiver/l1/calldata_retriever.d.ts +2 -2
- package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/archiver/l1/calldata_retriever.js +2 -2
- package/dest/archiver/l1/data_retrieval.d.ts +9 -11
- package/dest/archiver/l1/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/l1/data_retrieval.js +32 -51
- package/dest/archiver/l1/validate_trace.js +1 -1
- package/dest/archiver/test/fake_l1_state.d.ts +173 -0
- package/dest/archiver/test/fake_l1_state.d.ts.map +1 -0
- package/dest/archiver/test/fake_l1_state.js +364 -0
- package/dest/archiver/validation.d.ts +4 -4
- package/dest/archiver/validation.d.ts.map +1 -1
- package/dest/archiver/validation.js +1 -1
- package/dest/test/mock_l1_to_l2_message_source.d.ts +2 -2
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +12 -3
- package/dest/test/mock_l2_block_source.d.ts +8 -4
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +65 -19
- package/package.json +13 -13
- package/src/archiver/archive_source_base.ts +339 -0
- package/src/archiver/archiver.ts +166 -815
- package/src/archiver/archiver_store_updates.ts +321 -0
- package/src/archiver/config.ts +2 -2
- package/src/archiver/index.ts +2 -1
- package/src/archiver/kv_archiver_store/block_store.ts +36 -8
- package/src/archiver/kv_archiver_store/contract_class_store.ts +1 -1
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +1 -1
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +177 -11
- package/src/archiver/l1/calldata_retriever.ts +2 -2
- package/src/archiver/l1/data_retrieval.ts +51 -68
- package/src/archiver/l1/validate_trace.ts +1 -1
- package/src/archiver/test/fake_l1_state.ts +561 -0
- package/src/archiver/validation.ts +6 -6
- package/src/test/mock_l1_to_l2_message_source.ts +10 -4
- package/src/test/mock_l2_block_source.ts +76 -18
- package/dest/archiver/archiver_store.d.ts +0 -308
- package/dest/archiver/archiver_store.d.ts.map +0 -1
- package/dest/archiver/archiver_store.js +0 -4
- package/dest/archiver/archiver_store_test_suite.d.ts +0 -8
- package/dest/archiver/archiver_store_test_suite.d.ts.map +0 -1
- package/dest/archiver/archiver_store_test_suite.js +0 -2770
- package/src/archiver/archiver_store.ts +0 -372
- package/src/archiver/archiver_store_test_suite.ts +0 -2843
package/src/archiver/archiver.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
|
-
import { GENESIS_BLOCK_HEADER_HASH } from '@aztec/constants';
|
|
2
|
+
import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
3
3
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
4
4
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
5
5
|
import { BlockTagTooOldError, InboxContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
@@ -11,49 +11,26 @@ import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/f
|
|
|
11
11
|
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
12
12
|
import { merge, pick } from '@aztec/foundation/collection';
|
|
13
13
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
14
|
-
import
|
|
14
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
15
15
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
16
16
|
import { type PromiseWithResolvers, promiseWithResolvers } from '@aztec/foundation/promise';
|
|
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
20
|
import { isDefined } from '@aztec/foundation/types';
|
|
21
|
-
import type { CustomRange } from '@aztec/kv-store';
|
|
22
|
-
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
23
|
-
import {
|
|
24
|
-
ContractClassPublishedEvent,
|
|
25
|
-
PrivateFunctionBroadcastedEvent,
|
|
26
|
-
UtilityFunctionBroadcastedEvent,
|
|
27
|
-
} from '@aztec/protocol-contracts/class-registry';
|
|
28
|
-
import {
|
|
29
|
-
ContractInstancePublishedEvent,
|
|
30
|
-
ContractInstanceUpdatedEvent,
|
|
31
|
-
} from '@aztec/protocol-contracts/instance-registry';
|
|
32
|
-
import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
33
|
-
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
34
21
|
import {
|
|
35
22
|
type ArchiverEmitter,
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
type CheckpointId,
|
|
24
|
+
GENESIS_CHECKPOINT_HEADER_HASH,
|
|
38
25
|
L2Block,
|
|
39
26
|
L2BlockNew,
|
|
40
27
|
type L2BlockSink,
|
|
41
28
|
type L2BlockSource,
|
|
42
29
|
L2BlockSourceEvents,
|
|
43
30
|
type L2Tips,
|
|
44
|
-
PublishedL2Block,
|
|
45
31
|
} from '@aztec/stdlib/block';
|
|
46
32
|
import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
47
|
-
import {
|
|
48
|
-
type ContractClassPublic,
|
|
49
|
-
type ContractDataSource,
|
|
50
|
-
type ContractInstanceWithAddress,
|
|
51
|
-
type ExecutablePrivateFunctionWithMembershipProof,
|
|
52
|
-
type UtilityFunctionWithMembershipProof,
|
|
53
|
-
computePublicBytecodeCommitment,
|
|
54
|
-
isValidPrivateFunctionMembershipProof,
|
|
55
|
-
isValidUtilityFunctionMembershipProof,
|
|
56
|
-
} from '@aztec/stdlib/contract';
|
|
33
|
+
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
57
34
|
import {
|
|
58
35
|
type L1RollupConstants,
|
|
59
36
|
getEpochAtSlot,
|
|
@@ -62,21 +39,11 @@ import {
|
|
|
62
39
|
getSlotRangeForEpoch,
|
|
63
40
|
getTimestampRangeForEpoch,
|
|
64
41
|
} from '@aztec/stdlib/epoch-helpers';
|
|
65
|
-
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
66
42
|
import type { L2LogsSource } from '@aztec/stdlib/interfaces/server';
|
|
67
|
-
import {
|
|
68
|
-
|
|
69
|
-
type LogFilter,
|
|
70
|
-
type PrivateLog,
|
|
71
|
-
type PublicLog,
|
|
72
|
-
type SiloedTag,
|
|
73
|
-
Tag,
|
|
74
|
-
TxScopedL2Log,
|
|
75
|
-
} from '@aztec/stdlib/logs';
|
|
76
|
-
import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
43
|
+
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
44
|
+
import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
77
45
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
78
|
-
import {
|
|
79
|
-
import type { UInt64 } from '@aztec/stdlib/types';
|
|
46
|
+
import type { BlockHeader } from '@aztec/stdlib/tx';
|
|
80
47
|
import {
|
|
81
48
|
type TelemetryClient,
|
|
82
49
|
type Traceable,
|
|
@@ -87,14 +54,19 @@ import {
|
|
|
87
54
|
} from '@aztec/telemetry-client';
|
|
88
55
|
|
|
89
56
|
import { EventEmitter } from 'events';
|
|
90
|
-
import
|
|
91
|
-
import { type GetContractReturnType, type Hex, createPublicClient, fallback, http } from 'viem';
|
|
57
|
+
import { type Hex, createPublicClient, fallback, http } from 'viem';
|
|
92
58
|
|
|
93
|
-
import
|
|
59
|
+
import { ArchiveSourceBase } from './archive_source_base.js';
|
|
60
|
+
import {
|
|
61
|
+
addBlocksWithContractData,
|
|
62
|
+
addCheckpointsWithContractData,
|
|
63
|
+
unwindCheckpointsWithContractData,
|
|
64
|
+
} from './archiver_store_updates.js';
|
|
94
65
|
import type { ArchiverConfig } from './config.js';
|
|
95
66
|
import { InitialCheckpointNumberNotSequentialError, NoBlobBodiesFoundError } from './errors.js';
|
|
96
67
|
import { ArchiverInstrumentation } from './instrumentation.js';
|
|
97
68
|
import type { CheckpointData } from './kv_archiver_store/block_store.js';
|
|
69
|
+
import type { KVArchiverDataStore } from './kv_archiver_store/kv_archiver_store.js';
|
|
98
70
|
import {
|
|
99
71
|
retrieveCheckpointsFromRollup,
|
|
100
72
|
retrieveL1ToL2Message,
|
|
@@ -103,7 +75,7 @@ import {
|
|
|
103
75
|
} from './l1/data_retrieval.js';
|
|
104
76
|
import { validateAndLogTraceAvailability } from './l1/validate_trace.js';
|
|
105
77
|
import type { InboxMessage } from './structs/inbox_message.js';
|
|
106
|
-
import { type
|
|
78
|
+
import { type ValidateCheckpointResult, validateCheckpointAttestations } from './validation.js';
|
|
107
79
|
|
|
108
80
|
/**
|
|
109
81
|
* Helper interface to combine all sources this archiver implementation provides.
|
|
@@ -128,7 +100,7 @@ function mapArchiverConfig(config: Partial<ArchiverConfig>) {
|
|
|
128
100
|
return {
|
|
129
101
|
pollingIntervalMs: config.archiverPollingIntervalMS,
|
|
130
102
|
batchSize: config.archiverBatchSize,
|
|
131
|
-
|
|
103
|
+
skipValidateCheckpointAttestations: config.skipValidateCheckpointAttestations,
|
|
132
104
|
maxAllowedEthClientDriftSeconds: config.maxAllowedEthClientDriftSeconds,
|
|
133
105
|
ethereumAllowNoDebugHosts: config.ethereumAllowNoDebugHosts,
|
|
134
106
|
};
|
|
@@ -139,7 +111,7 @@ type RollupStatus = {
|
|
|
139
111
|
provenArchive: Hex;
|
|
140
112
|
pendingCheckpointNumber: CheckpointNumber;
|
|
141
113
|
pendingArchive: Hex;
|
|
142
|
-
validationResult:
|
|
114
|
+
validationResult: ValidateCheckpointResult | undefined;
|
|
143
115
|
lastRetrievedCheckpoint?: PublishedCheckpoint;
|
|
144
116
|
lastL1BlockWithCheckpoint?: bigint;
|
|
145
117
|
};
|
|
@@ -149,18 +121,13 @@ type RollupStatus = {
|
|
|
149
121
|
* Responsible for handling robust L1 polling so that other components do not need to
|
|
150
122
|
* concern themselves with it.
|
|
151
123
|
*/
|
|
152
|
-
export class Archiver
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
124
|
+
export class Archiver extends ArchiveSourceBase implements L2BlockSink, Traceable {
|
|
125
|
+
/** Event emitter for archiver events (L2BlockProven, L2PruneDetected, etc). */
|
|
126
|
+
public readonly events: ArchiverEmitter = new EventEmitter() as ArchiverEmitter;
|
|
127
|
+
|
|
156
128
|
/** A loop in which we will be continually fetching new checkpoints. */
|
|
157
129
|
private runningPromise: RunningPromise;
|
|
158
130
|
|
|
159
|
-
private rollup: RollupContract;
|
|
160
|
-
private inbox: InboxContract;
|
|
161
|
-
|
|
162
|
-
private store: ArchiverStoreHelper;
|
|
163
|
-
|
|
164
131
|
private l1BlockNumber: bigint | undefined;
|
|
165
132
|
private l1Timestamp: bigint | undefined;
|
|
166
133
|
private initialSyncComplete: boolean = false;
|
|
@@ -175,25 +142,32 @@ export class Archiver
|
|
|
175
142
|
* Creates a new instance of the Archiver.
|
|
176
143
|
* @param publicClient - A client for interacting with the Ethereum node.
|
|
177
144
|
* @param debugClient - A client for interacting with the Ethereum node for debug/trace methods.
|
|
178
|
-
* @param
|
|
179
|
-
* @param
|
|
180
|
-
* @param
|
|
181
|
-
* @param
|
|
182
|
-
* @param
|
|
145
|
+
* @param rollup - Rollup contract instance.
|
|
146
|
+
* @param inbox - Inbox contract instance.
|
|
147
|
+
* @param l1Addresses - L1 contract addresses (registry, governance proposer, slash factory, slashing proposer).
|
|
148
|
+
* @param dataStore - An archiver data store for storage & retrieval of blocks, encrypted logs & contract data.
|
|
149
|
+
* @param config - Archiver configuration options.
|
|
150
|
+
* @param blobClient - Client for retrieving blob data.
|
|
151
|
+
* @param epochCache - Cache for epoch-related data.
|
|
152
|
+
* @param dateProvider - Provider for current date/time.
|
|
153
|
+
* @param instrumentation - Instrumentation for metrics and tracing.
|
|
154
|
+
* @param l1constants - L1 rollup constants.
|
|
183
155
|
* @param log - A logger.
|
|
184
156
|
*/
|
|
185
157
|
constructor(
|
|
186
158
|
private readonly publicClient: ViemPublicClient,
|
|
187
159
|
private readonly debugClient: ViemPublicDebugClient,
|
|
160
|
+
private readonly rollup: RollupContract,
|
|
161
|
+
private readonly inbox: InboxContract,
|
|
188
162
|
private readonly l1Addresses: Pick<
|
|
189
163
|
L1ContractAddresses,
|
|
190
|
-
'
|
|
164
|
+
'registryAddress' | 'governanceProposerAddress' | 'slashFactoryAddress'
|
|
191
165
|
> & { slashingProposerAddress: EthAddress },
|
|
192
|
-
readonly dataStore:
|
|
166
|
+
readonly dataStore: KVArchiverDataStore,
|
|
193
167
|
private config: {
|
|
194
168
|
pollingIntervalMs: number;
|
|
195
169
|
batchSize: number;
|
|
196
|
-
|
|
170
|
+
skipValidateCheckpointAttestations?: boolean;
|
|
197
171
|
maxAllowedEthClientDriftSeconds: number;
|
|
198
172
|
ethereumAllowNoDebugHosts?: boolean;
|
|
199
173
|
},
|
|
@@ -204,13 +178,9 @@ export class Archiver
|
|
|
204
178
|
private readonly l1constants: L1RollupConstants & { l1StartBlockHash: Buffer32; genesisArchiveRoot: Fr },
|
|
205
179
|
private readonly log: Logger = createLogger('archiver'),
|
|
206
180
|
) {
|
|
207
|
-
super();
|
|
181
|
+
super(dataStore);
|
|
208
182
|
|
|
209
183
|
this.tracer = instrumentation.tracer;
|
|
210
|
-
this.store = new ArchiverStoreHelper(dataStore);
|
|
211
|
-
|
|
212
|
-
this.rollup = new RollupContract(publicClient, l1Addresses.rollupAddress);
|
|
213
|
-
this.inbox = new InboxContract(publicClient, l1Addresses.inboxAddress);
|
|
214
184
|
this.initialSyncPromise = promiseWithResolvers();
|
|
215
185
|
|
|
216
186
|
// Running promise starts with a small interval inbetween runs, so all iterations needed for the initial sync
|
|
@@ -232,7 +202,7 @@ export class Archiver
|
|
|
232
202
|
*/
|
|
233
203
|
public static async createAndSync(
|
|
234
204
|
config: ArchiverConfig,
|
|
235
|
-
archiverStore:
|
|
205
|
+
archiverStore: KVArchiverDataStore,
|
|
236
206
|
deps: ArchiverDeps,
|
|
237
207
|
blockUntilSynced = true,
|
|
238
208
|
): Promise<Archiver> {
|
|
@@ -252,6 +222,7 @@ export class Archiver
|
|
|
252
222
|
}) as ViemPublicDebugClient;
|
|
253
223
|
|
|
254
224
|
const rollup = new RollupContract(publicClient, config.l1Contracts.rollupAddress);
|
|
225
|
+
const inbox = new InboxContract(publicClient, config.l1Contracts.inboxAddress);
|
|
255
226
|
|
|
256
227
|
const [l1StartBlock, l1GenesisTime, proofSubmissionEpochs, genesisArchiveRoot, slashingProposerAddress] =
|
|
257
228
|
await Promise.all([
|
|
@@ -295,6 +266,8 @@ export class Archiver
|
|
|
295
266
|
const archiver = new Archiver(
|
|
296
267
|
publicClient,
|
|
297
268
|
debugClient,
|
|
269
|
+
rollup,
|
|
270
|
+
inbox,
|
|
298
271
|
{ ...config.l1Contracts, slashingProposerAddress },
|
|
299
272
|
archiverStore,
|
|
300
273
|
opts,
|
|
@@ -331,7 +304,7 @@ export class Archiver
|
|
|
331
304
|
const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = l1StartBlock } = await this.store.getSynchPoint();
|
|
332
305
|
const currentL2Checkpoint = await this.getSynchedCheckpointNumber();
|
|
333
306
|
this.log.info(
|
|
334
|
-
`Starting archiver sync to rollup contract ${this.
|
|
307
|
+
`Starting archiver sync to rollup contract ${this.rollup.address} from L1 block ${blocksSynchedTo} and L2 checkpoint ${currentL2Checkpoint}`,
|
|
335
308
|
{ blocksSynchedTo, messagesSynchedTo, currentL2Checkpoint },
|
|
336
309
|
);
|
|
337
310
|
|
|
@@ -381,7 +354,7 @@ export class Archiver
|
|
|
381
354
|
// Process each block individually to properly resolve/reject each promise
|
|
382
355
|
for (const { block, resolve, reject } of queuedItems) {
|
|
383
356
|
try {
|
|
384
|
-
await this.store
|
|
357
|
+
await addBlocksWithContractData(this.store, [block]);
|
|
385
358
|
this.log.debug(`Added block ${block.number} to store`);
|
|
386
359
|
resolve();
|
|
387
360
|
} catch (err: any) {
|
|
@@ -517,9 +490,16 @@ export class Archiver
|
|
|
517
490
|
this.l1Timestamp = currentL1Timestamp;
|
|
518
491
|
this.l1BlockNumber = currentL1BlockNumber;
|
|
519
492
|
|
|
493
|
+
const l1BlockNumberAtEnd = await this.publicClient.getBlockNumber();
|
|
494
|
+
this.log.trace(`Archiver sync iteration complete`, {
|
|
495
|
+
l1BlockNumberAtStart: currentL1BlockNumber,
|
|
496
|
+
l1TimestampAtStart: currentL1Timestamp,
|
|
497
|
+
l1BlockNumberAtEnd,
|
|
498
|
+
});
|
|
499
|
+
|
|
520
500
|
// We resolve the initial sync only once we've caught up with the latest L1 block number (with 1 block grace)
|
|
521
501
|
// so if the initial sync took too long, we still go for another iteration.
|
|
522
|
-
if (!this.initialSyncComplete && currentL1BlockNumber + 1n >=
|
|
502
|
+
if (!this.initialSyncComplete && currentL1BlockNumber + 1n >= l1BlockNumberAtEnd) {
|
|
523
503
|
this.log.info(`Initial archiver sync to L1 block ${currentL1BlockNumber} complete`, {
|
|
524
504
|
l1BlockNumber: currentL1BlockNumber,
|
|
525
505
|
syncPoint: await this.store.getSynchPoint(),
|
|
@@ -593,14 +573,11 @@ export class Archiver
|
|
|
593
573
|
);
|
|
594
574
|
const newBlocks = blockPromises.filter(isDefined).flat();
|
|
595
575
|
|
|
596
|
-
// TODO(pw/mbps): Don't convert to legacy blocks here
|
|
597
|
-
const blocks: L2Block[] = (await Promise.all(newBlocks.map(x => this.getBlock(x.number)))).filter(isDefined);
|
|
598
|
-
|
|
599
576
|
// Emit an event for listening services to react to the chain prune
|
|
600
|
-
this.emit(L2BlockSourceEvents.L2PruneDetected, {
|
|
577
|
+
this.events.emit(L2BlockSourceEvents.L2PruneDetected, {
|
|
601
578
|
type: L2BlockSourceEvents.L2PruneDetected,
|
|
602
579
|
epochNumber: pruneFromEpochNumber,
|
|
603
|
-
blocks,
|
|
580
|
+
blocks: newBlocks,
|
|
604
581
|
});
|
|
605
582
|
|
|
606
583
|
this.log.debug(
|
|
@@ -615,7 +592,7 @@ export class Archiver
|
|
|
615
592
|
this.instrumentation.processPrune(timer.ms());
|
|
616
593
|
// TODO(palla/reorg): Do we need to set the block synched L1 block number here?
|
|
617
594
|
// Seems like the next iteration should handle this.
|
|
618
|
-
// await this.store.
|
|
595
|
+
// await this.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
|
|
619
596
|
}
|
|
620
597
|
|
|
621
598
|
return { rollupCanPrune };
|
|
@@ -694,7 +671,7 @@ export class Archiver
|
|
|
694
671
|
do {
|
|
695
672
|
[searchStartBlock, searchEndBlock] = this.nextRange(searchEndBlock, currentL1BlockNumber);
|
|
696
673
|
this.log.trace(`Retrieving L1 to L2 messages between L1 blocks ${searchStartBlock} and ${searchEndBlock}.`);
|
|
697
|
-
const messages = await retrieveL1ToL2Messages(this.inbox
|
|
674
|
+
const messages = await retrieveL1ToL2Messages(this.inbox, searchStartBlock, searchEndBlock);
|
|
698
675
|
this.log.verbose(
|
|
699
676
|
`Retrieved ${messages.length} new L1 to L2 messages between L1 blocks ${searchStartBlock} and ${searchEndBlock}.`,
|
|
700
677
|
);
|
|
@@ -734,7 +711,7 @@ export class Archiver
|
|
|
734
711
|
do {
|
|
735
712
|
[searchStartBlock, searchEndBlock] = this.nextRange(searchEndBlock, currentL1BlockNumber);
|
|
736
713
|
|
|
737
|
-
const message = await retrieveL1ToL2Message(this.inbox
|
|
714
|
+
const message = await retrieveL1ToL2Message(this.inbox, leaf, searchStartBlock, searchEndBlock);
|
|
738
715
|
|
|
739
716
|
if (message) {
|
|
740
717
|
return message;
|
|
@@ -793,7 +770,8 @@ export class Archiver
|
|
|
793
770
|
@trackSpan('Archiver.handleCheckpoints')
|
|
794
771
|
private async handleCheckpoints(blocksSynchedTo: bigint, currentL1BlockNumber: bigint): Promise<RollupStatus> {
|
|
795
772
|
const localPendingCheckpointNumber = await this.getSynchedCheckpointNumber();
|
|
796
|
-
const initialValidationResult:
|
|
773
|
+
const initialValidationResult: ValidateCheckpointResult | undefined =
|
|
774
|
+
await this.store.getPendingChainValidationStatus();
|
|
797
775
|
const {
|
|
798
776
|
provenCheckpointNumber,
|
|
799
777
|
provenArchive,
|
|
@@ -866,7 +844,7 @@ export class Archiver
|
|
|
866
844
|
localCheckpointForDestinationProvenCheckpointNumber.numBlocks -
|
|
867
845
|
1;
|
|
868
846
|
|
|
869
|
-
this.emit(L2BlockSourceEvents.L2BlockProven, {
|
|
847
|
+
this.events.emit(L2BlockSourceEvents.L2BlockProven, {
|
|
870
848
|
type: L2BlockSourceEvents.L2BlockProven,
|
|
871
849
|
blockNumber: BlockNumber(lastBlockNumberInCheckpoint),
|
|
872
850
|
slotNumber: provenSlotNumber,
|
|
@@ -910,7 +888,7 @@ export class Archiver
|
|
|
910
888
|
// However, in the re-org scenario, our L1 node is temporarily lying to us and we end up potentially missing checkpoints.
|
|
911
889
|
// We must only set this block number based on actually retrieved logs.
|
|
912
890
|
// TODO(#8621): Tackle this properly when we handle L1 Re-orgs.
|
|
913
|
-
// await this.store.
|
|
891
|
+
// await this.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
|
|
914
892
|
this.log.debug(`No checkpoints to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
|
|
915
893
|
return rollupStatus;
|
|
916
894
|
}
|
|
@@ -975,7 +953,7 @@ export class Archiver
|
|
|
975
953
|
// TODO(md): Retrieve from blob client then from consensus client, then from peers
|
|
976
954
|
const retrievedCheckpoints = await execInSpan(this.tracer, 'Archiver.retrieveCheckpointsFromRollup', () =>
|
|
977
955
|
retrieveCheckpointsFromRollup(
|
|
978
|
-
this.rollup
|
|
956
|
+
this.rollup,
|
|
979
957
|
this.publicClient,
|
|
980
958
|
this.debugClient,
|
|
981
959
|
this.blobClient,
|
|
@@ -1008,7 +986,7 @@ export class Archiver
|
|
|
1008
986
|
const validCheckpoints: PublishedCheckpoint[] = [];
|
|
1009
987
|
|
|
1010
988
|
for (const published of publishedCheckpoints) {
|
|
1011
|
-
const validationResult = this.config.
|
|
989
|
+
const validationResult = this.config.skipValidateCheckpointAttestations
|
|
1012
990
|
? { valid: true as const }
|
|
1013
991
|
: await validateCheckpointAttestations(published, this.epochCache, this.l1constants, this.log);
|
|
1014
992
|
|
|
@@ -1021,7 +999,7 @@ export class Archiver
|
|
|
1021
999
|
rollupStatus.validationResult?.valid !== validationResult.valid ||
|
|
1022
1000
|
(!rollupStatus.validationResult.valid &&
|
|
1023
1001
|
!validationResult.valid &&
|
|
1024
|
-
rollupStatus.validationResult.
|
|
1002
|
+
rollupStatus.validationResult.checkpoint.checkpointNumber === validationResult.checkpoint.checkpointNumber)
|
|
1025
1003
|
) {
|
|
1026
1004
|
rollupStatus.validationResult = validationResult;
|
|
1027
1005
|
}
|
|
@@ -1033,9 +1011,9 @@ export class Archiver
|
|
|
1033
1011
|
...pick(validationResult, 'reason'),
|
|
1034
1012
|
});
|
|
1035
1013
|
|
|
1036
|
-
// Emit event for invalid
|
|
1037
|
-
this.emit(L2BlockSourceEvents.
|
|
1038
|
-
type: L2BlockSourceEvents.
|
|
1014
|
+
// Emit event for invalid checkpoint detection
|
|
1015
|
+
this.events.emit(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, {
|
|
1016
|
+
type: L2BlockSourceEvents.InvalidAttestationsCheckpointDetected,
|
|
1039
1017
|
validationResult,
|
|
1040
1018
|
});
|
|
1041
1019
|
|
|
@@ -1095,7 +1073,7 @@ export class Archiver
|
|
|
1095
1073
|
? await this.store.getCheckpointData(CheckpointNumber(previousCheckpointNumber))
|
|
1096
1074
|
: undefined;
|
|
1097
1075
|
const updatedL1SyncPoint = previousCheckpoint?.l1.blockNumber ?? this.l1constants.l1StartBlock;
|
|
1098
|
-
await this.store.
|
|
1076
|
+
await this.store.setCheckpointSynchedL1BlockNumber(updatedL1SyncPoint);
|
|
1099
1077
|
this.log.warn(
|
|
1100
1078
|
`Attempting to insert checkpoint ${newCheckpointNumber} with previous block ${previousCheckpointNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`,
|
|
1101
1079
|
{
|
|
@@ -1166,7 +1144,7 @@ export class Archiver
|
|
|
1166
1144
|
...status,
|
|
1167
1145
|
},
|
|
1168
1146
|
);
|
|
1169
|
-
await this.store.
|
|
1147
|
+
await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
|
|
1170
1148
|
} else {
|
|
1171
1149
|
this.log.trace(`No new checkpoints behind L1 sync point to retrieve.`, {
|
|
1172
1150
|
latestLocalCheckpointNumber,
|
|
@@ -1209,7 +1187,7 @@ export class Archiver
|
|
|
1209
1187
|
}
|
|
1210
1188
|
|
|
1211
1189
|
public getRollupAddress(): Promise<EthAddress> {
|
|
1212
|
-
return Promise.resolve(this.
|
|
1190
|
+
return Promise.resolve(EthAddress.fromString(this.rollup.address));
|
|
1213
1191
|
}
|
|
1214
1192
|
|
|
1215
1193
|
public getRegistryAddress(): Promise<EthAddress> {
|
|
@@ -1349,7 +1327,7 @@ export class Archiver
|
|
|
1349
1327
|
}
|
|
1350
1328
|
|
|
1351
1329
|
public unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number): Promise<boolean> {
|
|
1352
|
-
return this.store
|
|
1330
|
+
return unwindCheckpointsWithContractData(this.store, from, checkpointsToUnwind);
|
|
1353
1331
|
}
|
|
1354
1332
|
|
|
1355
1333
|
public async getLastBlockNumberInCheckpoint(checkpointNumber: CheckpointNumber): Promise<BlockNumber | undefined> {
|
|
@@ -1362,171 +1340,20 @@ export class Archiver
|
|
|
1362
1340
|
|
|
1363
1341
|
public addCheckpoints(
|
|
1364
1342
|
checkpoints: PublishedCheckpoint[],
|
|
1365
|
-
pendingChainValidationStatus?:
|
|
1343
|
+
pendingChainValidationStatus?: ValidateCheckpointResult,
|
|
1366
1344
|
): Promise<boolean> {
|
|
1367
|
-
return this.store
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
public getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
|
|
1371
|
-
return this.store.getBlockHeaderByHash(blockHash);
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
public getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
1375
|
-
return this.store.getBlockHeaderByArchive(archive);
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
/**
|
|
1379
|
-
* Gets an l2 block.
|
|
1380
|
-
* @param number - The block number to return.
|
|
1381
|
-
* @returns The requested L2 block.
|
|
1382
|
-
*/
|
|
1383
|
-
public async getL2BlockNew(number: BlockNumber): Promise<L2BlockNew | undefined> {
|
|
1384
|
-
// If the number provided is -ve, then return the latest block.
|
|
1385
|
-
if (number < 0) {
|
|
1386
|
-
number = await this.store.getSynchedL2BlockNumber();
|
|
1387
|
-
}
|
|
1388
|
-
if (number === 0) {
|
|
1389
|
-
return undefined;
|
|
1390
|
-
}
|
|
1391
|
-
const publishedBlock = await this.store.store.getBlock(number);
|
|
1392
|
-
return publishedBlock;
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
|
|
1396
|
-
if (number === 'latest') {
|
|
1397
|
-
number = await this.store.getSynchedL2BlockNumber();
|
|
1398
|
-
}
|
|
1399
|
-
if (number === 0) {
|
|
1400
|
-
return undefined;
|
|
1401
|
-
}
|
|
1402
|
-
const headers = await this.store.getBlockHeaders(number, 1);
|
|
1403
|
-
return headers.length === 0 ? undefined : headers[0];
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
|
|
1407
|
-
return this.store.getCheckpointedBlock(number);
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1411
|
-
return this.store.getCheckpointedBlockByHash(blockHash);
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
getProvenBlockNumber(): Promise<BlockNumber> {
|
|
1415
|
-
return this.store.getProvenBlockNumber();
|
|
1416
|
-
}
|
|
1417
|
-
getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1418
|
-
return this.store.getCheckpointedBlockByArchive(archive);
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
public getTxEffect(txHash: TxHash) {
|
|
1422
|
-
return this.store.getTxEffect(txHash);
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
public getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
|
|
1426
|
-
return this.store.getSettledTxReceipt(txHash);
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
|
-
getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
|
|
1430
|
-
return this.store.getPrivateLogsByTags(tags);
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
|
|
1434
|
-
return this.store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
/**
|
|
1438
|
-
* Gets public logs based on the provided filter.
|
|
1439
|
-
* @param filter - The filter to apply to the logs.
|
|
1440
|
-
* @returns The requested logs.
|
|
1441
|
-
*/
|
|
1442
|
-
getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
1443
|
-
return this.store.getPublicLogs(filter);
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
/**
|
|
1447
|
-
* Gets contract class logs based on the provided filter.
|
|
1448
|
-
* @param filter - The filter to apply to the logs.
|
|
1449
|
-
* @returns The requested logs.
|
|
1450
|
-
*/
|
|
1451
|
-
getContractClassLogs(filter: LogFilter): Promise<GetContractClassLogsResponse> {
|
|
1452
|
-
return this.store.getContractClassLogs(filter);
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
/**
|
|
1456
|
-
* Gets the number of the latest L2 block processed by the block source implementation.
|
|
1457
|
-
* This includes both checkpointed and uncheckpointed blocks.
|
|
1458
|
-
* @returns The number of the latest L2 block processed by the block source implementation.
|
|
1459
|
-
*/
|
|
1460
|
-
public getBlockNumber(): Promise<BlockNumber> {
|
|
1461
|
-
return this.store.getLatestBlockNumber();
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
|
|
1465
|
-
return this.store.getContractClass(id);
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
public getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
|
|
1469
|
-
return this.store.getBytecodeCommitment(id);
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
public async getContract(
|
|
1473
|
-
address: AztecAddress,
|
|
1474
|
-
maybeTimestamp?: UInt64,
|
|
1475
|
-
): Promise<ContractInstanceWithAddress | undefined> {
|
|
1476
|
-
let timestamp;
|
|
1477
|
-
if (maybeTimestamp === undefined) {
|
|
1478
|
-
const latestBlockHeader = await this.getBlockHeader('latest');
|
|
1479
|
-
// If we get undefined block header, it means that the archiver has not yet synced any block so we default to 0.
|
|
1480
|
-
timestamp = latestBlockHeader ? latestBlockHeader.globalVariables.timestamp : 0n;
|
|
1481
|
-
} else {
|
|
1482
|
-
timestamp = maybeTimestamp;
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
return this.store.getContractInstance(address, timestamp);
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
/**
|
|
1489
|
-
* Gets L1 to L2 message (to be) included in a given checkpoint.
|
|
1490
|
-
* @param checkpointNumber - Checkpoint number to get messages for.
|
|
1491
|
-
* @returns The L1 to L2 messages/leaves of the messages subtree (throws if not found).
|
|
1492
|
-
*/
|
|
1493
|
-
getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
|
|
1494
|
-
return this.store.getL1ToL2Messages(checkpointNumber);
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
/**
|
|
1498
|
-
* Gets the L1 to L2 message index in the L1 to L2 message tree.
|
|
1499
|
-
* @param l1ToL2Message - The L1 to L2 message.
|
|
1500
|
-
* @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found).
|
|
1501
|
-
*/
|
|
1502
|
-
getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
|
|
1503
|
-
return this.store.getL1ToL2MessageIndex(l1ToL2Message);
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
getContractClassIds(): Promise<Fr[]> {
|
|
1507
|
-
return this.store.getContractClassIds();
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
registerContractFunctionSignatures(signatures: string[]): Promise<void> {
|
|
1511
|
-
return this.store.registerContractFunctionSignatures(signatures);
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
getDebugFunctionName(address: AztecAddress, selector: FunctionSelector): Promise<string | undefined> {
|
|
1515
|
-
return this.store.getDebugFunctionName(address, selector);
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
async getPendingChainValidationStatus(): Promise<ValidateBlockResult> {
|
|
1519
|
-
return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
|
|
1345
|
+
return addCheckpointsWithContractData(this.store, checkpoints, pendingChainValidationStatus);
|
|
1520
1346
|
}
|
|
1521
1347
|
|
|
1522
|
-
|
|
1523
|
-
return this.
|
|
1348
|
+
getCheckpointedBlockNumber(): Promise<BlockNumber> {
|
|
1349
|
+
return this.store.getCheckpointedL2BlockNumber();
|
|
1524
1350
|
}
|
|
1525
1351
|
|
|
1526
|
-
async getL2Tips(): Promise<L2Tips> {
|
|
1527
|
-
const [latestBlockNumber, provenBlockNumber] = await Promise.all([
|
|
1352
|
+
public async getL2Tips(): Promise<L2Tips> {
|
|
1353
|
+
const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber] = await Promise.all([
|
|
1528
1354
|
this.getBlockNumber(),
|
|
1529
1355
|
this.getProvenBlockNumber(),
|
|
1356
|
+
this.getCheckpointedBlockNumber(),
|
|
1530
1357
|
] as const);
|
|
1531
1358
|
|
|
1532
1359
|
// TODO(#13569): Compute proper finalized block number based on L1 finalized block.
|
|
@@ -1534,44 +1361,112 @@ export class Archiver
|
|
|
1534
1361
|
// NOTE: update end-to-end/src/e2e_epochs/epochs_empty_blocks.test.ts as that uses finalized blocks in computations
|
|
1535
1362
|
const finalizedBlockNumber = BlockNumber(Math.max(provenBlockNumber - this.l1constants.epochDuration * 2, 0));
|
|
1536
1363
|
|
|
1537
|
-
const
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1364
|
+
const beforeInitialblockNumber = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
1365
|
+
|
|
1366
|
+
// Get the latest block header and checkpointed blocks for proven, finalised and checkpointed blocks
|
|
1367
|
+
const [latestBlockHeader, provenCheckpointedBlock, finalizedCheckpointedBlock, checkpointedBlock] =
|
|
1368
|
+
await Promise.all([
|
|
1369
|
+
latestBlockNumber > beforeInitialblockNumber ? this.getBlockHeader(latestBlockNumber) : undefined,
|
|
1370
|
+
provenBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(provenBlockNumber) : undefined,
|
|
1371
|
+
finalizedBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(finalizedBlockNumber) : undefined,
|
|
1372
|
+
checkpointedBlockNumber > beforeInitialblockNumber
|
|
1373
|
+
? this.getCheckpointedBlock(checkpointedBlockNumber)
|
|
1374
|
+
: undefined,
|
|
1375
|
+
] as const);
|
|
1542
1376
|
|
|
1543
|
-
if (latestBlockNumber >
|
|
1377
|
+
if (latestBlockNumber > beforeInitialblockNumber && !latestBlockHeader) {
|
|
1544
1378
|
throw new Error(`Failed to retrieve latest block header for block ${latestBlockNumber}`);
|
|
1545
1379
|
}
|
|
1546
1380
|
|
|
1547
|
-
if
|
|
1381
|
+
// Checkpointed blocks must exist for proven, finalized and checkpointed tips if they are beyond the initial block number.
|
|
1382
|
+
if (checkpointedBlockNumber > beforeInitialblockNumber && !checkpointedBlock?.block.header) {
|
|
1383
|
+
throw new Error(
|
|
1384
|
+
`Failed to retrieve checkpointed block header for block ${checkpointedBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
if (provenBlockNumber > beforeInitialblockNumber && !provenCheckpointedBlock?.block.header) {
|
|
1548
1389
|
throw new Error(
|
|
1549
|
-
`Failed to retrieve proven
|
|
1390
|
+
`Failed to retrieve proven checkpointed for block ${provenBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
1550
1391
|
);
|
|
1551
1392
|
}
|
|
1552
1393
|
|
|
1553
|
-
if (finalizedBlockNumber >
|
|
1394
|
+
if (finalizedBlockNumber > beforeInitialblockNumber && !finalizedCheckpointedBlock?.block.header) {
|
|
1554
1395
|
throw new Error(
|
|
1555
1396
|
`Failed to retrieve finalized block header for block ${finalizedBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
1556
1397
|
);
|
|
1557
1398
|
}
|
|
1558
1399
|
|
|
1559
1400
|
const latestBlockHeaderHash = (await latestBlockHeader?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
1560
|
-
const provenBlockHeaderHash = (await
|
|
1561
|
-
const finalizedBlockHeaderHash =
|
|
1401
|
+
const provenBlockHeaderHash = (await provenCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
1402
|
+
const finalizedBlockHeaderHash =
|
|
1403
|
+
(await finalizedCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
1404
|
+
const checkpointedBlockHeaderHash = (await checkpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
1405
|
+
|
|
1406
|
+
// Now attempt to retrieve checkpoints for proven, finalised and checkpointed blocks
|
|
1407
|
+
const [[provenBlockCheckpoint], [finalizedBlockCheckpoint], [checkpointedBlockCheckpoint]] = await Promise.all([
|
|
1408
|
+
provenCheckpointedBlock !== undefined
|
|
1409
|
+
? await this.getPublishedCheckpoints(provenCheckpointedBlock?.checkpointNumber, 1)
|
|
1410
|
+
: [undefined],
|
|
1411
|
+
finalizedCheckpointedBlock !== undefined
|
|
1412
|
+
? await this.getPublishedCheckpoints(finalizedCheckpointedBlock?.checkpointNumber, 1)
|
|
1413
|
+
: [undefined],
|
|
1414
|
+
checkpointedBlock !== undefined
|
|
1415
|
+
? await this.getPublishedCheckpoints(checkpointedBlock?.checkpointNumber, 1)
|
|
1416
|
+
: [undefined],
|
|
1417
|
+
]);
|
|
1418
|
+
|
|
1419
|
+
const initialcheckpointId: CheckpointId = {
|
|
1420
|
+
number: CheckpointNumber.ZERO,
|
|
1421
|
+
hash: GENESIS_CHECKPOINT_HEADER_HASH.toString(),
|
|
1422
|
+
};
|
|
1562
1423
|
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1424
|
+
const makeCheckpointId = (checkpoint: PublishedCheckpoint | undefined) => {
|
|
1425
|
+
if (checkpoint === undefined) {
|
|
1426
|
+
return initialcheckpointId;
|
|
1427
|
+
}
|
|
1428
|
+
return {
|
|
1429
|
+
number: checkpoint.checkpoint.number,
|
|
1430
|
+
hash: checkpoint.checkpoint.hash().toString(),
|
|
1431
|
+
};
|
|
1432
|
+
};
|
|
1433
|
+
|
|
1434
|
+
const l2Tips: L2Tips = {
|
|
1435
|
+
proposed: {
|
|
1436
|
+
number: latestBlockNumber,
|
|
1437
|
+
hash: latestBlockHeaderHash.toString(),
|
|
1438
|
+
},
|
|
1439
|
+
proven: {
|
|
1440
|
+
block: {
|
|
1441
|
+
number: provenBlockNumber,
|
|
1442
|
+
hash: provenBlockHeaderHash.toString(),
|
|
1443
|
+
},
|
|
1444
|
+
checkpoint: makeCheckpointId(provenBlockCheckpoint),
|
|
1445
|
+
},
|
|
1446
|
+
finalized: {
|
|
1447
|
+
block: {
|
|
1448
|
+
number: finalizedBlockNumber,
|
|
1449
|
+
hash: finalizedBlockHeaderHash.toString(),
|
|
1450
|
+
},
|
|
1451
|
+
checkpoint: makeCheckpointId(finalizedBlockCheckpoint),
|
|
1452
|
+
},
|
|
1453
|
+
checkpointed: {
|
|
1454
|
+
block: {
|
|
1455
|
+
number: checkpointedBlockNumber,
|
|
1456
|
+
hash: checkpointedBlockHeaderHash.toString(),
|
|
1457
|
+
},
|
|
1458
|
+
checkpoint: makeCheckpointId(checkpointedBlockCheckpoint),
|
|
1459
|
+
},
|
|
1567
1460
|
};
|
|
1461
|
+
|
|
1462
|
+
return l2Tips;
|
|
1568
1463
|
}
|
|
1569
1464
|
|
|
1570
1465
|
public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
|
|
1571
1466
|
// TODO(pw/mbps): This still assumes 1 block per checkpoint
|
|
1572
1467
|
const currentBlocks = await this.getL2Tips();
|
|
1573
|
-
const currentL2Block = currentBlocks.
|
|
1574
|
-
const currentProvenBlock = currentBlocks.proven.number;
|
|
1468
|
+
const currentL2Block = currentBlocks.proposed.number;
|
|
1469
|
+
const currentProvenBlock = currentBlocks.proven.block.number;
|
|
1575
1470
|
|
|
1576
1471
|
if (targetL2BlockNumber >= currentL2Block) {
|
|
1577
1472
|
throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
|
|
@@ -1585,11 +1480,11 @@ export class Archiver
|
|
|
1585
1480
|
const targetCheckpointNumber = CheckpointNumber.fromBlockNumber(targetL2BlockNumber);
|
|
1586
1481
|
const targetL1BlockHash = await this.getL1BlockHash(targetL1BlockNumber);
|
|
1587
1482
|
this.log.info(`Unwinding ${blocksToUnwind} checkpoints from L2 block ${currentL2Block}`);
|
|
1588
|
-
await this.store
|
|
1483
|
+
await unwindCheckpointsWithContractData(this.store, CheckpointNumber(currentL2Block), blocksToUnwind);
|
|
1589
1484
|
this.log.info(`Unwinding L1 to L2 messages to checkpoint ${targetCheckpointNumber}`);
|
|
1590
1485
|
await this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
1591
1486
|
this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`);
|
|
1592
|
-
await this.store.
|
|
1487
|
+
await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
|
|
1593
1488
|
await this.store.setMessageSynchedL1Block({ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash });
|
|
1594
1489
|
if (targetL2BlockNumber < currentProvenBlock) {
|
|
1595
1490
|
this.log.info(`Clearing proven L2 block number`);
|
|
@@ -1602,35 +1497,6 @@ export class Archiver
|
|
|
1602
1497
|
// }
|
|
1603
1498
|
}
|
|
1604
1499
|
|
|
1605
|
-
public async getPublishedCheckpoints(
|
|
1606
|
-
checkpointNumber: CheckpointNumber,
|
|
1607
|
-
limit: number,
|
|
1608
|
-
): Promise<PublishedCheckpoint[]> {
|
|
1609
|
-
const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
|
|
1610
|
-
const blocks = (
|
|
1611
|
-
await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
|
|
1612
|
-
).filter(isDefined);
|
|
1613
|
-
|
|
1614
|
-
const fullCheckpoints: PublishedCheckpoint[] = [];
|
|
1615
|
-
for (let i = 0; i < checkpoints.length; i++) {
|
|
1616
|
-
const blocksForCheckpoint = blocks[i];
|
|
1617
|
-
const checkpoint = checkpoints[i];
|
|
1618
|
-
const fullCheckpoint = new Checkpoint(
|
|
1619
|
-
checkpoint.archive,
|
|
1620
|
-
checkpoint.header,
|
|
1621
|
-
blocksForCheckpoint,
|
|
1622
|
-
checkpoint.checkpointNumber,
|
|
1623
|
-
);
|
|
1624
|
-
const publishedCheckpoint = new PublishedCheckpoint(
|
|
1625
|
-
fullCheckpoint,
|
|
1626
|
-
checkpoint.l1,
|
|
1627
|
-
checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
|
|
1628
|
-
);
|
|
1629
|
-
fullCheckpoints.push(publishedCheckpoint);
|
|
1630
|
-
}
|
|
1631
|
-
return fullCheckpoints;
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
1500
|
public async getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
|
|
1635
1501
|
const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants);
|
|
1636
1502
|
const checkpoints: Checkpoint[] = [];
|
|
@@ -1650,519 +1516,4 @@ export class Archiver
|
|
|
1650
1516
|
|
|
1651
1517
|
return checkpoints.reverse();
|
|
1652
1518
|
}
|
|
1653
|
-
|
|
1654
|
-
/* Legacy APIs */
|
|
1655
|
-
|
|
1656
|
-
public async getPublishedBlockByHash(blockHash: Fr): Promise<PublishedL2Block | undefined> {
|
|
1657
|
-
const checkpointedBlock = await this.store.getCheckpointedBlockByHash(blockHash);
|
|
1658
|
-
return this.buildOldBlockFromCheckpointedBlock(checkpointedBlock);
|
|
1659
|
-
}
|
|
1660
|
-
public async getPublishedBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
|
|
1661
|
-
const checkpointedBlock = await this.store.getCheckpointedBlockByArchive(archive);
|
|
1662
|
-
return this.buildOldBlockFromCheckpointedBlock(checkpointedBlock);
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
/**
|
|
1666
|
-
* Gets up to `limit` amount of L2 blocks starting from `from`.
|
|
1667
|
-
* @param from - Number of the first block to return (inclusive).
|
|
1668
|
-
* @param limit - The number of blocks to return.
|
|
1669
|
-
* @param proven - If true, only return blocks that have been proven.
|
|
1670
|
-
* @returns The requested L2 blocks.
|
|
1671
|
-
*/
|
|
1672
|
-
public async getBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<L2Block[]> {
|
|
1673
|
-
const publishedBlocks = await this.getPublishedBlocks(from, limit, proven);
|
|
1674
|
-
return publishedBlocks.map(x => x.block);
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
public async getPublishedBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<PublishedL2Block[]> {
|
|
1678
|
-
const checkpoints = await this.store.getRangeOfCheckpoints(CheckpointNumber(from), limit);
|
|
1679
|
-
const provenCheckpointNumber = await this.getProvenCheckpointNumber();
|
|
1680
|
-
const blocks = (
|
|
1681
|
-
await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
|
|
1682
|
-
).filter(isDefined);
|
|
1683
|
-
|
|
1684
|
-
const olbBlocks: PublishedL2Block[] = [];
|
|
1685
|
-
for (let i = 0; i < checkpoints.length; i++) {
|
|
1686
|
-
const blockForCheckpoint = blocks[i][0];
|
|
1687
|
-
const checkpoint = checkpoints[i];
|
|
1688
|
-
if (checkpoint.checkpointNumber > provenCheckpointNumber && proven === true) {
|
|
1689
|
-
// this checkpointisn't proven and we only want proven
|
|
1690
|
-
continue;
|
|
1691
|
-
}
|
|
1692
|
-
const oldCheckpoint = new Checkpoint(
|
|
1693
|
-
blockForCheckpoint.archive,
|
|
1694
|
-
checkpoint.header,
|
|
1695
|
-
[blockForCheckpoint],
|
|
1696
|
-
checkpoint.checkpointNumber,
|
|
1697
|
-
);
|
|
1698
|
-
const oldBlock = L2Block.fromCheckpoint(oldCheckpoint);
|
|
1699
|
-
const publishedBlock = new PublishedL2Block(
|
|
1700
|
-
oldBlock,
|
|
1701
|
-
checkpoint.l1,
|
|
1702
|
-
checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
|
|
1703
|
-
);
|
|
1704
|
-
olbBlocks.push(publishedBlock);
|
|
1705
|
-
}
|
|
1706
|
-
return olbBlocks;
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
private async buildOldBlockFromCheckpointedBlock(
|
|
1710
|
-
checkpointedBlock: CheckpointedL2Block | undefined,
|
|
1711
|
-
): Promise<PublishedL2Block | undefined> {
|
|
1712
|
-
if (!checkpointedBlock) {
|
|
1713
|
-
return undefined;
|
|
1714
|
-
}
|
|
1715
|
-
const checkpoint = await this.store.getCheckpointData(checkpointedBlock.checkpointNumber);
|
|
1716
|
-
if (!checkpoint) {
|
|
1717
|
-
return checkpoint;
|
|
1718
|
-
}
|
|
1719
|
-
const fullCheckpoint = new Checkpoint(
|
|
1720
|
-
checkpointedBlock?.block.archive,
|
|
1721
|
-
checkpoint?.header,
|
|
1722
|
-
[checkpointedBlock.block],
|
|
1723
|
-
checkpoint.checkpointNumber,
|
|
1724
|
-
);
|
|
1725
|
-
const oldBlock = L2Block.fromCheckpoint(fullCheckpoint);
|
|
1726
|
-
const published = new PublishedL2Block(
|
|
1727
|
-
oldBlock,
|
|
1728
|
-
checkpoint.l1,
|
|
1729
|
-
checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
|
|
1730
|
-
);
|
|
1731
|
-
return published;
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
public async getBlock(number: BlockNumber): Promise<L2Block | undefined> {
|
|
1735
|
-
// If the number provided is -ve, then return the latest block.
|
|
1736
|
-
if (number < 0) {
|
|
1737
|
-
number = await this.store.getSynchedL2BlockNumber();
|
|
1738
|
-
}
|
|
1739
|
-
if (number === 0) {
|
|
1740
|
-
return undefined;
|
|
1741
|
-
}
|
|
1742
|
-
const publishedBlocks = await this.getPublishedBlocks(number, 1);
|
|
1743
|
-
if (publishedBlocks.length === 0) {
|
|
1744
|
-
return undefined;
|
|
1745
|
-
}
|
|
1746
|
-
return publishedBlocks[0].block;
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
enum Operation {
|
|
1751
|
-
Store,
|
|
1752
|
-
Delete,
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
/**
|
|
1756
|
-
* A helper class that we use to deal with some of the logic needed when adding blocks.
|
|
1757
|
-
*
|
|
1758
|
-
* I would have preferred to not have this type. But it is useful for handling the logic that any
|
|
1759
|
-
* store would need to include otherwise while exposing fewer functions and logic directly to the archiver.
|
|
1760
|
-
*/
|
|
1761
|
-
export class ArchiverStoreHelper
|
|
1762
|
-
implements
|
|
1763
|
-
Omit<
|
|
1764
|
-
ArchiverDataStore,
|
|
1765
|
-
| 'addLogs'
|
|
1766
|
-
| 'deleteLogs'
|
|
1767
|
-
| 'addContractClasses'
|
|
1768
|
-
| 'deleteContractClasses'
|
|
1769
|
-
| 'addContractInstances'
|
|
1770
|
-
| 'deleteContractInstances'
|
|
1771
|
-
| 'addContractInstanceUpdates'
|
|
1772
|
-
| 'deleteContractInstanceUpdates'
|
|
1773
|
-
| 'addFunctions'
|
|
1774
|
-
| 'backupTo'
|
|
1775
|
-
| 'close'
|
|
1776
|
-
| 'transactionAsync'
|
|
1777
|
-
| 'addBlocks'
|
|
1778
|
-
| 'getBlock'
|
|
1779
|
-
| 'getBlocks'
|
|
1780
|
-
>
|
|
1781
|
-
{
|
|
1782
|
-
#log = createLogger('archiver:block-helper');
|
|
1783
|
-
|
|
1784
|
-
constructor(public readonly store: ArchiverDataStore) {}
|
|
1785
|
-
|
|
1786
|
-
/**
|
|
1787
|
-
* Extracts and stores contract classes out of ContractClassPublished events emitted by the class registry contract.
|
|
1788
|
-
* @param allLogs - All logs emitted in a bunch of blocks.
|
|
1789
|
-
*/
|
|
1790
|
-
async #updatePublishedContractClasses(allLogs: ContractClassLog[], blockNum: BlockNumber, operation: Operation) {
|
|
1791
|
-
const contractClassPublishedEvents = allLogs
|
|
1792
|
-
.filter(log => ContractClassPublishedEvent.isContractClassPublishedEvent(log))
|
|
1793
|
-
.map(log => ContractClassPublishedEvent.fromLog(log));
|
|
1794
|
-
|
|
1795
|
-
const contractClasses = await Promise.all(contractClassPublishedEvents.map(e => e.toContractClassPublic()));
|
|
1796
|
-
if (contractClasses.length > 0) {
|
|
1797
|
-
contractClasses.forEach(c => this.#log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
|
|
1798
|
-
if (operation == Operation.Store) {
|
|
1799
|
-
// TODO: Will probably want to create some worker threads to compute these bytecode commitments as they are expensive
|
|
1800
|
-
const commitments = await Promise.all(
|
|
1801
|
-
contractClasses.map(c => computePublicBytecodeCommitment(c.packedBytecode)),
|
|
1802
|
-
);
|
|
1803
|
-
return await this.store.addContractClasses(contractClasses, commitments, blockNum);
|
|
1804
|
-
} else if (operation == Operation.Delete) {
|
|
1805
|
-
return await this.store.deleteContractClasses(contractClasses, blockNum);
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
return true;
|
|
1809
|
-
}
|
|
1810
|
-
|
|
1811
|
-
/**
|
|
1812
|
-
* Extracts and stores contract instances out of ContractInstancePublished events emitted by the canonical deployer contract.
|
|
1813
|
-
* @param allLogs - All logs emitted in a bunch of blocks.
|
|
1814
|
-
*/
|
|
1815
|
-
async #updateDeployedContractInstances(allLogs: PrivateLog[], blockNum: BlockNumber, operation: Operation) {
|
|
1816
|
-
const contractInstances = allLogs
|
|
1817
|
-
.filter(log => ContractInstancePublishedEvent.isContractInstancePublishedEvent(log))
|
|
1818
|
-
.map(log => ContractInstancePublishedEvent.fromLog(log))
|
|
1819
|
-
.map(e => e.toContractInstance());
|
|
1820
|
-
if (contractInstances.length > 0) {
|
|
1821
|
-
contractInstances.forEach(c =>
|
|
1822
|
-
this.#log.verbose(`${Operation[operation]} contract instance at ${c.address.toString()}`),
|
|
1823
|
-
);
|
|
1824
|
-
if (operation == Operation.Store) {
|
|
1825
|
-
return await this.store.addContractInstances(contractInstances, blockNum);
|
|
1826
|
-
} else if (operation == Operation.Delete) {
|
|
1827
|
-
return await this.store.deleteContractInstances(contractInstances, blockNum);
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
return true;
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
/**
|
|
1834
|
-
* Extracts and stores contract instances out of ContractInstancePublished events emitted by the canonical deployer contract.
|
|
1835
|
-
* @param allLogs - All logs emitted in a bunch of blocks.
|
|
1836
|
-
* @param timestamp - Timestamp at which the updates were scheduled.
|
|
1837
|
-
* @param operation - The operation to perform on the contract instance updates (Store or Delete).
|
|
1838
|
-
*/
|
|
1839
|
-
async #updateUpdatedContractInstances(allLogs: PublicLog[], timestamp: UInt64, operation: Operation) {
|
|
1840
|
-
const contractUpdates = allLogs
|
|
1841
|
-
.filter(log => ContractInstanceUpdatedEvent.isContractInstanceUpdatedEvent(log))
|
|
1842
|
-
.map(log => ContractInstanceUpdatedEvent.fromLog(log))
|
|
1843
|
-
.map(e => e.toContractInstanceUpdate());
|
|
1844
|
-
|
|
1845
|
-
if (contractUpdates.length > 0) {
|
|
1846
|
-
contractUpdates.forEach(c =>
|
|
1847
|
-
this.#log.verbose(`${Operation[operation]} contract instance update at ${c.address.toString()}`),
|
|
1848
|
-
);
|
|
1849
|
-
if (operation == Operation.Store) {
|
|
1850
|
-
return await this.store.addContractInstanceUpdates(contractUpdates, timestamp);
|
|
1851
|
-
} else if (operation == Operation.Delete) {
|
|
1852
|
-
return await this.store.deleteContractInstanceUpdates(contractUpdates, timestamp);
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
return true;
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
/**
|
|
1859
|
-
* Stores the functions that were broadcasted individually
|
|
1860
|
-
*
|
|
1861
|
-
* @dev Beware that there is not a delete variant of this, since they are added to contract classes
|
|
1862
|
-
* and will be deleted as part of the class if needed.
|
|
1863
|
-
*
|
|
1864
|
-
* @param allLogs - The logs from the block
|
|
1865
|
-
* @param _blockNum - The block number
|
|
1866
|
-
* @returns
|
|
1867
|
-
*/
|
|
1868
|
-
async #storeBroadcastedIndividualFunctions(allLogs: ContractClassLog[], _blockNum: BlockNumber) {
|
|
1869
|
-
// Filter out private and utility function broadcast events
|
|
1870
|
-
const privateFnEvents = allLogs
|
|
1871
|
-
.filter(log => PrivateFunctionBroadcastedEvent.isPrivateFunctionBroadcastedEvent(log))
|
|
1872
|
-
.map(log => PrivateFunctionBroadcastedEvent.fromLog(log));
|
|
1873
|
-
const utilityFnEvents = allLogs
|
|
1874
|
-
.filter(log => UtilityFunctionBroadcastedEvent.isUtilityFunctionBroadcastedEvent(log))
|
|
1875
|
-
.map(log => UtilityFunctionBroadcastedEvent.fromLog(log));
|
|
1876
|
-
|
|
1877
|
-
// Group all events by contract class id
|
|
1878
|
-
for (const [classIdString, classEvents] of Object.entries(
|
|
1879
|
-
groupBy([...privateFnEvents, ...utilityFnEvents], e => e.contractClassId.toString()),
|
|
1880
|
-
)) {
|
|
1881
|
-
const contractClassId = Fr.fromHexString(classIdString);
|
|
1882
|
-
const contractClass = await this.getContractClass(contractClassId);
|
|
1883
|
-
if (!contractClass) {
|
|
1884
|
-
this.#log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`);
|
|
1885
|
-
continue;
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
// Split private and utility functions, and filter out invalid ones
|
|
1889
|
-
const allFns = classEvents.map(e => e.toFunctionWithMembershipProof());
|
|
1890
|
-
const privateFns = allFns.filter(
|
|
1891
|
-
(fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'utilityFunctionsTreeRoot' in fn,
|
|
1892
|
-
);
|
|
1893
|
-
const utilityFns = allFns.filter(
|
|
1894
|
-
(fn): fn is UtilityFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn,
|
|
1895
|
-
);
|
|
1896
|
-
|
|
1897
|
-
const privateFunctionsWithValidity = await Promise.all(
|
|
1898
|
-
privateFns.map(async fn => ({ fn, valid: await isValidPrivateFunctionMembershipProof(fn, contractClass) })),
|
|
1899
|
-
);
|
|
1900
|
-
const validPrivateFns = privateFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn);
|
|
1901
|
-
const utilityFunctionsWithValidity = await Promise.all(
|
|
1902
|
-
utilityFns.map(async fn => ({
|
|
1903
|
-
fn,
|
|
1904
|
-
valid: await isValidUtilityFunctionMembershipProof(fn, contractClass),
|
|
1905
|
-
})),
|
|
1906
|
-
);
|
|
1907
|
-
const validUtilityFns = utilityFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn);
|
|
1908
|
-
const validFnCount = validPrivateFns.length + validUtilityFns.length;
|
|
1909
|
-
if (validFnCount !== allFns.length) {
|
|
1910
|
-
this.#log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`);
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
// Store the functions in the contract class in a single operation
|
|
1914
|
-
if (validFnCount > 0) {
|
|
1915
|
-
this.#log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
|
|
1916
|
-
}
|
|
1917
|
-
return await this.store.addFunctions(contractClassId, validPrivateFns, validUtilityFns);
|
|
1918
|
-
}
|
|
1919
|
-
return true;
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
private async addBlockDataToDB(block: L2BlockNew) {
|
|
1923
|
-
const contractClassLogs = block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
|
|
1924
|
-
// ContractInstancePublished event logs are broadcast in privateLogs.
|
|
1925
|
-
const privateLogs = block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
|
|
1926
|
-
const publicLogs = block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
|
|
1927
|
-
|
|
1928
|
-
return (
|
|
1929
|
-
await Promise.all([
|
|
1930
|
-
this.#updatePublishedContractClasses(contractClassLogs, block.number, Operation.Store),
|
|
1931
|
-
this.#updateDeployedContractInstances(privateLogs, block.number, Operation.Store),
|
|
1932
|
-
this.#updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, Operation.Store),
|
|
1933
|
-
this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.number),
|
|
1934
|
-
])
|
|
1935
|
-
).every(Boolean);
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?: ValidateBlockResult): Promise<boolean> {
|
|
1939
|
-
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1940
|
-
// or if the previous block is not in the store.
|
|
1941
|
-
return this.store.transactionAsync(async () => {
|
|
1942
|
-
await this.store.addBlocks(blocks);
|
|
1943
|
-
|
|
1944
|
-
const opResults = await Promise.all([
|
|
1945
|
-
// Update the pending chain validation status if provided
|
|
1946
|
-
pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
|
|
1947
|
-
// Add any logs emitted during the retrieved blocks
|
|
1948
|
-
this.store.addLogs(blocks),
|
|
1949
|
-
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
1950
|
-
...blocks.map(block => {
|
|
1951
|
-
return this.addBlockDataToDB(block);
|
|
1952
|
-
}),
|
|
1953
|
-
]);
|
|
1954
|
-
|
|
1955
|
-
return opResults.every(Boolean);
|
|
1956
|
-
});
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
public addCheckpoints(
|
|
1960
|
-
checkpoints: PublishedCheckpoint[],
|
|
1961
|
-
pendingChainValidationStatus?: ValidateBlockResult,
|
|
1962
|
-
): Promise<boolean> {
|
|
1963
|
-
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1964
|
-
// or if the previous block is not in the store.
|
|
1965
|
-
return this.store.transactionAsync(async () => {
|
|
1966
|
-
await this.store.addCheckpoints(checkpoints);
|
|
1967
|
-
const allBlocks = checkpoints.flatMap((ch: PublishedCheckpoint) => ch.checkpoint.blocks);
|
|
1968
|
-
|
|
1969
|
-
const opResults = await Promise.all([
|
|
1970
|
-
// Update the pending chain validation status if provided
|
|
1971
|
-
pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
|
|
1972
|
-
// Add any logs emitted during the retrieved blocks
|
|
1973
|
-
this.store.addLogs(allBlocks),
|
|
1974
|
-
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
1975
|
-
...allBlocks.map(block => {
|
|
1976
|
-
return this.addBlockDataToDB(block);
|
|
1977
|
-
}),
|
|
1978
|
-
]);
|
|
1979
|
-
|
|
1980
|
-
return opResults.every(Boolean);
|
|
1981
|
-
});
|
|
1982
|
-
}
|
|
1983
|
-
|
|
1984
|
-
public async unwindCheckpoints(from: CheckpointNumber, checkpointsToUnwind: number): Promise<boolean> {
|
|
1985
|
-
if (checkpointsToUnwind <= 0) {
|
|
1986
|
-
throw new Error(`Cannot unwind ${checkpointsToUnwind} blocks`);
|
|
1987
|
-
}
|
|
1988
|
-
|
|
1989
|
-
const last = await this.getSynchedCheckpointNumber();
|
|
1990
|
-
if (from != last) {
|
|
1991
|
-
throw new Error(`Cannot unwind checkpoints from checkpoint ${from} when the last checkpoint is ${last}`);
|
|
1992
|
-
}
|
|
1993
|
-
|
|
1994
|
-
const blocks = [];
|
|
1995
|
-
const lastCheckpointNumber = from + checkpointsToUnwind - 1;
|
|
1996
|
-
for (let checkpointNumber = from; checkpointNumber <= lastCheckpointNumber; checkpointNumber++) {
|
|
1997
|
-
const blocksForCheckpoint = await this.store.getBlocksForCheckpoint(checkpointNumber);
|
|
1998
|
-
if (!blocksForCheckpoint) {
|
|
1999
|
-
continue;
|
|
2000
|
-
}
|
|
2001
|
-
blocks.push(...blocksForCheckpoint);
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
const opResults = await Promise.all([
|
|
2005
|
-
// Prune rolls back to the last proven block, which is by definition valid
|
|
2006
|
-
this.store.setPendingChainValidationStatus({ valid: true }),
|
|
2007
|
-
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
2008
|
-
...blocks.map(async block => {
|
|
2009
|
-
const contractClassLogs = block.body.txEffects.flatMap(txEffect => txEffect.contractClassLogs);
|
|
2010
|
-
// ContractInstancePublished event logs are broadcast in privateLogs.
|
|
2011
|
-
const privateLogs = block.body.txEffects.flatMap(txEffect => txEffect.privateLogs);
|
|
2012
|
-
const publicLogs = block.body.txEffects.flatMap(txEffect => txEffect.publicLogs);
|
|
2013
|
-
|
|
2014
|
-
return (
|
|
2015
|
-
await Promise.all([
|
|
2016
|
-
this.#updatePublishedContractClasses(contractClassLogs, block.number, Operation.Delete),
|
|
2017
|
-
this.#updateDeployedContractInstances(privateLogs, block.number, Operation.Delete),
|
|
2018
|
-
this.#updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, Operation.Delete),
|
|
2019
|
-
])
|
|
2020
|
-
).every(Boolean);
|
|
2021
|
-
}),
|
|
2022
|
-
|
|
2023
|
-
this.store.deleteLogs(blocks),
|
|
2024
|
-
this.store.unwindCheckpoints(from, checkpointsToUnwind),
|
|
2025
|
-
]);
|
|
2026
|
-
|
|
2027
|
-
return opResults.every(Boolean);
|
|
2028
|
-
}
|
|
2029
|
-
|
|
2030
|
-
getCheckpointData(checkpointNumber: CheckpointNumber): Promise<CheckpointData | undefined> {
|
|
2031
|
-
return this.store.getCheckpointData(checkpointNumber);
|
|
2032
|
-
}
|
|
2033
|
-
|
|
2034
|
-
getRangeOfCheckpoints(from: CheckpointNumber, limit: number): Promise<CheckpointData[]> {
|
|
2035
|
-
return this.store.getRangeOfCheckpoints(from, limit);
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
getCheckpointedL2BlockNumber(): Promise<BlockNumber> {
|
|
2039
|
-
return this.store.getCheckpointedL2BlockNumber();
|
|
2040
|
-
}
|
|
2041
|
-
getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
2042
|
-
return this.store.getSynchedCheckpointNumber();
|
|
2043
|
-
}
|
|
2044
|
-
setCheckpointSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
|
|
2045
|
-
return this.store.setCheckpointSynchedL1BlockNumber(l1BlockNumber);
|
|
2046
|
-
}
|
|
2047
|
-
getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
|
|
2048
|
-
return this.store.getCheckpointedBlock(number);
|
|
2049
|
-
}
|
|
2050
|
-
getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
2051
|
-
return this.store.getCheckpointedBlockByHash(blockHash);
|
|
2052
|
-
}
|
|
2053
|
-
getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
2054
|
-
return this.store.getCheckpointedBlockByArchive(archive);
|
|
2055
|
-
}
|
|
2056
|
-
getBlockHeaders(from: BlockNumber, limit: number): Promise<BlockHeader[]> {
|
|
2057
|
-
return this.store.getBlockHeaders(from, limit);
|
|
2058
|
-
}
|
|
2059
|
-
getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
|
|
2060
|
-
return this.store.getBlockHeaderByHash(blockHash);
|
|
2061
|
-
}
|
|
2062
|
-
getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
2063
|
-
return this.store.getBlockHeaderByArchive(archive);
|
|
2064
|
-
}
|
|
2065
|
-
getBlockByHash(blockHash: Fr): Promise<L2BlockNew | undefined> {
|
|
2066
|
-
return this.store.getBlockByHash(blockHash);
|
|
2067
|
-
}
|
|
2068
|
-
getBlockByArchive(archive: Fr): Promise<L2BlockNew | undefined> {
|
|
2069
|
-
return this.store.getBlockByArchive(archive);
|
|
2070
|
-
}
|
|
2071
|
-
getLatestBlockNumber(): Promise<BlockNumber> {
|
|
2072
|
-
return this.store.getLatestBlockNumber();
|
|
2073
|
-
}
|
|
2074
|
-
getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2BlockNew[] | undefined> {
|
|
2075
|
-
return this.store.getBlocksForCheckpoint(checkpointNumber);
|
|
2076
|
-
}
|
|
2077
|
-
getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
|
|
2078
|
-
return this.store.getTxEffect(txHash);
|
|
2079
|
-
}
|
|
2080
|
-
getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
|
|
2081
|
-
return this.store.getSettledTxReceipt(txHash);
|
|
2082
|
-
}
|
|
2083
|
-
addL1ToL2Messages(messages: InboxMessage[]): Promise<void> {
|
|
2084
|
-
return this.store.addL1ToL2Messages(messages);
|
|
2085
|
-
}
|
|
2086
|
-
getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
|
|
2087
|
-
return this.store.getL1ToL2Messages(checkpointNumber);
|
|
2088
|
-
}
|
|
2089
|
-
getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
|
|
2090
|
-
return this.store.getL1ToL2MessageIndex(l1ToL2Message);
|
|
2091
|
-
}
|
|
2092
|
-
getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
|
|
2093
|
-
return this.store.getPrivateLogsByTags(tags);
|
|
2094
|
-
}
|
|
2095
|
-
getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
|
|
2096
|
-
return this.store.getPublicLogsByTagsFromContract(contractAddress, tags);
|
|
2097
|
-
}
|
|
2098
|
-
getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
2099
|
-
return this.store.getPublicLogs(filter);
|
|
2100
|
-
}
|
|
2101
|
-
getContractClassLogs(filter: LogFilter): Promise<GetContractClassLogsResponse> {
|
|
2102
|
-
return this.store.getContractClassLogs(filter);
|
|
2103
|
-
}
|
|
2104
|
-
getSynchedL2BlockNumber(): Promise<BlockNumber> {
|
|
2105
|
-
return this.store.getCheckpointedL2BlockNumber();
|
|
2106
|
-
}
|
|
2107
|
-
getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
2108
|
-
return this.store.getProvenCheckpointNumber();
|
|
2109
|
-
}
|
|
2110
|
-
getProvenBlockNumber(): Promise<BlockNumber> {
|
|
2111
|
-
return this.store.getProvenBlockNumber();
|
|
2112
|
-
}
|
|
2113
|
-
setProvenCheckpointNumber(checkpointNumber: CheckpointNumber): Promise<void> {
|
|
2114
|
-
return this.store.setProvenCheckpointNumber(checkpointNumber);
|
|
2115
|
-
}
|
|
2116
|
-
setBlockSynchedL1BlockNumber(l1BlockNumber: bigint): Promise<void> {
|
|
2117
|
-
return this.store.setCheckpointSynchedL1BlockNumber(l1BlockNumber);
|
|
2118
|
-
}
|
|
2119
|
-
setMessageSynchedL1Block(l1Block: L1BlockId): Promise<void> {
|
|
2120
|
-
return this.store.setMessageSynchedL1Block(l1Block);
|
|
2121
|
-
}
|
|
2122
|
-
getSynchPoint(): Promise<ArchiverL1SynchPoint> {
|
|
2123
|
-
return this.store.getSynchPoint();
|
|
2124
|
-
}
|
|
2125
|
-
getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
|
|
2126
|
-
return this.store.getContractClass(id);
|
|
2127
|
-
}
|
|
2128
|
-
getBytecodeCommitment(contractClassId: Fr): Promise<Fr | undefined> {
|
|
2129
|
-
return this.store.getBytecodeCommitment(contractClassId);
|
|
2130
|
-
}
|
|
2131
|
-
getContractInstance(address: AztecAddress, timestamp: UInt64): Promise<ContractInstanceWithAddress | undefined> {
|
|
2132
|
-
return this.store.getContractInstance(address, timestamp);
|
|
2133
|
-
}
|
|
2134
|
-
getContractClassIds(): Promise<Fr[]> {
|
|
2135
|
-
return this.store.getContractClassIds();
|
|
2136
|
-
}
|
|
2137
|
-
registerContractFunctionSignatures(signatures: string[]): Promise<void> {
|
|
2138
|
-
return this.store.registerContractFunctionSignatures(signatures);
|
|
2139
|
-
}
|
|
2140
|
-
getDebugFunctionName(address: AztecAddress, selector: FunctionSelector): Promise<string | undefined> {
|
|
2141
|
-
return this.store.getDebugFunctionName(address, selector);
|
|
2142
|
-
}
|
|
2143
|
-
getTotalL1ToL2MessageCount(): Promise<bigint> {
|
|
2144
|
-
return this.store.getTotalL1ToL2MessageCount();
|
|
2145
|
-
}
|
|
2146
|
-
estimateSize(): Promise<{ mappingSize: number; physicalFileSize: number; actualSize: number; numItems: number }> {
|
|
2147
|
-
return this.store.estimateSize();
|
|
2148
|
-
}
|
|
2149
|
-
rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber: CheckpointNumber): Promise<void> {
|
|
2150
|
-
return this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
2151
|
-
}
|
|
2152
|
-
iterateL1ToL2Messages(range: CustomRange<bigint> = {}): AsyncIterableIterator<InboxMessage> {
|
|
2153
|
-
return this.store.iterateL1ToL2Messages(range);
|
|
2154
|
-
}
|
|
2155
|
-
removeL1ToL2Messages(startIndex: bigint): Promise<void> {
|
|
2156
|
-
return this.store.removeL1ToL2Messages(startIndex);
|
|
2157
|
-
}
|
|
2158
|
-
getLastL1ToL2Message(): Promise<InboxMessage | undefined> {
|
|
2159
|
-
return this.store.getLastL1ToL2Message();
|
|
2160
|
-
}
|
|
2161
|
-
getPendingChainValidationStatus(): Promise<ValidateBlockResult | undefined> {
|
|
2162
|
-
return this.store.getPendingChainValidationStatus();
|
|
2163
|
-
}
|
|
2164
|
-
setPendingChainValidationStatus(status: ValidateBlockResult | undefined): Promise<void> {
|
|
2165
|
-
this.#log.debug(`Setting pending chain validation status to valid ${status?.valid}`, status);
|
|
2166
|
-
return this.store.setPendingChainValidationStatus(status);
|
|
2167
|
-
}
|
|
2168
1519
|
}
|