@aztec/validator-client 0.0.1-commit.0c875d939 → 0.0.1-commit.10bd49492

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 +42 -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 +121 -61
  5. package/dest/checkpoint_builder.d.ts +13 -3
  6. package/dest/checkpoint_builder.d.ts.map +1 -1
  7. package/dest/checkpoint_builder.js +71 -18
  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 +1 -1
  12. package/dest/duties/validation_service.d.ts.map +1 -1
  13. package/dest/duties/validation_service.js +2 -8
  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 +12 -4
  25. package/dest/validator.d.ts.map +1 -1
  26. package/dest/validator.js +73 -21
  27. package/package.json +19 -19
  28. package/src/block_proposal_handler.ts +148 -80
  29. package/src/checkpoint_builder.ts +83 -14
  30. package/src/config.ts +22 -1
  31. package/src/duties/validation_service.ts +2 -8
  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 +73 -20
  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
@@ -150,16 +150,10 @@ export class ValidationService {
150
150
  );
151
151
 
152
152
  // TODO(spy/ha): Use checkpointNumber instead of blockNumber once CheckpointHeader includes it.
153
- // Currently using lastBlock.blockNumber as a proxy for checkpoint identification in HA signing.
153
+ // CheckpointProposalCore doesn't have lastBlock info, so use 0 as a proxy.
154
154
  // blockNumber is NOT used for the primary key so it's safe to use here.
155
155
  // See CheckpointHeader TODO and SigningContext types documentation.
