@aztec/validator-client 0.0.1-commit.5de5ca79e → 0.0.1-commit.6201a7b05

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
@@ -1,20 +1,11 @@
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';
5
- import {
6
- BlockNumber,
7
- CheckpointNumber,
8
- EpochNumber,
9
- IndexWithinCheckpoint,
10
- SlotNumber,
11
- } from '@aztec/foundation/branded-types';
4
+ import { CheckpointNumber, EpochNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
12
5
  import { Fr } from '@aztec/foundation/curves/bn254';
13
- import { TimeoutError } from '@aztec/foundation/error';
14
6
  import type { EthAddress } from '@aztec/foundation/eth-address';
15
7
  import type { Signature } from '@aztec/foundation/eth-signature';
16
8
  import { type LogData, type Logger, createLogger } from '@aztec/foundation/log';
17
- import { retryUntil } from '@aztec/foundation/retry';
18
9
  import { RunningPromise } from '@aztec/foundation/running-promise';
19
10
  import { sleep } from '@aztec/foundation/sleep';
20
11
  import { DateProvider } from '@aztec/foundation/timer';
@@ -23,17 +14,15 @@ import type { DuplicateAttestationInfo, DuplicateProposalInfo, P2P, PeerId } fro
23
14
  import { AuthRequest, AuthResponse, BlockProposalValidator, ReqRespSubProtocol } from '@aztec/p2p';
24
15
  import { OffenseType, WANT_TO_SLASH_EVENT, type Watcher, type WatcherEmitter } from '@aztec/slasher';
25
16
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
26
- import type { CommitteeAttestationsAndSigners, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
27
- import { validateCheckpoint } from '@aztec/stdlib/checkpoint';
28
- import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
17
+ import type { CommitteeAttestationsAndSigners, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
18
+ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
29
19
  import type {
30
- CreateCheckpointProposalLastBlockData,
31
20
  ITxProvider,
32
21
  Validator,
33
22
  ValidatorClientFullConfig,
34
23
  WorldStateSynchronizer,
35
24
  } from '@aztec/stdlib/interfaces/server';
36
- import { type L1ToL2MessageSource, accumulateCheckpointOutHashes } from '@aztec/stdlib/messaging';
25
+ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
37
26
  import {
38
27
  type BlockProposal,
39
28
  type BlockProposalOptions,
@@ -41,9 +30,10 @@ import {
41
30
  CheckpointProposal,
42
31
  type CheckpointProposalCore,
43
32
  type CheckpointProposalOptions,
33
+ type CoordinationSignatureContext,
44
34
  } from '@aztec/stdlib/p2p';
45
35
  import type { CheckpointHeader } from '@aztec/stdlib/rollup';
46
- import type { BlockHeader, CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
36
+ import type { BlockHeader, Tx } from '@aztec/stdlib/tx';
47
37
  import { AttestationTimeoutError } from '@aztec/stdlib/validators';
48
38
  import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
49
39
  import {
@@ -57,13 +47,13 @@ import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-
57
47
  import { EventEmitter } from 'events';
58
48
  import type { TypedDataDefinition } from 'viem';
59
49
 
60
- import { BlockProposalHandler, type BlockProposalValidationFailureReason } from './block_proposal_handler.js';
61
50
  import type { FullNodeCheckpointsBuilder } from './checkpoint_builder.js';
62
51
  import { ValidationService } from './duties/validation_service.js';
63
52
  import { HAKeyStore } from './key_store/ha_key_store.js';
64
53
  import type { ExtendedValidatorKeyStore } from './key_store/interface.js';
65
54
  import { NodeKeystoreAdapter } from './key_store/node_keystore_adapter.js';
66
55
  import { ValidatorMetrics } from './metrics.js';
56
+ import { type BlockProposalValidationFailureReason, ProposalHandler } from './proposal_handler.js';
67
57
 
68
58
  // We maintain a set of proposers who have proposed invalid blocks.
69
59
  // Just cap the set to avoid unbounded growth.
@@ -106,7 +96,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
106
96
  private keyStore: ExtendedValidatorKeyStore,
107
97
  private epochCache: EpochCache,
108
98
  private p2pClient: P2P,
109
- private blockProposalHandler: BlockProposalHandler,
99
+ private proposalHandler: ProposalHandler,
110
100
  private blockSource: L2BlockSource,
111
101
  private checkpointsBuilder: FullNodeCheckpointsBuilder,
112
102
  private worldState: WorldStateSynchronizer,
@@ -126,7 +116,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
126
116
  this.tracer = telemetry.getTracer('Validator');
127
117
  this.metrics = new ValidatorMetrics(telemetry);
128
118
 
129
- this.validationService = new ValidationService(keyStore, this.log.createChild('validation-service'));
119
+ this.validationService = new ValidationService(
120
+ keyStore,
121
+ this.getSignatureContext(),
122
+ this.log.createChild('validation-service'),
123
+ );
130
124
 
131
125
  // Refresh epoch cache every second to trigger alert if participation in committee changes
132
126
  this.epochCacheUpdateLoop = new RunningPromise(this.handleEpochCommitteeUpdate.bind(this), this.log, 1000);
@@ -207,8 +201,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
207
201
  const blockProposalValidator = new BlockProposalValidator(epochCache, {
208
202
  txsPermitted: !config.disableTransactions,
209
203
  maxTxsPerBlock: config.validateMaxTxsPerBlock,
204
+ signatureContext: {
205
+ chainId: config.l1ChainId,
206
+ rollupAddress: config.l1Contracts.rollupAddress,
207
+ },
210
208
  });
211
- const blockProposalHandler = new BlockProposalHandler(
209
+ const proposalHandler = new ProposalHandler(
212
210
  checkpointsBuilder,
213
211
  worldState,
214
212
  blockSource,
@@ -217,9 +215,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
217
215
  blockProposalValidator,
218
216
  epochCache,
219
217
  config,
218
+ blobClient,
220
219
  metrics,
221
220
  dateProvider,
222
221
  telemetry,
222
+ undefined,
223
223
  );
224
224
 
225
225
  const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
@@ -255,7 +255,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
255
255
  validatorKeyStore,
256
256
  epochCache,
257
257
  p2pClient,
258
- blockProposalHandler,
258
+ proposalHandler,
259
259
  blockSource,
260
260
  checkpointsBuilder,
261
261
  worldState,
@@ -276,14 +276,21 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
276
276
  .filter(addr => !this.config.disabledValidators.some(disabled => disabled.equals(addr)));
277
277
  }
278
278
 
279
- public getBlockProposalHandler() {
280
- return this.blockProposalHandler;
279
+ public getProposalHandler() {
280
+ return this.proposalHandler;
281
281
  }
282
282
 
283
283
  public signWithAddress(addr: EthAddress, msg: TypedDataDefinition, context: SigningContext) {
284
284
  return this.keyStore.signTypedDataWithAddress(addr, msg, context);
285
285
  }
286
286
 
287
+ private getSignatureContext(): CoordinationSignatureContext {
288
+ return {
289
+ chainId: this.config.l1ChainId,
290
+ rollupAddress: this.config.l1Contracts.rollupAddress,
291
+ };
292
+ }
293
+
287
294
  public getCoinbaseForAttestor(attestor: EthAddress): EthAddress {
288
295
  return this.keyStore.getCoinbaseAddress(attestor);
289
296
  }
@@ -303,7 +310,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
303
310
  public reloadKeystore(newManager: KeystoreManager): void {
304
311
  const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
305
312
  this.keyStore = new HAKeyStore(newAdapter, this.slashingProtectionSigner);
306
- this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service'));
313
+ this.validationService = new ValidationService(
314
+ this.keyStore,
315
+ this.getSignatureContext(),
316
+ this.log.createChild('validation-service'),
317
+ );
307
318
  }
308
319
 
309
320
  public async start() {
@@ -350,7 +361,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
350
361
  checkpoint: CheckpointProposalCore,
351
362
  proposalSender: PeerId,
352
363
  ): Promise<CheckpointAttestation[] | undefined> => this.attestToCheckpointProposal(checkpoint, proposalSender);
353
- this.p2pClient.registerCheckpointProposalHandler(checkpointHandler);
364
+ this.p2pClient.registerValidatorCheckpointProposalHandler(checkpointHandler);
354
365
 
355
366
  // Duplicate proposal handler - triggers slashing for equivocation
356
367
  this.p2pClient.registerDuplicateProposalCallback((info: DuplicateProposalInfo) => {
@@ -410,16 +421,15 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
410
421
 
411
422
  // Reexecute txs if we are part of the committee, or if slashing is enabled, or if we are configured to always reexecute.
412
423
  // In fisherman mode, we always reexecute to validate proposals.
413
- const { validatorReexecute, slashBroadcastedInvalidBlockPenalty, alwaysReexecuteBlockProposals, fishermanMode } =
414
- this.config;
424
+ const { slashBroadcastedInvalidBlockPenalty, alwaysReexecuteBlockProposals, fishermanMode } = this.config;
415
425
  const shouldReexecute =
416
426
  fishermanMode ||
417
- (slashBroadcastedInvalidBlockPenalty > 0n && validatorReexecute) ||
418
- (partOfCommittee && validatorReexecute) ||
427
+ slashBroadcastedInvalidBlockPenalty > 0n ||
428
+ partOfCommittee ||
419
429
  alwaysReexecuteBlockProposals ||
420
430
  this.blobClient.canUpload();
421
431
 
422
- const validationResult = await this.blockProposalHandler.handleBlockProposal(
432
+ const validationResult = await this.proposalHandler.handleBlockProposal(
423
433
  proposal,
424
434
  proposalSender,
425
435
  !!shouldReexecute && !escapeHatchOpen,
@@ -493,14 +503,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
493
503
  return undefined;
494
504
  }
495
505
 
496
- // Reject proposals with invalid signatures
497
- if (!proposer) {
498
- this.log.warn(`Received checkpoint proposal with invalid signature for proposal slot ${proposalSlotNumber}`);
499
- return undefined;
500
- }
501
-
502
506
  // Ignore proposals from ourselves (may happen in HA setups)
503
- if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
507
+ if (proposer && this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
504
508
  this.log.debug(`Ignoring block proposal from self for slot ${proposalSlotNumber}`, {
505
509
  proposer: proposer.toString(),
506
510
  proposalSlotNumber,
@@ -508,14 +512,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
508
512
  return undefined;
509
513
  }
510
514
 
511
- // Validate fee asset price modifier is within allowed range
512
- if (!validateFeeAssetPriceModifier(proposal.feeAssetPriceModifier)) {
513
- this.log.warn(
514
- `Received checkpoint proposal with invalid feeAssetPriceModifier ${proposal.feeAssetPriceModifier} for slot ${proposalSlotNumber}`,
515
- );
516
- return undefined;
517
- }
518
-
519
515
  // Check that I have any address in the committee where this checkpoint will land before attesting
520
516
  const inCommittee = await this.epochCache.filterInCommittee(proposalSlotNumber, this.getValidatorAddresses());
521
517
  const partOfCommittee = inCommittee.length > 0;
@@ -523,27 +519,26 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
523
519
  const proposalInfo = {
524
520
  proposalSlotNumber,
525
521
  archive: proposal.archive.toString(),
526
- proposer: proposer.toString(),
522
+ proposer: proposer?.toString(),
527
523
  };
528
524
  this.log.info(`Received checkpoint proposal for slot ${proposalSlotNumber}`, {
529
525
  ...proposalInfo,
530
526
  fishermanMode: this.config.fishermanMode || false,
531
527
  });
532
528
 
533
- // Validate the checkpoint proposal before attesting (unless skipCheckpointProposalValidation is set)
529
+ // Validate the checkpoint proposal before attesting (unless skipCheckpointProposalValidation is set).
530
+ // Uses the cached result from the all-nodes callback if available (avoids double validation).
531
+ let checkpointNumber: CheckpointNumber;
534
532
  if (this.config.skipCheckpointProposalValidation) {
535
533
  this.log.warn(`Skipping checkpoint proposal validation for slot ${proposalSlotNumber}`, proposalInfo);
534
+ checkpointNumber = CheckpointNumber(0);
536
535
  } else {
537
- const validationResult = await this.validateCheckpointProposal(proposal, proposalInfo);
536
+ const validationResult = await this.proposalHandler.handleCheckpointProposal(proposal, proposalInfo);
538
537
  if (!validationResult.isValid) {
539
538
  this.log.warn(`Checkpoint proposal validation failed: ${validationResult.reason}`, proposalInfo);
540
539
  return undefined;
541
540
  }
542
- }
543
-
544
- // Upload blobs to filestore if we can (fire and forget)
545
- if (this.blobClient.canUpload()) {
546
- void this.uploadBlobsForCheckpoint(proposal, proposalInfo);
541
+ checkpointNumber = validationResult.checkpointNumber;
547
542
  }
548
543
 
549
544
  // Check that I have any address in current committee before attesting
@@ -601,7 +596,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
601
596
  return undefined;
602
597
  }
603
598
 
604
- return await this.createCheckpointAttestationsFromProposal(proposal, attestors);
599
+ return await this.createCheckpointAttestationsFromProposal(proposal, attestors, checkpointNumber);
605
600
  }
606
601
 
607
602
  /**
@@ -628,13 +623,14 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
628
623
  private async createCheckpointAttestationsFromProposal(
629
624
  proposal: CheckpointProposalCore,
630
625
  attestors: EthAddress[] = [],
626
+ checkpointNumber: CheckpointNumber,
631
627
  ): Promise<CheckpointAttestation[] | undefined> {
632
628
  // Equivocation check: must happen right before signing to minimize the race window
633
629
  if (!this.shouldAttestToSlot(proposal.slotNumber)) {
634
630
  return undefined;
635
631
  }
636
632
 
637
- const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors);
633
+ const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors, checkpointNumber);
638
634
 
639
635
  // Track the proposal we attested to (to prevent equivocation)
640
636
  this.lastAttestedProposal = proposal;
@@ -643,172 +639,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
643
639
  return attestations;
644
640
  }
645
641
 
646
- /**
647
- * Validates a checkpoint proposal by building the full checkpoint and comparing it with the proposal.
648
- * @returns Validation result with isValid flag and reason if invalid.
649
- */
650
- private async validateCheckpointProposal(
651
- proposal: CheckpointProposalCore,
652
- proposalInfo: LogData,
653
- ): Promise<{ isValid: true } | { isValid: false; reason: string }> {
654
- const slot = proposal.slotNumber;
655
-
656
- // Timeout block syncing at the start of the next slot
657
- const config = this.checkpointsBuilder.getConfig();
658
- const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
659
- const timeoutSeconds = Math.max(1, nextSlotTimestampSeconds - Math.floor(this.dateProvider.now() / 1000));
660
-
661
- // Wait for last block to sync by archive
662
- let lastBlockHeader: BlockHeader | undefined;
663
- try {
664
- lastBlockHeader = await retryUntil(
665
- async () => {
666
- await this.blockSource.syncImmediate();
667
- return this.blockSource.getBlockHeaderByArchive(proposal.archive);
668
- },
669
- `waiting for block with archive ${proposal.archive.toString()} for slot ${slot}`,
670
- timeoutSeconds,
671
- 0.5,
672
- );
673
- } catch (err) {
674
- if (err instanceof TimeoutError) {
675
- this.log.warn(`Timed out waiting for block with archive matching checkpoint proposal`, proposalInfo);
676
- return { isValid: false, reason: 'last_block_not_found' };
677
- }
678
- this.log.error(`Error fetching last block for checkpoint proposal`, err, proposalInfo);
679
- return { isValid: false, reason: 'block_fetch_error' };
680
- }
681
-
682
- if (!lastBlockHeader) {
683
- this.log.warn(`Last block not found for checkpoint proposal`, proposalInfo);
684
- return { isValid: false, reason: 'last_block_not_found' };
685
- }
686
-
687
- // Get all full blocks for the slot and checkpoint
688
- const blocks = await this.blockSource.getBlocksForSlot(slot);
689
- if (blocks.length === 0) {
690
- this.log.warn(`No blocks found for slot ${slot}`, proposalInfo);
691
- return { isValid: false, reason: 'no_blocks_for_slot' };
692
- }
693
-
694
- // Ensure the last block for this slot matches the archive in the checkpoint proposal
695
- if (!blocks.at(-1)?.archive.root.equals(proposal.archive)) {
696
- this.log.warn(`Last block archive mismatch for checkpoint proposal`, proposalInfo);
697
- return { isValid: false, reason: 'last_block_archive_mismatch' };
698
- }
699
-
700
- this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
701
- ...proposalInfo,
702
- blockNumbers: blocks.map(b => b.number),
703
- });
704
-
705
- // Get checkpoint constants from first block
706
- const firstBlock = blocks[0];
707
- const constants = this.extractCheckpointConstants(firstBlock);
708
- const checkpointNumber = firstBlock.checkpointNumber;
709
-
710
- // Get L1-to-L2 messages for this checkpoint
711
- const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
712
-
713
- // Collect the out hashes of all the checkpoints before this one in the same epoch
714
- const epoch = getEpochAtSlot(slot, this.epochCache.getL1Constants());
715
- const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
716
- .filter(c => c.checkpointNumber < checkpointNumber)
717
- .map(c => c.checkpointOutHash);
718
-
719
- // Fork world state at the block before the first block
720
- const parentBlockNumber = BlockNumber(firstBlock.number - 1);
721
- const fork = await this.worldState.fork(parentBlockNumber);
722
-
723
- try {
724
- // Create checkpoint builder with all existing blocks
725
- const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
726
- checkpointNumber,
727
- constants,
728
- proposal.feeAssetPriceModifier,
729
- l1ToL2Messages,
730
- previousCheckpointOutHashes,
731
- fork,
732
- blocks,
733
- this.log.getBindings(),
734
- );
735
-
736
- // Complete the checkpoint to get computed values
737
- const computedCheckpoint = await checkpointBuilder.completeCheckpoint();
738
-
739
- // Compare checkpoint header with proposal
740
- if (!computedCheckpoint.header.equals(proposal.checkpointHeader)) {
741
- this.log.warn(`Checkpoint header mismatch`, {
742
- ...proposalInfo,
743
- computed: computedCheckpoint.header.toInspect(),
744
- proposal: proposal.checkpointHeader.toInspect(),
745
- });
746
- return { isValid: false, reason: 'checkpoint_header_mismatch' };
747
- }
748
-
749
- // Compare archive root with proposal
750
- if (!computedCheckpoint.archive.root.equals(proposal.archive)) {
751
- this.log.warn(`Archive root mismatch`, {
752
- ...proposalInfo,
753
- computed: computedCheckpoint.archive.root.toString(),
754
- proposal: proposal.archive.toString(),
755
- });
756
- return { isValid: false, reason: 'archive_mismatch' };
757
- }
758
-
759
- // Check that the accumulated epoch out hash matches the value in the proposal.
760
- // The epoch out hash is the accumulated hash of all checkpoint out hashes in the epoch.
761
- const checkpointOutHash = computedCheckpoint.getCheckpointOutHash();
762
- const computedEpochOutHash = accumulateCheckpointOutHashes([...previousCheckpointOutHashes, checkpointOutHash]);
763
- const proposalEpochOutHash = proposal.checkpointHeader.epochOutHash;
764
- if (!computedEpochOutHash.equals(proposalEpochOutHash)) {
765
- this.log.warn(`Epoch out hash mismatch`, {
766
- proposalEpochOutHash: proposalEpochOutHash.toString(),
767
- computedEpochOutHash: computedEpochOutHash.toString(),
768
- checkpointOutHash: checkpointOutHash.toString(),
769
- previousCheckpointOutHashes: previousCheckpointOutHashes.map(h => h.toString()),
770
- ...proposalInfo,
771
- });
772
- return { isValid: false, reason: 'out_hash_mismatch' };
773
- }
774
-
775
- // Final round of validations on the checkpoint, just in case.
776
- try {
777
- validateCheckpoint(computedCheckpoint, {
778
- rollupManaLimit: this.checkpointsBuilder.getConfig().rollupManaLimit,
779
- maxDABlockGas: this.config.validateMaxDABlockGas,
780
- maxL2BlockGas: this.config.validateMaxL2BlockGas,
781
- maxTxsPerBlock: this.config.validateMaxTxsPerBlock,
782
- maxTxsPerCheckpoint: this.config.validateMaxTxsPerCheckpoint,
783
- });
784
- } catch (err) {
785
- this.log.warn(`Checkpoint validation failed: ${err}`, proposalInfo);
786
- return { isValid: false, reason: 'checkpoint_validation_failed' };
787
- }
788
-
789
- this.log.verbose(`Checkpoint proposal validation successful for slot ${slot}`, proposalInfo);
790
- return { isValid: true };
791
- } finally {
792
- await fork.close();
793
- }
794
- }
795
-
796
- /**
797
- * Extract checkpoint global variables from a block.
798
- */
799
- private extractCheckpointConstants(block: L2Block): CheckpointGlobalVariables {
800
- const gv = block.header.globalVariables;
801
- return {
802
- chainId: gv.chainId,
803
- version: gv.version,
804
- slotNumber: gv.slotNumber,
805
- timestamp: gv.timestamp,
806
- coinbase: gv.coinbase,
807
- feeRecipient: gv.feeRecipient,
808
- gasFees: gv.gasFees,
809
- };
810
- }
811
-
812
642
  /**
813
643
  * Uploads blobs for a checkpoint to the filestore (fire and forget).
814
644
  */
@@ -913,6 +743,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
913
743
 
914
744
  async createBlockProposal(
915
745
  blockHeader: BlockHeader,
746
+ checkpointNumber: CheckpointNumber,
916
747
  indexWithinCheckpoint: IndexWithinCheckpoint,
917
748
  inHash: Fr,
918
749
  archive: Fr,
@@ -939,6 +770,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
939
770
  );
940
771
  const newProposal = await this.validationService.createBlockProposal(
941
772
  blockHeader,
773
+ checkpointNumber,
942
774
  indexWithinCheckpoint,
943
775
  inHash,
944
776
  archive,
@@ -956,8 +788,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
956
788
  async createCheckpointProposal(
957
789
  checkpointHeader: CheckpointHeader,
958
790
  archive: Fr,
791
+ checkpointNumber: CheckpointNumber,
959
792
  feeAssetPriceModifier: bigint,
960
- lastBlockInfo: CreateCheckpointProposalLastBlockData | undefined,
793
+ lastBlockProposal: BlockProposal | undefined,
961
794
  proposerAddress: EthAddress | undefined,
962
795
  options: CheckpointProposalOptions = {},
963
796
  ): Promise<CheckpointProposal> {
@@ -978,8 +811,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
978
811
  const newProposal = await this.validationService.createCheckpointProposal(
979
812
  checkpointHeader,
980
813
  archive,
814
+ checkpointNumber,
981
815
  feeAssetPriceModifier,
982
- lastBlockInfo,
816
+ lastBlockProposal,
983
817
  proposerAddress,
984
818
  options,
985
819
  );
@@ -995,16 +829,24 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
995
829
  attestationsAndSigners: CommitteeAttestationsAndSigners,
996
830
  proposer: EthAddress,
997
831
  slot: SlotNumber,
998
- blockNumber: BlockNumber | CheckpointNumber,
832
+ checkpointNumber: CheckpointNumber,
999
833
  ): Promise<Signature> {
1000
- return await this.validationService.signAttestationsAndSigners(attestationsAndSigners, proposer, slot, blockNumber);
834
+ return await this.validationService.signAttestationsAndSigners(
835
+ attestationsAndSigners,
836
+ proposer,
837
+ slot,
838
+ checkpointNumber,
839
+ );
1001
840
  }
1002
841
 
1003
- async collectOwnAttestations(proposal: CheckpointProposal): Promise<CheckpointAttestation[]> {
842
+ async collectOwnAttestations(
843
+ proposal: CheckpointProposal,
844
+ checkpointNumber: CheckpointNumber,
845
+ ): Promise<CheckpointAttestation[]> {
1004
846
  const slot = proposal.slotNumber;
1005
847
  const inCommittee = await this.epochCache.filterInCommittee(slot, this.getValidatorAddresses());
1006
848
  this.log.debug(`Collecting ${inCommittee.length} self-attestations for slot ${slot}`, { inCommittee });
1007
- const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee);
849
+ const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee, checkpointNumber);
1008
850
 
1009
851
  if (!attestations) {
1010
852
  return [];
@@ -1023,6 +865,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
1023
865
  proposal: CheckpointProposal,
1024
866
  required: number,
1025
867
  deadline: Date,
868
+ checkpointNumber: CheckpointNumber,
1026
869
  ): Promise<CheckpointAttestation[]> {
1027
870
  // Wait and poll the p2pClient's attestation pool for this checkpoint until we have enough attestations
1028
871
  const slot = proposal.slotNumber;
@@ -1035,7 +878,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
1035
878
  throw new AttestationTimeoutError(0, required, slot);
1036
879
  }
1037
880
 
1038
- await this.collectOwnAttestations(proposal);
881
+ await this.collectOwnAttestations(proposal, checkpointNumber);
1039
882
 
1040
883
  const proposalId = proposal.archive.toString();
1041
884
  const myAddresses = this.getValidatorAddresses();
@@ -1048,7 +891,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
1048
891
  attestation => {
1049
892
  if (!attestation.archive.equals(proposal.archive)) {
1050
893
  this.log.warn(
1051
- `Received attestation for slot ${slot} with mismatched archive from ${attestation.getSender()?.toString()}`,
894
+ `Received attestation for slot ${slot} with mismatched archive from ${attestation
895
+ .getSender()
896
+ ?.toString()}`,
1052
897
  { attestationArchive: attestation.archive.toString(), proposalArchive: proposal.archive.toString() },
1053
898
  );
1054
899
  return false;
@@ -1,64 +0,0 @@
1
- import type { EpochCache } from '@aztec/epoch-cache';
2
- import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
3
- import { Fr } from '@aztec/foundation/curves/bn254';
4
- import { DateProvider } from '@aztec/foundation/timer';
5
- import type { P2P, PeerId } from '@aztec/p2p';
6
- import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
7
- import type { L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
8
- import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
9
- import { type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
10
- import type { BlockProposal } from '@aztec/stdlib/p2p';
11
- import type { FailedTx, Tx } from '@aztec/stdlib/tx';
12
- import { type TelemetryClient, type Tracer } from '@aztec/telemetry-client';
13
- import type { FullNodeCheckpointsBuilder } from './checkpoint_builder.js';
14
- import type { ValidatorMetrics } from './metrics.js';
15
- export type BlockProposalValidationFailureReason = 'invalid_proposal' | 'parent_block_not_found' | 'block_source_not_synced' | 'parent_block_wrong_slot' | 'in_hash_mismatch' | 'global_variables_mismatch' | 'block_number_already_exists' | 'txs_not_available' | 'state_mismatch' | 'failed_txs' | 'initial_state_mismatch' | 'timeout' | 'unknown_error';
16
- type ReexecuteTransactionsResult = {
17
- block: L2Block;
18
- failedTxs: FailedTx[];
19
- reexecutionTimeMs: number;
20
- totalManaUsed: number;
21
- };
22
- export type BlockProposalValidationSuccessResult = {
23
- isValid: true;
24
- blockNumber: BlockNumber;
25
- reexecutionResult?: ReexecuteTransactionsResult;
26
- };
27
- export type BlockProposalValidationFailureResult = {
28
- isValid: false;
29
- reason: BlockProposalValidationFailureReason;
30
- blockNumber?: BlockNumber;
31
- reexecutionResult?: ReexecuteTransactionsResult;
32
- };
33
- export type BlockProposalValidationResult = BlockProposalValidationSuccessResult | BlockProposalValidationFailureResult;
34
- export declare class BlockProposalHandler {
35
- private checkpointsBuilder;
36
- private worldState;
37
- private blockSource;
38
- private l1ToL2MessageSource;
39
- private txProvider;
40
- private blockProposalValidator;
41
- private epochCache;
42
- private config;
43
- private metrics?;
44
- private dateProvider;
45
- private log;
46
- readonly tracer: Tracer;
47
- constructor(checkpointsBuilder: FullNodeCheckpointsBuilder, worldState: WorldStateSynchronizer, blockSource: L2BlockSource & L2BlockSink, l1ToL2MessageSource: L1ToL2MessageSource, txProvider: ITxProvider, blockProposalValidator: BlockProposalValidator, epochCache: EpochCache, config: ValidatorClientFullConfig, metrics?: ValidatorMetrics | undefined, dateProvider?: DateProvider, telemetry?: TelemetryClient, log?: import("@aztec/foundation/log").Logger);
48
- register(p2pClient: P2P, shouldReexecute: boolean): BlockProposalHandler;
49
- handleBlockProposal(proposal: BlockProposal, proposalSender: PeerId, shouldReexecute: boolean): Promise<BlockProposalValidationResult>;
50
- private getParentBlock;
51
- private computeCheckpointNumber;
52
- /**
53
- * Validates that a non-first block in a checkpoint has consistent global variables with its parent.
54
- * For blocks with indexWithinCheckpoint > 0, all global variables except blockNumber must match the parent.
55
- * @returns A failure result if validation fails, undefined if validation passes
56
- */
57
- private validateNonFirstBlockInCheckpoint;
58
- private getReexecutionDeadline;
59
- private waitForBlockSourceSync;
60
- private getReexecuteFailureReason;
61
- reexecuteTransactions(proposal: BlockProposal, blockNumber: BlockNumber, checkpointNumber: CheckpointNumber, txs: Tx[], l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[]): Promise<ReexecuteTransactionsResult>;
62
- }
63
- export {};
64
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvY2tfcHJvcG9zYWxfaGFuZGxlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Jsb2NrX3Byb3Bvc2FsX2hhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBYyxNQUFNLGlDQUFpQyxDQUFDO0FBRTVGLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUlwRCxPQUFPLEVBQUUsWUFBWSxFQUFTLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUM5QyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNuRSxPQUFPLEtBQUssRUFBYSxPQUFPLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRzFGLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSx5QkFBeUIsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3RILE9BQU8sRUFBRSxLQUFLLG1CQUFtQixFQUFtQyxNQUFNLHlCQUF5QixDQUFDO0FBQ3BHLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRXZELE9BQU8sS0FBSyxFQUE2QixRQUFRLEVBQUUsRUFBRSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFRaEYsT0FBTyxFQUFFLEtBQUssZUFBZSxFQUFFLEtBQUssTUFBTSxFQUFzQixNQUFNLHlCQUF5QixDQUFDO0FBRWhHLE9BQU8sS0FBSyxFQUFFLDBCQUEwQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDMUUsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFckQsTUFBTSxNQUFN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
@@ -1 +0,0 @@
1
- {"version":3,"file":"block_proposal_handler.d.ts","sourceRoot":"","sources":["../src/block_proposal_handler.ts"],"names":[],"mappings":"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"}