@aztec/validator-client 0.0.1-commit.7d4e6cd → 0.0.1-commit.8227e42

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 (69) hide show
  1. package/README.md +53 -24
  2. package/dest/block_proposal_handler.d.ts +9 -9
  3. package/dest/block_proposal_handler.d.ts.map +1 -1
  4. package/dest/block_proposal_handler.js +35 -54
  5. package/dest/checkpoint_builder.d.ts +24 -25
  6. package/dest/checkpoint_builder.d.ts.map +1 -1
  7. package/dest/checkpoint_builder.js +62 -37
  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 +20 -7
  12. package/dest/duties/validation_service.d.ts.map +1 -1
  13. package/dest/duties/validation_service.js +75 -22
  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/index.d.ts +1 -2
  18. package/dest/index.d.ts.map +1 -1
  19. package/dest/index.js +0 -1
  20. package/dest/key_store/ha_key_store.d.ts +99 -0
  21. package/dest/key_store/ha_key_store.d.ts.map +1 -0
  22. package/dest/key_store/ha_key_store.js +208 -0
  23. package/dest/key_store/index.d.ts +2 -1
  24. package/dest/key_store/index.d.ts.map +1 -1
  25. package/dest/key_store/index.js +1 -0
  26. package/dest/key_store/interface.d.ts +36 -6
  27. package/dest/key_store/interface.d.ts.map +1 -1
  28. package/dest/key_store/local_key_store.d.ts +10 -5
  29. package/dest/key_store/local_key_store.d.ts.map +1 -1
  30. package/dest/key_store/local_key_store.js +8 -4
  31. package/dest/key_store/node_keystore_adapter.d.ts +18 -5
  32. package/dest/key_store/node_keystore_adapter.d.ts.map +1 -1
  33. package/dest/key_store/node_keystore_adapter.js +18 -4
  34. package/dest/key_store/web3signer_key_store.d.ts +10 -5
  35. package/dest/key_store/web3signer_key_store.d.ts.map +1 -1
  36. package/dest/key_store/web3signer_key_store.js +8 -4
  37. package/dest/metrics.d.ts +4 -3
  38. package/dest/metrics.d.ts.map +1 -1
  39. package/dest/metrics.js +34 -5
  40. package/dest/validator.d.ts +43 -18
  41. package/dest/validator.d.ts.map +1 -1
  42. package/dest/validator.js +233 -94
  43. package/package.json +21 -17
  44. package/src/block_proposal_handler.ts +48 -69
  45. package/src/checkpoint_builder.ts +104 -43
  46. package/src/config.ts +11 -13
  47. package/src/duties/validation_service.ts +100 -25
  48. package/src/factory.ts +1 -0
  49. package/src/index.ts +0 -1
  50. package/src/key_store/ha_key_store.ts +269 -0
  51. package/src/key_store/index.ts +1 -0
  52. package/src/key_store/interface.ts +44 -5
  53. package/src/key_store/local_key_store.ts +13 -4
  54. package/src/key_store/node_keystore_adapter.ts +27 -4
  55. package/src/key_store/web3signer_key_store.ts +17 -4
  56. package/src/metrics.ts +45 -6
  57. package/src/validator.ts +303 -114
  58. package/dest/tx_validator/index.d.ts +0 -3
  59. package/dest/tx_validator/index.d.ts.map +0 -1
  60. package/dest/tx_validator/index.js +0 -2
  61. package/dest/tx_validator/nullifier_cache.d.ts +0 -14
  62. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  63. package/dest/tx_validator/nullifier_cache.js +0 -24
  64. package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
  65. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
  66. package/dest/tx_validator/tx_validator_factory.js +0 -53
  67. package/src/tx_validator/index.ts +0 -2
  68. package/src/tx_validator/nullifier_cache.ts +0 -30
  69. package/src/tx_validator/tx_validator_factory.ts +0 -133
@@ -1,4 +1,5 @@
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';
3
4
  import { Fr } from '@aztec/foundation/curves/bn254';
4
5
  import { TimeoutError } from '@aztec/foundation/error';
@@ -6,14 +7,13 @@ import { createLogger } from '@aztec/foundation/log';
6
7
  import { retryUntil } from '@aztec/foundation/retry';
7
8
  import { DateProvider, Timer } from '@aztec/foundation/timer';
8
9
  import type { P2P, PeerId } from '@aztec/p2p';
9
- import { TxProvider } from '@aztec/p2p';
10
10
  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';
