@aztec/validator-client 0.0.1-commit.3d8f95d → 0.0.1-commit.3e3d0c9cd

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.
Files changed (48) hide show
  1. package/README.md +43 -0
  2. package/dest/block_proposal_handler.d.ts +5 -4
  3. package/dest/block_proposal_handler.d.ts.map +1 -1
  4. package/dest/block_proposal_handler.js +122 -62
  5. package/dest/checkpoint_builder.d.ts +16 -5
  6. package/dest/checkpoint_builder.d.ts.map +1 -1
  7. package/dest/checkpoint_builder.js +121 -44
  8. package/dest/config.d.ts +1 -1
  9. package/dest/config.d.ts.map +1 -1
  10. package/dest/config.js +22 -1
  11. package/dest/duties/validation_service.d.ts +2 -2
  12. package/dest/duties/validation_service.d.ts.map +1 -1
  13. package/dest/duties/validation_service.js +6 -12
  14. package/dest/factory.d.ts +1 -1
  15. package/dest/factory.d.ts.map +1 -1
  16. package/dest/factory.js +2 -1
  17. package/dest/index.d.ts +1 -2
  18. package/dest/index.d.ts.map +1 -1
  19. package/dest/index.js +0 -1
  20. package/dest/key_store/ha_key_store.js +1 -1
  21. package/dest/metrics.d.ts +9 -1
  22. package/dest/metrics.d.ts.map +1 -1
  23. package/dest/metrics.js +12 -0
  24. package/dest/validator.d.ts +13 -5
  25. package/dest/validator.d.ts.map +1 -1
  26. package/dest/validator.js +88 -27
  27. package/package.json +19 -19
  28. package/src/block_proposal_handler.ts +149 -80
  29. package/src/checkpoint_builder.ts +137 -39
  30. package/src/config.ts +22 -1
  31. package/src/duties/validation_service.ts +12 -11
  32. package/src/factory.ts +1 -0
  33. package/src/index.ts +0 -1
  34. package/src/key_store/ha_key_store.ts +1 -1
  35. package/src/metrics.ts +18 -0
  36. package/src/validator.ts +91 -22
  37. package/dest/tx_validator/index.d.ts +0 -3
  38. package/dest/tx_validator/index.d.ts.map +0 -1
  39. package/dest/tx_validator/index.js +0 -2
  40. package/dest/tx_validator/nullifier_cache.d.ts +0 -14
  41. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  42. package/dest/tx_validator/nullifier_cache.js +0 -24
  43. package/dest/tx_validator/tx_validator_factory.d.ts +0 -19
  44. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
  45. package/dest/tx_validator/tx_validator_factory.js +0 -54
  46. package/src/tx_validator/index.ts +0 -2
  47. package/src/tx_validator/nullifier_cache.ts +0 -30
  48. package/src/tx_validator/tx_validator_factory.ts +0 -154
package/src/validator.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { BlobClientInterface } from '@aztec/blob-client/client';
2
2
  import { type Blob, getBlobsPerL1Block } from '@aztec/blob-lib';
3
3
  import type { EpochCache } from '@aztec/epoch-cache';
