@aztec/validator-client 0.0.1-commit.7d4e6cd → 0.0.1-commit.858058eac

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 (57) hide show
  1. package/README.md +53 -24
  2. package/dest/block_proposal_handler.d.ts +8 -8
  3. package/dest/block_proposal_handler.d.ts.map +1 -1
  4. package/dest/block_proposal_handler.js +27 -32
  5. package/dest/checkpoint_builder.d.ts +21 -25
  6. package/dest/checkpoint_builder.d.ts.map +1 -1
  7. package/dest/checkpoint_builder.js +50 -32
  8. package/dest/config.d.ts +1 -1
  9. package/dest/config.d.ts.map +1 -1
  10. package/dest/config.js +12 -14
  11. package/dest/duties/validation_service.d.ts +19 -6
  12. package/dest/duties/validation_service.d.ts.map +1 -1
  13. package/dest/duties/validation_service.js +72 -19
  14. package/dest/factory.d.ts +2 -2
  15. package/dest/factory.d.ts.map +1 -1
  16. package/dest/factory.js +1 -1
  17. package/dest/key_store/ha_key_store.d.ts +99 -0
  18. package/dest/key_store/ha_key_store.d.ts.map +1 -0
  19. package/dest/key_store/ha_key_store.js +208 -0
  20. package/dest/key_store/index.d.ts +2 -1
  21. package/dest/key_store/index.d.ts.map +1 -1
  22. package/dest/key_store/index.js +1 -0
  23. package/dest/key_store/interface.d.ts +36 -6
  24. package/dest/key_store/interface.d.ts.map +1 -1
  25. package/dest/key_store/local_key_store.d.ts +10 -5
  26. package/dest/key_store/local_key_store.d.ts.map +1 -1
  27. package/dest/key_store/local_key_store.js +8 -4
  28. package/dest/key_store/node_keystore_adapter.d.ts +18 -5
  29. package/dest/key_store/node_keystore_adapter.d.ts.map +1 -1
  30. package/dest/key_store/node_keystore_adapter.js +18 -4
  31. package/dest/key_store/web3signer_key_store.d.ts +10 -5
  32. package/dest/key_store/web3signer_key_store.d.ts.map +1 -1
  33. package/dest/key_store/web3signer_key_store.js +8 -4
  34. package/dest/metrics.d.ts +4 -3
  35. package/dest/metrics.d.ts.map +1 -1
  36. package/dest/metrics.js +34 -5
  37. package/dest/tx_validator/tx_validator_factory.d.ts +4 -3
  38. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  39. package/dest/tx_validator/tx_validator_factory.js +17 -16
  40. package/dest/validator.d.ts +35 -16
  41. package/dest/validator.d.ts.map +1 -1
  42. package/dest/validator.js +194 -91
  43. package/package.json +21 -17
  44. package/src/block_proposal_handler.ts +41 -42
  45. package/src/checkpoint_builder.ts +85 -38
  46. package/src/config.ts +11 -13
  47. package/src/duties/validation_service.ts +91 -23
  48. package/src/factory.ts +1 -0
  49. package/src/key_store/ha_key_store.ts +269 -0
  50. package/src/key_store/index.ts +1 -0
  51. package/src/key_store/interface.ts +44 -5
  52. package/src/key_store/local_key_store.ts +13 -4
  53. package/src/key_store/node_keystore_adapter.ts +27 -4
  54. package/src/key_store/web3signer_key_store.ts +17 -4
  55. package/src/metrics.ts +45 -6
  56. package/src/tx_validator/tx_validator_factory.ts +52 -31
  57. package/src/validator.ts +253 -111
@@ -1,17 +1,22 @@
1
1
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
+ import type { EpochCache } from '@aztec/epoch-cache';
2
3
  import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
4
+ import { chunkBy } from '@aztec/foundation/collection';
3
5
  import { Fr } from '@aztec/foundation/curves/bn254';
4
6
  import { TimeoutError } from '@aztec/foundation/error';
5
7
  import { createLogger } from '@aztec/foundation/log';
6
8
  import { retryUntil } from '@aztec/foundation/retry';
