@aztec/archiver 0.0.1-commit.3fd054f6 → 0.0.1-commit.42ee6df9b
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 +12 -6
- package/dest/archiver.d.ts +3 -2
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +5 -2
- package/dest/errors.d.ts +14 -2
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +18 -2
- package/dest/l1/calldata_retriever.d.ts +1 -1
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +2 -1
- package/dest/l1/data_retrieval.d.ts +2 -2
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +13 -14
- package/dest/modules/data_source_base.d.ts +4 -2
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +6 -0
- package/dest/modules/data_store_updater.d.ts +3 -2
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +9 -0
- package/dest/modules/l1_synchronizer.d.ts +3 -2
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +128 -123
- package/dest/modules/validation.d.ts +1 -1
- package/dest/modules/validation.d.ts.map +1 -1
- package/dest/modules/validation.js +2 -2
- package/dest/store/block_store.d.ts +38 -4
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +185 -60
- package/dest/store/kv_archiver_store.d.ts +21 -8
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +25 -8
- package/dest/store/l2_tips_cache.d.ts +2 -1
- package/dest/store/l2_tips_cache.d.ts.map +1 -1
- package/dest/store/l2_tips_cache.js +25 -5
- package/dest/store/message_store.d.ts +3 -3
- package/dest/store/message_store.d.ts.map +1 -1
- package/dest/store/message_store.js +9 -10
- package/dest/test/fake_l1_state.d.ts +2 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +28 -6
- package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +2 -1
- package/dest/test/mock_l2_block_source.d.ts +7 -2
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +28 -3
- package/package.json +13 -13
- package/src/archiver.ts +9 -2
- package/src/errors.ts +30 -2
- package/src/l1/calldata_retriever.ts +2 -1
- package/src/l1/data_retrieval.ts +7 -11
- package/src/modules/data_source_base.ts +15 -1
- package/src/modules/data_store_updater.ts +14 -1
- package/src/modules/l1_synchronizer.ts +137 -147
- package/src/modules/validation.ts +2 -2
- package/src/store/block_store.ts +242 -71
- package/src/store/kv_archiver_store.ts +43 -12
- package/src/store/l2_tips_cache.ts +50 -11
- package/src/store/message_store.ts +10 -12
- package/src/structs/inbox_message.ts +1 -1
- package/src/test/fake_l1_state.ts +41 -7
- package/src/test/mock_l1_to_l2_message_source.ts +1 -0
- package/src/test/mock_l2_block_source.ts +37 -2
package/src/store/block_store.ts
CHANGED
|
@@ -19,7 +19,15 @@ import {
|
|
|
19
19
|
deserializeValidateCheckpointResult,
|
|
20
20
|
serializeValidateCheckpointResult,
|
|
21
21
|
} from '@aztec/stdlib/block';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
Checkpoint,
|
|
24
|
+
type CheckpointData,
|
|
25
|
+
type CommonCheckpointData,
|
|
26
|
+
L1PublishedData,
|
|
27
|
+
type ProposedCheckpointData,
|
|
28
|
+
type ProposedCheckpointInput,
|
|
29
|
+
PublishedCheckpoint,
|
|
30
|
+
} from '@aztec/stdlib/checkpoint';
|
|
23
31
|
import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
24
32
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
25
33
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
@@ -44,6 +52,8 @@ import {
|
|
|
44
52
|
CheckpointNotFoundError,
|
|
45
53
|
CheckpointNumberNotSequentialError,
|
|
46
54
|
InitialCheckpointNumberNotSequentialError,
|
|
55
|
+
ProposedCheckpointNotSequentialError,
|
|
56
|
+
ProposedCheckpointStaleError,
|
|
47
57
|
} from '../errors.js';
|
|
48
58
|
|
|
49
59
|
export { TxReceipt, type TxEffect, type TxHash } from '@aztec/stdlib/tx';
|
|
@@ -58,17 +68,27 @@ type BlockStorage = {
|
|
|
58
68
|
indexWithinCheckpoint: number;
|
|
59
69
|
};
|
|
60
70
|
|
|
61
|
-
|
|
71
|
+
/** Checkpoint Storage shared between Checkpoints + Proposed Checkpoints */
|
|
72
|
+
type CommonCheckpointStorage = {
|
|
62
73
|
header: Buffer;
|
|
63
74
|
archive: Buffer;
|
|
64
75
|
checkpointOutHash: Buffer;
|
|
65
76
|
checkpointNumber: number;
|
|
66
77
|
startBlock: number;
|
|
67
78
|
blockCount: number;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
type CheckpointStorage = CommonCheckpointStorage & {
|
|
68
82
|
l1: Buffer;
|
|
69
83
|
attestations: Buffer[];
|
|
70
84
|
};
|
|
71
85
|
|
|
86
|
+
/** Storage format for a proposed checkpoint (attested but not yet L1-confirmed). */
|
|
87
|
+
type ProposedCheckpointStorage = CommonCheckpointStorage & {
|
|
88
|
+
totalManaUsed: string;
|
|
89
|
+
feeAssetPriceModifier: string;
|
|
90
|
+
};
|
|
91
|
+
|
|
72
92
|
export type RemoveCheckpointsResult = { blocksRemoved: L2Block[] | undefined };
|
|
73
93
|
|
|
74
94
|
/**
|
|
@@ -111,6 +131,9 @@ export class BlockStore {
|
|
|
111
131
|
/** Index mapping block archive to block number */
|
|
112
132
|
#blockArchiveIndex: AztecAsyncMap<string, number>;
|
|
113
133
|
|
|
134
|
+
/** Singleton: assumes max 1-deep pipeline. For deeper pipelining, replace with a map keyed by checkpoint number. */
|
|
135
|
+
#proposedCheckpoint: AztecAsyncSingleton<ProposedCheckpointStorage>;
|
|
136
|
+
|
|
114
137
|
#log = createLogger('archiver:block_store');
|
|
115
138
|
|
|
116
139
|
constructor(private db: AztecAsyncKVStore) {
|
|
@@ -126,6 +149,7 @@ export class BlockStore {
|
|
|
126
149
|
this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
|
|
127
150
|
this.#checkpoints = db.openMap('archiver_checkpoints');
|
|
128
151
|
this.#slotToCheckpoint = db.openMap('archiver_slot_to_checkpoint');
|
|
152
|
+
this.#proposedCheckpoint = db.openSingleton('proposed_checkpoint_data');
|
|
129
153
|
}
|
|
130
154
|
|
|
131
155
|
/**
|
|
@@ -160,7 +184,8 @@ export class BlockStore {
|
|
|
160
184
|
const blockLastArchive = block.header.lastArchive.root;
|
|
161
185
|
|
|
162
186
|
// Extract the latest block and checkpoint numbers
|
|
163
|
-
const previousBlockNumber = await this.
|
|
187
|
+
const previousBlockNumber = await this.getLatestL2BlockNumber();
|
|
188
|
+
const proposedCheckpointNumber = await this.getProposedCheckpointNumber();
|
|
164
189
|
const previousCheckpointNumber = await this.getLatestCheckpointNumber();
|
|
165
190
|
|
|
166
191
|
// Verify we're not overwriting checkpointed blocks
|
|
@@ -179,9 +204,19 @@ export class BlockStore {
|
|
|
179
204
|
throw new BlockNumberNotSequentialError(blockNumber, previousBlockNumber);
|
|
180
205
|
}
|
|
181
206
|
|
|
182
|
-
// The same check as above but for checkpoints
|
|
183
|
-
|
|
184
|
-
|
|
207
|
+
// The same check as above but for checkpoints. Accept the block if either the confirmed
|
|
208
|
+
// checkpoint or the pending (locally validated but not yet confirmed) checkpoint matches.
|
|
209
|
+
const expectedCheckpointNumber = blockCheckpointNumber - 1;
|
|
210
|
+
if (
|
|
211
|
+
!opts.force &&
|
|
212
|
+
previousCheckpointNumber !== expectedCheckpointNumber &&
|
|
213
|
+
proposedCheckpointNumber !== expectedCheckpointNumber
|
|
214
|
+
) {
|
|
215
|
+
const [reported, source]: [CheckpointNumber, 'confirmed' | 'proposed'] =
|
|
216
|
+
proposedCheckpointNumber > previousCheckpointNumber
|
|
217
|
+
? [proposedCheckpointNumber, 'proposed']
|
|
218
|
+
: [previousCheckpointNumber, 'confirmed'];
|
|
219
|
+
throw new CheckpointNumberNotSequentialError(blockCheckpointNumber, reported, source);
|
|
185
220
|
}
|
|
186
221
|
|
|
187
222
|
// Extract the previous block if there is one and see if it is for the same checkpoint or not
|
|
@@ -217,7 +252,7 @@ export class BlockStore {
|
|
|
217
252
|
}
|
|
218
253
|
|
|
219
254
|
/**
|
|
220
|
-
* Append new
|
|
255
|
+
* Append new checkpoints to the store's list.
|
|
221
256
|
* @param checkpoints - The L2 checkpoints to be added to the store.
|
|
222
257
|
* @returns True if the operation is successful.
|
|
223
258
|
*/
|
|
@@ -235,28 +270,8 @@ export class BlockStore {
|
|
|
235
270
|
throw new InitialCheckpointNumberNotSequentialError(firstCheckpointNumber, previousCheckpointNumber);
|
|
236
271
|
}
|
|
237
272
|
|
|
238
|
-
//
|
|
239
|
-
let
|
|
240
|
-
if (previousCheckpointNumber !== INITIAL_CHECKPOINT_NUMBER - 1) {
|
|
241
|
-
// There should be a previous checkpoint
|
|
242
|
-
previousCheckpointData = await this.getCheckpointData(previousCheckpointNumber);
|
|
243
|
-
if (previousCheckpointData === undefined) {
|
|
244
|
-
throw new CheckpointNotFoundError(previousCheckpointNumber);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
let previousBlockNumber: BlockNumber | undefined = undefined;
|
|
249
|
-
let previousBlock: L2Block | undefined = undefined;
|
|
250
|
-
|
|
251
|
-
// If we have a previous checkpoint then we need to get the previous block number
|
|
252
|
-
if (previousCheckpointData !== undefined) {
|
|
253
|
-
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.blockCount - 1);
|
|
254
|
-
previousBlock = await this.getBlock(previousBlockNumber);
|
|
255
|
-
if (previousBlock === undefined) {
|
|
256
|
-
// We should be able to get the required previous block
|
|
257
|
-
throw new BlockNotFoundError(previousBlockNumber);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
273
|
+
// Get the last block of the previous checkpoint for archive chaining
|
|
274
|
+
let previousBlock = await this.getPreviousCheckpointBlock(firstCheckpointNumber);
|
|
260
275
|
|
|
261
276
|
// Iterate over checkpoints array and insert them, checking that the block numbers are sequential.
|
|
262
277
|
let previousCheckpoint: PublishedCheckpoint | undefined = undefined;
|
|
@@ -273,42 +288,14 @@ export class BlockStore {
|
|
|
273
288
|
}
|
|
274
289
|
previousCheckpoint = checkpoint;
|
|
275
290
|
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
const block = checkpoint.checkpoint.blocks[i];
|
|
279
|
-
if (previousBlock) {
|
|
280
|
-
// The blocks should have a sequential block number
|
|
281
|
-
if (previousBlock.number !== block.number - 1) {
|
|
282
|
-
throw new BlockNumberNotSequentialError(block.number, previousBlock.number);
|
|
283
|
-
}
|
|
284
|
-
// If the blocks are for the same checkpoint then they should have sequential indexes
|
|
285
|
-
if (
|
|
286
|
-
previousBlock.checkpointNumber === block.checkpointNumber &&
|
|
287
|
-
previousBlock.indexWithinCheckpoint !== block.indexWithinCheckpoint - 1
|
|
288
|
-
) {
|
|
289
|
-
throw new BlockIndexNotSequentialError(block.indexWithinCheckpoint, previousBlock.indexWithinCheckpoint);
|
|
290
|
-
}
|
|
291
|
-
if (!previousBlock.archive.root.equals(block.header.lastArchive.root)) {
|
|
292
|
-
throw new BlockArchiveNotConsistentError(
|
|
293
|
-
block.number,
|
|
294
|
-
previousBlock.number,
|
|
295
|
-
block.header.lastArchive.root,
|
|
296
|
-
previousBlock.archive.root,
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
} else {
|
|
300
|
-
// No previous block, must be block 1 at checkpoint index 0
|
|
301
|
-
if (block.indexWithinCheckpoint !== 0) {
|
|
302
|
-
throw new BlockIndexNotSequentialError(block.indexWithinCheckpoint, undefined);
|
|
303
|
-
}
|
|
304
|
-
if (block.number !== INITIAL_L2_BLOCK_NUM) {
|
|
305
|
-
throw new BlockNumberNotSequentialError(block.number, undefined);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
291
|
+
// Validate block sequencing, indexes, and archive chaining
|
|
292
|
+
this.validateCheckpointBlocks(checkpoint.checkpoint.blocks, previousBlock);
|
|
308
293
|
|
|
309
|
-
|
|
310
|
-
|
|
294
|
+
// Store every block in the database (may already exist, but L1 data is authoritative)
|
|
295
|
+
for (let i = 0; i < checkpoint.checkpoint.blocks.length; i++) {
|
|
296
|
+
await this.addBlockToDatabase(checkpoint.checkpoint.blocks[i], checkpoint.checkpoint.number, i);
|
|
311
297
|
}
|
|
298
|
+
previousBlock = checkpoint.checkpoint.blocks.at(-1);
|
|
312
299
|
|
|
313
300
|
// Store the checkpoint in the database
|
|
314
301
|
await this.#checkpoints.set(checkpoint.checkpoint.number, {
|
|
@@ -326,11 +313,77 @@ export class BlockStore {
|
|
|
326
313
|
await this.#slotToCheckpoint.set(checkpoint.checkpoint.header.slotNumber, checkpoint.checkpoint.number);
|
|
327
314
|
}
|
|
328
315
|
|
|
316
|
+
// Clear the proposed checkpoint if any of the confirmed checkpoints match or supersede it
|
|
317
|
+
const lastConfirmedCheckpointNumber = checkpoints[checkpoints.length - 1].checkpoint.number;
|
|
318
|
+
await this.clearProposedCheckpointIfSuperseded(lastConfirmedCheckpointNumber);
|
|
319
|
+
|
|
329
320
|
await this.#lastSynchedL1Block.set(checkpoints[checkpoints.length - 1].l1.blockNumber);
|
|
330
321
|
return true;
|
|
331
322
|
});
|
|
332
323
|
}
|
|
333
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Gets the last block of the checkpoint before the given one.
|
|
327
|
+
* Returns undefined if there is no previous checkpoint (i.e. genesis).
|
|
328
|
+
*/
|
|
329
|
+
private async getPreviousCheckpointBlock(checkpointNumber: CheckpointNumber): Promise<L2Block | undefined> {
|
|
330
|
+
const previousCheckpointNumber = CheckpointNumber(checkpointNumber - 1);
|
|
331
|
+
if (previousCheckpointNumber === INITIAL_CHECKPOINT_NUMBER - 1) {
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const previousCheckpointData = await this.getCheckpointData(previousCheckpointNumber);
|
|
336
|
+
if (previousCheckpointData === undefined) {
|
|
337
|
+
throw new CheckpointNotFoundError(previousCheckpointNumber);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.blockCount - 1);
|
|
341
|
+
const previousBlock = await this.getBlock(previousBlockNumber);
|
|
342
|
+
if (previousBlock === undefined) {
|
|
343
|
+
throw new BlockNotFoundError(previousBlockNumber);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return previousBlock;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Validates that blocks are sequential, have correct indexes, and chain via archive roots.
|
|
351
|
+
* This is the same validation used for both confirmed checkpoints (addCheckpoints) and
|
|
352
|
+
* proposed checkpoints (setProposedCheckpoint).
|
|
353
|
+
*/
|
|
354
|
+
private validateCheckpointBlocks(blocks: L2Block[], previousBlock: L2Block | undefined): void {
|
|
355
|
+
for (const block of blocks) {
|
|
356
|
+
if (previousBlock) {
|
|
357
|
+
if (previousBlock.number !== block.number - 1) {
|
|
358
|
+
throw new BlockNumberNotSequentialError(block.number, previousBlock.number);
|
|
359
|
+
}
|
|
360
|
+
if (previousBlock.checkpointNumber === block.checkpointNumber) {
|
|
361
|
+
if (previousBlock.indexWithinCheckpoint !== block.indexWithinCheckpoint - 1) {
|
|
362
|
+
throw new BlockIndexNotSequentialError(block.indexWithinCheckpoint, previousBlock.indexWithinCheckpoint);
|
|
363
|
+
}
|
|
364
|
+
} else if (block.indexWithinCheckpoint !== 0) {
|
|
365
|
+
throw new BlockIndexNotSequentialError(block.indexWithinCheckpoint, previousBlock.indexWithinCheckpoint);
|
|
366
|
+
}
|
|
367
|
+
if (!previousBlock.archive.root.equals(block.header.lastArchive.root)) {
|
|
368
|
+
throw new BlockArchiveNotConsistentError(
|
|
369
|
+
block.number,
|
|
370
|
+
previousBlock.number,
|
|
371
|
+
block.header.lastArchive.root,
|
|
372
|
+
previousBlock.archive.root,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
if (block.indexWithinCheckpoint !== 0) {
|
|
377
|
+
throw new BlockIndexNotSequentialError(block.indexWithinCheckpoint, undefined);
|
|
378
|
+
}
|
|
379
|
+
if (block.number !== INITIAL_L2_BLOCK_NUM) {
|
|
380
|
+
throw new BlockNumberNotSequentialError(block.number, undefined);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
previousBlock = block;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
334
387
|
private async addBlockToDatabase(block: L2Block, checkpointNumber: number, indexWithinCheckpoint: number) {
|
|
335
388
|
const blockHash = await block.hash();
|
|
336
389
|
|
|
@@ -423,6 +476,12 @@ export class BlockStore {
|
|
|
423
476
|
this.#log.debug(`Removed checkpoint ${c}`);
|
|
424
477
|
}
|
|
425
478
|
|
|
479
|
+
// Clear any proposed checkpoint that was orphaned by the removal (its base chain no longer exists)
|
|
480
|
+
const proposedCheckpointNumber = await this.getProposedCheckpointNumber();
|
|
481
|
+
if (proposedCheckpointNumber > checkpointNumber) {
|
|
482
|
+
await this.#proposedCheckpoint.delete();
|
|
483
|
+
}
|
|
484
|
+
|
|
426
485
|
return { blocksRemoved };
|
|
427
486
|
});
|
|
428
487
|
}
|
|
@@ -528,7 +587,7 @@ export class BlockStore {
|
|
|
528
587
|
const removedBlocks: L2Block[] = [];
|
|
529
588
|
|
|
530
589
|
// Get the latest block number to determine the range
|
|
531
|
-
const latestBlockNumber = await this.
|
|
590
|
+
const latestBlockNumber = await this.getLatestL2BlockNumber();
|
|
532
591
|
|
|
533
592
|
// Iterate from blockNumber + 1 to latestBlockNumber
|
|
534
593
|
for (let bn = blockNumber + 1; bn <= latestBlockNumber; bn++) {
|
|
@@ -561,13 +620,6 @@ export class BlockStore {
|
|
|
561
620
|
}
|
|
562
621
|
}
|
|
563
622
|
|
|
564
|
-
async getLatestBlockNumber(): Promise<BlockNumber> {
|
|
565
|
-
const [latestBlocknumber] = await toArray(this.#blocks.keysAsync({ reverse: true, limit: 1 }));
|
|
566
|
-
return typeof latestBlocknumber === 'number'
|
|
567
|
-
? BlockNumber(latestBlocknumber)
|
|
568
|
-
: BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
623
|
async getLatestCheckpointNumber(): Promise<CheckpointNumber> {
|
|
572
624
|
const [latestCheckpointNumber] = await toArray(this.#checkpoints.keysAsync({ reverse: true, limit: 1 }));
|
|
573
625
|
if (latestCheckpointNumber === undefined) {
|
|
@@ -576,6 +628,84 @@ export class BlockStore {
|
|
|
576
628
|
return CheckpointNumber(latestCheckpointNumber);
|
|
577
629
|
}
|
|
578
630
|
|
|
631
|
+
async hasProposedCheckpoint(): Promise<boolean> {
|
|
632
|
+
const proposed = await this.#proposedCheckpoint.getAsync();
|
|
633
|
+
return proposed !== undefined;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/** Deletes the proposed checkpoint from storage. */
|
|
637
|
+
async deleteProposedCheckpoint(): Promise<void> {
|
|
638
|
+
await this.#proposedCheckpoint.delete();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** Clears the proposed checkpoint if the given confirmed checkpoint number supersedes it. */
|
|
642
|
+
async clearProposedCheckpointIfSuperseded(confirmedCheckpointNumber: CheckpointNumber): Promise<void> {
|
|
643
|
+
const proposedCheckpointNumber = await this.getProposedCheckpointNumber();
|
|
644
|
+
if (proposedCheckpointNumber <= confirmedCheckpointNumber) {
|
|
645
|
+
await this.#proposedCheckpoint.delete();
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/** Returns the proposed checkpoint data, or undefined if no proposed checkpoint exists. No fallback to confirmed. */
|
|
650
|
+
async getProposedCheckpointOnly(): Promise<ProposedCheckpointData | undefined> {
|
|
651
|
+
const stored = await this.#proposedCheckpoint.getAsync();
|
|
652
|
+
if (!stored) {
|
|
653
|
+
return undefined;
|
|
654
|
+
}
|
|
655
|
+
return this.convertToProposedCheckpointData(stored);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Gets the checkpoint at the proposed tip
|
|
660
|
+
* - pending checkpoint if it exists
|
|
661
|
+
* - fallsback to latest confirmed checkpoint otherwise
|
|
662
|
+
* @returns CommonCheckpointData
|
|
663
|
+
*/
|
|
664
|
+
async getProposedCheckpoint(): Promise<CommonCheckpointData | undefined> {
|
|
665
|
+
const stored = await this.#proposedCheckpoint.getAsync();
|
|
666
|
+
if (!stored) {
|
|
667
|
+
return this.getCheckpointData(await this.getLatestCheckpointNumber());
|
|
668
|
+
}
|
|
669
|
+
return this.convertToProposedCheckpointData(stored);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
private convertToProposedCheckpointData(stored: ProposedCheckpointStorage): ProposedCheckpointData {
|
|
673
|
+
return {
|
|
674
|
+
checkpointNumber: CheckpointNumber(stored.checkpointNumber),
|
|
675
|
+
header: CheckpointHeader.fromBuffer(stored.header),
|
|
676
|
+
archive: AppendOnlyTreeSnapshot.fromBuffer(stored.archive),
|
|
677
|
+
checkpointOutHash: Fr.fromBuffer(stored.checkpointOutHash),
|
|
678
|
+
startBlock: BlockNumber(stored.startBlock),
|
|
679
|
+
blockCount: stored.blockCount,
|
|
680
|
+
totalManaUsed: BigInt(stored.totalManaUsed),
|
|
681
|
+
feeAssetPriceModifier: BigInt(stored.feeAssetPriceModifier),
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Attempts to get the proposedCheckpoint's number, if there is not one, then fallback to the latest confirmed checkpoint number.
|
|
687
|
+
* @returns CheckpointNumber
|
|
688
|
+
*/
|
|
689
|
+
async getProposedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
690
|
+
const proposed = await this.getProposedCheckpoint();
|
|
691
|
+
if (!proposed) {
|
|
692
|
+
return await this.getLatestCheckpointNumber();
|
|
693
|
+
}
|
|
694
|
+
return CheckpointNumber(proposed.checkpointNumber);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Attempts to get the proposedCheckpoint's block number, if there is not one, then fallback to the checkpointed block number
|
|
699
|
+
* @returns BlockNumber
|
|
700
|
+
*/
|
|
701
|
+
async getProposedCheckpointL2BlockNumber(): Promise<BlockNumber> {
|
|
702
|
+
const proposed = await this.getProposedCheckpoint();
|
|
703
|
+
if (!proposed) {
|
|
704
|
+
return await this.getCheckpointedL2BlockNumber();
|
|
705
|
+
}
|
|
706
|
+
return BlockNumber(proposed.startBlock + proposed.blockCount - 1);
|
|
707
|
+
}
|
|
708
|
+
|
|
579
709
|
async getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
|
|
580
710
|
const blockStorage = await this.#blocks.getAsync(number);
|
|
581
711
|
if (!blockStorage) {
|
|
@@ -950,6 +1080,47 @@ export class BlockStore {
|
|
|
950
1080
|
return this.#lastSynchedL1Block.set(l1BlockNumber);
|
|
951
1081
|
}
|
|
952
1082
|
|
|
1083
|
+
/** Sets the proposed checkpoint (not yet L1-confirmed). Only accepts confirmed + 1.
|
|
1084
|
+
* Computes archive and checkpointOutHash from the stored blocks. */
|
|
1085
|
+
async setProposedCheckpoint(proposed: ProposedCheckpointInput) {
|
|
1086
|
+
return await this.db.transactionAsync(async () => {
|
|
1087
|
+
const current = await this.getProposedCheckpointNumber();
|
|
1088
|
+
if (proposed.checkpointNumber <= current) {
|
|
1089
|
+
throw new ProposedCheckpointStaleError(proposed.checkpointNumber, current);
|
|
1090
|
+
}
|
|
1091
|
+
const confirmed = await this.getLatestCheckpointNumber();
|
|
1092
|
+
if (proposed.checkpointNumber !== confirmed + 1) {
|
|
1093
|
+
throw new ProposedCheckpointNotSequentialError(proposed.checkpointNumber, confirmed);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// Ensure the previous checkpoint + blocks exist
|
|
1097
|
+
const previousBlock = await this.getPreviousCheckpointBlock(proposed.checkpointNumber);
|
|
1098
|
+
const blocks: L2Block[] = [];
|
|
1099
|
+
for (let i = 0; i < proposed.blockCount; i++) {
|
|
1100
|
+
const block = await this.getBlock(BlockNumber(proposed.startBlock + i));
|
|
1101
|
+
if (!block) {
|
|
1102
|
+
throw new BlockNotFoundError(proposed.startBlock + i);
|
|
1103
|
+
}
|
|
1104
|
+
blocks.push(block);
|
|
1105
|
+
}
|
|
1106
|
+
this.validateCheckpointBlocks(blocks, previousBlock);
|
|
1107
|
+
|
|
1108
|
+
const archive = blocks[blocks.length - 1].archive;
|
|
1109
|
+
const checkpointOutHash = Checkpoint.getCheckpointOutHash(blocks);
|
|
1110
|
+
|
|
1111
|
+
await this.#proposedCheckpoint.set({
|
|
1112
|
+
header: proposed.header.toBuffer(),
|
|
1113
|
+
archive: archive.toBuffer(),
|
|
1114
|
+
checkpointOutHash: checkpointOutHash.toBuffer(),
|
|
1115
|
+
checkpointNumber: proposed.checkpointNumber,
|
|
1116
|
+
startBlock: proposed.startBlock,
|
|
1117
|
+
blockCount: proposed.blockCount,
|
|
1118
|
+
totalManaUsed: proposed.totalManaUsed.toString(),
|
|
1119
|
+
feeAssetPriceModifier: proposed.feeAssetPriceModifier.toString(),
|
|
1120
|
+
});
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
|
|
953
1124
|
async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
954
1125
|
const [latestCheckpointNumber, provenCheckpointNumber] = await Promise.all([
|
|
955
1126
|
this.getLatestCheckpointNumber(),
|
|
@@ -13,7 +13,13 @@ import {
|
|
|
13
13
|
L2Block,
|
|
14
14
|
type ValidateCheckpointResult,
|
|
15
15
|
} from '@aztec/stdlib/block';
|
|
16
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
CheckpointData,
|
|
18
|
+
CommonCheckpointData,
|
|
19
|
+
ProposedCheckpointData,
|
|
20
|
+
ProposedCheckpointInput,
|
|
21
|
+
PublishedCheckpoint,
|
|
22
|
+
} from '@aztec/stdlib/checkpoint';
|
|
17
23
|
import type {
|
|
18
24
|
ContractClassPublic,
|
|
19
25
|
ContractClassPublicWithCommitment,
|
|
@@ -254,7 +260,7 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
254
260
|
* @returns The number of the latest block
|
|
255
261
|
*/
|
|
256
262
|
getLatestBlockNumber(): Promise<BlockNumber> {
|
|
257
|
-
return this.#blockStore.
|
|
263
|
+
return this.#blockStore.getLatestL2BlockNumber();
|
|
258
264
|
}
|
|
259
265
|
|
|
260
266
|
/**
|
|
@@ -552,13 +558,6 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
552
558
|
await this.#blockStore.setSynchedL1BlockNumber(l1BlockNumber);
|
|
553
559
|
}
|
|
554
560
|
|
|
555
|
-
/**
|
|
556
|
-
* Stores the l1 block that messages have been synched until
|
|
557
|
-
*/
|
|
558
|
-
async setMessageSynchedL1Block(l1Block: L1BlockId) {
|
|
559
|
-
await this.#messageStore.setSynchedL1Block(l1Block);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
561
|
/**
|
|
563
562
|
* Returns the number of the most recent proven block
|
|
564
563
|
* @returns The number of the most recent proven block
|
|
@@ -591,9 +590,9 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
591
590
|
return this.#messageStore.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
592
591
|
}
|
|
593
592
|
|
|
594
|
-
/**
|
|
595
|
-
public
|
|
596
|
-
return this.#messageStore.
|
|
593
|
+
/** Atomically updates the message sync state: the L1 sync point and the inbox tree-in-progress marker. */
|
|
594
|
+
public setMessageSyncState(l1Block: L1BlockId, treeInProgress: bigint | undefined): Promise<void> {
|
|
595
|
+
return this.#messageStore.setMessageSyncState(l1Block, treeInProgress);
|
|
597
596
|
}
|
|
598
597
|
|
|
599
598
|
/** Returns an async iterator to all L1 to L2 messages on the range. */
|
|
@@ -616,6 +615,38 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
616
615
|
return this.#blockStore.setPendingChainValidationStatus(status);
|
|
617
616
|
}
|
|
618
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Gets the L2 block number of the proposed checkpoint.
|
|
620
|
+
* @returns The block number of the proposed checkpoint, or the checkpointed block number if none.
|
|
621
|
+
*/
|
|
622
|
+
public getProposedCheckpointL2BlockNumber(): Promise<BlockNumber> {
|
|
623
|
+
return this.#blockStore.getProposedCheckpointL2BlockNumber();
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** Returns the checkpoint data at the proposed tip */
|
|
627
|
+
public getProposedCheckpoint(): Promise<CommonCheckpointData | undefined> {
|
|
628
|
+
return this.#blockStore.getProposedCheckpoint();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/** Returns the proposed checkpoint data, or undefined if no proposed checkpoint exists. No fallback to confirmed. */
|
|
632
|
+
public getProposedCheckpointOnly(): Promise<ProposedCheckpointData | undefined> {
|
|
633
|
+
return this.#blockStore.getProposedCheckpointOnly();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Set proposed checkpoint
|
|
638
|
+
* @param proposedCheckpoint
|
|
639
|
+
* @returns
|
|
640
|
+
*/
|
|
641
|
+
public setProposedCheckpoint(proposedCheckpoint: ProposedCheckpointInput): Promise<void> {
|
|
642
|
+
return this.#blockStore.setProposedCheckpoint(proposedCheckpoint);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/** Deletes the proposed checkpoint from storage. */
|
|
646
|
+
public deleteProposedCheckpoint(): Promise<void> {
|
|
647
|
+
return this.#blockStore.deleteProposedCheckpoint();
|
|
648
|
+
}
|
|
649
|
+
|
|
619
650
|
/**
|
|
620
651
|
* Gets the number of the latest L2 block processed.
|
|
621
652
|
* @returns The number of the latest L2 block processed.
|
|
@@ -26,9 +26,16 @@ export class L2TipsCache {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
private async loadFromStore(): Promise<L2Tips> {
|
|
29
|
-
const [
|
|
30
|
-
|
|
29
|
+
const [
|
|
30
|
+
latestBlockNumber,
|
|
31
|
+
provenBlockNumber,
|
|
32
|
+
proposedCheckpointBlockNumber,
|
|
33
|
+
checkpointedBlockNumber,
|
|
34
|
+
finalizedBlockNumber,
|
|
35
|
+
] = await Promise.all([
|
|
36
|
+
this.blockStore.getLatestL2BlockNumber(),
|
|
31
37
|
this.blockStore.getProvenBlockNumber(),
|
|
38
|
+
this.blockStore.getProposedCheckpointL2BlockNumber(),
|
|
32
39
|
this.blockStore.getCheckpointedL2BlockNumber(),
|
|
33
40
|
this.blockStore.getFinalizedL2BlockNumber(),
|
|
34
41
|
]);
|
|
@@ -42,19 +49,34 @@ export class L2TipsCache {
|
|
|
42
49
|
const getBlockData = (blockNumber: BlockNumber) =>
|
|
43
50
|
blockNumber > beforeInitialBlockNumber ? this.blockStore.getBlockData(blockNumber) : genesisBlockHeader;
|
|
44
51
|
|
|
45
|
-
const [latestBlockData, provenBlockData, checkpointedBlockData, finalizedBlockData] =
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
const [latestBlockData, provenBlockData, proposedCheckpointBlockData, checkpointedBlockData, finalizedBlockData] =
|
|
53
|
+
await Promise.all(
|
|
54
|
+
[
|
|
55
|
+
latestBlockNumber,
|
|
56
|
+
provenBlockNumber,
|
|
57
|
+
proposedCheckpointBlockNumber,
|
|
58
|
+
checkpointedBlockNumber,
|
|
59
|
+
finalizedBlockNumber,
|
|
60
|
+
].map(getBlockData),
|
|
61
|
+
);
|
|
48
62
|
|
|
49
|
-
if (
|
|
63
|
+
if (
|
|
64
|
+
!latestBlockData ||
|
|
65
|
+
!provenBlockData ||
|
|
66
|
+
!finalizedBlockData ||
|
|
67
|
+
!checkpointedBlockData ||
|
|
68
|
+
!proposedCheckpointBlockData
|
|
69
|
+
) {
|
|
50
70
|
throw new Error('Failed to load block data for L2 tips');
|
|
51
71
|
}
|
|
52
72
|
|
|
53
|
-
const [provenCheckpointId, finalizedCheckpointId, checkpointedCheckpointId] =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
73
|
+
const [provenCheckpointId, finalizedCheckpointId, proposedCheckpointId, checkpointedCheckpointId] =
|
|
74
|
+
await Promise.all([
|
|
75
|
+
this.getCheckpointIdForBlock(provenBlockData),
|
|
76
|
+
this.getCheckpointIdForBlock(finalizedBlockData),
|
|
77
|
+
this.getCheckpointIdForProposedCheckpoint(checkpointedBlockData),
|
|
78
|
+
this.getCheckpointIdForBlock(checkpointedBlockData),
|
|
79
|
+
]);
|
|
58
80
|
|
|
59
81
|
return {
|
|
60
82
|
proposed: { number: latestBlockNumber, hash: latestBlockData.blockHash.toString() },
|
|
@@ -62,6 +84,10 @@ export class L2TipsCache {
|
|
|
62
84
|
block: { number: provenBlockNumber, hash: provenBlockData.blockHash.toString() },
|
|
63
85
|
checkpoint: provenCheckpointId,
|
|
64
86
|
},
|
|
87
|
+
proposedCheckpoint: {
|
|
88
|
+
block: { number: proposedCheckpointBlockNumber, hash: proposedCheckpointBlockData.blockHash.toString() },
|
|
89
|
+
checkpoint: proposedCheckpointId,
|
|
90
|
+
},
|
|
65
91
|
finalized: {
|
|
66
92
|
block: { number: finalizedBlockNumber, hash: finalizedBlockData.blockHash.toString() },
|
|
67
93
|
checkpoint: finalizedCheckpointId,
|
|
@@ -73,6 +99,19 @@ export class L2TipsCache {
|
|
|
73
99
|
};
|
|
74
100
|
}
|
|
75
101
|
|
|
102
|
+
private async getCheckpointIdForProposedCheckpoint(
|
|
103
|
+
checkpointedBlockData: Pick<BlockData, 'checkpointNumber'>,
|
|
104
|
+
): Promise<CheckpointId> {
|
|
105
|
+
const checkpointData = await this.blockStore.getProposedCheckpointOnly();
|
|
106
|
+
if (!checkpointData) {
|
|
107
|
+
return this.getCheckpointIdForBlock(checkpointedBlockData);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
number: checkpointData.checkpointNumber,
|
|
111
|
+
hash: checkpointData.header.hash().toString(),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
76
115
|
private async getCheckpointIdForBlock(blockData: Pick<BlockData, 'checkpointNumber'>): Promise<CheckpointId> {
|
|
77
116
|
const checkpointData = await this.blockStore.getCheckpointData(blockData.checkpointNumber);
|
|
78
117
|
if (!checkpointData) {
|
|
@@ -161,15 +161,6 @@ export class MessageStore {
|
|
|
161
161
|
lastMessage = message;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
// Update the L1 sync point to that of the last message added.
|
|
165
|
-
const currentSyncPoint = await this.getSynchedL1Block();
|
|
166
|
-
if (!currentSyncPoint || currentSyncPoint.l1BlockNumber < lastMessage!.l1BlockNumber) {
|
|
167
|
-
await this.setSynchedL1Block({
|
|
168
|
-
l1BlockNumber: lastMessage!.l1BlockNumber,
|
|
169
|
-
l1BlockHash: lastMessage!.l1BlockHash,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
|
|
173
164
|
// Update total message count with the number of inserted messages.
|
|
174
165
|
await this.increaseTotalMessageCount(messageCount);
|
|
175
166
|
});
|
|
@@ -194,9 +185,16 @@ export class MessageStore {
|
|
|
194
185
|
return this.#inboxTreeInProgress.getAsync();
|
|
195
186
|
}
|
|
196
187
|
|
|
197
|
-
/**
|
|
198
|
-
public
|
|
199
|
-
|
|
188
|
+
/** Atomically updates the message sync state: the L1 sync point and the inbox tree-in-progress marker. */
|
|
189
|
+
public setMessageSyncState(l1Block: L1BlockId, treeInProgress: bigint | undefined): Promise<void> {
|
|
190
|
+
return this.db.transactionAsync(async () => {
|
|
191
|
+
await this.setSynchedL1Block(l1Block);
|
|
192
|
+
if (treeInProgress !== undefined) {
|
|
193
|
+
await this.#inboxTreeInProgress.set(treeInProgress);
|
|
194
|
+
} else {
|
|
195
|
+
await this.#inboxTreeInProgress.delete();
|
|
196
|
+
}
|
|
197
|
+
});
|
|
200
198
|
}
|
|
201
199
|
|
|
202
200
|
public async getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
|