156
- let blockNumber: BlockNumber;
157
- try {
158
- blockNumber = proposal.blockNumber;
159
- } catch {
160
- // Checkpoint proposal may not have lastBlock, use 0 as fallback
161
- blockNumber = BlockNumber(0);
162
- }
156
+ const blockNumber = BlockNumber(0);
163
157
  const context: SigningContext = {
164
158
  slot: proposal.slotNumber,
165
159
  blockNumber,
package/src/factory.ts CHANGED
@@ -29,6 +29,7 @@ export function createBlockProposalHandler(
29
29
  const metrics = new ValidatorMetrics(deps.telemetry);
30
30
  const blockProposalValidator = new BlockProposalValidator(deps.epochCache, {
31
31
  txsPermitted: !config.disableTransactions,
32
+ maxTxsPerBlock: config.validateMaxTxsPerBlock,
32
33
  });
33
34
  return new BlockProposalHandler(
34
35
  deps.checkpointsBuilder,
package/src/index.ts CHANGED
@@ -4,4 +4,3 @@ export * from './config.js';
4
4
  export * from './factory.js';
5
5
  export * from './validator.js';
6
6
  export * from './key_store/index.js';
7
- export * from './tx_validator/index.js';
@@ -240,7 +240,7 @@ export class HAKeyStore implements ExtendedValidatorKeyStore {
240
240
  }
241
241
 
242
242
  if (error instanceof SlashingProtectionError) {
243
- this.log.warn(`Duty already signed by another node with different payload`, {
243
+ this.log.info(`Duty already signed by another node with different payload`, {
244
244
  dutyType: context.dutyType,
245
245
  slot: context.slot,
246
246
  existingMessageHash: error.existingMessageHash,
package/src/metrics.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { EpochNumber } from '@aztec/foundation/branded-types';
2
+ import type { EthAddress } from '@aztec/foundation/eth-address';
1
3
  import type { BlockProposal } from '@aztec/stdlib/p2p';
2
4
  import {
3
5
  Attributes,
@@ -16,6 +18,8 @@ export class ValidatorMetrics {
16
18
  private successfulAttestationsCount: UpDownCounter;
17
19
  private failedAttestationsBadProposalCount: UpDownCounter;
18
20
  private failedAttestationsNodeIssueCount: UpDownCounter;
21
+ private currentEpoch: Gauge;
22
+ private attestedEpochCount: UpDownCounter;
19
23
 
20
24
  private reexMana: Histogram;
21
25
  private reexTx: Histogram;
@@ -64,6 +68,10 @@ export class ValidatorMetrics {
64
68
  },
65
69
  );
66
70
 
71
+ this.currentEpoch = meter.createGauge(Metrics.VALIDATOR_CURRENT_EPOCH);
72
+
73
+ this.attestedEpochCount = createUpDownCounterWithDefault(meter, Metrics.VALIDATOR_ATTESTED_EPOCH_COUNT);
74
+
67
75
  this.reexMana = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_MANA);
68
76
 
69
77
  this.reexTx = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_TX_COUNT);
@@ -110,4 +118,14 @@ export class ValidatorMetrics {
110
118
  [Attributes.IS_COMMITTEE_MEMBER]: inCommittee,
111
119
  });
112
120
  }
121
+
122
+ /** Update the gauge tracking the current epoch number (proxy for total epochs elapsed). */
123
+ public setCurrentEpoch(epoch: EpochNumber) {
124
+ this.currentEpoch.record(Number(epoch));
125
+ }
126
+
127
+ /** Increment the count of epochs in which the given attester submitted at least one attestation. */
128
+ public incAttestedEpochCount(attester: EthAddress) {
129
+ this.attestedEpochCount.add(1, { [Attributes.ATTESTER_ADDRESS]: attester.toString() });
130
+ }
113
131
  }
package/src/validator.ts CHANGED
@@ -24,6 +24,7 @@ import { AuthRequest, AuthResponse, BlockProposalValidator, ReqRespSubProtocol }
24
24
  import { OffenseType, WANT_TO_SLASH_EVENT, type Watcher, type WatcherEmitter } from '@aztec/slasher';
25
25
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
26
26
  import type { CommitteeAttestationsAndSigners, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
27
+ import { validateCheckpoint } from '@aztec/stdlib/checkpoint';
27
28
  import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
28
29
  import type {
29
30
  CreateCheckpointProposalLastBlockData,
@@ -45,8 +46,9 @@ import type { CheckpointHeader } from '@aztec/stdlib/rollup';
45
46
  import type { BlockHeader, CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
46
47
  import { AttestationTimeoutError } from '@aztec/stdlib/validators';
47
48
  import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
48
- import { createHASigner } from '@aztec/validator-ha-signer/factory';
49
+ import { createHASigner, createLocalSignerWithProtection } from '@aztec/validator-ha-signer/factory';
49
50
  import { DutyType, type SigningContext } from '@aztec/validator-ha-signer/types';
51
+ import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer';
50
52
 
51
53
  import { EventEmitter } from 'events';
52
54
  import type { TypedDataDefinition } from 'viem';
@@ -77,7 +79,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
77
79
  private validationService: ValidationService;
78
80
  private metrics: ValidatorMetrics;
79
81
  private log: Logger;
80
-
81
82
  // Whether it has already registered handlers on the p2p client
82
83
  private hasRegisteredHandlers = false;
83
84
 
@@ -89,6 +90,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
89
90
 
90
91
  private lastEpochForCommitteeUpdateLoop: EpochNumber | undefined;
91
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();
92
95
 
93
96
  private proposersOfInvalidBlocks: Set<string> = new Set();
94
97
 
@@ -106,6 +109,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
106
109
  private l1ToL2MessageSource: L1ToL2MessageSource,
107
110
  private config: ValidatorClientFullConfig,
108
111
  private blobClient: BlobClientInterface,
112
+ private slashingProtectionSigner: ValidatorHASigner,
109
113
  private dateProvider: DateProvider = new DateProvider(),
110
114
  telemetry: TelemetryClient = getTelemetryClient(),
111
115
  log = createLogger('validator'),
@@ -159,6 +163,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
159
163
  this.log.trace(`No committee found for slot`);
160
164
  return;
161
165
  }
166
+ this.metrics.setCurrentEpoch(epoch);
162
167
  if (epoch !== this.lastEpochForCommitteeUpdateLoop) {
163
168
  const me = this.getValidatorAddresses();
164
169
  const committeeSet = new Set(committee.map(v => v.toString()));
@@ -196,6 +201,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
196
201
  const metrics = new ValidatorMetrics(telemetry);
197
202
  const blockProposalValidator = new BlockProposalValidator(epochCache, {
198
203
  txsPermitted: !config.disableTransactions,
204
+ maxTxsPerBlock: config.validateMaxTxsPerBlock,
199
205
  });
200
206
  const blockProposalHandler = new BlockProposalHandler(
201
207
  checkpointsBuilder,
@@ -211,16 +217,28 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
211
217
  telemetry,
212
218
  );
213
219
 
214
- let validatorKeyStore: ExtendedValidatorKeyStore = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
220
+ const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
221
+ let slashingProtectionSigner: ValidatorHASigner;
215
222
  if (config.haSigningEnabled) {
223
+ // Multi-node HA mode: use PostgreSQL-backed distributed locking.
216
224
  // If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
217
225
  const haConfig = {
218
226
  ...config,
219
227
  maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
220
228
  };
221
- const { signer } = await createHASigner(haConfig);
222
- 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
+ }));
223
240
  }
241
+ const validatorKeyStore: ExtendedValidatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, slashingProtectionSigner);
224
242
 
225
243
  const validator = new ValidatorClient(
226
244
  validatorKeyStore,
@@ -233,6 +251,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
233
251
  l1ToL2MessageSource,
234
252
  config,
235
253
  blobClient,
254
+ slashingProtectionSigner,
236
255
  dateProvider,
237
256
  telemetry,
238
257
  );
@@ -270,6 +289,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
270
289
  this.config = { ...this.config, ...config };
271
290
  }
272
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
+
273
298
  public async start() {
274
299
  if (this.epochCacheUpdateLoop.isRunning()) {
275
300
  this.log.warn(`Validator client already started`);
@@ -355,7 +380,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
355
380
 
356
381
  // Ignore proposals from ourselves (may happen in HA setups)
357
382
  if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
358
- this.log.warn(`Ignoring block proposal from self for slot ${slotNumber}`, {
383
+ this.log.debug(`Ignoring block proposal from self for slot ${slotNumber}`, {
359
384
  proposer: proposer.toString(),
360
385
  slotNumber,
361
386
  });
@@ -391,9 +416,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
391
416
  );
392
417
 
393
418
  if (!validationResult.isValid) {
394
- this.log.warn(`Block proposal validation failed: ${validationResult.reason}`, proposalInfo);
395
-
396
419
  const reason = validationResult.reason || 'unknown';
420
+
421
+ this.log.warn(`Block proposal validation failed: ${reason}`, proposalInfo);
422
+
397
423
  // Classify failure reason: bad proposal vs node issue
398
424
  const badProposalReasons: BlockProposalValidationFailureReason[] = [
399
425
  'invalid_proposal',
@@ -465,7 +491,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
465
491
 
466
492
  // Ignore proposals from ourselves (may happen in HA setups)
467
493
  if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
468
- this.log.warn(`Ignoring block proposal from self for slot ${slotNumber}`, {
494
+ this.log.debug(`Ignoring block proposal from self for slot ${slotNumber}`, {
469
495
  proposer: proposer.toString(),
470
496
  slotNumber,
471
497
  });
@@ -488,11 +514,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
488
514
  slotNumber,
489
515
  archive: proposal.archive.toString(),
490
516
  proposer: proposer.toString(),
491
- txCount: proposal.txHashes.length,
492
517
  };
493
518
  this.log.info(`Received checkpoint proposal for slot ${slotNumber}`, {
494
519
  ...proposalInfo,
495
- txHashes: proposal.txHashes.map(t => t.toString()),
496
520
  fishermanMode: this.config.fishermanMode || false,
497
521
  });
498
522
 
@@ -528,6 +552,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
528
552
 
529
553
  this.metrics.incSuccessfulAttestations(inCommittee.length);
530
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
+
531
566
  // Determine which validators should attest
532
567
  let attestors: EthAddress[];
533
568
  if (partOfCommittee) {
@@ -643,6 +678,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
643
678
  return { isValid: false, reason: 'no_blocks_for_slot' };
644
679
  }
645
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
+
646
687
  this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
647
688
  ...proposalInfo,
648
689
  blockNumbers: blocks.map(b => b.number),
@@ -656,14 +697,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
656
697
  // Get L1-to-L2 messages for this checkpoint
657
698
  const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
658
699
 
659
- // Compute the previous checkpoint out hashes for the epoch.
660
- // TODO: There can be a more efficient way to get the previous checkpoint out hashes without having to fetch the
661
- // actual checkpoints and the blocks/txs in them.
700
+ // Collect the out hashes of all the checkpoints before this one in the same epoch
662
701
  const epoch = getEpochAtSlot(slot, this.epochCache.getL1Constants());
663
- const previousCheckpoints = (await this.blockSource.getCheckpointsForEpoch(epoch))
664
- .filter(b => b.number < checkpointNumber)
665
- .sort((a, b) => a.number - b.number);
666
- 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);
667
705
 
668
706
  // Fork world state at the block before the first block
669
707
  const parentBlockNumber = BlockNumber(firstBlock.number - 1);
@@ -721,6 +759,20 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
721
759
  return { isValid: false, reason: 'out_hash_mismatch' };
722
760
  }
723
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
+
724
776
  this.log.verbose(`Checkpoint proposal validation successful for slot ${slot}`, proposalInfo);
725
777
  return { isValid: true };
726
778
  } finally {
@@ -737,6 +789,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
737
789
  chainId: gv.chainId,
738
790
  version: gv.version,
739
791
  slotNumber: gv.slotNumber,
792
+ timestamp: gv.timestamp,
740
793
  coinbase: gv.coinbase,
741
794
  feeRecipient: gv.feeRecipient,
742
795
  gasFees: gv.gasFees,
@@ -746,7 +799,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
746
799
  /**
747
800
  * Uploads blobs for a checkpoint to the filestore (fire and forget).
748
801
  */
749
- private async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
802
+ protected async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
750
803
  try {
751
804
  const lastBlockHeader = await this.blockSource.getBlockHeaderByArchive(proposal.archive);
752
805
  if (!lastBlockHeader) {
@@ -761,7 +814,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
761
814
  }
762
815
 
763
816
  const blobFields = blocks.flatMap(b => b.toBlobFields());
764
- const blobs: Blob[] = getBlobsPerL1Block(blobFields);
817
+ const blobs: Blob[] = await getBlobsPerL1Block(blobFields);
765
818
  await this.blobClient.sendBlobsToFilestore(blobs);
766
819
  this.log.debug(`Uploaded ${blobs.length} blobs to filestore for checkpoint at slot ${proposal.slotNumber}`, {
767
820
  ...proposalInfo,
@@ -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
- }