1
1
import {
2
2
type FromLogType ,
3
3
type GetUnencryptedLogsResponse ,
4
+ type InboxLeaf ,
4
5
type L1ToL2MessageSource ,
5
6
type L2Block ,
6
7
type L2BlockL2Logs ,
@@ -13,14 +14,15 @@ import {
13
14
type TxReceipt ,
14
15
type UnencryptedL2Log ,
15
16
} from '@aztec/circuit-types' ;
16
- import { ContractClassRegisteredEvent , type FunctionSelector } from '@aztec/circuits.js' ;
17
17
import {
18
+ ContractClassRegisteredEvent ,
18
19
ContractInstanceDeployedEvent ,
20
+ type FunctionSelector ,
19
21
PrivateFunctionBroadcastedEvent ,
20
22
UnconstrainedFunctionBroadcastedEvent ,
21
23
isValidPrivateFunctionMembershipProof ,
22
24
isValidUnconstrainedFunctionMembershipProof ,
23
- } from '@aztec/circuits.js/contract ' ;
25
+ } from '@aztec/circuits.js' ;
24
26
import { createEthereumChain } from '@aztec/ethereum' ;
25
27
import { type ContractArtifact } from '@aztec/foundation/abi' ;
26
28
import { type AztecAddress } from '@aztec/foundation/aztec-address' ;
@@ -52,11 +54,12 @@ import {
52
54
http ,
53
55
} from 'viem' ;
54
56
55
- import { type ArchiverDataStore } from './archiver_store.js' ;
57
+ import { type ArchiverDataStore , type ArchiverL1SynchPoint } from './archiver_store.js' ;
56
58
import { type ArchiverConfig } from './config.js' ;
57
59
import { retrieveBlockFromRollup , retrieveL1ToL2Messages } from './data_retrieval.js' ;
58
60
import { ArchiverInstrumentation } from './instrumentation.js' ;
59
- import { type SingletonDataRetrieval } from './structs/data_retrieval.js' ;
61
+ import { type DataRetrieval , type SingletonDataRetrieval } from './structs/data_retrieval.js' ;
62
+ import { type L1Published } from './structs/published.js' ;
60
63
61
64
/**
62
65
* Helper interface to combine all sources this archiver implementation provides.
@@ -77,6 +80,8 @@ export class Archiver implements ArchiveSource {
77
80
private rollup : GetContractReturnType < typeof RollupAbi , PublicClient < HttpTransport , Chain > > ;
78
81
private inbox : GetContractReturnType < typeof InboxAbi , PublicClient < HttpTransport , Chain > > ;
79
82
83
+ private store : ArchiverStoreHelper ;
84
+
80
85
/**
81
86
* Creates a new instance of the Archiver.
82
87
* @param publicClient - A client for interacting with the Ethereum node.
@@ -90,14 +95,16 @@ export class Archiver implements ArchiveSource {
90
95
constructor (
91
96
private readonly publicClient : PublicClient < HttpTransport , Chain > ,
92
97
private readonly rollupAddress : EthAddress ,
93
- private readonly inboxAddress : EthAddress ,
98
+ readonly inboxAddress : EthAddress ,
94
99
private readonly registryAddress : EthAddress ,
95
- private readonly store : ArchiverDataStore ,
100
+ readonly dataStore : ArchiverDataStore ,
96
101
private readonly pollingIntervalMs = 10_000 ,
97
102
private readonly instrumentation : ArchiverInstrumentation ,
98
103
private readonly l1StartBlock : bigint = 0n ,
99
104
private readonly log : DebugLogger = createDebugLogger ( 'aztec:archiver' ) ,
100
105
) {
106
+ this . store = new ArchiverStoreHelper ( dataStore ) ;
107
+
101
108
this . rollup = getContract ( {
102
109
address : rollupAddress . toString ( ) ,
103
110
abi : RollupAbi ,
@@ -340,29 +347,6 @@ export class Archiver implements ArchiveSource {
340
347
. join ( ',' ) } with last processed L1 block ${ lastProcessedL1BlockNumber } `,
341
348
) ;
342
349
343
- await Promise . all (
344
- retrievedBlocks . map ( block => {
345
- return this . store . addLogs (
346
- block . data . body . noteEncryptedLogs ,
347
- block . data . body . encryptedLogs ,
348
- block . data . body . unencryptedLogs ,
349
- block . data . number ,
350
- ) ;
351
- } ) ,
352
- ) ;
353
-
354
- // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
355
- await Promise . all (
356
- retrievedBlocks . map ( async block => {
357
- const blockLogs = block . data . body . txEffects
358
- . flatMap ( txEffect => ( txEffect ? [ txEffect . unencryptedLogs ] : [ ] ) )
359
- . flatMap ( txLog => txLog . unrollLogs ( ) ) ;
360
- await this . storeRegisteredContractClasses ( blockLogs , block . data . number ) ;
361
- await this . storeDeployedContractInstances ( blockLogs , block . data . number ) ;
362
- await this . storeBroadcastedIndividualFunctions ( blockLogs , block . data . number ) ;
363
- } ) ,
364
- ) ;
365
-
366
350
const timer = new Timer ( ) ;
367
351
await this . store . addBlocks ( retrievedBlocks ) ;
368
352
this . instrumentation . processNewBlocks (
@@ -373,73 +357,6 @@ export class Archiver implements ArchiveSource {
373
357
this . log . verbose ( `Processed ${ retrievedBlocks . length } new L2 blocks up to ${ lastL2BlockNumber } ` ) ;
374
358
}
375
359
376
- /**
377
- * Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract.
378
- * @param allLogs - All logs emitted in a bunch of blocks.
379
- */
380
- private async storeRegisteredContractClasses ( allLogs : UnencryptedL2Log [ ] , blockNum : number ) {
381
- const contractClasses = ContractClassRegisteredEvent . fromLogs ( allLogs , ClassRegistererAddress ) . map ( e =>
382
- e . toContractClassPublic ( ) ,
383
- ) ;
384
- if ( contractClasses . length > 0 ) {
385
- contractClasses . forEach ( c => this . log . verbose ( `Registering contract class ${ c . id . toString ( ) } ` ) ) ;
386
- await this . store . addContractClasses ( contractClasses , blockNum ) ;
387
- }
388
- }
389
-
390
- /**
391
- * Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
392
- * @param allLogs - All logs emitted in a bunch of blocks.
393
- */
394
- private async storeDeployedContractInstances ( allLogs : UnencryptedL2Log [ ] , blockNum : number ) {
395
- const contractInstances = ContractInstanceDeployedEvent . fromLogs ( allLogs ) . map ( e => e . toContractInstance ( ) ) ;
396
- if ( contractInstances . length > 0 ) {
397
- contractInstances . forEach ( c => this . log . verbose ( `Storing contract instance at ${ c . address . toString ( ) } ` ) ) ;
398
- await this . store . addContractInstances ( contractInstances , blockNum ) ;
399
- }
400
- }
401
-
402
- private async storeBroadcastedIndividualFunctions ( allLogs : UnencryptedL2Log [ ] , _blockNum : number ) {
403
- // Filter out private and unconstrained function broadcast events
404
- const privateFnEvents = PrivateFunctionBroadcastedEvent . fromLogs ( allLogs , ClassRegistererAddress ) ;
405
- const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent . fromLogs ( allLogs , ClassRegistererAddress ) ;
406
-
407
- // Group all events by contract class id
408
- for ( const [ classIdString , classEvents ] of Object . entries (
409
- groupBy ( [ ...privateFnEvents , ...unconstrainedFnEvents ] , e => e . contractClassId . toString ( ) ) ,
410
- ) ) {
411
- const contractClassId = Fr . fromString ( classIdString ) ;
412
- const contractClass = await this . store . getContractClass ( contractClassId ) ;
413
- if ( ! contractClass ) {
414
- this . log . warn ( `Skipping broadcasted functions as contract class ${ contractClassId . toString ( ) } was not found` ) ;
415
- continue ;
416
- }
417
-
418
- // Split private and unconstrained functions, and filter out invalid ones
419
- const allFns = classEvents . map ( e => e . toFunctionWithMembershipProof ( ) ) ;
420
- const privateFns = allFns . filter (
421
- ( fn ) : fn is ExecutablePrivateFunctionWithMembershipProof => 'unconstrainedFunctionsArtifactTreeRoot' in fn ,
422
- ) ;
423
- const unconstrainedFns = allFns . filter (
424
- ( fn ) : fn is UnconstrainedFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn ,
425
- ) ;
426
- const validPrivateFns = privateFns . filter ( fn => isValidPrivateFunctionMembershipProof ( fn , contractClass ) ) ;
427
- const validUnconstrainedFns = unconstrainedFns . filter ( fn =>
428
- isValidUnconstrainedFunctionMembershipProof ( fn , contractClass ) ,
429
- ) ;
430
- const validFnCount = validPrivateFns . length + validUnconstrainedFns . length ;
431
- if ( validFnCount !== allFns . length ) {
432
- this . log . warn ( `Skipping ${ allFns . length - validFnCount } invalid functions` ) ;
433
- }
434
-
435
- // Store the functions in the contract class in a single operation
436
- if ( validFnCount > 0 ) {
437
- this . log . verbose ( `Storing ${ validFnCount } functions for contract class ${ contractClassId . toString ( ) } ` ) ;
438
- }
439
- await this . store . addFunctions ( contractClassId , validPrivateFns , validUnconstrainedFns ) ;
440
- }
441
- }
442
-
443
360
/**
444
361
* Stops the archiver.
445
362
* @returns A promise signalling completion of the stop process.
@@ -597,3 +514,174 @@ export class Archiver implements ArchiveSource {
597
514
return this . store . getContractArtifact ( address ) ;
598
515
}
599
516
}
517
+
518
+ /**
519
+ * A helper class that we use to deal with some of the logic needed when adding blocks.
520
+ *
521
+ * I would have preferred to not have this type. But it is useful for handling the logic that any
522
+ * store would need to include otherwise while exposing fewer functions and logic directly to the archiver.
523
+ */
524
+ class ArchiverStoreHelper
525
+ implements Omit < ArchiverDataStore , 'addLogs' | 'addContractClasses' | 'addContractInstances' | 'addFunctions' >
526
+ {
527
+ #log = createDebugLogger ( 'aztec:archiver:block-helper' ) ;
528
+
529
+ constructor ( private readonly store : ArchiverDataStore ) { }
530
+
531
+ /**
532
+ * Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract.
533
+ * @param allLogs - All logs emitted in a bunch of blocks.
534
+ */
535
+ async #storeRegisteredContractClasses( allLogs : UnencryptedL2Log [ ] , blockNum : number ) {
536
+ const contractClasses = ContractClassRegisteredEvent . fromLogs ( allLogs , ClassRegistererAddress ) . map ( e =>
537
+ e . toContractClassPublic ( ) ,
538
+ ) ;
539
+ if ( contractClasses . length > 0 ) {
540
+ contractClasses . forEach ( c => this . #log. verbose ( `Registering contract class ${ c . id . toString ( ) } ` ) ) ;
541
+ return await this . store . addContractClasses ( contractClasses , blockNum ) ;
542
+ }
543
+ return true ;
544
+ }
545
+
546
+ /**
547
+ * Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
548
+ * @param allLogs - All logs emitted in a bunch of blocks.
549
+ */
550
+ async #storeDeployedContractInstances( allLogs : UnencryptedL2Log [ ] , blockNum : number ) {
551
+ const contractInstances = ContractInstanceDeployedEvent . fromLogs ( allLogs ) . map ( e => e . toContractInstance ( ) ) ;
552
+ if ( contractInstances . length > 0 ) {
553
+ contractInstances . forEach ( c => this . #log. verbose ( `Storing contract instance at ${ c . address . toString ( ) } ` ) ) ;
554
+ return await this . store . addContractInstances ( contractInstances , blockNum ) ;
555
+ }
556
+ return true ;
557
+ }
558
+
559
+ async #storeBroadcastedIndividualFunctions( allLogs : UnencryptedL2Log [ ] , _blockNum : number ) {
560
+ // Filter out private and unconstrained function broadcast events
561
+ const privateFnEvents = PrivateFunctionBroadcastedEvent . fromLogs ( allLogs , ClassRegistererAddress ) ;
562
+ const unconstrainedFnEvents = UnconstrainedFunctionBroadcastedEvent . fromLogs ( allLogs , ClassRegistererAddress ) ;
563
+
564
+ // Group all events by contract class id
565
+ for ( const [ classIdString , classEvents ] of Object . entries (
566
+ groupBy ( [ ...privateFnEvents , ...unconstrainedFnEvents ] , e => e . contractClassId . toString ( ) ) ,
567
+ ) ) {
568
+ const contractClassId = Fr . fromString ( classIdString ) ;
569
+ const contractClass = await this . getContractClass ( contractClassId ) ;
570
+ if ( ! contractClass ) {
571
+ this . #log. warn ( `Skipping broadcasted functions as contract class ${ contractClassId . toString ( ) } was not found` ) ;
572
+ continue ;
573
+ }
574
+
575
+ // Split private and unconstrained functions, and filter out invalid ones
576
+ const allFns = classEvents . map ( e => e . toFunctionWithMembershipProof ( ) ) ;
577
+ const privateFns = allFns . filter (
578
+ ( fn ) : fn is ExecutablePrivateFunctionWithMembershipProof => 'unconstrainedFunctionsArtifactTreeRoot' in fn ,
579
+ ) ;
580
+ const unconstrainedFns = allFns . filter (
581
+ ( fn ) : fn is UnconstrainedFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn ,
582
+ ) ;
583
+ const validPrivateFns = privateFns . filter ( fn => isValidPrivateFunctionMembershipProof ( fn , contractClass ) ) ;
584
+ const validUnconstrainedFns = unconstrainedFns . filter ( fn =>
585
+ isValidUnconstrainedFunctionMembershipProof ( fn , contractClass ) ,
586
+ ) ;
587
+ const validFnCount = validPrivateFns . length + validUnconstrainedFns . length ;
588
+ if ( validFnCount !== allFns . length ) {
589
+ this . #log. warn ( `Skipping ${ allFns . length - validFnCount } invalid functions` ) ;
590
+ }
591
+
592
+ // Store the functions in the contract class in a single operation
593
+ if ( validFnCount > 0 ) {
594
+ this . #log. verbose ( `Storing ${ validFnCount } functions for contract class ${ contractClassId . toString ( ) } ` ) ;
595
+ }
596
+ return await this . store . addFunctions ( contractClassId , validPrivateFns , validUnconstrainedFns ) ;
597
+ }
598
+ return true ;
599
+ }
600
+
601
+ async addBlocks ( blocks : L1Published < L2Block > [ ] ) : Promise < boolean > {
602
+ return [
603
+ this . store . addLogs ( blocks . map ( block => block . data ) ) ,
604
+ // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
605
+ ...( await Promise . all (
606
+ blocks . map ( async block => {
607
+ const blockLogs = block . data . body . txEffects
608
+ . flatMap ( txEffect => ( txEffect ? [ txEffect . unencryptedLogs ] : [ ] ) )
609
+ . flatMap ( txLog => txLog . unrollLogs ( ) ) ;
610
+
611
+ return (
612
+ await Promise . all ( [
613
+ this . #storeRegisteredContractClasses( blockLogs , block . data . number ) ,
614
+ this . #storeDeployedContractInstances( blockLogs , block . data . number ) ,
615
+ this . #storeBroadcastedIndividualFunctions( blockLogs , block . data . number ) ,
616
+ ] )
617
+ ) . every ( Boolean ) ;
618
+ } ) ,
619
+ ) ) ,
620
+ this . store . addBlocks ( blocks ) ,
621
+ ] . every ( Boolean ) ;
622
+ }
623
+
624
+ getBlocks ( from : number , limit : number ) : Promise < L1Published < L2Block > [ ] > {
625
+ return this . store . getBlocks ( from , limit ) ;
626
+ }
627
+
628
+ getTxEffect ( txHash : TxHash ) : Promise < TxEffect | undefined > {
629
+ return this . store . getTxEffect ( txHash ) ;
630
+ }
631
+
632
+ getSettledTxReceipt ( txHash : TxHash ) : Promise < TxReceipt | undefined > {
633
+ return this . store . getSettledTxReceipt ( txHash ) ;
634
+ }
635
+ addL1ToL2Messages ( messages : DataRetrieval < InboxLeaf > ) : Promise < boolean > {
636
+ return this . store . addL1ToL2Messages ( messages ) ;
637
+ }
638
+ getL1ToL2Messages ( blockNumber : bigint ) : Promise < Fr [ ] > {
639
+ return this . store . getL1ToL2Messages ( blockNumber ) ;
640
+ }
641
+ getL1ToL2MessageIndex ( l1ToL2Message : Fr , startIndex : bigint ) : Promise < bigint | undefined > {
642
+ return this . store . getL1ToL2MessageIndex ( l1ToL2Message , startIndex ) ;
643
+ }
644
+ getLogs < TLogType extends LogType > (
645
+ from : number ,
646
+ limit : number ,
647
+ logType : TLogType ,
648
+ ) : Promise < L2BlockL2Logs < FromLogType < TLogType > > [ ] > {
649
+ return this . store . getLogs ( from , limit , logType ) ;
650
+ }
651
+ getUnencryptedLogs ( filter : LogFilter ) : Promise < GetUnencryptedLogsResponse > {
652
+ return this . store . getUnencryptedLogs ( filter ) ;
653
+ }
654
+ getSynchedL2BlockNumber ( ) : Promise < number > {
655
+ return this . store . getSynchedL2BlockNumber ( ) ;
656
+ }
657
+ getProvenL2BlockNumber ( ) : Promise < number > {
658
+ return this . store . getProvenL2BlockNumber ( ) ;
659
+ }
660
+ setProvenL2BlockNumber ( l2BlockNumber : SingletonDataRetrieval < number > ) : Promise < void > {
661
+ return this . store . setProvenL2BlockNumber ( l2BlockNumber ) ;
662
+ }
663
+ setBlockSynchedL1BlockNumber ( l1BlockNumber : bigint ) : Promise < void > {
664
+ return this . store . setBlockSynchedL1BlockNumber ( l1BlockNumber ) ;
665
+ }
666
+ setMessageSynchedL1BlockNumber ( l1BlockNumber : bigint ) : Promise < void > {
667
+ return this . store . setMessageSynchedL1BlockNumber ( l1BlockNumber ) ;
668
+ }
669
+ getSynchPoint ( ) : Promise < ArchiverL1SynchPoint > {
670
+ return this . store . getSynchPoint ( ) ;
671
+ }
672
+ getContractClass ( id : Fr ) : Promise < ContractClassPublic | undefined > {
673
+ return this . store . getContractClass ( id ) ;
674
+ }
675
+ getContractInstance ( address : AztecAddress ) : Promise < ContractInstanceWithAddress | undefined > {
676
+ return this . store . getContractInstance ( address ) ;
677
+ }
678
+ getContractClassIds ( ) : Promise < Fr [ ] > {
679
+ return this . store . getContractClassIds ( ) ;
680
+ }
681
+ addContractArtifact ( address : AztecAddress , contract : ContractArtifact ) : Promise < void > {
682
+ return this . store . addContractArtifact ( address , contract ) ;
683
+ }
684
+ getContractArtifact ( address : AztecAddress ) : Promise < ContractArtifact | undefined > {
685
+ return this . store . getContractArtifact ( address ) ;
686
+ }
687
+ }
0 commit comments