@aztec/validator-client 0.0.1-commit.f295ac2 → 0.0.1-commit.fc805bf

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,9 +1,9 @@
1
1
  import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
2
  import { merge, pick } from '@aztec/foundation/collection';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
- import { createLogger } from '@aztec/foundation/log';
4
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
5
5
  import { bufferToHex } from '@aztec/foundation/string';
6
- import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
6
+ import { DateProvider, elapsed } from '@aztec/foundation/timer';
7
7
  import { getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators';
8
8
  import { LightweightCheckpointBuilder } from '@aztec/prover-client/light';
9
9
  import {
@@ -12,7 +12,7 @@ import {
12
12
  PublicProcessor,
13
13
  createPublicTxSimulatorForBlockBuilding,
14
14
  } from '@aztec/simulator/server';
15
- import { L2BlockNew } from '@aztec/stdlib/block';
15
+ import { L2Block } from '@aztec/stdlib/block';
16
16
  import { Checkpoint } from '@aztec/stdlib/checkpoint';
17
17
  import type { ContractDataSource } from '@aztec/stdlib/contract';
18
18
  import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
@@ -24,6 +24,7 @@ import {
24
24
  type ICheckpointBlockBuilder,
25
25
  type ICheckpointsBuilder,
26
26
  type MerkleTreeWriteOperations,
27
+ NoValidTxsError,
27
28
  type PublicProcessorLimits,
28
29
  type WorldStateSynchronizer,
29
30
  } from '@aztec/stdlib/interfaces/server';
@@ -36,18 +37,13 @@ import { createValidatorForBlockBuilding } from './tx_validator/tx_validator_fac
36
37
  // Re-export for backward compatibility
37
38
  export type { BuildBlockInCheckpointResult } from '@aztec/stdlib/interfaces/server';
38
39
 
39
- const log = createLogger('checkpoint-builder');
40
-
41
- /** Result of building a block within a checkpoint. Extends the base interface with timer. */
42
- export interface BuildBlockInCheckpointResultWithTimer extends BuildBlockInCheckpointResult {
43
- blockBuildingTimer: Timer;
44
- }
45
-
46
40
  /**
47
41
  * Builder for a single checkpoint. Handles building blocks within the checkpoint
48
42
  * and completing it.
49
43
  */
50
44
  export class CheckpointBuilder implements ICheckpointBlockBuilder {
45
+ private log: Logger;
46
+
51
47
  constructor(
52
48
  private checkpointBuilder: LightweightCheckpointBuilder,
53
49
  private fork: MerkleTreeWriteOperations,
@@ -55,7 +51,13 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
55
51
  private contractDataSource: ContractDataSource,
56
52
  private dateProvider: DateProvider,
57
53
  private telemetryClient: TelemetryClient,
58
- ) {}
54
+ bindings?: LoggerBindings,
55
+ ) {
56
+ this.log = createLogger('checkpoint-builder', {
57
+ ...bindings,
58
+ instanceId: `checkpoint-${checkpointBuilder.checkpointNumber}`,
59
+ });
60
+ }
59
61
 
60
62
  getConstantData(): CheckpointGlobalVariables {
61
63
  return this.checkpointBuilder.constants;
@@ -68,12 +70,11 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
68
70
  pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
69
71
  blockNumber: BlockNumber,
70
72
  timestamp: bigint,
71
- opts: PublicProcessorLimits & { expectedEndState?: StateReference },
72
- ): Promise<BuildBlockInCheckpointResultWithTimer> {
73
- const blockBuildingTimer = new Timer();
73
+ opts: PublicProcessorLimits & { expectedEndState?: StateReference } = {},
74
+ ): Promise<BuildBlockInCheckpointResult> {
74
75
  const slot = this.checkpointBuilder.constants.slotNumber;
75
76
 
76
- log.verbose(`Building block ${blockNumber} for slot ${slot} within checkpoint`, {
77
+ this.log.verbose(`Building block ${blockNumber} for slot ${slot} within checkpoint`, {
77
78
  slot,
78
79
  blockNumber,
79
80
  ...opts,
@@ -97,6 +98,12 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
97
98
  processor.process(pendingTxs, opts, validator),
98
99
  );
99
100
 
101
+ // Throw if we didn't collect a single valid tx and we're not allowed to build empty blocks
102
+ // (only the first block in a checkpoint can be empty)
103
+ if (processedTxs.length === 0 && this.checkpointBuilder.getBlockCount() > 0) {
104
+ throw new NoValidTxsError(failedTxs);
105
+ }
106
+
100
107
  // Add block to checkpoint
101
108
  const block = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
102
109
  expectedEndState: opts.expectedEndState,
@@ -105,25 +112,28 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
105
112
  // How much public gas was processed
106
113
  const publicGas = processedTxs.reduce((acc, tx) => acc.add(tx.gasUsed.publicGas), Gas.empty());
107
114
 
108
- const res = {
115
+ this.log.debug('Built block within checkpoint', {
116
+ header: block.header.toInspect(),
117
+ processedTxs: processedTxs.map(tx => tx.hash.toString()),
118
+ failedTxs: failedTxs.map(tx => tx.tx.txHash.toString()),
119
+ });
120
+
121
+ return {
109
122
  block,
110
123
  publicGas,
111
124
  publicProcessorDuration,
112
125
  numTxs: processedTxs.length,
113
126
  failedTxs,
114
- blockBuildingTimer,
115
127
  usedTxs,
116
128
  usedTxBlobFields,
117
129
  };
118
- log.debug('Built block within checkpoint', res.block.header);
119
- return res;
120
130
  }
121
131
 
122
132
  /** Completes the checkpoint and returns it. */
123
133
  async completeCheckpoint(): Promise<Checkpoint> {
124
134
  const checkpoint = await this.checkpointBuilder.completeCheckpoint();
125
135
 
126
- log.verbose(`Completed checkpoint ${checkpoint.number}`, {
136
+ this.log.verbose(`Completed checkpoint ${checkpoint.number}`, {
127
137
  checkpointNumber: checkpoint.number,
128
138
  numBlocks: checkpoint.blocks.length,
129
139
  archiveRoot: checkpoint.archive.root.toString(),
@@ -139,14 +149,16 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
139
149
 
140
150
  protected async makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations) {
141
151
  const txPublicSetupAllowList = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
142
- const contractsDB = new PublicContractsDB(this.contractDataSource);
152
+ const contractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings());
143
153
  const guardedFork = new GuardedMerkleTreeOperations(fork);
144
154
 
155
+ const bindings = this.log.getBindings();
145
156
  const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
146
157
  guardedFork,
147
158
  contractsDB,
148
159
  globalVariables,
149
160
  this.telemetryClient,
161
+ bindings,
150
162
  );
151
163
 
152
164
  const processor = new PublicProcessor(
@@ -156,7 +168,7 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
156
168
  publicTxSimulator,
157
169
  this.dateProvider,
158
170
  this.telemetryClient,
159
- undefined,
171
+ createLogger('simulator:public-processor', bindings),
160
172
  this.config,
161
173
  );
162
174
 
@@ -165,6 +177,7 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
165
177
  this.contractDataSource,
166
178
  globalVariables,
167
179
  txPublicSetupAllowList,
180
+ this.log.getBindings(),
168
181
  );
169
182
 
170
183
  return {
@@ -176,13 +189,17 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
176
189
 
177
190
  /** Factory for creating checkpoint builders. */
178
191
  export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
192
+ private log: Logger;
193
+
179
194
  constructor(
180
195
  private config: FullNodeBlockBuilderConfig & Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>,
181
196
  private worldState: WorldStateSynchronizer,
182
197
  private contractDataSource: ContractDataSource,
183
198
  private dateProvider: DateProvider,
184
199
  private telemetryClient: TelemetryClient = getTelemetryClient(),
185
- ) {}
200
+ ) {
201
+ this.log = createLogger('checkpoint-builder');
202
+ }
186
203
 
187
204
  public getConfig(): FullNodeBlockBuilderConfig {
188
205
  return this.config;
@@ -201,11 +218,12 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
201
218
  l1ToL2Messages: Fr[],
202
219
  previousCheckpointOutHashes: Fr[],
203
220
  fork: MerkleTreeWriteOperations,
221
+ bindings?: LoggerBindings,
204
222
  ): Promise<CheckpointBuilder> {
205
223
  const stateReference = await fork.getStateReference();
206
224
  const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
207
225
 
208
- log.verbose(`Building new checkpoint ${checkpointNumber}`, {
226
+ this.log.verbose(`Building new checkpoint ${checkpointNumber}`, {
209
227
  checkpointNumber,
210
228
  msgCount: l1ToL2Messages.length,
211
229
  initialStateReference: stateReference.toInspect(),
@@ -219,6 +237,7 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
219
237
  l1ToL2Messages,
220
238
  previousCheckpointOutHashes,
221
239
  fork,
240
+ bindings,
222
241
  );
223
242
 
224
243
  return new CheckpointBuilder(
@@ -228,6 +247,7 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
228
247
  this.contractDataSource,
229
248
  this.dateProvider,
230
249
  this.telemetryClient,
250
+ bindings,
231
251
  );
232
252
  }
233
253
 
@@ -240,16 +260,24 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
240
260
  l1ToL2Messages: Fr[],
241
261
  previousCheckpointOutHashes: Fr[],
242
262
  fork: MerkleTreeWriteOperations,
243
- existingBlocks: L2BlockNew[] = [],
263
+ existingBlocks: L2Block[] = [],
264
+ bindings?: LoggerBindings,
244
265
  ): Promise<CheckpointBuilder> {
245
266
  const stateReference = await fork.getStateReference();
246
267
  const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
247
268
 
248
269
  if (existingBlocks.length === 0) {
249
- return this.startCheckpoint(checkpointNumber, constants, l1ToL2Messages, previousCheckpointOutHashes, fork);
270
+ return this.startCheckpoint(
271
+ checkpointNumber,
272
+ constants,
273
+ l1ToL2Messages,
274
+ previousCheckpointOutHashes,
275
+ fork,
276
+ bindings,
277
+ );
250
278
  }
251
279
 
252
- log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
280
+ this.log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
253
281
  checkpointNumber,
254
282
  msgCount: l1ToL2Messages.length,
255
283
  existingBlockCount: existingBlocks.length,
@@ -265,6 +293,7 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
265
293
  previousCheckpointOutHashes,
266
294
  fork,
267
295
  existingBlocks,
296
+ bindings,
268
297
  );
269
298
 
270
299
  return new CheckpointBuilder(
@@ -274,6 +303,7 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
274
303
  this.contractDataSource,
275
304
  this.dateProvider,
276
305
  this.telemetryClient,
306
+ bindings,
277
307
  );
278
308
  }
279
309
 
package/src/config.ts CHANGED
@@ -65,15 +65,13 @@ export const validatorClientConfigMappings: ConfigMappingsType<ValidatorClientCo
65
65
  'Whether to run in fisherman mode: validates all proposals and attestations but does not broadcast attestations or participate in consensus.',
66
66
  ...booleanConfigHelper(false),
67
67
  },
68
- // TODO(palla/mbps): Change default to false once checkpoint validation is stable
69
68
  skipCheckpointProposalValidation: {
70
- description: 'Skip checkpoint proposal validation and always attest (default: true)',
71
- defaultValue: true,
69
+ description: 'Skip checkpoint proposal validation and always attest (default: false)',
70
+ defaultValue: false,
72
71
  },
73
- // TODO(palla/mbps): Change default to false once block sync is stable
74
72
  skipPushProposedBlocksToArchiver: {
75
- description: 'Skip pushing re-executed blocks to archiver (default: true)',
76
- defaultValue: true,
73
+ description: 'Skip pushing re-executed blocks to archiver (default: false)',
74
+ defaultValue: false,
77
75
  },
78
76
  ...validatorHASignerConfigMappings,
79
77
  };
package/src/metrics.ts CHANGED
@@ -6,8 +6,11 @@ import {
6
6
  Metrics,
7
7
  type TelemetryClient,
8
8
  type UpDownCounter,
9
+ createUpDownCounterWithDefault,
9
10
  } from '@aztec/telemetry-client';
10
11
 
12
+ import type { BlockProposalValidationFailureReason } from './block_proposal_handler.js';
13
+
11
14
  export class ValidatorMetrics {
12
15
  private failedReexecutionCounter: UpDownCounter;
13
16
  private successfulAttestationsCount: UpDownCounter;
@@ -21,16 +24,44 @@ export class ValidatorMetrics {
21
24
  constructor(telemetryClient: TelemetryClient) {
22
25
  const meter = telemetryClient.getMeter('Validator');
23
26
 
24
- this.failedReexecutionCounter = meter.createUpDownCounter(Metrics.VALIDATOR_FAILED_REEXECUTION_COUNT);
27
+ this.failedReexecutionCounter = createUpDownCounterWithDefault(meter, Metrics.VALIDATOR_FAILED_REEXECUTION_COUNT, {
28
+ [Attributes.STATUS]: ['failed'],
29
+ });
25
30
 
26
- this.successfulAttestationsCount = meter.createUpDownCounter(Metrics.VALIDATOR_ATTESTATION_SUCCESS_COUNT);
31
+ this.successfulAttestationsCount = createUpDownCounterWithDefault(
32
+ meter,
33
+ Metrics.VALIDATOR_ATTESTATION_SUCCESS_COUNT,
34
+ );
27
35
 
28
- this.failedAttestationsBadProposalCount = meter.createUpDownCounter(
36
+ this.failedAttestationsBadProposalCount = createUpDownCounterWithDefault(
37
+ meter,
29
38
  Metrics.VALIDATOR_ATTESTATION_FAILED_BAD_PROPOSAL_COUNT,
39
+ {
40
+ [Attributes.ERROR_TYPE]: [
41
+ 'invalid_proposal',
42
+ 'state_mismatch',
43
+ 'failed_txs',
44
+ 'in_hash_mismatch',
45
+ 'parent_block_wrong_slot',
46
+ ],
47
+ [Attributes.IS_COMMITTEE_MEMBER]: [true, false],
48
+ },
30
49
  );
31
50
 
32
- this.failedAttestationsNodeIssueCount = meter.createUpDownCounter(
51
+ this.failedAttestationsNodeIssueCount = createUpDownCounterWithDefault(
52
+ meter,
33
53
  Metrics.VALIDATOR_ATTESTATION_FAILED_NODE_ISSUE_COUNT,
54
+ {
55
+ [Attributes.ERROR_TYPE]: [
56
+ 'parent_block_not_found',
57
+ 'global_variables_mismatch',
58
+ 'block_number_already_exists',
59
+ 'txs_not_available',
60
+ 'timeout',
61
+ 'unknown_error',
62
+ ],
63
+ [Attributes.IS_COMMITTEE_MEMBER]: [true, false],
64
+ },
34
65
  );
35
66
 
36
67
  this.reexMana = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_MANA);
@@ -58,14 +89,22 @@ export class ValidatorMetrics {
58
89
  this.successfulAttestationsCount.add(num);
59
90
  }
60
91
 
61
- public incFailedAttestationsBadProposal(num: number, reason: string, inCommittee: boolean) {
92
+ public incFailedAttestationsBadProposal(
93
+ num: number,
94
+ reason: BlockProposalValidationFailureReason,
95
+ inCommittee: boolean,
96
+ ) {
62
97
  this.failedAttestationsBadProposalCount.add(num, {
63
98
  [Attributes.ERROR_TYPE]: reason,
64
99
  [Attributes.IS_COMMITTEE_MEMBER]: inCommittee,
65
100
  });
66
101
  }
67
102
 
68
- public incFailedAttestationsNodeIssue(num: number, reason: string, inCommittee: boolean) {
103
+ public incFailedAttestationsNodeIssue(
104
+ num: number,
105
+ reason: BlockProposalValidationFailureReason,
106
+ inCommittee: boolean,
107
+ ) {
69
108
  this.failedAttestationsNodeIssueCount.add(num, {
70
109
  [Attributes.ERROR_TYPE]: reason,
71
110
  [Attributes.IS_COMMITTEE_MEMBER]: inCommittee,
@@ -1,5 +1,6 @@
1
1
  import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import type { LoggerBindings } from '@aztec/foundation/log';
3
4
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
4
5
  import {
5
6
  AggregateTxValidator,
@@ -10,6 +11,7 @@ import {
10
11
  GasTxValidator,
11
12
  MetadataTxValidator,
12
13
  PhasesTxValidator,
14
+ SizeTxValidator,
13
15
  TimestampTxValidator,
14
16
  TxPermittedValidator,
15
17
  TxProofValidator,
@@ -52,31 +54,41 @@ export function createValidatorForAcceptingTxs(
52
54
  blockNumber: BlockNumber;
53
55
  txsPermitted: boolean;
54
56
  },
57
+ bindings?: LoggerBindings,
55
58
  ): TxValidator<Tx> {
56
59
  const validators: TxValidator<Tx>[] = [
57
- new TxPermittedValidator(txsPermitted),
58
- new DataTxValidator(),
59
- new MetadataTxValidator({
60
- l1ChainId: new Fr(l1ChainId),
61
- rollupVersion: new Fr(rollupVersion),
62
- protocolContractsHash,
63
- vkTreeRoot: getVKTreeRoot(),
64
- }),
65
- new TimestampTxValidator({
66
- timestamp,
67
- blockNumber,
68
- }),
69
- new DoubleSpendTxValidator(new NullifierCache(db)),
70
- new PhasesTxValidator(contractDataSource, setupAllowList, timestamp),
71
- new BlockHeaderTxValidator(new ArchiveCache(db)),
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),
72
82
  ];
73
83
 
74
84
  if (!skipFeeEnforcement) {
75
- validators.push(new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, gasFees));
85
+ validators.push(
86
+ new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, gasFees, bindings),
87
+ );
76
88
  }
77
89
 
78
90
  if (verifier) {
79
- validators.push(new TxProofValidator(verifier));
91
+ validators.push(new TxProofValidator(verifier, bindings));
80
92
  }
81
93
 
82
94
  return new AggregateTxValidator(...validators);
@@ -87,6 +99,7 @@ export function createValidatorForBlockBuilding(
87
99
  contractDataSource: ContractDataSource,
88
100
  globalVariables: GlobalVariables,
89
101
  setupAllowList: AllowedElement[],
102
+ bindings?: LoggerBindings,
90
103
  ): PublicProcessorValidator {
91
104
  const nullifierCache = new NullifierCache(db);
92
105
  const archiveCache = new ArchiveCache(db);
@@ -100,6 +113,7 @@ export function createValidatorForBlockBuilding(
100
113
  contractDataSource,
101
114
  globalVariables,
102
115
  setupAllowList,
116
+ bindings,
103
117
  ),
104
118
  nullifierCache,
105
119
  };
@@ -112,22 +126,29 @@ function preprocessValidator(
112
126
  contractDataSource: ContractDataSource,
113
127
  globalVariables: GlobalVariables,
114
128
  setupAllowList: AllowedElement[],
129
+ bindings?: LoggerBindings,
115
130
  ): TxValidator<Tx> {
116
131
  // We don't include the TxProofValidator nor the DataTxValidator here because they are already checked by the time we get to block building.
117
132
  return new AggregateTxValidator(
118
- new MetadataTxValidator({
119
- l1ChainId: globalVariables.chainId,
120
- rollupVersion: globalVariables.version,
121
- protocolContractsHash,
122
- vkTreeRoot: getVKTreeRoot(),
123
- }),
124
- new TimestampTxValidator({
125
- timestamp: globalVariables.timestamp,
126
- blockNumber: globalVariables.blockNumber,
127
- }),
128
- new DoubleSpendTxValidator(nullifierCache),
129
- new PhasesTxValidator(contractDataSource, setupAllowList, globalVariables.timestamp),
130
- new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice, globalVariables.gasFees),
131
- new BlockHeaderTxValidator(archiveCache),
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),
132
153
  );
133
154
  }
package/src/validator.ts CHANGED
@@ -18,19 +18,20 @@ import { RunningPromise } from '@aztec/foundation/running-promise';
18
18
  import { sleep } from '@aztec/foundation/sleep';
19
19
  import { DateProvider } from '@aztec/foundation/timer';
20
20
  import type { KeystoreManager } from '@aztec/node-keystore';
21
- import type { P2P, PeerId, TxProvider } from '@aztec/p2p';
21
+ import type { P2P, PeerId } from '@aztec/p2p';
22
22
  import { AuthRequest, AuthResponse, BlockProposalValidator, ReqRespSubProtocol } from '@aztec/p2p';
23
23
  import { OffenseType, WANT_TO_SLASH_EVENT, type Watcher, type WatcherEmitter } from '@aztec/slasher';
24
24
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
25
- import type { CommitteeAttestationsAndSigners, L2BlockNew, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
25
+ import type { CommitteeAttestationsAndSigners, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
26
26
  import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
27
27
  import type {
28
28
  CreateCheckpointProposalLastBlockData,
29
+ ITxProvider,
29
30
  Validator,
30
31
  ValidatorClientFullConfig,
31
32
  WorldStateSynchronizer,
32
33
  } from '@aztec/stdlib/interfaces/server';
33
- import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
34
+ import { type L1ToL2MessageSource, accumulateCheckpointOutHashes } from '@aztec/stdlib/messaging';
34
35
  import type {
35
36
  BlockProposal,
36
37
  BlockProposalOptions,
@@ -87,11 +88,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
87
88
 
88
89
  private proposersOfInvalidBlocks: Set<string> = new Set();
89
90
 
90
- // TODO(palla/mbps): Remove this once checkpoint validation is stable and we can validate all blocks properly.
91
- // Tracks slots for which we have successfully validated a block proposal, so we can attest to checkpoint proposals for those slots.
92
- // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
93
- private validatedBlockSlots: Set<SlotNumber> = new Set();
94
-
95
91
  protected constructor(
96
92
  private keyStore: ExtendedValidatorKeyStore,
97
93
  private epochCache: EpochCache,
@@ -184,7 +180,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
184
180
  p2pClient: P2P,
185
181
  blockSource: L2BlockSource & L2BlockSink,
186
182
  l1ToL2MessageSource: L1ToL2MessageSource,
187
- txProvider: TxProvider,
183
+ txProvider: ITxProvider,
188
184
  keyStoreManager: KeystoreManager,
189
185
  blobClient: BlobClientInterface,
190
186
  dateProvider: DateProvider = new DateProvider(),
@@ -413,10 +409,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
413
409
  return false;
414
410
  }
415
411
 
416
- // TODO(palla/mbps): Remove this once checkpoint validation is stable.
417
- // Track that we successfully validated a block for this slot, so we can attest to checkpoint proposals for it.
418
- this.validatedBlockSlots.add(slotNumber);
419
-
420
412
  return true;
421
413
  }
422
414
 
@@ -461,17 +453,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
461
453
  fishermanMode: this.config.fishermanMode || false,
462
454
  });
463
455
 
464
- // TODO(palla/mbps): Remove this once checkpoint validation is stable.
465
- // Check that we have successfully validated a block for this slot before attesting to the checkpoint.
466
- if (!this.validatedBlockSlots.has(slotNumber)) {
467
- this.log.warn(`No validated block found for slot ${slotNumber}, refusing to attest to checkpoint`, proposalInfo);
468
- return undefined;
469
- }
470
-
471
456
  // Validate the checkpoint proposal before attesting (unless skipCheckpointProposalValidation is set)
472
- // TODO(palla/mbps): Change default to false once checkpoint validation is stable.
473
- if (this.config.skipCheckpointProposalValidation !== false) {
474
- this.log.verbose(`Skipping checkpoint proposal validation for slot ${slotNumber}`, proposalInfo);
457
+ if (this.config.skipCheckpointProposalValidation) {
458
+ this.log.warn(`Skipping checkpoint proposal validation for slot ${slotNumber}`, proposalInfo);
475
459
  } else {
476
460
  const validationResult = await this.validateCheckpointProposal(proposal, proposalInfo);
477
461
  if (!validationResult.isValid) {
@@ -547,7 +531,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
547
531
  proposalInfo: LogData,
548
532
  ): Promise<{ isValid: true } | { isValid: false; reason: string }> {
549
533
  const slot = proposal.slotNumber;
550
- const timeoutSeconds = 10;
534
+ const timeoutSeconds = 10; // TODO(palla/mbps): This should map to the timetable settings
551
535
 
552
536
  // Wait for last block to sync by archive
553
537
  let lastBlockHeader: BlockHeader | undefined;
@@ -617,6 +601,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
617
601
  previousCheckpointOutHashes,
618
602
  fork,
619
603
  blocks,
604
+ this.log.getBindings(),
620
605
  );
621
606
 
622
607
  // Complete the checkpoint to get computed values
@@ -642,13 +627,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
642
627
  return { isValid: false, reason: 'archive_mismatch' };
643
628
  }
644
629
 
645
- // Check that the accumulated out hash matches the value in the proposal.
646
- const computedOutHash = computedCheckpoint.getCheckpointOutHash();
647
- const proposalOutHash = proposal.checkpointHeader.epochOutHash;
648
- if (!computedOutHash.equals(proposalOutHash)) {
630
+ // Check that the accumulated epoch out hash matches the value in the proposal.
631
+ // The epoch out hash is the accumulated hash of all checkpoint out hashes in the epoch.
632
+ const checkpointOutHash = computedCheckpoint.getCheckpointOutHash();
633
+ const computedEpochOutHash = accumulateCheckpointOutHashes([...previousCheckpointOutHashes, checkpointOutHash]);
634
+ const proposalEpochOutHash = proposal.checkpointHeader.epochOutHash;
635
+ if (!computedEpochOutHash.equals(proposalEpochOutHash)) {
649
636
  this.log.warn(`Epoch out hash mismatch`, {
650
- proposalOutHash: proposalOutHash.toString(),
651
- computedOutHash: computedOutHash.toString(),
637
+ proposalEpochOutHash: proposalEpochOutHash.toString(),
638
+ computedEpochOutHash: computedEpochOutHash.toString(),
639
+ checkpointOutHash: checkpointOutHash.toString(),
640
+ previousCheckpointOutHashes: previousCheckpointOutHashes.map(h => h.toString()),
652
641
  ...proposalInfo,
653
642
  });
654
643
  return { isValid: false, reason: 'out_hash_mismatch' };
@@ -664,7 +653,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
664
653
  /**
665
654
  * Extract checkpoint global variables from a block.
666
655
  */
667
- private extractCheckpointConstants(block: L2BlockNew): CheckpointGlobalVariables {
656
+ private extractCheckpointConstants(block: L2Block): CheckpointGlobalVariables {
668
657
  const gv = block.header.globalVariables;
669
658
  return {
670
659
  chainId: gv.chainId,
@@ -739,7 +728,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
739
728
  archive: Fr,
740
729
  txs: Tx[],
741
730
  proposerAddress: EthAddress | undefined,
742
- options: BlockProposalOptions,
731
+ options: BlockProposalOptions = {},
743
732
  ): Promise<BlockProposal> {
744
733
  // TODO(palla/mbps): Prevent double proposals properly
745
734
  // if (this.previousProposal?.slotNumber === blockHeader.globalVariables.slotNumber) {
@@ -771,7 +760,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
771
760
  archive: Fr,
772
761
  lastBlockInfo: CreateCheckpointProposalLastBlockData | undefined,
773
762
  proposerAddress: EthAddress | undefined,
774
- options: CheckpointProposalOptions,
763
+ options: CheckpointProposalOptions = {},
775
764
  ): Promise<CheckpointProposal> {
776
765
  this.log.info(`Assembling checkpoint proposal for slot ${checkpointHeader.slotNumber}`);
777
766
  return await this.validationService.createCheckpointProposal(