@aztec/validator-client 0.0.1-commit.033589e → 0.0.1-commit.04852196a

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/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,7 +46,7 @@ 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';
50
51
  import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer';
51
52
 
@@ -108,7 +109,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
108
109
  private l1ToL2MessageSource: L1ToL2MessageSource,
109
110
  private config: ValidatorClientFullConfig,
110
111
  private blobClient: BlobClientInterface,
111
- private haSigner: ValidatorHASigner | undefined,
112
+ private slashingProtectionSigner: ValidatorHASigner,
112
113
  private dateProvider: DateProvider = new DateProvider(),
113
114
  telemetry: TelemetryClient = getTelemetryClient(),
114
115
  log = createLogger('validator'),
@@ -200,7 +201,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
200
201
  const metrics = new ValidatorMetrics(telemetry);
201
202
  const blockProposalValidator = new BlockProposalValidator(epochCache, {
202
203
  txsPermitted: !config.disableTransactions,
203
- maxTxsPerBlock: config.maxTxsPerBlock,
204
+ maxTxsPerBlock: config.validateMaxTxsPerBlock,
204
205
  });
205
206
  const blockProposalHandler = new BlockProposalHandler(
206
207
  checkpointsBuilder,
@@ -217,18 +218,27 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
217
218
  );
218
219
 
219
220
  const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
220
- let validatorKeyStore: ExtendedValidatorKeyStore = nodeKeystoreAdapter;
221
- let haSigner: ValidatorHASigner | undefined;
221
+ let slashingProtectionSigner: ValidatorHASigner;
222
222
  if (config.haSigningEnabled) {
223
+ // Multi-node HA mode: use PostgreSQL-backed distributed locking.
223
224
  // If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
224
225
  const haConfig = {
225
226
  ...config,
226
227
  maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
227
228
  };
228
- const { signer } = await createHASigner(haConfig, { telemetryClient: telemetry, dateProvider });
229
- haSigner = signer;
230
- validatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, 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
+ }));
231
240
  }
241
+ const validatorKeyStore: ExtendedValidatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, slashingProtectionSigner);
232
242
 
233
243
  const validator = new ValidatorClient(
234
244
  validatorKeyStore,
@@ -241,7 +251,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
241
251
  l1ToL2MessageSource,
242
252
  config,
243
253
  blobClient,
244
- haSigner,
254
+ slashingProtectionSigner,
245
255
  dateProvider,
246
256
  telemetry,
247
257
  );
@@ -280,24 +290,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
280
290
  }
281
291
 
282
292
  public reloadKeystore(newManager: KeystoreManager): void {
283
- if (this.config.haSigningEnabled && !this.haSigner) {
284
- this.log.warn(
285
- 'HA signing is enabled in config but was not initialized at startup. ' +
286
- 'Restart the node to enable HA signing.',
287
- );
288
- } else if (!this.config.haSigningEnabled && this.haSigner) {
289
- this.log.warn(
290
- 'HA signing was disabled via config update but the HA signer is still active. ' +
291
- 'Restart the node to fully disable HA signing.',
292
- );
293
- }
294
-
295
293
  const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
296
- if (this.haSigner) {
297
- this.keyStore = new HAKeyStore(newAdapter, this.haSigner);
298
- } else {
299
- this.keyStore = newAdapter;
300
- }
294
+ this.keyStore = new HAKeyStore(newAdapter, this.slashingProtectionSigner);
301
295
  this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service'));
302
296
  }
303
297
 
@@ -519,11 +513,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
519
513
  slotNumber,
520
514
  archive: proposal.archive.toString(),
521
515
  proposer: proposer.toString(),
522
- txCount: proposal.txHashes.length,
523
516
  };
524
517
  this.log.info(`Received checkpoint proposal for slot ${slotNumber}`, {
525
518
  ...proposalInfo,
526
- txHashes: proposal.txHashes.map(t => t.toString()),
527
519
  fishermanMode: this.config.fishermanMode || false,
528
520
  });
529
521
 
@@ -766,6 +758,20 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
766
758
  return { isValid: false, reason: 'out_hash_mismatch' };
767
759
  }
768
760
 
761
+ // Final round of validations on the checkpoint, just in case.
762
+ try {
763
+ validateCheckpoint(computedCheckpoint, {
764
+ rollupManaLimit: this.checkpointsBuilder.getConfig().rollupManaLimit,
765
+ maxDABlockGas: this.config.validateMaxDABlockGas,
766
+ maxL2BlockGas: this.config.validateMaxL2BlockGas,
767
+ maxTxsPerBlock: this.config.validateMaxTxsPerBlock,
768
+ maxTxsPerCheckpoint: this.config.validateMaxTxsPerCheckpoint,
769
+ });
770
+ } catch (err) {
771
+ this.log.warn(`Checkpoint validation failed: ${err}`, proposalInfo);
772
+ return { isValid: false, reason: 'checkpoint_validation_failed' };
773
+ }
774
+
769
775
  this.log.verbose(`Checkpoint proposal validation successful for slot ${slot}`, proposalInfo);
770
776
  return { isValid: true };
771
777
  } finally {