7
9
  import { DateProvider, Timer } from '@aztec/foundation/timer';
8
10
  import type { P2P, PeerId } from '@aztec/p2p';
9
- import { TxProvider } from '@aztec/p2p';
10
11
  import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
11
- import type { L2BlockNew, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
12
- import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
13
- import type { ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
14
- import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
12
+ import type { L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
13
+ import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
14
+ import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
15
+ import {
16
+ type L1ToL2MessageSource,
17
+ computeCheckpointOutHash,
18
+ computeInHashFromL1ToL2Messages,
19
+ } from '@aztec/stdlib/messaging';
15
20
  import type { BlockProposal } from '@aztec/stdlib/p2p';
16
21
  import { BlockHeader, type CheckpointGlobalVariables, type FailedTx, type Tx } from '@aztec/stdlib/tx';
17
22
  import {
@@ -39,7 +44,7 @@ export type BlockProposalValidationFailureReason =
39
44
  | 'unknown_error';
40
45
 
41
46
  type ReexecuteTransactionsResult = {
42
- block: L2BlockNew;
47
+ block: L2Block;
43
48
  failedTxs: FailedTx[];
44
49
  reexecutionTimeMs: number;
45
50
  totalManaUsed: number;
@@ -72,8 +77,9 @@ export class BlockProposalHandler {
72
77
  private worldState: WorldStateSynchronizer,
73
78
  private blockSource: L2BlockSource & L2BlockSink,
74
79
  private l1ToL2MessageSource: L1ToL2MessageSource,
75
- private txProvider: TxProvider,
80
+ private txProvider: ITxProvider,
76
81
  private blockProposalValidator: BlockProposalValidator,
82
+ private epochCache: EpochCache,
77
83
  private config: ValidatorClientFullConfig,
78
84
  private metrics?: ValidatorMetrics,
79
85
  private dateProvider: DateProvider = new DateProvider(),
@@ -140,8 +146,8 @@ export class BlockProposalHandler {
140
146
 
141
147
  // Check that the proposal is from the current proposer, or the next proposer
142
148
  // This should have been handled by the p2p layer, but we double check here out of caution
143
- const invalidProposal = await this.blockProposalValidator.validate(proposal);
144
- if (invalidProposal) {
149
+ const validationResult = await this.blockProposalValidator.validate(proposal);
150
+ if (validationResult.result !== 'accept') {
145
151
  this.log.warn(`Proposal is not valid, skipping processing`, proposalInfo);
146
152
  return { isValid: false, reason: 'invalid_proposal' };
147
153
  }
@@ -153,9 +159,9 @@ export class BlockProposalHandler {
153
159
  return { isValid: false, reason: 'parent_block_not_found' };
154
160
  }
155
161
 
156
- // Check that the parent block's slot is less than the proposal's slot (should not happen, but we check anyway)
157
- if (parentBlockHeader !== 'genesis' && parentBlockHeader.getSlot() >= slotNumber) {
158
- this.log.warn(`Parent block slot is greater than or equal to proposal slot, skipping processing`, {
162
+ // Check that the parent block's slot is not greater than the proposal's slot.
163
+ if (parentBlockHeader !== 'genesis' && parentBlockHeader.getSlot() > slotNumber) {
164
+ this.log.warn(`Parent block slot is greater than proposal slot, skipping processing`, {
159
165
  parentBlockSlot: parentBlockHeader.getSlot().toString(),
160
166
  proposalSlot: slotNumber.toString(),
161
167
  ...proposalInfo,
@@ -212,6 +218,18 @@ export class BlockProposalHandler {
212
218
  // Try re-executing the transactions in the proposal if needed
213
219
  let reexecutionResult;
214
220
  if (shouldReexecute) {
221
+ // Compute the previous checkpoint out hashes for the epoch.
222
+ // TODO(leila/mbps): There can be a more efficient way to get the previous checkpoint out
223
+ // hashes without having to fetch all the blocks.
224
+ const epoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
225
+ const checkpointedBlocks = (await this.blockSource.getCheckpointedBlocksForEpoch(epoch))
226
+ .filter(b => b.block.number < blockNumber)
227
+ .sort((a, b) => a.block.number - b.block.number);
228
+ const blocksByCheckpoint = chunkBy(checkpointedBlocks, b => b.checkpointNumber);
229
+ const previousCheckpointOutHashes = blocksByCheckpoint.map(checkpointBlocks =>
230
+ computeCheckpointOutHash(checkpointBlocks.map(b => b.block.body.txEffects.map(tx => tx.l2ToL1Msgs))),
231
+ );
232
+
215
233
  try {
216
234
  this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
217
235
  reexecutionResult = await this.reexecuteTransactions(
@@ -220,6 +238,7 @@ export class BlockProposalHandler {
220
238
  checkpointNumber,
221
239
  txs,
222
240
  l1ToL2Messages,
241
+ previousCheckpointOutHashes,
223
242
  );
224
243
  } catch (error) {
225
244
  this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
@@ -229,7 +248,6 @@ export class BlockProposalHandler {
229
248
  }
230
249
 
231
250
  // If we succeeded, push this block into the archiver (unless disabled)
232
- // TODO(palla/mbps): Change default to false once block sync is stable.
233
251
  if (reexecutionResult?.block && this.config.skipPushProposedBlocksToArchiver === false) {
234
252
  await this.blockSource.addBlock(reexecutionResult?.block);
235
253
  }
@@ -297,7 +315,7 @@ export class BlockProposalHandler {
297
315
  // TODO(palla/mbps): The block header should include the checkpoint number to avoid this lookup,
298
316
  // or at least the L2BlockSource should return a different struct that includes it.
299
317
  const parentBlockNumber = parentBlockHeader.getBlockNumber();
300
- const parentBlock = await this.blockSource.getL2BlockNew(parentBlockNumber);
318
+ const parentBlock = await this.blockSource.getL2Block(parentBlockNumber);
301
319
  if (!parentBlock) {
302
320
  this.log.warn(`Parent block ${parentBlockNumber} not found in archiver`, proposalInfo);
303
321
  return { reason: 'invalid_proposal' };
@@ -338,7 +356,7 @@ export class BlockProposalHandler {
338
356
  */
339
357
  private validateNonFirstBlockInCheckpoint(
340
358
  proposal: BlockProposal,
341
- parentBlock: L2BlockNew,
359
+ parentBlock: L2Block,
342
360
  proposalInfo: object,
343
361
  ): CheckpointComputationResult | undefined {
344
362
  const proposalGlobals = proposal.blockHeader.globalVariables;
@@ -414,31 +432,7 @@ export class BlockProposalHandler {
414
432
 
415
433
  private getReexecutionDeadline(slot: SlotNumber, config: { l1GenesisTime: bigint; slotDuration: number }): Date {
416
434
  const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
417
- const msNeededForPropagationAndPublishing = this.config.validatorReexecuteDeadlineMs;
418
- return new Date(nextSlotTimestampSeconds * 1000 - msNeededForPropagationAndPublishing);
419
- }
420
-
421
- /**
422
- * Gets all prior blocks in the same checkpoint (same slot and checkpoint number) up to but not including upToBlockNumber.
423
- */
424
- private async getBlocksInCheckpoint(
425
- slot: SlotNumber,
426
- upToBlockNumber: BlockNumber,
427
- checkpointNumber: CheckpointNumber,
428
- ): Promise<L2BlockNew[]> {
429
- const blocks: L2BlockNew[] = [];
430
- let currentBlockNumber = BlockNumber(upToBlockNumber - 1);
431
-
432
- while (currentBlockNumber >= INITIAL_L2_BLOCK_NUM) {
433
- const block = await this.blockSource.getL2BlockNew(currentBlockNumber);
434
- if (!block || block.header.getSlot() !== slot || block.checkpointNumber !== checkpointNumber) {
435
- break;
436
- }
437
- blocks.unshift(block);
438
- currentBlockNumber = BlockNumber(currentBlockNumber - 1);
439
- }
440
-
441
- return blocks;
435
+ return new Date(nextSlotTimestampSeconds * 1000);
442
436
  }
443
437
 
444
438
  private getReexecuteFailureReason(err: any) {
@@ -459,6 +453,7 @@ export class BlockProposalHandler {
459
453
  checkpointNumber: CheckpointNumber,
460
454
  txs: Tx[],
461
455
  l1ToL2Messages: Fr[],
456
+ previousCheckpointOutHashes: Fr[],
462
457
  ): Promise<ReexecuteTransactionsResult> {
463
458
  const { blockHeader, txHashes } = proposal;
464
459
 
@@ -473,11 +468,13 @@ export class BlockProposalHandler {
473
468
  const slot = proposal.slotNumber;
474
469
  const config = this.checkpointsBuilder.getConfig();
475
470
 
476
- // Get prior blocks in this checkpoint (same slot and checkpoint number)
477
- const priorBlocks = await this.getBlocksInCheckpoint(slot, blockNumber, checkpointNumber);
471
+ // Get prior blocks in this checkpoint (same slot before current block)
472
+ const allBlocksInSlot = await this.blockSource.getBlocksForSlot(slot);
473
+ const priorBlocks = allBlocksInSlot.filter(b => b.number < blockNumber && b.header.getSlot() === slot);
478
474
 
479
475
  // Fork before the block to be built
480
476
  const parentBlockNumber = BlockNumber(blockNumber - 1);
477
+ await this.worldState.syncImmediate(parentBlockNumber);
481
478
  using fork = await this.worldState.fork(parentBlockNumber);
482
479
 
483
480
  // Build checkpoint constants from proposal (excludes blockNumber and timestamp which are per-block)
@@ -495,8 +492,10 @@ export class BlockProposalHandler {
495
492
  checkpointNumber,
496
493
  constants,
497
494
  l1ToL2Messages,
495
+ previousCheckpointOutHashes,
498
496
  fork,
499
497
  priorBlocks,
498
+ this.log.getBindings(),
500
499
  );
501
500
 
502
501
  // Build the new block
@@ -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,39 +12,38 @@ 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
+ import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
18
19
  import { Gas } from '@aztec/stdlib/gas';
19
20
  import {
21
+ type BuildBlockInCheckpointResult,
20
22
  type FullNodeBlockBuilderConfig,
21
23
  FullNodeBlockBuilderConfigKeys,
24
+ type ICheckpointBlockBuilder,
25
+ type ICheckpointsBuilder,
22
26
  type MerkleTreeWriteOperations,
27
+ NoValidTxsError,
23
28
  type PublicProcessorLimits,
29
+ type WorldStateSynchronizer,
24
30
  } from '@aztec/stdlib/interfaces/server';
25
31
  import { MerkleTreeId } from '@aztec/stdlib/trees';
26
- import { type CheckpointGlobalVariables, type FailedTx, GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
32
+ import { type CheckpointGlobalVariables, GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
27
33
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
28
34
 
29
35
  import { createValidatorForBlockBuilding } from './tx_validator/tx_validator_factory.js';
30
36
 
31
- const log = createLogger('checkpoint-builder');
32
-
33
- export interface BuildBlockInCheckpointResult {
34
- block: L2BlockNew;
35
- publicGas: Gas;
36
- publicProcessorDuration: number;
37
- numTxs: number;
38
- failedTxs: FailedTx[];
39
- blockBuildingTimer: Timer;
40
- usedTxs: Tx[];
41
- }
37
+ // Re-export for backward compatibility
38
+ export type { BuildBlockInCheckpointResult } from '@aztec/stdlib/interfaces/server';
42
39
 
43
40
  /**
44
41
  * Builder for a single checkpoint. Handles building blocks within the checkpoint
45
42
  * and completing it.
46
43
  */
47
- export class CheckpointBuilder {
44
+ export class CheckpointBuilder implements ICheckpointBlockBuilder {
45
+ private log: Logger;
46
+
48
47
  constructor(
49
48
  private checkpointBuilder: LightweightCheckpointBuilder,
50
49
  private fork: MerkleTreeWriteOperations,
@@ -52,7 +51,13 @@ export class CheckpointBuilder {
52
51
  private contractDataSource: ContractDataSource,
53
52
  private dateProvider: DateProvider,
54
53
  private telemetryClient: TelemetryClient,
55
- ) {}
54
+ bindings?: LoggerBindings,
55
+ ) {
56
+ this.log = createLogger('checkpoint-builder', {
57
+ ...bindings,
58
+ instanceId: `checkpoint-${checkpointBuilder.checkpointNumber}`,
59
+ });
60
+ }
56
61
 
57
62
  getConstantData(): CheckpointGlobalVariables {
58
63
  return this.checkpointBuilder.constants;
@@ -65,12 +70,16 @@ export class CheckpointBuilder {
65
70
  pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
66
71
  blockNumber: BlockNumber,
67
72
  timestamp: bigint,
68
- opts: PublicProcessorLimits & { expectedEndState?: StateReference },
73
+ opts: PublicProcessorLimits & { expectedEndState?: StateReference } = {},
69
74
  ): Promise<BuildBlockInCheckpointResult> {
70
- const blockBuildingTimer = new Timer();
71
75
  const slot = this.checkpointBuilder.constants.slotNumber;
72
76
 
73
- log.verbose(`Building block ${blockNumber} for slot ${slot} within checkpoint`, { slot, blockNumber, ...opts });
77
+ this.log.verbose(`Building block ${blockNumber} for slot ${slot} within checkpoint`, {
78
+ slot,
79
+ blockNumber,
80
+ ...opts,
81
+ currentTime: new Date(this.dateProvider.now()),
82
+ });
74
83
 
75
84
  const constants = this.checkpointBuilder.constants;
76
85
  const globalVariables = GlobalVariables.from({
@@ -85,10 +94,16 @@ export class CheckpointBuilder {
85
94
  });
86
95
  const { processor, validator } = await this.makeBlockBuilderDeps(globalVariables, this.fork);
87
96
 
88
- const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() =>
97
+ const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs, _, usedTxBlobFields]] = await elapsed(() =>
89
98
  processor.process(pendingTxs, opts, validator),
90
99
  );
91
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
+
92
107
  // Add block to checkpoint
93
108
  const block = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
94
109
  expectedEndState: opts.expectedEndState,
@@ -97,24 +112,28 @@ export class CheckpointBuilder {
97
112
  // How much public gas was processed
98
113
  const publicGas = processedTxs.reduce((acc, tx) => acc.add(tx.gasUsed.publicGas), Gas.empty());
99
114
 
100
- 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 {
101
122
  block,
102
123
  publicGas,
103
124
  publicProcessorDuration,
104
125
  numTxs: processedTxs.length,
105
126
  failedTxs,
106
- blockBuildingTimer,
107
127
  usedTxs,
128
+ usedTxBlobFields,
108
129
  };
109
- log.debug('Built block within checkpoint', res.block.header);
110
- return res;
111
130
  }
112
131
 
113
132
  /** Completes the checkpoint and returns it. */
114
133
  async completeCheckpoint(): Promise<Checkpoint> {
115
134
  const checkpoint = await this.checkpointBuilder.completeCheckpoint();
116
135
 
117
- log.verbose(`Completed checkpoint ${checkpoint.number}`, {
136
+ this.log.verbose(`Completed checkpoint ${checkpoint.number}`, {
118
137
  checkpointNumber: checkpoint.number,
119
138
  numBlocks: checkpoint.blocks.length,
120
139
  archiveRoot: checkpoint.archive.root.toString(),
@@ -130,14 +149,16 @@ export class CheckpointBuilder {
130
149
 
131
150
  protected async makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations) {
132
151
  const txPublicSetupAllowList = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
133
- const contractsDB = new PublicContractsDB(this.contractDataSource);
152
+ const contractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings());
134
153
  const guardedFork = new GuardedMerkleTreeOperations(fork);
135
154
 
155
+ const bindings = this.log.getBindings();
136
156
  const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
137
157
  guardedFork,
138
158
  contractsDB,
139
159
  globalVariables,
140
160
  this.telemetryClient,
161
+ bindings,
141
162
  );
142
163
 
143
164
  const processor = new PublicProcessor(
@@ -147,7 +168,7 @@ export class CheckpointBuilder {
147
168
  publicTxSimulator,
148
169
  this.dateProvider,
149
170
  this.telemetryClient,
150
- undefined,
171
+ createLogger('simulator:public-processor', bindings),
151
172
  this.config,
152
173
  );
153
174
 
@@ -156,6 +177,7 @@ export class CheckpointBuilder {
156
177
  this.contractDataSource,
157
178
  globalVariables,
158
179
  txPublicSetupAllowList,
180
+ this.log.getBindings(),
159
181
  );
160
182
 
161
183
  return {
@@ -165,16 +187,19 @@ export class CheckpointBuilder {
165
187
  }
166
188
  }
167
189
 
168
- /**
169
- * Factory for creating checkpoint builders.
170
- */
171
- export class FullNodeCheckpointsBuilder {
190
+ /** Factory for creating checkpoint builders. */
191
+ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
192
+ private log: Logger;
193
+
172
194
  constructor(
173
- private config: FullNodeBlockBuilderConfig,
195
+ private config: FullNodeBlockBuilderConfig & Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>,
196
+ private worldState: WorldStateSynchronizer,
174
197
  private contractDataSource: ContractDataSource,
175
198
  private dateProvider: DateProvider,
176
199
  private telemetryClient: TelemetryClient = getTelemetryClient(),
177
- ) {}
200
+ ) {
201
+ this.log = createLogger('checkpoint-builder');
202
+ }
178
203
 
179
204
  public getConfig(): FullNodeBlockBuilderConfig {
180
205
  return this.config;
@@ -191,12 +216,14 @@ export class FullNodeCheckpointsBuilder {
191
216
  checkpointNumber: CheckpointNumber,
192
217
  constants: CheckpointGlobalVariables,
193
218
  l1ToL2Messages: Fr[],
219
+ previousCheckpointOutHashes: Fr[],
194
220
  fork: MerkleTreeWriteOperations,
221
+ bindings?: LoggerBindings,
195
222
  ): Promise<CheckpointBuilder> {
196
223
  const stateReference = await fork.getStateReference();
197
224
  const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
198
225
 
199
- log.verbose(`Building new checkpoint ${checkpointNumber}`, {
226
+ this.log.verbose(`Building new checkpoint ${checkpointNumber}`, {
200
227
  checkpointNumber,
201
228
  msgCount: l1ToL2Messages.length,
202
229
  initialStateReference: stateReference.toInspect(),
@@ -208,7 +235,9 @@ export class FullNodeCheckpointsBuilder {
208
235
  checkpointNumber,
209
236
  constants,
210
237
  l1ToL2Messages,
238
+ previousCheckpointOutHashes,
211
239
  fork,
240
+ bindings,
212
241
  );
213
242
 
214
243
  return new CheckpointBuilder(
@@ -218,6 +247,7 @@ export class FullNodeCheckpointsBuilder {
218
247
  this.contractDataSource,
219
248
  this.dateProvider,
220
249
  this.telemetryClient,
250
+ bindings,
221
251
  );
222
252
  }
223
253
 
@@ -228,17 +258,26 @@ export class FullNodeCheckpointsBuilder {
228
258
  checkpointNumber: CheckpointNumber,
229
259
  constants: CheckpointGlobalVariables,
230
260
  l1ToL2Messages: Fr[],
261
+ previousCheckpointOutHashes: Fr[],
231
262
  fork: MerkleTreeWriteOperations,
232
- existingBlocks: L2BlockNew[] = [],
263
+ existingBlocks: L2Block[] = [],
264
+ bindings?: LoggerBindings,
233
265
  ): Promise<CheckpointBuilder> {
234
266
  const stateReference = await fork.getStateReference();
235
267
  const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
236
268
 
237
269
  if (existingBlocks.length === 0) {
238
- return this.startCheckpoint(checkpointNumber, constants, l1ToL2Messages, fork);
270
+ return this.startCheckpoint(
271
+ checkpointNumber,
272
+ constants,
273
+ l1ToL2Messages,
274
+ previousCheckpointOutHashes,
275
+ fork,
276
+ bindings,
277
+ );
239
278
  }
240
279
 
241
- log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
280
+ this.log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
242
281
  checkpointNumber,
243
282
  msgCount: l1ToL2Messages.length,
244
283
  existingBlockCount: existingBlocks.length,
@@ -251,8 +290,10 @@ export class FullNodeCheckpointsBuilder {
251
290
  checkpointNumber,
252
291
  constants,
253
292
  l1ToL2Messages,
293
+ previousCheckpointOutHashes,
254
294
  fork,
255
295
  existingBlocks,
296
+ bindings,
256
297
  );
257
298
 
258
299
  return new CheckpointBuilder(
@@ -262,6 +303,12 @@ export class FullNodeCheckpointsBuilder {
262
303
  this.contractDataSource,
263
304
  this.dateProvider,
264
305
  this.telemetryClient,
306
+ bindings,
265
307
  );
266
308
  }
309
+
310
+ /** Returns a fork of the world state at the given block number. */
311
+ getFork(blockNumber: BlockNumber): Promise<MerkleTreeWriteOperations> {
312
+ return this.worldState.fork(blockNumber);
313
+ }
267
314
  }
package/src/config.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  } from '@aztec/foundation/config';
8
8
  import { EthAddress } from '@aztec/foundation/eth-address';
9
9
  import type { ValidatorClientConfig } from '@aztec/stdlib/interfaces/server';
10
+ import { validatorHASignerConfigMappings } from '@aztec/validator-ha-signer/config';
10
11
 
11
12
  export type { ValidatorClientConfig };
12
13
 
@@ -53,16 +54,10 @@ export const validatorClientConfigMappings: ConfigMappingsType<ValidatorClientCo
53
54
  description: 'Re-execute transactions before attesting',
54
55
  ...booleanConfigHelper(true),
55
56
  },
56
- validatorReexecuteDeadlineMs: {
57
- env: 'VALIDATOR_REEXECUTE_DEADLINE_MS',
58
- description: 'Will re-execute until this many milliseconds are left in the slot',
59
- ...numberConfigHelper(6000),
60
- },
61
57
  alwaysReexecuteBlockProposals: {
62
- env: 'ALWAYS_REEXECUTE_BLOCK_PROPOSALS',
63
58
  description:
64
59
  'Whether to always reexecute block proposals, even for non-validator nodes (useful for monitoring network status).',
65
- ...booleanConfigHelper(false),
60
+ defaultValue: true,
66
61
  },
67
62
  fishermanMode: {
68
63
  env: 'FISHERMAN_MODE',
@@ -70,16 +65,19 @@ export const validatorClientConfigMappings: ConfigMappingsType<ValidatorClientCo
70
65
  'Whether to run in fisherman mode: validates all proposals and attestations but does not broadcast attestations or participate in consensus.',
71
66
  ...booleanConfigHelper(false),
72
67
  },
73
- // TODO(palla/mbps): Change default to false once checkpoint validation is stable
74
68
  skipCheckpointProposalValidation: {
75
- description: 'Skip checkpoint proposal validation and always attest (default: true)',
76
- defaultValue: true,
69
+ description: 'Skip checkpoint proposal validation and always attest (default: false)',
70
+ defaultValue: false,
77
71
  },
78
- // TODO(palla/mbps): Change default to false once block sync is stable
79
72
  skipPushProposedBlocksToArchiver: {
80
- description: 'Skip pushing re-executed blocks to archiver (default: true)',
81
- defaultValue: true,
73
+ description: 'Skip pushing re-executed blocks to archiver (default: false)',
74
+ defaultValue: false,
75
+ },
76
+ attestToEquivocatedProposals: {
77
+ description: 'Agree to attest to equivocated checkpoint proposals (for testing purposes only)',
78
+ ...booleanConfigHelper(false),
82
79
  },
80
+ ...validatorHASignerConfigMappings,
83
81
  };
84
82
 
85
83
  /**