11
+ import type { BlockData, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
12
+ import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
13
+ import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
14
14
  import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
15
15
  import type { BlockProposal } from '@aztec/stdlib/p2p';
16
- import { BlockHeader, type CheckpointGlobalVariables, type FailedTx, type Tx } from '@aztec/stdlib/tx';
16
+ import type { CheckpointGlobalVariables, FailedTx, Tx } from '@aztec/stdlib/tx';
17
17
  import {
18
18
  ReExFailedTxsError,
19
19
  ReExStateMismatchError,
@@ -39,7 +39,7 @@ export type BlockProposalValidationFailureReason =
39
39
  | 'unknown_error';
40
40
 
41
41
  type ReexecuteTransactionsResult = {
42
- block: L2BlockNew;
42
+ block: L2Block;
43
43
  failedTxs: FailedTx[];
44
44
  reexecutionTimeMs: number;
45
45
  totalManaUsed: number;
@@ -72,8 +72,9 @@ export class BlockProposalHandler {
72
72
  private worldState: WorldStateSynchronizer,
73
73
  private blockSource: L2BlockSource & L2BlockSink,
74
74
  private l1ToL2MessageSource: L1ToL2MessageSource,
75
- private txProvider: TxProvider,
75
+ private txProvider: ITxProvider,
76
76
  private blockProposalValidator: BlockProposalValidator,
77
+ private epochCache: EpochCache,
77
78
  private config: ValidatorClientFullConfig,
78
79
  private metrics?: ValidatorMetrics,
79
80
  private dateProvider: DateProvider = new DateProvider(),
@@ -140,23 +141,23 @@ export class BlockProposalHandler {
140
141
 
141
142
  // Check that the proposal is from the current proposer, or the next proposer
142
143
  // 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) {
144
+ const validationResult = await this.blockProposalValidator.validate(proposal);
145
+ if (validationResult.result !== 'accept') {
145
146
  this.log.warn(`Proposal is not valid, skipping processing`, proposalInfo);
146
147
  return { isValid: false, reason: 'invalid_proposal' };
147
148
  }
148
149
 
149
150
  // Check that the parent proposal is a block we know, otherwise reexecution would fail
150
- const parentBlockHeader = await this.getParentBlock(proposal);
151
- if (parentBlockHeader === undefined) {
151
+ const parentBlock = await this.getParentBlock(proposal);
152
+ if (parentBlock === undefined) {
152
153
  this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
153
154
  return { isValid: false, reason: 'parent_block_not_found' };
154
155
  }
155
156
 
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`, {
159
- parentBlockSlot: parentBlockHeader.getSlot().toString(),
157
+ // Check that the parent block's slot is not greater than the proposal's slot.
158
+ if (parentBlock !== 'genesis' && parentBlock.header.getSlot() > slotNumber) {
159
+ this.log.warn(`Parent block slot is greater than proposal slot, skipping processing`, {
160
+ parentBlockSlot: parentBlock.header.getSlot().toString(),
160
161
  proposalSlot: slotNumber.toString(),
161
162
  ...proposalInfo,
162
163
  });
@@ -165,9 +166,9 @@ export class BlockProposalHandler {
165
166
 
166
167
  // Compute the block number based on the parent block
167
168
  const blockNumber =
168
- parentBlockHeader === 'genesis'
169
+ parentBlock === 'genesis'
169
170
  ? BlockNumber(INITIAL_L2_BLOCK_NUM)
170
- : BlockNumber(parentBlockHeader.getBlockNumber() + 1);
171
+ : BlockNumber(parentBlock.header.getBlockNumber() + 1);
171
172
 
172
173
  // Check that this block number does not exist already
173
174
  const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
@@ -184,7 +185,7 @@ export class BlockProposalHandler {
184
185
  });
185
186
 
186
187
  // Compute the checkpoint number for this block and validate checkpoint consistency
187
- const checkpointResult = await this.computeCheckpointNumber(proposal, parentBlockHeader, proposalInfo);
188
+ const checkpointResult = this.computeCheckpointNumber(proposal, parentBlock, proposalInfo);
188
189
  if (checkpointResult.reason) {
189
190
  return { isValid: false, blockNumber, reason: checkpointResult.reason };
190
191
  }
@@ -212,6 +213,12 @@ export class BlockProposalHandler {
212
213
  // Try re-executing the transactions in the proposal if needed
213
214
  let reexecutionResult;
214
215
  if (shouldReexecute) {
216
+ // Collect the out hashes of all the checkpoints before this one in the same epoch
217
+ const epoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
218
+ const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
219
+ .filter(c => c.checkpointNumber < checkpointNumber)
220
+ .map(c => c.checkpointOutHash);
221
+
215
222
  try {
216
223
  this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
217
224
  reexecutionResult = await this.reexecuteTransactions(
@@ -220,6 +227,7 @@ export class BlockProposalHandler {
220
227
  checkpointNumber,
221
228
  txs,
222
229
  l1ToL2Messages,
230
+ previousCheckpointOutHashes,
223
231
  );
224
232
  } catch (error) {
225
233
  this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
@@ -229,7 +237,6 @@ export class BlockProposalHandler {
229
237
  }
230
238
 
231
239
  // 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
240
  if (reexecutionResult?.block && this.config.skipPushProposedBlocksToArchiver === false) {
234
241
  await this.blockSource.addBlock(reexecutionResult?.block);
235
242
  }
@@ -242,7 +249,7 @@ export class BlockProposalHandler {
242
249
  return { isValid: true, blockNumber, reexecutionResult };
243
250
  }
244
251
 
245
- private async getParentBlock(proposal: BlockProposal): Promise<'genesis' | BlockHeader | undefined> {
252
+ private async getParentBlock(proposal: BlockProposal): Promise<'genesis' | BlockData | undefined> {
246
253
  const parentArchive = proposal.blockHeader.lastArchive.root;
247
254
  const slot = proposal.slotNumber;
248
255
  const config = this.checkpointsBuilder.getConfig();
@@ -258,12 +265,11 @@ export class BlockProposalHandler {
258
265
 
259
266
  try {
260
267
  return (
261
- (await this.blockSource.getBlockHeaderByArchive(parentArchive)) ??
268
+ (await this.blockSource.getBlockDataByArchive(parentArchive)) ??
262
269
  (timeoutDurationMs <= 0
263
270
  ? undefined
264
271
  : await retryUntil(
265
- () =>
266
- this.blockSource.syncImmediate().then(() => this.blockSource.getBlockHeaderByArchive(parentArchive)),
272
+ () => this.blockSource.syncImmediate().then(() => this.blockSource.getBlockDataByArchive(parentArchive)),
267
273
  'force archiver sync',
268
274
  timeoutDurationMs / 1000,
269
275
  0.5,
@@ -279,12 +285,12 @@ export class BlockProposalHandler {
279
285
  }
280
286
  }
281
287
 
282
- private async computeCheckpointNumber(
288
+ private computeCheckpointNumber(
283
289
  proposal: BlockProposal,
284
- parentBlockHeader: 'genesis' | BlockHeader,
290
+ parentBlock: 'genesis' | BlockData,
285
291
  proposalInfo: object,
286
- ): Promise<CheckpointComputationResult> {
287
- if (parentBlockHeader === 'genesis') {
292
+ ): CheckpointComputationResult {
293
+ if (parentBlock === 'genesis') {
288
294
  // First block is in checkpoint 1
289
295
  if (proposal.indexWithinCheckpoint !== 0) {
290
296
  this.log.warn(`First block proposal has non-zero indexWithinCheckpoint`, proposalInfo);
@@ -293,19 +299,9 @@ export class BlockProposalHandler {
293
299
  return { checkpointNumber: CheckpointNumber.INITIAL };
294
300
  }
295
301
 
296
- // Get the parent block to find its checkpoint number
297
- // TODO(palla/mbps): The block header should include the checkpoint number to avoid this lookup,
298
- // or at least the L2BlockSource should return a different struct that includes it.
299
- const parentBlockNumber = parentBlockHeader.getBlockNumber();
300
- const parentBlock = await this.blockSource.getL2BlockNew(parentBlockNumber);
301
- if (!parentBlock) {
302
- this.log.warn(`Parent block ${parentBlockNumber} not found in archiver`, proposalInfo);
303
- return { reason: 'invalid_proposal' };
304
- }
305
-
306
302
  if (proposal.indexWithinCheckpoint === 0) {
307
303
  // If this is the first block in a new checkpoint, increment the checkpoint number
308
- if (!(proposal.blockHeader.getSlot() > parentBlockHeader.getSlot())) {
304
+ if (!(proposal.blockHeader.getSlot() > parentBlock.header.getSlot())) {
309
305
  this.log.warn(`Slot should be greater than parent block slot for first block in checkpoint`, proposalInfo);
310
306
  return { reason: 'invalid_proposal' };
311
307
  }
@@ -317,7 +313,7 @@ export class BlockProposalHandler {
317
313
  this.log.warn(`Non-sequential indexWithinCheckpoint`, proposalInfo);
318
314
  return { reason: 'invalid_proposal' };
319
315
  }
320
- if (proposal.blockHeader.getSlot() !== parentBlockHeader.getSlot()) {
316
+ if (proposal.blockHeader.getSlot() !== parentBlock.header.getSlot()) {
321
317
  this.log.warn(`Slot should be equal to parent block slot for non-first block in checkpoint`, proposalInfo);
322
318
  return { reason: 'invalid_proposal' };
323
319
  }
@@ -338,7 +334,7 @@ export class BlockProposalHandler {
338
334
  */
339
335
  private validateNonFirstBlockInCheckpoint(
340
336
  proposal: BlockProposal,
341
- parentBlock: L2BlockNew,
337
+ parentBlock: BlockData,
342
338
  proposalInfo: object,
343
339
  ): CheckpointComputationResult | undefined {
344
340
  const proposalGlobals = proposal.blockHeader.globalVariables;
@@ -414,31 +410,7 @@ export class BlockProposalHandler {
414
410
 
415
411
  private getReexecutionDeadline(slot: SlotNumber, config: { l1GenesisTime: bigint; slotDuration: number }): Date {
416
412
  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;
413
+ return new Date(nextSlotTimestampSeconds * 1000);
442
414
  }
443
415
 
444
416
  private getReexecuteFailureReason(err: any) {
@@ -459,6 +431,7 @@ export class BlockProposalHandler {
459
431
  checkpointNumber: CheckpointNumber,
460
432
  txs: Tx[],
461
433
  l1ToL2Messages: Fr[],
434
+ previousCheckpointOutHashes: Fr[],
462
435
  ): Promise<ReexecuteTransactionsResult> {
463
436
  const { blockHeader, txHashes } = proposal;
464
437
 
@@ -473,18 +446,21 @@ export class BlockProposalHandler {
473
446
  const slot = proposal.slotNumber;
474
447
  const config = this.checkpointsBuilder.getConfig();
475
448
 
476
- // Get prior blocks in this checkpoint (same slot and checkpoint number)
477
- const priorBlocks = await this.getBlocksInCheckpoint(slot, blockNumber, checkpointNumber);
449
+ // Get prior blocks in this checkpoint (same slot before current block)
450
+ const allBlocksInSlot = await this.blockSource.getBlocksForSlot(slot);
451
+ const priorBlocks = allBlocksInSlot.filter(b => b.number < blockNumber && b.header.getSlot() === slot);
478
452
 
479
453
  // Fork before the block to be built
480
454
  const parentBlockNumber = BlockNumber(blockNumber - 1);
481
- using fork = await this.worldState.fork(parentBlockNumber);
455
+ await this.worldState.syncImmediate(parentBlockNumber);
456
+ await using fork = await this.worldState.fork(parentBlockNumber);
482
457
 
483
- // Build checkpoint constants from proposal (excludes blockNumber and timestamp which are per-block)
458
+ // Build checkpoint constants from proposal (excludes blockNumber which is per-block)
484
459
  const constants: CheckpointGlobalVariables = {
485
460
  chainId: new Fr(config.l1ChainId),
486
461
  version: new Fr(config.rollupVersion),
487
462
  slotNumber: slot,
463
+ timestamp: blockHeader.globalVariables.timestamp,
488
464
  coinbase: blockHeader.globalVariables.coinbase,
489
465
  feeRecipient: blockHeader.globalVariables.feeRecipient,
490
466
  gasFees: blockHeader.globalVariables.gasFees,
@@ -494,9 +470,12 @@ export class BlockProposalHandler {
494
470
  const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
495
471
  checkpointNumber,
496
472
  constants,
473
+ 0n, // only takes effect in the following checkpoint.
497
474
  l1ToL2Messages,
475
+ previousCheckpointOutHashes,
498
476
  fork,
499
477
  priorBlocks,
478
+ this.log.getBindings(),
500
479
  );
501
480
 
502
481
  // Build the new block
@@ -1,10 +1,10 @@
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';
7
- import { getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators';
6
+ import { DateProvider, elapsed } from '@aztec/foundation/timer';
7
+ import { createTxValidatorForBlockBuilding, getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators';
8
8
  import { LightweightCheckpointBuilder } from '@aztec/prover-client/light';
9
9
  import {
10
10
  GuardedMerkleTreeOperations,
@@ -12,39 +12,37 @@ 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';
31
+ import { type DebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
25
32
  import { MerkleTreeId } from '@aztec/stdlib/trees';
26
- import { type CheckpointGlobalVariables, type FailedTx, GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
33
+ import { type CheckpointGlobalVariables, GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
27
34
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
28
35
 
29
- import { createValidatorForBlockBuilding } from './tx_validator/tx_validator_factory.js';
30
-
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
- }
36
+ // Re-export for backward compatibility
37
+ export type { BuildBlockInCheckpointResult } from '@aztec/stdlib/interfaces/server';
42
38
 
43
39
  /**
44
40
  * Builder for a single checkpoint. Handles building blocks within the checkpoint
45
41
  * and completing it.
46
42
  */
47
- export class CheckpointBuilder {
43
+ export class CheckpointBuilder implements ICheckpointBlockBuilder {
44
+ private log: Logger;
45
+
48
46
  constructor(
49
47
  private checkpointBuilder: LightweightCheckpointBuilder,
50
48
  private fork: MerkleTreeWriteOperations,
@@ -52,7 +50,14 @@ export class CheckpointBuilder {
52
50
  private contractDataSource: ContractDataSource,
53
51
  private dateProvider: DateProvider,
54
52
  private telemetryClient: TelemetryClient,
55
- ) {}
53
+ bindings?: LoggerBindings,
54
+ private debugLogStore: DebugLogStore = new NullDebugLogStore(),
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,36 +94,46 @@ 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
- const block = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
108
+ const { block } = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
94
109
  expectedEndState: opts.expectedEndState,
95
110
  });
96
111
 
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,19 @@ 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 collectDebugLogs = this.debugLogStore.isEnabled;
156
+
157
+ const bindings = this.log.getBindings();
136
158
  const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
137
159
  guardedFork,
138
160
  contractsDB,
139
161
  globalVariables,
140
162
  this.telemetryClient,
163
+ bindings,
164
+ collectDebugLogs,
141
165
  );
142
166
 
143
167
  const processor = new PublicProcessor(
@@ -147,15 +171,17 @@ export class CheckpointBuilder {
147
171
  publicTxSimulator,
148
172
  this.dateProvider,
149
173
  this.telemetryClient,
150
- undefined,
174
+ createLogger('simulator:public-processor', bindings),
151
175
  this.config,
176
+ this.debugLogStore,
152
177
  );
153
178
 
154
- const validator = createValidatorForBlockBuilding(
179
+ const validator = createTxValidatorForBlockBuilding(
155
180
  fork,
156
181
  this.contractDataSource,
157
182
  globalVariables,
158
183
  txPublicSetupAllowList,
184
+ this.log.getBindings(),
159
185
  );
160
186
 
161
187
  return {
@@ -165,16 +191,20 @@ export class CheckpointBuilder {
165
191
  }
166
192
  }
167
193
 
168
- /**
169
- * Factory for creating checkpoint builders.
170
- */
171
- export class FullNodeCheckpointsBuilder {
194
+ /** Factory for creating checkpoint builders. */
195
+ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
196
+ private log: Logger;
197
+
172
198
  constructor(
173
- private config: FullNodeBlockBuilderConfig,
199
+ private config: FullNodeBlockBuilderConfig & Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>,
200
+ private worldState: WorldStateSynchronizer,
174
201
  private contractDataSource: ContractDataSource,
175
202
  private dateProvider: DateProvider,
176
203
  private telemetryClient: TelemetryClient = getTelemetryClient(),
177
- ) {}
204
+ private debugLogStore: DebugLogStore = new NullDebugLogStore(),
205
+ ) {
206
+ this.log = createLogger('checkpoint-builder');
207
+ }
178
208
 
179
209
  public getConfig(): FullNodeBlockBuilderConfig {
180
210
  return this.config;
@@ -190,25 +220,32 @@ export class FullNodeCheckpointsBuilder {
190
220
  async startCheckpoint(
191
221
  checkpointNumber: CheckpointNumber,
192
222
  constants: CheckpointGlobalVariables,
223
+ feeAssetPriceModifier: bigint,
193
224
  l1ToL2Messages: Fr[],
225
+ previousCheckpointOutHashes: Fr[],
194
226
  fork: MerkleTreeWriteOperations,
227
+ bindings?: LoggerBindings,
195
228
  ): Promise<CheckpointBuilder> {
196
229
  const stateReference = await fork.getStateReference();
197
230
  const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
198
231
 
199
- log.verbose(`Building new checkpoint ${checkpointNumber}`, {
232
+ this.log.verbose(`Building new checkpoint ${checkpointNumber}`, {
200
233
  checkpointNumber,
201
234
  msgCount: l1ToL2Messages.length,
202
235
  initialStateReference: stateReference.toInspect(),
203
236
  initialArchiveRoot: bufferToHex(archiveTree.root),
204
237
  constants,
238
+ feeAssetPriceModifier,
205
239
  });
206
240
 
207
241
  const lightweightBuilder = await LightweightCheckpointBuilder.startNewCheckpoint(
208
242
  checkpointNumber,
209
243
  constants,
210
244
  l1ToL2Messages,
245
+ previousCheckpointOutHashes,
211
246
  fork,
247
+ bindings,
248
+ feeAssetPriceModifier,
212
249
  );
213
250
 
214
251
  return new CheckpointBuilder(
@@ -218,6 +255,8 @@ export class FullNodeCheckpointsBuilder {
218
255
  this.contractDataSource,
219
256
  this.dateProvider,
220
257
  this.telemetryClient,
258
+ bindings,
259
+ this.debugLogStore,
221
260
  );
222
261
  }
223
262
 
@@ -227,32 +266,47 @@ export class FullNodeCheckpointsBuilder {
227
266
  async openCheckpoint(
228
267
  checkpointNumber: CheckpointNumber,
229
268
  constants: CheckpointGlobalVariables,
269
+ feeAssetPriceModifier: bigint,
230
270
  l1ToL2Messages: Fr[],
271
+ previousCheckpointOutHashes: Fr[],
231
272
  fork: MerkleTreeWriteOperations,
232
- existingBlocks: L2BlockNew[] = [],
273
+ existingBlocks: L2Block[] = [],
274
+ bindings?: LoggerBindings,
233
275
  ): Promise<CheckpointBuilder> {
234
276
  const stateReference = await fork.getStateReference();
235
277
  const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
236
278
 
237
279
  if (existingBlocks.length === 0) {
238
- return this.startCheckpoint(checkpointNumber, constants, l1ToL2Messages, fork);
280
+ return this.startCheckpoint(
281
+ checkpointNumber,
282
+ constants,
283
+ feeAssetPriceModifier,
284
+ l1ToL2Messages,
285
+ previousCheckpointOutHashes,
286
+ fork,
287
+ bindings,
288
+ );
239
289
  }
240
290
 
241
- log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
291
+ this.log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
242
292
  checkpointNumber,
243
293
  msgCount: l1ToL2Messages.length,
244
294
  existingBlockCount: existingBlocks.length,
245
295
  initialStateReference: stateReference.toInspect(),
246
296
  initialArchiveRoot: bufferToHex(archiveTree.root),
247
297
  constants,
298
+ feeAssetPriceModifier,
248
299
  });
249
300
 
250
301
  const lightweightBuilder = await LightweightCheckpointBuilder.resumeCheckpoint(
251
302
  checkpointNumber,
252
303
  constants,
304
+ feeAssetPriceModifier,
253
305
  l1ToL2Messages,
306
+ previousCheckpointOutHashes,
254
307
  fork,
255
308
  existingBlocks,
309
+ bindings,
256
310
  );
257
311
 
258
312
  return new CheckpointBuilder(
@@ -262,6 +316,13 @@ export class FullNodeCheckpointsBuilder {
262
316
  this.contractDataSource,
263
317
  this.dateProvider,
264
318
  this.telemetryClient,
319
+ bindings,
320
+ this.debugLogStore,
265
321
  );
266
322
  }
323
+
324
+ /** Returns a fork of the world state at the given block number. */
325
+ getFork(blockNumber: BlockNumber): Promise<MerkleTreeWriteOperations> {
326
+ return this.worldState.fork(blockNumber);
327
+ }
267
328
  }
package/src/config.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  secretValueConfigHelper,
7
7
  } from '@aztec/foundation/config';
8
8
  import { EthAddress } from '@aztec/foundation/eth-address';
9
+ import { validatorHASignerConfigMappings } from '@aztec/stdlib/ha-signing';
9
10
  import type { ValidatorClientConfig } from '@aztec/stdlib/interfaces/server';
10
11
 
11
12
  export type { ValidatorClientConfig };
@@ -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
  /**