@aztec/validator-client 0.0.1-commit.b2a5d0dd1 → 0.0.1-commit.b3d3157a

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.
@@ -1,11 +1,9 @@
1
1
  import { type CheckpointNumber, IndexWithinCheckpoint, type SlotNumber } from '@aztec/foundation/branded-types';
2
- import { Buffer32 } from '@aztec/foundation/buffer';
3
- import { keccak256 } from '@aztec/foundation/crypto/keccak';
4
2
  import { Fr } from '@aztec/foundation/curves/bn254';
5
3
  import type { EthAddress } from '@aztec/foundation/eth-address';
6
4
  import type { Signature } from '@aztec/foundation/eth-signature';
7
5
  import { createLogger } from '@aztec/foundation/log';
8
- import type { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
6
+ import { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
9
7
  import {
10
8
  BlockProposal,
11
9
  type BlockProposalOptions,
@@ -14,9 +12,10 @@ import {
14
12
  type CheckpointProposalCore,
15
13
  type CheckpointProposalOptions,
16
14
  ConsensusPayload,
17
- SignatureDomainSeparator,
15
+ type CoordinationSignatureContext,
16
+ getCoordinationSignatureTypedData,
18
17
  } from '@aztec/stdlib/p2p';
19
- import type { CheckpointHeader } from '@aztec/stdlib/rollup';
18
+ import { CheckpointHeader } from '@aztec/stdlib/rollup';
20
19
  import type { BlockHeader, Tx } from '@aztec/stdlib/tx';
21
20
  import { DutyAlreadySignedError, SlashingProtectionError } from '@aztec/validator-ha-signer/errors';
22
21
  import { DutyType, type SigningContext } from '@aztec/validator-ha-signer/types';
@@ -26,6 +25,7 @@ import type { ValidatorKeyStore } from '../key_store/interface.js';
26
25
  export class ValidationService {
27
26
  constructor(
28
27
  private keyStore: ValidatorKeyStore,
28
+ private signatureContext: CoordinationSignatureContext,
29
29
  private log = createLogger('validator:validation-service'),
30
30
  ) {}
31
31
 
@@ -62,8 +62,14 @@ export class ValidationService {
62
62
 
63
63
  // Create a signer that uses the appropriate address
64
64
  const address = proposerAttesterAddress ?? this.keyStore.getAddress(0);
65
- const payloadSigner = (payload: Buffer32, context: SigningContext) =>
66
- this.keyStore.signMessageWithAddress(address, payload, context);
65
+ const payloadSigner = (
66
+ typedData: Parameters<ValidatorKeyStore['signTypedDataWithAddress']>[1],
67
+ context: SigningContext,
68
+ ) => this.keyStore.signTypedDataWithAddress(address, typedData, context);
69
+ const txsSigner = (
70
+ typedData: Parameters<ValidatorKeyStore['signTypedDataWithAddress']>[1],
71
+ context: SigningContext,
72
+ ) => this.keyStore.signTypedDataWithAddress(address, typedData, context);
67
73
 
68
74
  return BlockProposal.createProposalFromSigner(
69
75
  blockHeader,
@@ -73,7 +79,9 @@ export class ValidationService {
73
79
  archive,
74
80
  txs.map(tx => tx.getTxHash()),
75
81
  options.publishFullTxs ? txs : undefined,
82
+ this.signatureContext,
76
83
  payloadSigner,
84
+ txsSigner,
77
85
  );
78
86
  }
79
87
 
@@ -97,18 +105,26 @@ export class ValidationService {
97
105
  proposerAttesterAddress: EthAddress | undefined,
98
106
  options: CheckpointProposalOptions,
99
107
  ): Promise<CheckpointProposal> {
100
- // For testing: change the archive to trigger state_mismatch validation failure.
101
- // If there's a last block proposal, use its (already invalid) archive to keep signatures consistent
102
- // so P2P validation passes and the slasher can detect the offense.
108
+ // For testing: corrupt the checkpoint so observers' checkpoint validation fails.
109
+ //
110
+ // Keep `archive` aligned with `lastBlockProposal.archiveRoot` so the archive-based lookup
111
+ // in `validateCheckpointProposal` (`getBlockData({ archive })`) still succeeds
103
112
  if (options.broadcastInvalidCheckpointProposal) {
104
113
  archive = lastBlockProposal?.archiveRoot ?? Fr.random();
114
+ checkpointHeader = CheckpointHeader.from({
115
+ ...checkpointHeader,
116
+ epochOutHash: Fr.random(),
117
+ });
105
118
  this.log.warn(`Creating INVALID checkpoint proposal for slot ${checkpointHeader.slotNumber}`);
106
119
  }
107
120
 
108
121
  // Create a signer that takes payload and context, and uses the appropriate address
109
- const payloadSigner = (payload: Buffer32, context: SigningContext) => {
122
+ const payloadSigner = (
123
+ typedData: Parameters<ValidatorKeyStore['signTypedDataWithAddress']>[1],
124
+ context: SigningContext,
125
+ ) => {
110
126
  const address = proposerAttesterAddress ?? this.keyStore.getAddress(0);
111
- return this.keyStore.signMessageWithAddress(address, payload, context);
127
+ return this.keyStore.signTypedDataWithAddress(address, typedData, context);
112
128
  };
113
129
 
114
130
  return CheckpointProposal.createProposalFromSigner(
@@ -117,6 +133,7 @@ export class ValidationService {
117
133
  checkpointNumber,
118
134
  feeAssetPriceModifier,
119
135
  lastBlockProposal,
136
+ this.signatureContext,
120
137
  payloadSigner,
121
138
  );
122
139
  }
@@ -137,10 +154,13 @@ export class ValidationService {
137
154
  checkpointNumber: CheckpointNumber,
138
155
  ): Promise<CheckpointAttestation[]> {
139
156
  // Create the attestation payload from the checkpoint proposal
140
- const payload = new ConsensusPayload(proposal.checkpointHeader, proposal.archive, proposal.feeAssetPriceModifier);
141
- const buf = Buffer32.fromBuffer(
142
- keccak256(payload.getPayloadToSign(SignatureDomainSeparator.checkpointAttestation)),
157
+ const payload = new ConsensusPayload(
158
+ proposal.checkpointHeader,
159
+ proposal.archive,
160
+ proposal.feeAssetPriceModifier,
161
+ this.signatureContext,
143
162
  );
163
+ const typedData = getCoordinationSignatureTypedData(payload);
144
164
 
145
165
  const context: SigningContext = {
146
166
  slot: proposal.slotNumber,
@@ -151,8 +171,7 @@ export class ValidationService {
151
171
  // Sign each attestor in parallel, catching HA errors per-attestor
152
172
  const results = await Promise.allSettled(
153
173
  attestors.map(async attestor => {
154
- const sig = await this.keyStore.signMessageWithAddress(attestor, buf, context);
155
- // return new BlockAttestation(proposal.payload, sig, proposal.signature);
174
+ const sig = await this.keyStore.signTypedDataWithAddress(attestor, typedData, context);
156
175
  return new CheckpointAttestation(payload, sig, proposal.signature);
157
176
  }),
158
177
  );
@@ -199,9 +218,7 @@ export class ValidationService {
199
218
  dutyType: DutyType.ATTESTATIONS_AND_SIGNERS,
200
219
  };
201
220
 
202
- const buf = Buffer32.fromBuffer(
203
- keccak256(attestationsAndSigners.getPayloadToSign(SignatureDomainSeparator.attestationsAndSigners)),
204
- );
205
- return this.keyStore.signMessageWithAddress(proposer, buf, context);
221
+ const typedData = getCoordinationSignatureTypedData(attestationsAndSigners);
222
+ return this.keyStore.signTypedDataWithAddress(proposer, typedData, context);
206
223
  }
207
224
  }
package/src/factory.ts CHANGED
@@ -4,6 +4,7 @@ import type { DateProvider } from '@aztec/foundation/timer';
4
4
  import type { KeystoreManager } from '@aztec/node-keystore';
5
5
  import { BlockProposalValidator, type P2PClient } from '@aztec/p2p';
6
6
  import type { L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
7
+ import type { CheckpointReexecutionTracker } from '@aztec/stdlib/checkpoint';
7
8
  import type { ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
8
9
  import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
9
10
  import type { TelemetryClient } from '@aztec/telemetry-client';
@@ -26,12 +27,18 @@ export function createProposalHandler(
26
27
  blobClient: BlobClientInterface;
27
28
  dateProvider: DateProvider;
28
29
  telemetry: TelemetryClient;
30
+ reexecutionTracker: CheckpointReexecutionTracker;
29
31
  },
30
32
  ) {
31
33
  const metrics = new ValidatorMetrics(deps.telemetry);
32
34
  const blockProposalValidator = new BlockProposalValidator(deps.epochCache, {
33
35
  txsPermitted: !config.disableTransactions,
34
36
  maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
37
+ maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint,
38
+ signatureContext: {
39
+ chainId: config.l1ChainId,
40
+ rollupAddress: config.rollupAddress,
41
+ },
35
42
  });
36
43
  return new ProposalHandler(
37
44
  deps.checkpointsBuilder,
@@ -43,6 +50,7 @@ export function createProposalHandler(
43
50
  deps.epochCache,
44
51
  config,
45
52
  deps.blobClient,
53
+ deps.reexecutionTracker,
46
54
  metrics,
47
55
  deps.dateProvider,
48
56
  deps.telemetry,
@@ -63,6 +71,7 @@ export function createValidatorClient(
63
71
  epochCache: EpochCache;
64
72
  keyStoreManager: KeystoreManager | undefined;
65
73
  blobClient: BlobClientInterface;
74
+ reexecutionTracker: CheckpointReexecutionTracker;
66
75
  slashingProtectionDb?: SlashingProtectionDatabase;
67
76
  },
68
77
  ) {
@@ -82,6 +91,7 @@ export function createValidatorClient(
82
91
  txProvider,
83
92
  deps.keyStoreManager,
84
93
  deps.blobClient,
94
+ deps.reexecutionTracker,
85
95
  deps.dateProvider,
86
96
  deps.telemetry,
87
97
  deps.slashingProtectionDb,