@aztec/archiver 0.57.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/archiver/archiver.d.ts +5 -3
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +77 -21
- package/dest/archiver/archiver_store.d.ts +1 -2
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +10 -14
- package/dest/archiver/data_retrieval.js +3 -3
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +1 -2
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +2 -2
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +1 -2
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +2 -2
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +1 -2
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +1 -1
- package/dest/archiver/kv_archiver_store/message_store.d.ts +1 -1
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +8 -11
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +2 -2
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +9 -14
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +1 -2
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +2 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +17 -1
- package/dest/test/index.d.ts +2 -0
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +3 -1
- package/dest/test/mock_archiver.d.ts +22 -0
- package/dest/test/mock_archiver.d.ts.map +1 -0
- package/dest/test/mock_archiver.js +44 -0
- package/dest/test/mock_l1_to_l2_message_source.d.ts +16 -0
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -0
- package/dest/test/mock_l1_to_l2_message_source.js +25 -0
- package/dest/test/mock_l2_block_source.d.ts +11 -9
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +36 -16
- package/package.json +10 -10
- package/src/archiver/archiver.ts +109 -34
- package/src/archiver/archiver_store.ts +5 -4
- package/src/archiver/archiver_store_test_suite.ts +20 -21
- package/src/archiver/data_retrieval.ts +2 -2
- package/src/archiver/kv_archiver_store/contract_class_store.ts +6 -4
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +1 -2
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +7 -6
- package/src/archiver/kv_archiver_store/message_store.ts +7 -18
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +9 -19
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +6 -4
- package/src/factory.ts +18 -0
- package/src/test/index.ts +2 -0
- package/src/test/mock_archiver.ts +55 -0
- package/src/test/mock_l1_to_l2_message_source.ts +31 -0
- package/src/test/mock_l2_block_source.ts +42 -17
package/src/archiver/archiver.ts
CHANGED
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
type InboxLeaf,
|
|
5
5
|
type L1ToL2MessageSource,
|
|
6
6
|
type L2Block,
|
|
7
|
+
type L2BlockId,
|
|
7
8
|
type L2BlockL2Logs,
|
|
8
9
|
type L2BlockSource,
|
|
9
10
|
type L2LogsSource,
|
|
11
|
+
type L2Tips,
|
|
10
12
|
type LogFilter,
|
|
11
13
|
type LogType,
|
|
12
14
|
type TxEffect,
|
|
@@ -15,12 +17,18 @@ import {
|
|
|
15
17
|
type UnencryptedL2Log,
|
|
16
18
|
} from '@aztec/circuit-types';
|
|
17
19
|
import {
|
|
20
|
+
type ContractClassPublic,
|
|
18
21
|
ContractClassRegisteredEvent,
|
|
22
|
+
type ContractDataSource,
|
|
19
23
|
ContractInstanceDeployedEvent,
|
|
24
|
+
type ContractInstanceWithAddress,
|
|
25
|
+
type ExecutablePrivateFunctionWithMembershipProof,
|
|
20
26
|
type FunctionSelector,
|
|
21
27
|
type Header,
|
|
22
28
|
PrivateFunctionBroadcastedEvent,
|
|
29
|
+
type PublicFunction,
|
|
23
30
|
UnconstrainedFunctionBroadcastedEvent,
|
|
31
|
+
type UnconstrainedFunctionWithMembershipProof,
|
|
24
32
|
isValidPrivateFunctionMembershipProof,
|
|
25
33
|
isValidUnconstrainedFunctionMembershipProof,
|
|
26
34
|
} from '@aztec/circuits.js';
|
|
@@ -33,16 +41,8 @@ import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
|
|
|
33
41
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
34
42
|
import { Timer } from '@aztec/foundation/timer';
|
|
35
43
|
import { InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
36
|
-
import {
|
|
44
|
+
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
37
45
|
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
38
|
-
import {
|
|
39
|
-
type ContractClassPublic,
|
|
40
|
-
type ContractDataSource,
|
|
41
|
-
type ContractInstanceWithAddress,
|
|
42
|
-
type ExecutablePrivateFunctionWithMembershipProof,
|
|
43
|
-
type PublicFunction,
|
|
44
|
-
type UnconstrainedFunctionWithMembershipProof,
|
|
45
|
-
} from '@aztec/types/contracts';
|
|
46
46
|
|
|
47
47
|
import groupBy from 'lodash.groupby';
|
|
48
48
|
import {
|
|
@@ -246,7 +246,16 @@ export class Archiver implements ArchiveSource {
|
|
|
246
246
|
await this.handleL1ToL2Messages(blockUntilSynced, messagesSynchedTo, currentL1BlockNumber);
|
|
247
247
|
|
|
248
248
|
// ********** Events that are processed per L2 block **********
|
|
249
|
-
|
|
249
|
+
if (currentL1BlockNumber > blocksSynchedTo) {
|
|
250
|
+
// First we retrieve new L2 blocks
|
|
251
|
+
const { provenBlockNumber } = await this.handleL2blocks(blockUntilSynced, blocksSynchedTo, currentL1BlockNumber);
|
|
252
|
+
// And then we prune the current epoch if it'd reorg on next submission.
|
|
253
|
+
// Note that we don't do this before retrieving L2 blocks because we may need to retrieve
|
|
254
|
+
// blocks from more than 2 epochs ago, so we want to make sure we have the latest view of
|
|
255
|
+
// the chain locally before we start unwinding stuff. This can be optimized by figuring out
|
|
256
|
+
// up to which point we're pruning, and then requesting L2 blocks up to that point only.
|
|
257
|
+
await this.handleEpochPrune(provenBlockNumber, currentL1BlockNumber);
|
|
258
|
+
}
|
|
250
259
|
|
|
251
260
|
// Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
|
|
252
261
|
if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
|
|
@@ -255,6 +264,27 @@ export class Archiver implements ArchiveSource {
|
|
|
255
264
|
}
|
|
256
265
|
}
|
|
257
266
|
|
|
267
|
+
/** Checks if there'd be a reorg for the next block submission and start pruning now. */
|
|
268
|
+
private async handleEpochPrune(provenBlockNumber: bigint, currentL1BlockNumber: bigint) {
|
|
269
|
+
const localPendingBlockNumber = BigInt(await this.getBlockNumber());
|
|
270
|
+
|
|
271
|
+
const canPrune =
|
|
272
|
+
localPendingBlockNumber > provenBlockNumber &&
|
|
273
|
+
(await this.rollup.read.canPrune({ blockNumber: currentL1BlockNumber }));
|
|
274
|
+
|
|
275
|
+
if (canPrune) {
|
|
276
|
+
this.log.verbose(`L2 prune will occur on next submission. Rolling back to last proven block.`);
|
|
277
|
+
const blocksToUnwind = localPendingBlockNumber - provenBlockNumber;
|
|
278
|
+
this.log.verbose(
|
|
279
|
+
`Unwinding ${blocksToUnwind} block${blocksToUnwind > 1n ? 's' : ''} from block ${localPendingBlockNumber}`,
|
|
280
|
+
);
|
|
281
|
+
await this.store.unwindBlocks(Number(localPendingBlockNumber), Number(blocksToUnwind));
|
|
282
|
+
// TODO(palla/reorg): Do we need to set the block synched L1 block number here?
|
|
283
|
+
// Seems like the next iteration should handle this.
|
|
284
|
+
// await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
258
288
|
private async handleL1ToL2Messages(
|
|
259
289
|
blockUntilSynced: boolean,
|
|
260
290
|
messagesSynchedTo: bigint,
|
|
@@ -291,11 +321,11 @@ export class Archiver implements ArchiveSource {
|
|
|
291
321
|
);
|
|
292
322
|
}
|
|
293
323
|
|
|
294
|
-
private async handleL2blocks(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
324
|
+
private async handleL2blocks(
|
|
325
|
+
blockUntilSynced: boolean,
|
|
326
|
+
blocksSynchedTo: bigint,
|
|
327
|
+
currentL1BlockNumber: bigint,
|
|
328
|
+
): Promise<{ provenBlockNumber: bigint }> {
|
|
299
329
|
const localPendingBlockNumber = BigInt(await this.getBlockNumber());
|
|
300
330
|
const [
|
|
301
331
|
provenBlockNumber,
|
|
@@ -304,7 +334,7 @@ export class Archiver implements ArchiveSource {
|
|
|
304
334
|
pendingArchive,
|
|
305
335
|
archiveForLocalPendingBlockNumber,
|
|
306
336
|
provenEpochNumber,
|
|
307
|
-
] = await this.rollup.read.status([localPendingBlockNumber]);
|
|
337
|
+
] = await this.rollup.read.status([localPendingBlockNumber], { blockNumber: currentL1BlockNumber });
|
|
308
338
|
|
|
309
339
|
const updateProvenBlock = async () => {
|
|
310
340
|
const localBlockForDestinationProvenBlockNumber = await this.getBlock(Number(provenBlockNumber));
|
|
@@ -317,6 +347,7 @@ export class Archiver implements ArchiveSource {
|
|
|
317
347
|
// if we are here then we must have a valid proven epoch number
|
|
318
348
|
await this.store.setProvenL2EpochNumber(Number(provenEpochNumber));
|
|
319
349
|
}
|
|
350
|
+
this.instrumentation.updateLastProvenBlock(Number(provenBlockNumber));
|
|
320
351
|
};
|
|
321
352
|
|
|
322
353
|
// This is an edge case that we only hit if there are no proposed blocks.
|
|
@@ -325,7 +356,7 @@ export class Archiver implements ArchiveSource {
|
|
|
325
356
|
if (noBlocks) {
|
|
326
357
|
await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
|
|
327
358
|
this.log.verbose(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
|
|
328
|
-
return;
|
|
359
|
+
return { provenBlockNumber };
|
|
329
360
|
}
|
|
330
361
|
|
|
331
362
|
await updateProvenBlock();
|
|
@@ -342,7 +373,7 @@ export class Archiver implements ArchiveSource {
|
|
|
342
373
|
if (noBlockSinceLast) {
|
|
343
374
|
await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
|
|
344
375
|
this.log.verbose(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
|
|
345
|
-
return;
|
|
376
|
+
return { provenBlockNumber };
|
|
346
377
|
}
|
|
347
378
|
|
|
348
379
|
const localPendingBlockInChain = archiveForLocalPendingBlockNumber === localPendingBlock.archive.root.toString();
|
|
@@ -382,7 +413,7 @@ export class Archiver implements ArchiveSource {
|
|
|
382
413
|
this.rollup,
|
|
383
414
|
this.publicClient,
|
|
384
415
|
blockUntilSynced,
|
|
385
|
-
blocksSynchedTo + 1n,
|
|
416
|
+
blocksSynchedTo + 1n, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
|
|
386
417
|
currentL1BlockNumber,
|
|
387
418
|
this.log,
|
|
388
419
|
);
|
|
@@ -390,8 +421,8 @@ export class Archiver implements ArchiveSource {
|
|
|
390
421
|
if (retrievedBlocks.length === 0) {
|
|
391
422
|
// We are not calling `setBlockSynchedL1BlockNumber` because it may cause sync issues if based off infura.
|
|
392
423
|
// See further details in earlier comments.
|
|
393
|
-
this.log.verbose(`Retrieved no new blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
|
|
394
|
-
return;
|
|
424
|
+
this.log.verbose(`Retrieved no new L2 blocks from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
|
|
425
|
+
return { provenBlockNumber };
|
|
395
426
|
}
|
|
396
427
|
|
|
397
428
|
this.log.debug(
|
|
@@ -402,14 +433,14 @@ export class Archiver implements ArchiveSource {
|
|
|
402
433
|
|
|
403
434
|
const lastProcessedL1BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].l1.blockNumber;
|
|
404
435
|
|
|
405
|
-
this.log.debug(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
);
|
|
436
|
+
this.log.debug(`last processed L1 block: [${lastProcessedL1BlockNumber}]`);
|
|
437
|
+
for (const block of retrievedBlocks) {
|
|
438
|
+
this.log.debug(`ingesting new L2 block`, block.data.header.globalVariables.toFriendlyJSON());
|
|
439
|
+
}
|
|
410
440
|
|
|
411
441
|
const timer = new Timer();
|
|
412
442
|
await this.store.addBlocks(retrievedBlocks);
|
|
443
|
+
|
|
413
444
|
// Important that we update AFTER inserting the blocks.
|
|
414
445
|
await updateProvenBlock();
|
|
415
446
|
this.instrumentation.processNewBlocks(
|
|
@@ -418,6 +449,8 @@ export class Archiver implements ArchiveSource {
|
|
|
418
449
|
);
|
|
419
450
|
const lastL2BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].data.number;
|
|
420
451
|
this.log.verbose(`Processed ${retrievedBlocks.length} new L2 blocks up to ${lastL2BlockNumber}`);
|
|
452
|
+
|
|
453
|
+
return { provenBlockNumber };
|
|
421
454
|
}
|
|
422
455
|
|
|
423
456
|
/**
|
|
@@ -497,7 +530,10 @@ export class Archiver implements ArchiveSource {
|
|
|
497
530
|
const [_startTimestamp, endTimestamp] = getTimestampRangeForEpoch(epochNumber, this.l1constants);
|
|
498
531
|
|
|
499
532
|
// For this computation, we throw in a few extra seconds just for good measure,
|
|
500
|
-
// since we know the next L1 block won't be mined within this range
|
|
533
|
+
// since we know the next L1 block won't be mined within this range. Remember that
|
|
534
|
+
// l1timestamp is the timestamp of the last l1 block we've seen, so this 3s rely on
|
|
535
|
+
// the fact that L1 won't mine two blocks within 3s of each other.
|
|
536
|
+
// TODO(palla/reorg): Is the above a safe assumption?
|
|
501
537
|
const leeway = 3n;
|
|
502
538
|
return l1Timestamp + leeway >= endTimestamp;
|
|
503
539
|
}
|
|
@@ -537,8 +573,14 @@ export class Archiver implements ArchiveSource {
|
|
|
537
573
|
if (number === 'latest') {
|
|
538
574
|
number = await this.store.getSynchedL2BlockNumber();
|
|
539
575
|
}
|
|
540
|
-
|
|
541
|
-
|
|
576
|
+
try {
|
|
577
|
+
const headers = await this.store.getBlockHeaders(number, 1);
|
|
578
|
+
return headers.length === 0 ? undefined : headers[0];
|
|
579
|
+
} catch (e) {
|
|
580
|
+
// If the latest is 0, then getBlockHeaders will throw an error
|
|
581
|
+
this.log.error(`getBlockHeader: error fetching block number: ${number}`);
|
|
582
|
+
return undefined;
|
|
583
|
+
}
|
|
542
584
|
}
|
|
543
585
|
|
|
544
586
|
public getTxEffect(txHash: TxHash): Promise<TxEffect | undefined> {
|
|
@@ -653,6 +695,32 @@ export class Archiver implements ArchiveSource {
|
|
|
653
695
|
getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
|
|
654
696
|
return this.store.getContractArtifact(address);
|
|
655
697
|
}
|
|
698
|
+
|
|
699
|
+
async getL2Tips(): Promise<L2Tips> {
|
|
700
|
+
const [latestBlockNumber, provenBlockNumber] = await Promise.all([
|
|
701
|
+
this.getBlockNumber(),
|
|
702
|
+
this.getProvenBlockNumber(),
|
|
703
|
+
] as const);
|
|
704
|
+
|
|
705
|
+
const [latestBlockHeader, provenBlockHeader] = await Promise.all([
|
|
706
|
+
latestBlockNumber > 0 ? this.getBlockHeader(latestBlockNumber) : undefined,
|
|
707
|
+
provenBlockNumber > 0 ? this.getBlockHeader(provenBlockNumber) : undefined,
|
|
708
|
+
] as const);
|
|
709
|
+
|
|
710
|
+
if (latestBlockNumber > 0 && !latestBlockHeader) {
|
|
711
|
+
throw new Error('Failed to retrieve latest block header');
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
if (provenBlockNumber > 0 && !provenBlockHeader) {
|
|
715
|
+
throw new Error('Failed to retrieve proven block header');
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return {
|
|
719
|
+
latest: { number: latestBlockNumber, hash: latestBlockHeader?.hash().toString() } as L2BlockId,
|
|
720
|
+
proven: { number: provenBlockNumber, hash: provenBlockHeader?.hash().toString() } as L2BlockId,
|
|
721
|
+
finalized: { number: provenBlockNumber, hash: provenBlockHeader?.hash().toString() } as L2BlockId,
|
|
722
|
+
};
|
|
723
|
+
}
|
|
656
724
|
}
|
|
657
725
|
|
|
658
726
|
enum Operation {
|
|
@@ -688,9 +756,10 @@ class ArchiverStoreHelper
|
|
|
688
756
|
* @param allLogs - All logs emitted in a bunch of blocks.
|
|
689
757
|
*/
|
|
690
758
|
async #updateRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number, operation: Operation) {
|
|
691
|
-
const contractClasses = ContractClassRegisteredEvent.fromLogs(
|
|
692
|
-
|
|
693
|
-
|
|
759
|
+
const contractClasses = ContractClassRegisteredEvent.fromLogs(
|
|
760
|
+
allLogs,
|
|
761
|
+
ProtocolContractAddress.ContractClassRegisterer,
|
|
762
|
+
).map(e => e.toContractClassPublic());
|
|
694
763
|
if (contractClasses.length > 0) {
|
|
695
764
|
contractClasses.forEach(c => this.#log.verbose(`Registering contract class ${c.id.toString()}`));
|
|
696
765
|
if (operation == Operation.Store) {
|
|
@@ -733,8 +802,14 @@ class ArchiverStoreHelper
|
|
|
733
802
|
*/
|
|
734
803
|
async #storeBroadcastedIndividualFunctions(allLogs: UnencryptedL2Log[], _blockNum: number) {
|
|
735
804
|
// Filter out private and unconstrained function broadcast events
|
|
736
|
-
const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(
|
|
737
|
-
|
|
805
|
+
const privateFnEvents = PrivateFunctionBroadcastedEvent.fromLogs(
|
|
806
|
+
allLogs,
|
|
807
|
+
ProtocolContractAddress.ContractClassRegisterer,
|
|
808
|
+
);
|
|
809
|
+
const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent.fromLogs(
|
|
810
|
+
allLogs,
|
|
811
|
+
ProtocolContractAddress.ContractClassRegisterer,
|
|
812
|
+
);
|
|
738
813
|
|
|
739
814
|
// Group all events by contract class id
|
|
740
815
|
for (const [classIdString, classEvents] of Object.entries(
|
|
@@ -10,15 +10,16 @@ import {
|
|
|
10
10
|
type TxHash,
|
|
11
11
|
type TxReceipt,
|
|
12
12
|
} from '@aztec/circuit-types';
|
|
13
|
-
import { type Fr, type Header } from '@aztec/circuits.js';
|
|
14
|
-
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
15
|
-
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
16
13
|
import {
|
|
17
14
|
type ContractClassPublic,
|
|
18
15
|
type ContractInstanceWithAddress,
|
|
19
16
|
type ExecutablePrivateFunctionWithMembershipProof,
|
|
17
|
+
type Fr,
|
|
18
|
+
type Header,
|
|
20
19
|
type UnconstrainedFunctionWithMembershipProof,
|
|
21
|
-
} from '@aztec/
|
|
20
|
+
} from '@aztec/circuits.js';
|
|
21
|
+
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
22
|
+
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
22
23
|
|
|
23
24
|
import { type DataRetrieval } from './structs/data_retrieval.js';
|
|
24
25
|
import { type L1Published } from './structs/published.js';
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { InboxLeaf, L2Block, LogId, LogType, TxHash } from '@aztec/circuit-types';
|
|
2
2
|
import '@aztec/circuit-types/jest';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AztecAddress,
|
|
5
|
+
type ContractClassPublic,
|
|
6
|
+
type ContractInstanceWithAddress,
|
|
7
|
+
Fr,
|
|
8
|
+
INITIAL_L2_BLOCK_NUM,
|
|
9
|
+
L1_TO_L2_MSG_SUBTREE_HEIGHT,
|
|
10
|
+
SerializableContractInstance,
|
|
11
|
+
} from '@aztec/circuits.js';
|
|
4
12
|
import {
|
|
5
13
|
makeContractClassPublic,
|
|
6
14
|
makeExecutablePrivateFunctionWithMembershipProof,
|
|
@@ -8,11 +16,6 @@ import {
|
|
|
8
16
|
} from '@aztec/circuits.js/testing';
|
|
9
17
|
import { times } from '@aztec/foundation/collection';
|
|
10
18
|
import { randomBytes, randomInt } from '@aztec/foundation/crypto';
|
|
11
|
-
import {
|
|
12
|
-
type ContractClassPublic,
|
|
13
|
-
type ContractInstanceWithAddress,
|
|
14
|
-
SerializableContractInstance,
|
|
15
|
-
} from '@aztec/types/contracts';
|
|
16
19
|
|
|
17
20
|
import { type ArchiverDataStore, type ArchiverL1SynchPoint } from './archiver_store.js';
|
|
18
21
|
import { type L1Published } from './structs/published.js';
|
|
@@ -118,7 +121,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
118
121
|
it('returns the L1 block number that most recently added messages from inbox', async () => {
|
|
119
122
|
await store.addL1ToL2Messages({
|
|
120
123
|
lastProcessedL1BlockNumber: 1n,
|
|
121
|
-
retrievedData: [new InboxLeaf(
|
|
124
|
+
retrievedData: [new InboxLeaf(1n, Fr.ZERO)],
|
|
122
125
|
});
|
|
123
126
|
await expect(store.getSynchPoint()).resolves.toEqual({
|
|
124
127
|
blocksSynchedTo: undefined,
|
|
@@ -226,7 +229,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
226
229
|
const l1ToL2MessageSubtreeSize = 2 ** L1_TO_L2_MSG_SUBTREE_HEIGHT;
|
|
227
230
|
|
|
228
231
|
const generateBlockMessages = (blockNumber: bigint, numMessages: number) =>
|
|
229
|
-
Array.from(
|
|
232
|
+
Array.from(
|
|
233
|
+
{ length: numMessages },
|
|
234
|
+
(_, i) => new InboxLeaf(InboxLeaf.smallestIndexFromL2Block(blockNumber) + BigInt(i), Fr.random()),
|
|
235
|
+
);
|
|
230
236
|
|
|
231
237
|
it('returns messages in correct order', async () => {
|
|
232
238
|
const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize);
|
|
@@ -241,8 +247,9 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
241
247
|
it('throws if it is impossible to sequence messages correctly', async () => {
|
|
242
248
|
const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize - 1);
|
|
243
249
|
// We replace a message with index 4 with a message with index at the end of the tree
|
|
244
|
-
// --> with that there will be a gap and it will be impossible to sequence the
|
|
245
|
-
|
|
250
|
+
// --> with that there will be a gap and it will be impossible to sequence the
|
|
251
|
+
// end of tree = start of next tree/block - 1
|
|
252
|
+
msgs[4] = new InboxLeaf(InboxLeaf.smallestIndexFromL2Block(l2BlockNumber + 1n) - 1n, Fr.random());
|
|
246
253
|
|
|
247
254
|
await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
|
|
248
255
|
await expect(async () => {
|
|
@@ -250,26 +257,18 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
|
|
|
250
257
|
}).rejects.toThrow(`L1 to L2 message gap found in block ${l2BlockNumber}`);
|
|
251
258
|
});
|
|
252
259
|
|
|
253
|
-
it('throws if adding more messages than fits into a block', async () => {
|
|
254
|
-
const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize + 1);
|
|
255
|
-
|
|
256
|
-
await expect(async () => {
|
|
257
|
-
await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
|
|
258
|
-
}).rejects.toThrow(`Message index ${l1ToL2MessageSubtreeSize} out of subtree range`);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
260
|
it('correctly handles duplicate messages', async () => {
|
|
262
261
|
const messageHash = Fr.random();
|
|
263
|
-
|
|
264
|
-
const msgs = [new InboxLeaf(1n, 0n, messageHash), new InboxLeaf(2n, 0n, messageHash)];
|
|
262
|
+
const msgs = [new InboxLeaf(0n, messageHash), new InboxLeaf(16n, messageHash)];
|
|
265
263
|
|
|
266
264
|
await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs });
|
|
267
265
|
|
|
268
266
|
const index1 = (await store.getL1ToL2MessageIndex(messageHash, 0n))!;
|
|
267
|
+
expect(index1).toBe(0n);
|
|
269
268
|
const index2 = await store.getL1ToL2MessageIndex(messageHash, index1 + 1n);
|
|
270
|
-
|
|
271
269
|
expect(index2).toBeDefined();
|
|
272
270
|
expect(index2).toBeGreaterThan(index1);
|
|
271
|
+
expect(index2).toBe(16n);
|
|
273
272
|
});
|
|
274
273
|
});
|
|
275
274
|
|
|
@@ -194,8 +194,8 @@ export async function retrieveL1ToL2Messages(
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
for (const log of messageSentLogs) {
|
|
197
|
-
const {
|
|
198
|
-
retrievedL1ToL2Messages.push(new InboxLeaf(
|
|
197
|
+
const { index, hash } = log.args;
|
|
198
|
+
retrievedL1ToL2Messages.push(new InboxLeaf(index!, Fr.fromString(hash!)));
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
// handles the case when there are no new messages:
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { Fr, FunctionSelector, Vector } from '@aztec/circuits.js';
|
|
2
|
-
import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
3
|
-
import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
|
|
4
1
|
import {
|
|
5
2
|
type ContractClassPublic,
|
|
6
3
|
type ContractClassPublicWithBlockNumber,
|
|
7
4
|
type ExecutablePrivateFunctionWithMembershipProof,
|
|
5
|
+
Fr,
|
|
6
|
+
FunctionSelector,
|
|
8
7
|
type UnconstrainedFunctionWithMembershipProof,
|
|
9
|
-
|
|
8
|
+
Vector,
|
|
9
|
+
} from '@aztec/circuits.js';
|
|
10
|
+
import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
11
|
+
import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* LMDB implementation of the ArchiverDataStore interface.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { type AztecAddress } from '@aztec/circuits.js';
|
|
1
|
+
import { type AztecAddress, type ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/circuits.js';
|
|
2
2
|
import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
|
|
3
|
-
import { type ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* LMDB implementation of the ArchiverDataStore interface.
|
|
@@ -10,17 +10,18 @@ import {
|
|
|
10
10
|
type TxHash,
|
|
11
11
|
type TxReceipt,
|
|
12
12
|
} from '@aztec/circuit-types';
|
|
13
|
-
import { type Fr, type Header } from '@aztec/circuits.js';
|
|
14
|
-
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
15
|
-
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
16
|
-
import { createDebugLogger } from '@aztec/foundation/log';
|
|
17
|
-
import { type AztecKVStore } from '@aztec/kv-store';
|
|
18
13
|
import {
|
|
19
14
|
type ContractClassPublic,
|
|
20
15
|
type ContractInstanceWithAddress,
|
|
21
16
|
type ExecutablePrivateFunctionWithMembershipProof,
|
|
17
|
+
type Fr,
|
|
18
|
+
type Header,
|
|
22
19
|
type UnconstrainedFunctionWithMembershipProof,
|
|
23
|
-
} from '@aztec/
|
|
20
|
+
} from '@aztec/circuits.js';
|
|
21
|
+
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
22
|
+
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
23
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
24
|
+
import { type AztecKVStore } from '@aztec/kv-store';
|
|
24
25
|
|
|
25
26
|
import { type ArchiverDataStore, type ArchiverL1SynchPoint } from '../archiver_store.js';
|
|
26
27
|
import { type DataRetrieval } from '../structs/data_retrieval.js';
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
Fr,
|
|
4
|
-
INITIAL_L2_BLOCK_NUM,
|
|
5
|
-
L1_TO_L2_MSG_SUBTREE_HEIGHT,
|
|
6
|
-
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
7
|
-
} from '@aztec/circuits.js';
|
|
1
|
+
import { InboxLeaf } from '@aztec/circuit-types';
|
|
2
|
+
import { Fr, L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js';
|
|
8
3
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
9
4
|
import { type AztecKVStore, type AztecMap, type AztecSingleton } from '@aztec/kv-store';
|
|
10
5
|
|
|
@@ -61,18 +56,11 @@ export class MessageStore {
|
|
|
61
56
|
void this.#lastSynchedL1Block.set(messages.lastProcessedL1BlockNumber);
|
|
62
57
|
|
|
63
58
|
for (const message of messages.retrievedData) {
|
|
64
|
-
|
|
65
|
-
throw new Error(`Message index ${message.index} out of subtree range`);
|
|
66
|
-
}
|
|
67
|
-
const key = `${message.blockNumber}-${message.index}`;
|
|
59
|
+
const key = `${message.index}`;
|
|
68
60
|
void this.#l1ToL2Messages.setIfNotExists(key, message.leaf.toBuffer());
|
|
69
61
|
|
|
70
|
-
const indexInTheWholeTree =
|
|
71
|
-
(message.blockNumber - BigInt(INITIAL_L2_BLOCK_NUM)) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) +
|
|
72
|
-
message.index;
|
|
73
|
-
|
|
74
62
|
const indices = this.#l1ToL2MessageIndices.get(message.leaf.toString()) ?? [];
|
|
75
|
-
indices.push(
|
|
63
|
+
indices.push(message.index);
|
|
76
64
|
void this.#l1ToL2MessageIndices.set(message.leaf.toString(), indices);
|
|
77
65
|
}
|
|
78
66
|
|
|
@@ -98,9 +86,10 @@ export class MessageStore {
|
|
|
98
86
|
getL1ToL2Messages(blockNumber: bigint): Fr[] {
|
|
99
87
|
const messages: Fr[] = [];
|
|
100
88
|
let undefinedMessageFound = false;
|
|
101
|
-
|
|
89
|
+
const startIndex = Number(InboxLeaf.smallestIndexFromL2Block(blockNumber));
|
|
90
|
+
for (let i = startIndex; i < startIndex + this.#l1ToL2MessagesSubtreeSize; i++) {
|
|
102
91
|
// This is inefficient but probably fine for now.
|
|
103
|
-
const key = `${
|
|
92
|
+
const key = `${i}`;
|
|
104
93
|
const message = this.#l1ToL2Messages.get(key);
|
|
105
94
|
if (message) {
|
|
106
95
|
if (undefinedMessageFound) {
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
INITIAL_L2_BLOCK_NUM,
|
|
4
|
-
L1_TO_L2_MSG_SUBTREE_HEIGHT,
|
|
5
|
-
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
6
|
-
} from '@aztec/circuits.js/constants';
|
|
1
|
+
import { InboxLeaf } from '@aztec/circuit-types';
|
|
2
|
+
import { L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js/constants';
|
|
7
3
|
import { type Fr } from '@aztec/foundation/fields';
|
|
8
4
|
|
|
9
5
|
/**
|
|
@@ -11,7 +7,7 @@ import { type Fr } from '@aztec/foundation/fields';
|
|
|
11
7
|
*/
|
|
12
8
|
export class L1ToL2MessageStore {
|
|
13
9
|
/**
|
|
14
|
-
* A map pointing from a key in a "
|
|
10
|
+
* A map pointing from a key in a "messageIndex" format to the corresponding L1 to L2 message hash.
|
|
15
11
|
*/
|
|
16
12
|
protected store: Map<string, Fr> = new Map();
|
|
17
13
|
|
|
@@ -24,20 +20,17 @@ export class L1ToL2MessageStore {
|
|
|
24
20
|
}
|
|
25
21
|
|
|
26
22
|
addMessage(message: InboxLeaf) {
|
|
27
|
-
|
|
28
|
-
throw new Error(`Message index ${message.index} out of subtree range`);
|
|
29
|
-
}
|
|
30
|
-
const key = `${message.blockNumber}-${message.index}`;
|
|
31
|
-
this.store.set(key, message.leaf);
|
|
23
|
+
this.store.set(`${message.index}`, message.leaf);
|
|
32
24
|
}
|
|
33
25
|
|
|
34
26
|
getMessages(blockNumber: bigint): Fr[] {
|
|
35
27
|
const messages: Fr[] = [];
|
|
36
28
|
let undefinedMessageFound = false;
|
|
37
|
-
|
|
29
|
+
const startIndex = Number(InboxLeaf.smallestIndexFromL2Block(blockNumber));
|
|
30
|
+
|
|
31
|
+
for (let i = startIndex; i < startIndex + this.#l1ToL2MessagesSubtreeSize; i++) {
|
|
38
32
|
// This is inefficient but probably fine for now.
|
|
39
|
-
const
|
|
40
|
-
const message = this.store.get(key);
|
|
33
|
+
const message = this.store.get(`${i}`);
|
|
41
34
|
if (message) {
|
|
42
35
|
if (undefinedMessageFound) {
|
|
43
36
|
throw new Error(`L1 to L2 message gap found in block ${blockNumber}`);
|
|
@@ -61,10 +54,7 @@ export class L1ToL2MessageStore {
|
|
|
61
54
|
getMessageIndex(l1ToL2Message: Fr, startIndex: bigint): bigint | undefined {
|
|
62
55
|
for (const [key, message] of this.store.entries()) {
|
|
63
56
|
if (message.equals(l1ToL2Message)) {
|
|
64
|
-
const
|
|
65
|
-
const [blockNumber, messageIndex] = [BigInt(keyParts[0]), BigInt(keyParts[1])];
|
|
66
|
-
const indexInTheWholeTree =
|
|
67
|
-
(blockNumber - BigInt(INITIAL_L2_BLOCK_NUM)) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) + messageIndex;
|
|
57
|
+
const indexInTheWholeTree = BigInt(key);
|
|
68
58
|
if (indexInTheWholeTree < startIndex) {
|
|
69
59
|
continue;
|
|
70
60
|
}
|
|
@@ -15,16 +15,18 @@ import {
|
|
|
15
15
|
TxReceipt,
|
|
16
16
|
type UnencryptedL2BlockL2Logs,
|
|
17
17
|
} from '@aztec/circuit-types';
|
|
18
|
-
import { Fr, type Header, INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js';
|
|
19
|
-
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
20
|
-
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
21
18
|
import {
|
|
22
19
|
type ContractClassPublic,
|
|
23
20
|
type ContractClassPublicWithBlockNumber,
|
|
24
21
|
type ContractInstanceWithAddress,
|
|
25
22
|
type ExecutablePrivateFunctionWithMembershipProof,
|
|
23
|
+
Fr,
|
|
24
|
+
type Header,
|
|
25
|
+
INITIAL_L2_BLOCK_NUM,
|
|
26
26
|
type UnconstrainedFunctionWithMembershipProof,
|
|
27
|
-
} from '@aztec/
|
|
27
|
+
} from '@aztec/circuits.js';
|
|
28
|
+
import { type ContractArtifact } from '@aztec/foundation/abi';
|
|
29
|
+
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
28
30
|
|
|
29
31
|
import { type ArchiverDataStore, type ArchiverL1SynchPoint } from '../archiver_store.js';
|
|
30
32
|
import { type DataRetrieval } from '../structs/data_retrieval.js';
|
package/src/factory.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { type ContractClassPublic } from '@aztec/circuits.js';
|
|
1
2
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
2
3
|
import { createStore } from '@aztec/kv-store/utils';
|
|
4
|
+
import { getCanonicalProtocolContract, protocolContractNames } from '@aztec/protocol-contracts';
|
|
3
5
|
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
4
6
|
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
|
|
5
7
|
|
|
@@ -16,8 +18,24 @@ export async function createArchiver(
|
|
|
16
18
|
if (!config.archiverUrl) {
|
|
17
19
|
const store = await createStore('archiver', config, createDebugLogger('aztec:archiver:lmdb'));
|
|
18
20
|
const archiverStore = new KVArchiverDataStore(store, config.maxLogs);
|
|
21
|
+
await initWithProtocolContracts(archiverStore);
|
|
19
22
|
return Archiver.createAndSync(config, archiverStore, telemetry, opts.blockUntilSync);
|
|
20
23
|
} else {
|
|
21
24
|
return createArchiverClient(config.archiverUrl);
|
|
22
25
|
}
|
|
23
26
|
}
|
|
27
|
+
|
|
28
|
+
async function initWithProtocolContracts(store: KVArchiverDataStore) {
|
|
29
|
+
const blockNumber = 0;
|
|
30
|
+
for (const name of protocolContractNames) {
|
|
31
|
+
const contract = getCanonicalProtocolContract(name);
|
|
32
|
+
const contractClassPublic: ContractClassPublic = {
|
|
33
|
+
...contract.contractClass,
|
|
34
|
+
privateFunctions: [],
|
|
35
|
+
unconstrainedFunctions: [],
|
|
36
|
+
};
|
|
37
|
+
await store.addContractArtifact(contract.address, contract.artifact);
|
|
38
|
+
await store.addContractClasses([contractClassPublic], blockNumber);
|
|
39
|
+
await store.addContractInstances([contract.instance], blockNumber);
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/test/index.ts
CHANGED