4
+ import { validateFeeAssetPriceModifier } from '@aztec/ethereum/contracts';
4
5
  import {
5
6
  BlockNumber,
6
7
  CheckpointNumber,
@@ -23,7 +24,8 @@ import { AuthRequest, AuthResponse, BlockProposalValidator, ReqRespSubProtocol }
23
24
  import { OffenseType, WANT_TO_SLASH_EVENT, type Watcher, type WatcherEmitter } from '@aztec/slasher';
24
25
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
25
26
  import type { CommitteeAttestationsAndSigners, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
26
- import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
27
+ import { validateCheckpoint } from '@aztec/stdlib/checkpoint';
28
+ import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
27
29
  import type {
28
30
  CreateCheckpointProposalLastBlockData,
29
31
  ITxProvider,
@@ -44,8 +46,9 @@ import type { CheckpointHeader } from '@aztec/stdlib/rollup';
44
46
  import type { BlockHeader, CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
45
47
  import { AttestationTimeoutError } from '@aztec/stdlib/validators';
46
48
  import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
47
- import { createHASigner } from '@aztec/validator-ha-signer/factory';
49
+ import { createHASigner, createLocalSignerWithProtection } from '@aztec/validator-ha-signer/factory';
48
50
  import { DutyType, type SigningContext } from '@aztec/validator-ha-signer/types';
51
+ import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer';
49
52
 
50
53
  import { EventEmitter } from 'events';
51
54
  import type { TypedDataDefinition } from 'viem';
@@ -76,7 +79,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
76
79
  private validationService: ValidationService;
77
80
  private metrics: ValidatorMetrics;
78
81
  private log: Logger;
79
-
80
82
  // Whether it has already registered handlers on the p2p client
81
83
  private hasRegisteredHandlers = false;
82
84
 
@@ -88,6 +90,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
88
90
 
89
91
  private lastEpochForCommitteeUpdateLoop: EpochNumber | undefined;
90
92
  private epochCacheUpdateLoop: RunningPromise;
93
+ /** Tracks the last epoch in which each attester successfully submitted at least one attestation. */
94
+ private lastAttestedEpochByAttester: Map<string, EpochNumber> = new Map();
91
95
 
92
96
  private proposersOfInvalidBlocks: Set<string> = new Set();
93
97
 
@@ -105,6 +109,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
105
109
  private l1ToL2MessageSource: L1ToL2MessageSource,
106
110
  private config: ValidatorClientFullConfig,
107
111
  private blobClient: BlobClientInterface,
112
+ private slashingProtectionSigner: ValidatorHASigner,
108
113
  private dateProvider: DateProvider = new DateProvider(),
109
114
  telemetry: TelemetryClient = getTelemetryClient(),
110
115
  log = createLogger('validator'),
@@ -158,6 +163,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
158
163
  this.log.trace(`No committee found for slot`);
159
164
  return;
160
165
  }
166
+ this.metrics.setCurrentEpoch(epoch);
161
167
  if (epoch !== this.lastEpochForCommitteeUpdateLoop) {
162
168
  const me = this.getValidatorAddresses();
163
169
  const committeeSet = new Set(committee.map(v => v.toString()));
@@ -195,6 +201,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
195
201
  const metrics = new ValidatorMetrics(telemetry);
196
202
  const blockProposalValidator = new BlockProposalValidator(epochCache, {
197
203
  txsPermitted: !config.disableTransactions,
204
+ maxTxsPerBlock: config.validateMaxTxsPerBlock,
198
205
  });
199
206
  const blockProposalHandler = new BlockProposalHandler(
200
207
  checkpointsBuilder,
@@ -210,16 +217,28 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
210
217
  telemetry,
211
218
  );
212
219
 
213
- let validatorKeyStore: ExtendedValidatorKeyStore = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
220
+ const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
221
+ let slashingProtectionSigner: ValidatorHASigner;
214
222
  if (config.haSigningEnabled) {
223
+ // Multi-node HA mode: use PostgreSQL-backed distributed locking.
215
224
  // If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
216
225
  const haConfig = {
217
226
  ...config,
218
227
  maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
219
228
  };
220
- const { signer } = await createHASigner(haConfig);
221
- validatorKeyStore = new HAKeyStore(validatorKeyStore, signer);
229
+ ({ signer: slashingProtectionSigner } = await createHASigner(haConfig, {
230
+ telemetryClient: telemetry,
231
+ dateProvider,
232
+ }));
233
+ } else {
234
+ // Single-node mode: use LMDB-backed local signing protection.
235
+ // This prevents double-signing if the node crashes and restarts mid-proposal.
236
+ ({ signer: slashingProtectionSigner } = await createLocalSignerWithProtection(config, {
237
+ telemetryClient: telemetry,
238
+ dateProvider,
239
+ }));
222
240
  }
241
+ const validatorKeyStore: ExtendedValidatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, slashingProtectionSigner);
223
242
 
224
243
  const validator = new ValidatorClient(
225
244
  validatorKeyStore,
@@ -232,6 +251,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
232
251
  l1ToL2MessageSource,
233
252
  config,
234
253
  blobClient,
254
+ slashingProtectionSigner,
235
255
  dateProvider,
236
256
  telemetry,
237
257
  );
@@ -269,6 +289,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
269
289
  this.config = { ...this.config, ...config };
270
290
  }
271
291
 
292
+ public reloadKeystore(newManager: KeystoreManager): void {
293
+ const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
294
+ this.keyStore = new HAKeyStore(newAdapter, this.slashingProtectionSigner);
295
+ this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service'));
296
+ }
297
+
272
298
  public async start() {
273
299
  if (this.epochCacheUpdateLoop.isRunning()) {
274
300
  this.log.warn(`Validator client already started`);
@@ -354,7 +380,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
354
380
 
355
381
  // Ignore proposals from ourselves (may happen in HA setups)
356
382
  if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
357
- this.log.warn(`Ignoring block proposal from self for slot ${slotNumber}`, {
383
+ this.log.debug(`Ignoring block proposal from self for slot ${slotNumber}`, {
358
384
  proposer: proposer.toString(),
359
385
  slotNumber,
360
386
  });
@@ -390,9 +416,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
390
416
  );
391
417
 
392
418
  if (!validationResult.isValid) {
393
- this.log.warn(`Block proposal validation failed: ${validationResult.reason}`, proposalInfo);
394
-
395
419
  const reason = validationResult.reason || 'unknown';
420
+
421
+ this.log.warn(`Block proposal validation failed: ${reason}`, proposalInfo);
422
+
396
423
  // Classify failure reason: bad proposal vs node issue
397
424
  const badProposalReasons: BlockProposalValidationFailureReason[] = [
398
425
  'invalid_proposal',
@@ -464,13 +491,21 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
464
491
 
465
492
  // Ignore proposals from ourselves (may happen in HA setups)
466
493
  if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
467
- this.log.warn(`Ignoring block proposal from self for slot ${slotNumber}`, {
494
+ this.log.debug(`Ignoring block proposal from self for slot ${slotNumber}`, {
468
495
  proposer: proposer.toString(),
469
496
  slotNumber,
470
497
  });
471
498
  return undefined;
472
499
  }
473
500
 
501
+ // Validate fee asset price modifier is within allowed range
502
+ if (!validateFeeAssetPriceModifier(proposal.feeAssetPriceModifier)) {
503
+ this.log.warn(
504
+ `Received checkpoint proposal with invalid feeAssetPriceModifier ${proposal.feeAssetPriceModifier} for slot ${slotNumber}`,
505
+ );
506
+ return undefined;
507
+ }
508
+
474
509
  // Check that I have any address in current committee before attesting
475
510
  const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
476
511
  const partOfCommittee = inCommittee.length > 0;
@@ -479,11 +514,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
479
514
  slotNumber,
480
515
  archive: proposal.archive.toString(),
481
516
  proposer: proposer.toString(),
482
- txCount: proposal.txHashes.length,
483
517
  };
484
518
  this.log.info(`Received checkpoint proposal for slot ${slotNumber}`, {
485
519
  ...proposalInfo,
486
- txHashes: proposal.txHashes.map(t => t.toString()),
487
520
  fishermanMode: this.config.fishermanMode || false,
488
521
  });
489
522
 
@@ -519,6 +552,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
519
552
 
520
553
  this.metrics.incSuccessfulAttestations(inCommittee.length);
521
554
 
555
+ // Track epoch participation per attester: count each (attester, epoch) pair at most once
556
+ const proposalEpoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
557
+ for (const attester of inCommittee) {
558
+ const key = attester.toString();
559
+ const lastEpoch = this.lastAttestedEpochByAttester.get(key);
560
+ if (lastEpoch === undefined || proposalEpoch > lastEpoch) {
561
+ this.lastAttestedEpochByAttester.set(key, proposalEpoch);
562
+ this.metrics.incAttestedEpochCount(attester);
563
+ }
564
+ }
565
+
522
566
  // Determine which validators should attest
523
567
  let attestors: EthAddress[];
524
568
  if (partOfCommittee) {
@@ -595,7 +639,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
595
639
  proposalInfo: LogData,
596
640
  ): Promise<{ isValid: true } | { isValid: false; reason: string }> {
597
641
  const slot = proposal.slotNumber;
598
- const timeoutSeconds = 10; // TODO(palla/mbps): This should map to the timetable settings
642
+
643
+ // Timeout block syncing at the start of the next slot
644
+ const config = this.checkpointsBuilder.getConfig();
645
+ const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
646
+ const timeoutSeconds = Math.max(1, nextSlotTimestampSeconds - Math.floor(this.dateProvider.now() / 1000));
599
647
 
600
648
  // Wait for last block to sync by archive
601
649
  let lastBlockHeader: BlockHeader | undefined;
@@ -630,6 +678,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
630
678
  return { isValid: false, reason: 'no_blocks_for_slot' };
631
679
  }
632
680
 
681
+ // Ensure the last block for this slot matches the archive in the checkpoint proposal
682
+ if (!blocks.at(-1)?.archive.root.equals(proposal.archive)) {
683
+ this.log.warn(`Last block archive mismatch for checkpoint proposal`, proposalInfo);
684
+ return { isValid: false, reason: 'last_block_archive_mismatch' };
685
+ }
686
+
633
687
  this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
634
688
  ...proposalInfo,
635
689
  blockNumbers: blocks.map(b => b.number),
@@ -643,14 +697,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
643
697
  // Get L1-to-L2 messages for this checkpoint
644
698
  const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
645
699
 
646
- // Compute the previous checkpoint out hashes for the epoch.
647
- // TODO: There can be a more efficient way to get the previous checkpoint out hashes without having to fetch the
648
- // actual checkpoints and the blocks/txs in them.
700
+ // Collect the out hashes of all the checkpoints before this one in the same epoch
649
701
  const epoch = getEpochAtSlot(slot, this.epochCache.getL1Constants());
650
- const previousCheckpoints = (await this.blockSource.getCheckpointsForEpoch(epoch))
651
- .filter(b => b.number < checkpointNumber)
652
- .sort((a, b) => a.number - b.number);
653
- const previousCheckpointOutHashes = previousCheckpoints.map(c => c.getCheckpointOutHash());
702
+ const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
703
+ .filter(c => c.checkpointNumber < checkpointNumber)
704
+ .map(c => c.checkpointOutHash);
654
705
 
655
706
  // Fork world state at the block before the first block
656
707
  const parentBlockNumber = BlockNumber(firstBlock.number - 1);
@@ -661,6 +712,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
661
712
  const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
662
713
  checkpointNumber,
663
714
  constants,
715
+ proposal.feeAssetPriceModifier,
664
716
  l1ToL2Messages,
665
717
  previousCheckpointOutHashes,
666
718
  fork,
@@ -707,6 +759,20 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
707
759
  return { isValid: false, reason: 'out_hash_mismatch' };
708
760
  }
709
761
 
762
+ // Final round of validations on the checkpoint, just in case.
763
+ try {
764
+ validateCheckpoint(computedCheckpoint, {
765
+ rollupManaLimit: this.checkpointsBuilder.getConfig().rollupManaLimit,
766
+ maxDABlockGas: this.config.validateMaxDABlockGas,
767
+ maxL2BlockGas: this.config.validateMaxL2BlockGas,
768
+ maxTxsPerBlock: this.config.validateMaxTxsPerBlock,
769
+ maxTxsPerCheckpoint: this.config.validateMaxTxsPerCheckpoint,
770
+ });
771
+ } catch (err) {
772
+ this.log.warn(`Checkpoint validation failed: ${err}`, proposalInfo);
773
+ return { isValid: false, reason: 'checkpoint_validation_failed' };
774
+ }
775
+
710
776
  this.log.verbose(`Checkpoint proposal validation successful for slot ${slot}`, proposalInfo);
711
777
  return { isValid: true };
712
778
  } finally {
@@ -723,6 +789,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
723
789
  chainId: gv.chainId,
724
790
  version: gv.version,
725
791
  slotNumber: gv.slotNumber,
792
+ timestamp: gv.timestamp,
726
793
  coinbase: gv.coinbase,
727
794
  feeRecipient: gv.feeRecipient,
728
795
  gasFees: gv.gasFees,
@@ -732,7 +799,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
732
799
  /**
733
800
  * Uploads blobs for a checkpoint to the filestore (fire and forget).
734
801
  */
735
- private async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
802
+ protected async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
736
803
  try {
737
804
  const lastBlockHeader = await this.blockSource.getBlockHeaderByArchive(proposal.archive);
738
805
  if (!lastBlockHeader) {
@@ -747,7 +814,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
747
814
  }
748
815
 
749
816
  const blobFields = blocks.flatMap(b => b.toBlobFields());
750
- const blobs: Blob[] = getBlobsPerL1Block(blobFields);
817
+ const blobs: Blob[] = await getBlobsPerL1Block(blobFields);
751
818
  await this.blobClient.sendBlobsToFilestore(blobs);
752
819
  this.log.debug(`Uploaded ${blobs.length} blobs to filestore for checkpoint at slot ${proposal.slotNumber}`, {
753
820
  ...proposalInfo,
@@ -876,6 +943,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
876
943
  async createCheckpointProposal(
877
944
  checkpointHeader: CheckpointHeader,
878
945
  archive: Fr,
946
+ feeAssetPriceModifier: bigint,
879
947
  lastBlockInfo: CreateCheckpointProposalLastBlockData | undefined,
880
948
  proposerAddress: EthAddress | undefined,
881
949
  options: CheckpointProposalOptions = {},
@@ -897,6 +965,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
897
965
  const newProposal = await this.validationService.createCheckpointProposal(
898
966
  checkpointHeader,
899
967
  archive,
968
+ feeAssetPriceModifier,
900
969
  lastBlockInfo,
901
970
  proposerAddress,
902
971
  options,
@@ -1,3 +0,0 @@
1
- export * from './nullifier_cache.js';
2
- export * from './tx_validator_factory.js';
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eF92YWxpZGF0b3IvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLDJCQUEyQixDQUFDIn0=
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tx_validator/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC"}
@@ -1,2 +0,0 @@
1
- export * from './nullifier_cache.js';
2
- export * from './tx_validator_factory.js';
@@ -1,14 +0,0 @@
1
- import type { NullifierSource } from '@aztec/p2p';
2
- import type { MerkleTreeReadOperations } from '@aztec/stdlib/interfaces/server';
3
- /**
4
- * Implements a nullifier source by checking a DB and an in-memory collection.
5
- * Intended for validating transactions as they are added to a block.
6
- */
7
- export declare class NullifierCache implements NullifierSource {
8
- private db;
9
- nullifiers: Set<string>;
10
- constructor(db: MerkleTreeReadOperations);
11
- nullifiersExist(nullifiers: Buffer[]): Promise<boolean[]>;
12
- addNullifiers(nullifiers: Buffer[]): void;
13
- }
14
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibnVsbGlmaWVyX2NhY2hlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHhfdmFsaWRhdG9yL251bGxpZmllcl9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxlQUFlLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDbEQsT0FBTyxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUdoRjs7O0dBR0c7QUFDSCxxQkFBYSxjQUFlLFlBQVcsZUFBZTtJQUd4QyxPQUFPLENBQUMsRUFBRTtJQUZ0QixVQUFVLEVBQUUsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRXhCLFlBQW9CLEVBQUUsRUFBRSx3QkFBd0IsRUFFL0M7SUFFWSxlQUFlLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQU9yRTtJQUVNLGFBQWEsQ0FBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLFFBSXhDO0NBQ0YifQ==
@@ -1 +0,0 @@
1
- {"version":3,"file":"nullifier_cache.d.ts","sourceRoot":"","sources":["../../src/tx_validator/nullifier_cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAGhF;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IAGxC,OAAO,CAAC,EAAE;IAFtB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAExB,YAAoB,EAAE,EAAE,wBAAwB,EAE/C;IAEY,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAOrE;IAEM,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAIxC;CACF"}
@@ -1,24 +0,0 @@
1
- import { MerkleTreeId } from '@aztec/stdlib/trees';
2
- /**
3
- * Implements a nullifier source by checking a DB and an in-memory collection.
4
- * Intended for validating transactions as they are added to a block.
5
- */ export class NullifierCache {
6
- db;
7
- nullifiers;
8
- constructor(db){
9
- this.db = db;
10
- this.nullifiers = new Set();
11
- }
12
- async nullifiersExist(nullifiers) {
13
- const cacheResults = nullifiers.map((n)=>this.nullifiers.has(n.toString()));
14
- const toCheckDb = nullifiers.filter((_n, index)=>!cacheResults[index]);
15
- const dbHits = await this.db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, toCheckDb);
16
- let dbIndex = 0;
17
- return nullifiers.map((_n, index)=>cacheResults[index] || dbHits[dbIndex++] !== undefined);
18
- }
19
- addNullifiers(nullifiers) {
20
- for (const nullifier of nullifiers){
21
- this.nullifiers.add(nullifier.toString());
22
- }
23
- }
24
- }
@@ -1,19 +0,0 @@
1
- import { BlockNumber } from '@aztec/foundation/branded-types';
2
- import type { LoggerBindings } from '@aztec/foundation/log';
3
- import type { ContractDataSource } from '@aztec/stdlib/contract';
4
- import type { GasFees } from '@aztec/stdlib/gas';
5
- import type { AllowedElement, ClientProtocolCircuitVerifier, MerkleTreeReadOperations, PublicProcessorValidator } from '@aztec/stdlib/interfaces/server';
6
- import { GlobalVariables, type Tx, type TxValidator } from '@aztec/stdlib/tx';
7
- import type { UInt64 } from '@aztec/stdlib/types';
8
- export declare function createValidatorForAcceptingTxs(db: MerkleTreeReadOperations, contractDataSource: ContractDataSource, verifier: ClientProtocolCircuitVerifier | undefined, { l1ChainId, rollupVersion, setupAllowList, gasFees, skipFeeEnforcement, timestamp, blockNumber, txsPermitted }: {
9
- l1ChainId: number;
10
- rollupVersion: number;
11
- setupAllowList: AllowedElement[];
12
- gasFees: GasFees;
13
- skipFeeEnforcement?: boolean;
14
- timestamp: UInt64;
15
- blockNumber: BlockNumber;
16
- txsPermitted: boolean;
17
- }, bindings?: LoggerBindings): TxValidator<Tx>;
18
- export declare function createValidatorForBlockBuilding(db: MerkleTreeReadOperations, contractDataSource: ContractDataSource, globalVariables: GlobalVariables, setupAllowList: AllowedElement[], bindings?: LoggerBindings): PublicProcessorValidator;
19
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhfdmFsaWRhdG9yX2ZhY3RvcnkuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eF92YWxpZGF0b3IvdHhfdmFsaWRhdG9yX2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTlELE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBaUI1RCxPQUFPLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ2pFLE9BQU8sS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxFQUNWLGNBQWMsRUFDZCw2QkFBNkIsRUFDN0Isd0JBQXdCLEVBQ3hCLHdCQUF3QixFQUN6QixNQUFNLGlDQUFpQyxDQUFDO0FBRXpDLE9BQU8sRUFBRSxlQUFlLEVBQUUsS0FBSyxFQUFFLEVBQUUsS0FBSyxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5RSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUlsRCx3QkFBZ0IsOEJBQThCLENBQzVDLEVBQUUsRUFBRSx3QkFBd0IsRUFDNUIsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQ3RDLFFBQVEsRUFBRSw2QkFBNkIsR0FBRyxTQUFTLEVBQ25ELEVBQ0UsU0FBUyxFQUNULGFBQWEsRUFDYixjQUFjLEVBQ2QsT0FBTyxFQUNQLGtCQUFrQixFQUNsQixTQUFTLEVBQ1QsV0FBVyxFQUNYLFlBQVksRUFDYixFQUFFO0lBQ0QsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLGNBQWMsRUFBRSxjQUFjLEVBQUUsQ0FBQztJQUNqQyxPQUFPLEVBQUUsT0FBTyxDQUFDO0lBQ2pCLGtCQUFrQixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBQzdCLFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsV0FBVyxFQUFFLFdBQVcsQ0FBQztJQUN6QixZQUFZLEVBQUUsT0FBTyxDQUFDO0NBQ3ZCLEVBQ0QsUUFBUSxDQUFDLEVBQUUsY0FBYyxHQUN4QixXQUFXLENBQUMsRUFBRSxDQUFDLENBcUNqQjtBQUVELHdCQUFnQiwrQkFBK0IsQ0FDN0MsRUFBRSxFQUFFLHdCQUF3QixFQUM1QixrQkFBa0IsRUFBRSxrQkFBa0IsRUFDdEMsZUFBZSxFQUFFLGVBQWUsRUFDaEMsY0FBYyxFQUFFLGNBQWMsRUFBRSxFQUNoQyxRQUFRLENBQUMsRUFBRSxjQUFjLEdBQ3hCLHdCQUF3QixDQWlCMUIifQ==
@@ -1 +0,0 @@
1
- {"version":3,"file":"tx_validator_factory.d.ts","sourceRoot":"","sources":["../../src/tx_validator/tx_validator_factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAiB5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC7B,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAIlD,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,wBAAwB,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,QAAQ,EAAE,6BAA6B,GAAG,SAAS,EACnD,EACE,SAAS,EACT,aAAa,EACb,cAAc,EACd,OAAO,EACP,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,YAAY,EACb,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,cAAc,EAAE,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,OAAO,CAAC;CACvB,EACD,QAAQ,CAAC,EAAE,cAAc,GACxB,WAAW,CAAC,EAAE,CAAC,CAqCjB;AAED,wBAAgB,+BAA+B,CAC7C,EAAE,EAAE,wBAAwB,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,cAAc,EAAE,EAChC,QAAQ,CAAC,EAAE,cAAc,GACxB,wBAAwB,CAiB1B"}
@@ -1,54 +0,0 @@
1
- import { Fr } from '@aztec/foundation/curves/bn254';
2
- import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
3
- import { AggregateTxValidator, ArchiveCache, BlockHeaderTxValidator, DataTxValidator, DoubleSpendTxValidator, GasTxValidator, MetadataTxValidator, PhasesTxValidator, SizeTxValidator, TimestampTxValidator, TxPermittedValidator, TxProofValidator } from '@aztec/p2p';
4
- import { ProtocolContractAddress, protocolContractsHash } from '@aztec/protocol-contracts';
5
- import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
6
- import { NullifierCache } from './nullifier_cache.js';
7
- export function createValidatorForAcceptingTxs(db, contractDataSource, verifier, { l1ChainId, rollupVersion, setupAllowList, gasFees, skipFeeEnforcement, timestamp, blockNumber, txsPermitted }, bindings) {
8
- const validators = [
9
- new TxPermittedValidator(txsPermitted, bindings),
10
- new SizeTxValidator(bindings),
11
- new DataTxValidator(bindings),
12
- new MetadataTxValidator({
13
- l1ChainId: new Fr(l1ChainId),
14
- rollupVersion: new Fr(rollupVersion),
15
- protocolContractsHash,
16
- vkTreeRoot: getVKTreeRoot()
17
- }, bindings),
18
- new TimestampTxValidator({
19
- timestamp,
20
- blockNumber
21
- }, bindings),
22
- new DoubleSpendTxValidator(new NullifierCache(db), bindings),
23
- new PhasesTxValidator(contractDataSource, setupAllowList, timestamp, bindings),
24
- new BlockHeaderTxValidator(new ArchiveCache(db), bindings)
25
- ];
26
- if (!skipFeeEnforcement) {
27
- validators.push(new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, gasFees, bindings));
28
- }
29
- if (verifier) {
30
- validators.push(new TxProofValidator(verifier, bindings));
31
- }
32
- return new AggregateTxValidator(...validators);
33
- }
34
- export function createValidatorForBlockBuilding(db, contractDataSource, globalVariables, setupAllowList, bindings) {
35
- const nullifierCache = new NullifierCache(db);
36
- const archiveCache = new ArchiveCache(db);
37
- const publicStateSource = new DatabasePublicStateSource(db);
38
- return {
39
- preprocessValidator: preprocessValidator(nullifierCache, archiveCache, publicStateSource, contractDataSource, globalVariables, setupAllowList, bindings),
40
- nullifierCache
41
- };
42
- }
43
- function preprocessValidator(nullifierCache, archiveCache, publicStateSource, contractDataSource, globalVariables, setupAllowList, bindings) {
44
- // We don't include the TxProofValidator nor the DataTxValidator here because they are already checked by the time we get to block building.
45
- return new AggregateTxValidator(new MetadataTxValidator({
46
- l1ChainId: globalVariables.chainId,
47
- rollupVersion: globalVariables.version,
48
- protocolContractsHash,
49
- vkTreeRoot: getVKTreeRoot()
50
- }, bindings), new TimestampTxValidator({
51
- timestamp: globalVariables.timestamp,
52
- blockNumber: globalVariables.blockNumber
53
- }, bindings), new DoubleSpendTxValidator(nullifierCache, bindings), new PhasesTxValidator(contractDataSource, setupAllowList, globalVariables.timestamp, bindings), new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice, globalVariables.gasFees, bindings), new BlockHeaderTxValidator(archiveCache, bindings));
54
- }
@@ -1,2 +0,0 @@
1
- export * from './nullifier_cache.js';
2
- export * from './tx_validator_factory.js';
@@ -1,30 +0,0 @@
1
- import type { NullifierSource } from '@aztec/p2p';
2
- import type { MerkleTreeReadOperations } from '@aztec/stdlib/interfaces/server';
3
- import { MerkleTreeId } from '@aztec/stdlib/trees';
4
-
5
- /**
6
- * Implements a nullifier source by checking a DB and an in-memory collection.
7
- * Intended for validating transactions as they are added to a block.
8
- */
9
- export class NullifierCache implements NullifierSource {
10
- nullifiers: Set<string>;
11
-
12
- constructor(private db: MerkleTreeReadOperations) {
13
- this.nullifiers = new Set();
14
- }
15
-
16
- public async nullifiersExist(nullifiers: Buffer[]): Promise<boolean[]> {
17
- const cacheResults = nullifiers.map(n => this.nullifiers.has(n.toString()));
18
- const toCheckDb = nullifiers.filter((_n, index) => !cacheResults[index]);
19
- const dbHits = await this.db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, toCheckDb);
20
-
21
- let dbIndex = 0;
22
- return nullifiers.map((_n, index) => cacheResults[index] || dbHits[dbIndex++] !== undefined);
23
- }
24
-
25
- public addNullifiers(nullifiers: Buffer[]) {
26
- for (const nullifier of nullifiers) {
27
- this.nullifiers.add(nullifier.toString());
28
- }
29
- }
30
- }
@@ -1,154 +0,0 @@
1
- import { BlockNumber } from '@aztec/foundation/branded-types';
2
- import { Fr } from '@aztec/foundation/curves/bn254';
3
- import type { LoggerBindings } from '@aztec/foundation/log';
4
- import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
5
- import {
6
- AggregateTxValidator,
7
- ArchiveCache,
8
- BlockHeaderTxValidator,
9
- DataTxValidator,
10
- DoubleSpendTxValidator,
11
- GasTxValidator,
12
- MetadataTxValidator,
13
- PhasesTxValidator,
14
- SizeTxValidator,
15
- TimestampTxValidator,
16
- TxPermittedValidator,
17
- TxProofValidator,
18
- } from '@aztec/p2p';
19
- import { ProtocolContractAddress, protocolContractsHash } from '@aztec/protocol-contracts';
20
- import type { ContractDataSource } from '@aztec/stdlib/contract';
21
- import type { GasFees } from '@aztec/stdlib/gas';
22
- import type {
23
- AllowedElement,
24
- ClientProtocolCircuitVerifier,
25
- MerkleTreeReadOperations,
26
- PublicProcessorValidator,
27
- } from '@aztec/stdlib/interfaces/server';
28
- import { DatabasePublicStateSource, type PublicStateSource } from '@aztec/stdlib/trees';
29
- import { GlobalVariables, type Tx, type TxValidator } from '@aztec/stdlib/tx';
30
- import type { UInt64 } from '@aztec/stdlib/types';
31
-
32
- import { NullifierCache } from './nullifier_cache.js';
33
-
34
- export function createValidatorForAcceptingTxs(
35
- db: MerkleTreeReadOperations,
36
- contractDataSource: ContractDataSource,
37
- verifier: ClientProtocolCircuitVerifier | undefined,
38
- {
39
- l1ChainId,
40
- rollupVersion,
41
- setupAllowList,
42
- gasFees,
43
- skipFeeEnforcement,
44
- timestamp,
45
- blockNumber,
46
- txsPermitted,
47
- }: {
48
- l1ChainId: number;
49
- rollupVersion: number;
50
- setupAllowList: AllowedElement[];
51
- gasFees: GasFees;
52
- skipFeeEnforcement?: boolean;
53
- timestamp: UInt64;
54
- blockNumber: BlockNumber;
55
- txsPermitted: boolean;
56
- },
57
- bindings?: LoggerBindings,
58
- ): TxValidator<Tx> {
59
- const validators: TxValidator<Tx>[] = [
60
- new TxPermittedValidator(txsPermitted, bindings),
61
- new SizeTxValidator(bindings),
62
- new DataTxValidator(bindings),
63
- new MetadataTxValidator(
64
- {
65
- l1ChainId: new Fr(l1ChainId),
66
- rollupVersion: new Fr(rollupVersion),
67
- protocolContractsHash,
68
- vkTreeRoot: getVKTreeRoot(),
69
- },
70
- bindings,
71
- ),
72
- new TimestampTxValidator(
73
- {
74
- timestamp,
75
- blockNumber,
76
- },
77
- bindings,
78
- ),
79
- new DoubleSpendTxValidator(new NullifierCache(db), bindings),
80
- new PhasesTxValidator(contractDataSource, setupAllowList, timestamp, bindings),
81
- new BlockHeaderTxValidator(new ArchiveCache(db), bindings),
82
- ];
83
-
84
- if (!skipFeeEnforcement) {
85
- validators.push(
86
- new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, gasFees, bindings),
87
- );
88
- }
89
-
90
- if (verifier) {
91
- validators.push(new TxProofValidator(verifier, bindings));
92
- }
93
-
94
- return new AggregateTxValidator(...validators);
95
- }
96
-
97
- export function createValidatorForBlockBuilding(
98
- db: MerkleTreeReadOperations,
99
- contractDataSource: ContractDataSource,
100
- globalVariables: GlobalVariables,
101
- setupAllowList: AllowedElement[],
102
- bindings?: LoggerBindings,
103
- ): PublicProcessorValidator {
104
- const nullifierCache = new NullifierCache(db);
105
- const archiveCache = new ArchiveCache(db);
106
- const publicStateSource = new DatabasePublicStateSource(db);
107
-
108
- return {
109
- preprocessValidator: preprocessValidator(
110
- nullifierCache,
111
- archiveCache,
112
- publicStateSource,
113
- contractDataSource,
114
- globalVariables,
115
- setupAllowList,
116
- bindings,
117
- ),
118
- nullifierCache,
119
- };
120
- }
121
-
122
- function preprocessValidator(
123
- nullifierCache: NullifierCache,
124
- archiveCache: ArchiveCache,
125
- publicStateSource: PublicStateSource,
126
- contractDataSource: ContractDataSource,
127
- globalVariables: GlobalVariables,
128
- setupAllowList: AllowedElement[],
129
- bindings?: LoggerBindings,
130
- ): TxValidator<Tx> {
131
- // We don't include the TxProofValidator nor the DataTxValidator here because they are already checked by the time we get to block building.
132
- return new AggregateTxValidator(
133
- new MetadataTxValidator(
134
- {
135
- l1ChainId: globalVariables.chainId,
136
- rollupVersion: globalVariables.version,
137
- protocolContractsHash,
138
- vkTreeRoot: getVKTreeRoot(),
139
- },
140
- bindings,
141
- ),
142
- new TimestampTxValidator(
143
- {
144
- timestamp: globalVariables.timestamp,
145
- blockNumber: globalVariables.blockNumber,
146
- },
147
- bindings,
148
- ),
149
- new DoubleSpendTxValidator(nullifierCache, bindings),
150
- new PhasesTxValidator(contractDataSource, setupAllowList, globalVariables.timestamp, bindings),
151
- new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice, globalVariables.gasFees, bindings),
152
- new BlockHeaderTxValidator(archiveCache, bindings),
153
- );
154
- }