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

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 (50) hide show
  1. package/README.md +22 -19
  2. package/dest/block_proposal_handler.d.ts +6 -8
  3. package/dest/block_proposal_handler.d.ts.map +1 -1
  4. package/dest/block_proposal_handler.js +28 -57
  5. package/dest/checkpoint_builder.d.ts +15 -13
  6. package/dest/checkpoint_builder.d.ts.map +1 -1
  7. package/dest/checkpoint_builder.js +55 -32
  8. package/dest/config.d.ts +1 -1
  9. package/dest/config.d.ts.map +1 -1
  10. package/dest/config.js +9 -7
  11. package/dest/duties/validation_service.d.ts +2 -2
  12. package/dest/duties/validation_service.d.ts.map +1 -1
  13. package/dest/duties/validation_service.js +3 -3
  14. package/dest/factory.d.ts +1 -1
  15. package/dest/factory.d.ts.map +1 -1
  16. package/dest/factory.js +2 -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 +1 -1
  21. package/dest/key_store/ha_key_store.d.ts.map +1 -1
  22. package/dest/key_store/ha_key_store.js +2 -2
  23. package/dest/metrics.d.ts +12 -3
  24. package/dest/metrics.d.ts.map +1 -1
  25. package/dest/metrics.js +46 -5
  26. package/dest/validator.d.ts +40 -14
  27. package/dest/validator.d.ts.map +1 -1
  28. package/dest/validator.js +196 -52
  29. package/package.json +19 -17
  30. package/src/block_proposal_handler.ts +40 -81
  31. package/src/checkpoint_builder.ts +80 -33
  32. package/src/config.ts +9 -7
  33. package/src/duties/validation_service.ts +9 -2
  34. package/src/factory.ts +1 -0
  35. package/src/index.ts +0 -1
  36. package/src/key_store/ha_key_store.ts +2 -2
  37. package/src/metrics.ts +63 -6
  38. package/src/validator.ts +253 -65
  39. package/dest/tx_validator/index.d.ts +0 -3
  40. package/dest/tx_validator/index.d.ts.map +0 -1
  41. package/dest/tx_validator/index.js +0 -2
  42. package/dest/tx_validator/nullifier_cache.d.ts +0 -14
  43. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  44. package/dest/tx_validator/nullifier_cache.js +0 -24
  45. package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
  46. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
  47. package/dest/tx_validator/tx_validator_factory.js +0 -53
  48. package/src/tx_validator/index.ts +0 -2
  49. package/src/tx_validator/nullifier_cache.ts +0 -30
  50. package/src/tx_validator/tx_validator_factory.ts +0 -133
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/validator-client",
3
- "version": "0.0.1-commit.f295ac2",
3
+ "version": "0.0.1-commit.f504929",
4
4
  "main": "dest/index.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -64,28 +64,30 @@
64
64
  ]
65
65
  },
66
66
  "dependencies": {
67
- "@aztec/blob-client": "0.0.1-commit.f295ac2",
68
- "@aztec/blob-lib": "0.0.1-commit.f295ac2",
69
- "@aztec/constants": "0.0.1-commit.f295ac2",
70
- "@aztec/epoch-cache": "0.0.1-commit.f295ac2",
71
- "@aztec/ethereum": "0.0.1-commit.f295ac2",
72
- "@aztec/foundation": "0.0.1-commit.f295ac2",
73
- "@aztec/node-keystore": "0.0.1-commit.f295ac2",
74
- "@aztec/noir-protocol-circuits-types": "0.0.1-commit.f295ac2",
75
- "@aztec/p2p": "0.0.1-commit.f295ac2",
76
- "@aztec/protocol-contracts": "0.0.1-commit.f295ac2",
77
- "@aztec/prover-client": "0.0.1-commit.f295ac2",
78
- "@aztec/simulator": "0.0.1-commit.f295ac2",
79
- "@aztec/slasher": "0.0.1-commit.f295ac2",
80
- "@aztec/stdlib": "0.0.1-commit.f295ac2",
81
- "@aztec/telemetry-client": "0.0.1-commit.f295ac2",
82
- "@aztec/validator-ha-signer": "0.0.1-commit.f295ac2",
67
+ "@aztec/blob-client": "0.0.1-commit.f504929",
68
+ "@aztec/blob-lib": "0.0.1-commit.f504929",
69
+ "@aztec/constants": "0.0.1-commit.f504929",
70
+ "@aztec/epoch-cache": "0.0.1-commit.f504929",
71
+ "@aztec/ethereum": "0.0.1-commit.f504929",
72
+ "@aztec/foundation": "0.0.1-commit.f504929",
73
+ "@aztec/node-keystore": "0.0.1-commit.f504929",
74
+ "@aztec/noir-protocol-circuits-types": "0.0.1-commit.f504929",
75
+ "@aztec/p2p": "0.0.1-commit.f504929",
76
+ "@aztec/protocol-contracts": "0.0.1-commit.f504929",
77
+ "@aztec/prover-client": "0.0.1-commit.f504929",
78
+ "@aztec/simulator": "0.0.1-commit.f504929",
79
+ "@aztec/slasher": "0.0.1-commit.f504929",
80
+ "@aztec/stdlib": "0.0.1-commit.f504929",
81
+ "@aztec/telemetry-client": "0.0.1-commit.f504929",
82
+ "@aztec/validator-ha-signer": "0.0.1-commit.f504929",
83
83
  "koa": "^2.16.1",
84
84
  "koa-router": "^13.1.1",
85
85
  "tslib": "^2.4.0",
86
86
  "viem": "npm:@aztec/viem@2.38.2"
87
87
  },
88
88
  "devDependencies": {
89
+ "@aztec/archiver": "0.0.1-commit.f504929",
90
+ "@aztec/world-state": "0.0.1-commit.f504929",
89
91
  "@electric-sql/pglite": "^0.3.14",
90
92
  "@jest/globals": "^30.0.0",
91
93
  "@types/jest": "^30.0.0",
@@ -7,18 +7,13 @@ import { createLogger } from '@aztec/foundation/log';
7
7
  import { retryUntil } from '@aztec/foundation/retry';
8
8
  import { DateProvider, Timer } from '@aztec/foundation/timer';
9
9
  import type { P2P, PeerId } from '@aztec/p2p';
10
- import { TxProvider } from '@aztec/p2p';
11
10
  import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
12
- import type { L2BlockNew, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
11
+ import type { BlockData, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
13
12
  import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
14
- import type { ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
15
- import {
16
- type L1ToL2MessageSource,
17
- computeCheckpointOutHash,
18
- computeInHashFromL1ToL2Messages,
19
- } from '@aztec/stdlib/messaging';
13
+ import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
14
+ import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
20
15
  import type { BlockProposal } from '@aztec/stdlib/p2p';
21
- import { BlockHeader, type CheckpointGlobalVariables, type FailedTx, type Tx } from '@aztec/stdlib/tx';
16
+ import type { CheckpointGlobalVariables, FailedTx, Tx } from '@aztec/stdlib/tx';
22
17
  import {
23
18
  ReExFailedTxsError,
24
19
  ReExStateMismatchError,
@@ -44,7 +39,7 @@ export type BlockProposalValidationFailureReason =
44
39
  | 'unknown_error';
45
40
 
46
41
  type ReexecuteTransactionsResult = {
47
- block: L2BlockNew;
42
+ block: L2Block;
48
43
  failedTxs: FailedTx[];
49
44
  reexecutionTimeMs: number;
50
45
  totalManaUsed: number;
@@ -77,7 +72,7 @@ export class BlockProposalHandler {
77
72
  private worldState: WorldStateSynchronizer,
78
73
  private blockSource: L2BlockSource & L2BlockSink,
79
74
  private l1ToL2MessageSource: L1ToL2MessageSource,
80
- private txProvider: TxProvider,
75
+ private txProvider: ITxProvider,
81
76
  private blockProposalValidator: BlockProposalValidator,
82
77
  private epochCache: EpochCache,
83
78
  private config: ValidatorClientFullConfig,
@@ -146,23 +141,23 @@ export class BlockProposalHandler {
146
141
 
147
142
  // Check that the proposal is from the current proposer, or the next proposer
148
143
  // This should have been handled by the p2p layer, but we double check here out of caution
149
- const invalidProposal = await this.blockProposalValidator.validate(proposal);
150
- if (invalidProposal) {
144
+ const validationResult = await this.blockProposalValidator.validate(proposal);
145
+ if (validationResult.result !== 'accept') {
151
146
  this.log.warn(`Proposal is not valid, skipping processing`, proposalInfo);
152
147
  return { isValid: false, reason: 'invalid_proposal' };
153
148
  }
154
149
 
155
150
  // Check that the parent proposal is a block we know, otherwise reexecution would fail
156
- const parentBlockHeader = await this.getParentBlock(proposal);
157
- if (parentBlockHeader === undefined) {
151
+ const parentBlock = await this.getParentBlock(proposal);
152
+ if (parentBlock === undefined) {
158
153
  this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
159
154
  return { isValid: false, reason: 'parent_block_not_found' };
160
155
  }
161
156
 
162
- // Check that the parent block's slot is less than the proposal's slot (should not happen, but we check anyway)
163
- if (parentBlockHeader !== 'genesis' && parentBlockHeader.getSlot() >= slotNumber) {
164
- this.log.warn(`Parent block slot is greater than or equal to proposal slot, skipping processing`, {
165
- 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(),
166
161
  proposalSlot: slotNumber.toString(),
167
162
  ...proposalInfo,
168
163
  });
@@ -171,9 +166,9 @@ export class BlockProposalHandler {
171
166
 
172
167
  // Compute the block number based on the parent block
173
168
  const blockNumber =
174
- parentBlockHeader === 'genesis'
169
+ parentBlock === 'genesis'
175
170
  ? BlockNumber(INITIAL_L2_BLOCK_NUM)
176
- : BlockNumber(parentBlockHeader.getBlockNumber() + 1);
171
+ : BlockNumber(parentBlock.header.getBlockNumber() + 1);
177
172
 
178
173
  // Check that this block number does not exist already
179
174
  const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
@@ -190,7 +185,7 @@ export class BlockProposalHandler {
190
185
  });
191
186
 
192
187
  // Compute the checkpoint number for this block and validate checkpoint consistency
193
- const checkpointResult = await this.computeCheckpointNumber(proposal, parentBlockHeader, proposalInfo);
188
+ const checkpointResult = this.computeCheckpointNumber(proposal, parentBlock, proposalInfo);
194
189
  if (checkpointResult.reason) {
195
190
  return { isValid: false, blockNumber, reason: checkpointResult.reason };
196
191
  }
@@ -218,17 +213,11 @@ export class BlockProposalHandler {
218
213
  // Try re-executing the transactions in the proposal if needed
219
214
  let reexecutionResult;
220
215
  if (shouldReexecute) {
221
- // Compute the previous checkpoint out hashes for the epoch.
222
- // TODO(mbps): This assumes one block per checkpoint, which is only true for now.
223
- // TODO: There can be a more efficient way to get the previous checkpoint out hashes without having to fetch all
224
- // the blocks.
216
+ // Collect the out hashes of all the checkpoints before this one in the same epoch
225
217
  const epoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
226
- const previousBlocks = (await this.blockSource.getBlocksForEpoch(epoch))
227
- .filter(b => b.number < blockNumber)
228
- .sort((a, b) => a.number - b.number);
229
- const previousCheckpointOutHashes = previousBlocks.map(b =>
230
- computeCheckpointOutHash([b.body.txEffects.map(tx => tx.l2ToL1Msgs)]),
231
- );
218
+ const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
219
+ .filter(c => c.checkpointNumber < checkpointNumber)
220
+ .map(c => c.checkpointOutHash);
232
221
 
233
222
  try {
234
223
  this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
@@ -248,7 +237,6 @@ export class BlockProposalHandler {
248
237
  }
249
238
 
250
239
  // If we succeeded, push this block into the archiver (unless disabled)
251
- // TODO(palla/mbps): Change default to false once block sync is stable.
252
240
  if (reexecutionResult?.block && this.config.skipPushProposedBlocksToArchiver === false) {
253
241
  await this.blockSource.addBlock(reexecutionResult?.block);
254
242
  }
@@ -261,7 +249,7 @@ export class BlockProposalHandler {
261
249
  return { isValid: true, blockNumber, reexecutionResult };
262
250
  }
263
251
 
264
- private async getParentBlock(proposal: BlockProposal): Promise<'genesis' | BlockHeader | undefined> {
252
+ private async getParentBlock(proposal: BlockProposal): Promise<'genesis' | BlockData | undefined> {
265
253
  const parentArchive = proposal.blockHeader.lastArchive.root;
266
254
  const slot = proposal.slotNumber;
267
255
  const config = this.checkpointsBuilder.getConfig();
@@ -277,12 +265,11 @@ export class BlockProposalHandler {
277
265
 
278
266
  try {
279
267
  return (
280
- (await this.blockSource.getBlockHeaderByArchive(parentArchive)) ??
268
+ (await this.blockSource.getBlockDataByArchive(parentArchive)) ??
281
269
  (timeoutDurationMs <= 0
282
270
  ? undefined
283
271
  : await retryUntil(
284
- () =>
285
- this.blockSource.syncImmediate().then(() => this.blockSource.getBlockHeaderByArchive(parentArchive)),
272
+ () => this.blockSource.syncImmediate().then(() => this.blockSource.getBlockDataByArchive(parentArchive)),
286
273
  'force archiver sync',
287
274
  timeoutDurationMs / 1000,
288
275
  0.5,
@@ -298,12 +285,12 @@ export class BlockProposalHandler {
298
285
  }
299
286
  }
300
287
 
301
- private async computeCheckpointNumber(
288
+ private computeCheckpointNumber(
302
289
  proposal: BlockProposal,
303
- parentBlockHeader: 'genesis' | BlockHeader,
290
+ parentBlock: 'genesis' | BlockData,
304
291
  proposalInfo: object,
305
- ): Promise<CheckpointComputationResult> {
306
- if (parentBlockHeader === 'genesis') {
292
+ ): CheckpointComputationResult {
293
+ if (parentBlock === 'genesis') {
307
294
  // First block is in checkpoint 1
308
295
  if (proposal.indexWithinCheckpoint !== 0) {
309
296
  this.log.warn(`First block proposal has non-zero indexWithinCheckpoint`, proposalInfo);
@@ -312,19 +299,9 @@ export class BlockProposalHandler {
312
299
  return { checkpointNumber: CheckpointNumber.INITIAL };
313
300
  }
314
301
 
315
- // Get the parent block to find its checkpoint number
316
- // TODO(palla/mbps): The block header should include the checkpoint number to avoid this lookup,
317
- // or at least the L2BlockSource should return a different struct that includes it.
318
- const parentBlockNumber = parentBlockHeader.getBlockNumber();
319
- const parentBlock = await this.blockSource.getL2BlockNew(parentBlockNumber);
320
- if (!parentBlock) {
321
- this.log.warn(`Parent block ${parentBlockNumber} not found in archiver`, proposalInfo);
322
- return { reason: 'invalid_proposal' };
323
- }
324
-
325
302
  if (proposal.indexWithinCheckpoint === 0) {
326
303
  // If this is the first block in a new checkpoint, increment the checkpoint number
327
- if (!(proposal.blockHeader.getSlot() > parentBlockHeader.getSlot())) {
304
+ if (!(proposal.blockHeader.getSlot() > parentBlock.header.getSlot())) {
328
305
  this.log.warn(`Slot should be greater than parent block slot for first block in checkpoint`, proposalInfo);
329
306
  return { reason: 'invalid_proposal' };
330
307
  }
@@ -336,7 +313,7 @@ export class BlockProposalHandler {
336
313
  this.log.warn(`Non-sequential indexWithinCheckpoint`, proposalInfo);
337
314
  return { reason: 'invalid_proposal' };
338
315
  }
339
- if (proposal.blockHeader.getSlot() !== parentBlockHeader.getSlot()) {
316
+ if (proposal.blockHeader.getSlot() !== parentBlock.header.getSlot()) {
340
317
  this.log.warn(`Slot should be equal to parent block slot for non-first block in checkpoint`, proposalInfo);
341
318
  return { reason: 'invalid_proposal' };
342
319
  }
@@ -357,7 +334,7 @@ export class BlockProposalHandler {
357
334
  */
358
335
  private validateNonFirstBlockInCheckpoint(
359
336
  proposal: BlockProposal,
360
- parentBlock: L2BlockNew,
337
+ parentBlock: BlockData,
361
338
  proposalInfo: object,
362
339
  ): CheckpointComputationResult | undefined {
363
340
  const proposalGlobals = proposal.blockHeader.globalVariables;
@@ -436,29 +413,6 @@ export class BlockProposalHandler {
436
413
  return new Date(nextSlotTimestampSeconds * 1000);
437
414
  }
438
415
 
439
- /**
440
- * Gets all prior blocks in the same checkpoint (same slot and checkpoint number) up to but not including upToBlockNumber.
441
- */
442
- private async getBlocksInCheckpoint(
443
- slot: SlotNumber,
444
- upToBlockNumber: BlockNumber,
445
- checkpointNumber: CheckpointNumber,
446
- ): Promise<L2BlockNew[]> {
447
- const blocks: L2BlockNew[] = [];
448
- let currentBlockNumber = BlockNumber(upToBlockNumber - 1);
449
-
450
- while (currentBlockNumber >= INITIAL_L2_BLOCK_NUM) {
451
- const block = await this.blockSource.getL2BlockNew(currentBlockNumber);
452
- if (!block || block.header.getSlot() !== slot || block.checkpointNumber !== checkpointNumber) {
453
- break;
454
- }
455
- blocks.unshift(block);
456
- currentBlockNumber = BlockNumber(currentBlockNumber - 1);
457
- }
458
-
459
- return blocks;
460
- }
461
-
462
416
  private getReexecuteFailureReason(err: any) {
463
417
  if (err instanceof ReExStateMismatchError) {
464
418
  return 'state_mismatch';
@@ -492,18 +446,21 @@ export class BlockProposalHandler {
492
446
  const slot = proposal.slotNumber;
493
447
  const config = this.checkpointsBuilder.getConfig();
494
448
 
495
- // Get prior blocks in this checkpoint (same slot and checkpoint number)
496
- 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);
497
452
 
498
453
  // Fork before the block to be built
499
454
  const parentBlockNumber = BlockNumber(blockNumber - 1);
500
- using fork = await this.worldState.fork(parentBlockNumber);
455
+ await this.worldState.syncImmediate(parentBlockNumber);
456
+ await using fork = await this.worldState.fork(parentBlockNumber);
501
457
 
502
- // 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)
503
459
  const constants: CheckpointGlobalVariables = {
504
460
  chainId: new Fr(config.l1ChainId),
505
461
  version: new Fr(config.rollupVersion),
506
462
  slotNumber: slot,
463
+ timestamp: blockHeader.globalVariables.timestamp,
507
464
  coinbase: blockHeader.globalVariables.coinbase,
508
465
  feeRecipient: blockHeader.globalVariables.feeRecipient,
509
466
  gasFees: blockHeader.globalVariables.gasFees,
@@ -513,10 +470,12 @@ export class BlockProposalHandler {
513
470
  const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
514
471
  checkpointNumber,
515
472
  constants,
473
+ 0n, // only takes effect in the following checkpoint.
516
474
  l1ToL2Messages,
517
475
  previousCheckpointOutHashes,
518
476
  fork,
519
477
  priorBlocks,
478
+ this.log.getBindings(),
520
479
  );
521
480
 
522
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,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,30 +24,25 @@ 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';
31
+ import { type DebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
30
32
  import { MerkleTreeId } from '@aztec/stdlib/trees';
31
33
  import { type CheckpointGlobalVariables, GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
32
34
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
33
35
 
34
- import { createValidatorForBlockBuilding } from './tx_validator/tx_validator_factory.js';
35
-
36
36
  // Re-export for backward compatibility
37
37
  export type { BuildBlockInCheckpointResult } from '@aztec/stdlib/interfaces/server';
38
38
 
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
39
  /**
47
40
  * Builder for a single checkpoint. Handles building blocks within the checkpoint
48
41
  * and completing it.
49
42
  */
50
43
  export class CheckpointBuilder implements ICheckpointBlockBuilder {
44
+ private log: Logger;
45
+
51
46
  constructor(
52
47
  private checkpointBuilder: LightweightCheckpointBuilder,
53
48
  private fork: MerkleTreeWriteOperations,
@@ -55,7 +50,14 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
55
50
  private contractDataSource: ContractDataSource,
56
51
  private dateProvider: DateProvider,
57
52
  private telemetryClient: TelemetryClient,
58
- ) {}
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
+ }
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,33 +98,42 @@ 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
- const block = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
108
+ const { block } = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
102
109
  expectedEndState: opts.expectedEndState,
103
110
  });
104
111
 
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(),
@@ -138,15 +148,23 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
138
148
  }
139
149
 
140
150
  protected async makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations) {
141
- const txPublicSetupAllowList = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
142
- const contractsDB = new PublicContractsDB(this.contractDataSource);
151
+ const txPublicSetupAllowList = [
152
+ ...(await getDefaultAllowedSetupFunctions()),
153
+ ...(this.config.txPublicSetupAllowListExtend ?? []),
154
+ ];
155
+ const contractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings());
143
156
  const guardedFork = new GuardedMerkleTreeOperations(fork);
144
157
 
158
+ const collectDebugLogs = this.debugLogStore.isEnabled;
159
+
160
+ const bindings = this.log.getBindings();
145
161
  const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
146
162
  guardedFork,
147
163
  contractsDB,
148
164
  globalVariables,
149
165
  this.telemetryClient,
166
+ bindings,
167
+ collectDebugLogs,
150
168
  );
151
169
 
152
170
  const processor = new PublicProcessor(
@@ -156,15 +174,17 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
156
174
  publicTxSimulator,
157
175
  this.dateProvider,
158
176
  this.telemetryClient,
159
- undefined,
177
+ createLogger('simulator:public-processor', bindings),
160
178
  this.config,
179
+ this.debugLogStore,
161
180
  );
162
181
 
163
- const validator = createValidatorForBlockBuilding(
182
+ const validator = createTxValidatorForBlockBuilding(
164
183
  fork,
165
184
  this.contractDataSource,
166
185
  globalVariables,
167
186
  txPublicSetupAllowList,
187
+ this.log.getBindings(),
168
188
  );
169
189
 
170
190
  return {
@@ -176,13 +196,18 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
176
196
 
177
197
  /** Factory for creating checkpoint builders. */
178
198
  export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
199
+ private log: Logger;
200
+
179
201
  constructor(
180
202
  private config: FullNodeBlockBuilderConfig & Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>,
181
203
  private worldState: WorldStateSynchronizer,
182
204
  private contractDataSource: ContractDataSource,
183
205
  private dateProvider: DateProvider,
184
206
  private telemetryClient: TelemetryClient = getTelemetryClient(),
185
- ) {}
207
+ private debugLogStore: DebugLogStore = new NullDebugLogStore(),
208
+ ) {
209
+ this.log = createLogger('checkpoint-builder');
210
+ }
186
211
 
187
212
  public getConfig(): FullNodeBlockBuilderConfig {
188
213
  return this.config;
@@ -198,19 +223,22 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
198
223
  async startCheckpoint(
199
224
  checkpointNumber: CheckpointNumber,
200
225
  constants: CheckpointGlobalVariables,
226
+ feeAssetPriceModifier: bigint,
201
227
  l1ToL2Messages: Fr[],
202
228
  previousCheckpointOutHashes: Fr[],
203
229
  fork: MerkleTreeWriteOperations,
230
+ bindings?: LoggerBindings,
204
231
  ): Promise<CheckpointBuilder> {
205
232
  const stateReference = await fork.getStateReference();
206
233
  const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
207
234
 
208
- log.verbose(`Building new checkpoint ${checkpointNumber}`, {
235
+ this.log.verbose(`Building new checkpoint ${checkpointNumber}`, {
209
236
  checkpointNumber,
210
237
  msgCount: l1ToL2Messages.length,
211
238
  initialStateReference: stateReference.toInspect(),
212
239
  initialArchiveRoot: bufferToHex(archiveTree.root),
213
240
  constants,
241
+ feeAssetPriceModifier,
214
242
  });
215
243
 
216
244
  const lightweightBuilder = await LightweightCheckpointBuilder.startNewCheckpoint(
@@ -219,6 +247,8 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
219
247
  l1ToL2Messages,
220
248
  previousCheckpointOutHashes,
221
249
  fork,
250
+ bindings,
251
+ feeAssetPriceModifier,
222
252
  );
223
253
 
224
254
  return new CheckpointBuilder(
@@ -228,6 +258,8 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
228
258
  this.contractDataSource,
229
259
  this.dateProvider,
230
260
  this.telemetryClient,
261
+ bindings,
262
+ this.debugLogStore,
231
263
  );
232
264
  }
233
265
 
@@ -237,34 +269,47 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
237
269
  async openCheckpoint(
238
270
  checkpointNumber: CheckpointNumber,
239
271
  constants: CheckpointGlobalVariables,
272
+ feeAssetPriceModifier: bigint,
240
273
  l1ToL2Messages: Fr[],
241
274
  previousCheckpointOutHashes: Fr[],
242
275
  fork: MerkleTreeWriteOperations,
243
- existingBlocks: L2BlockNew[] = [],
276
+ existingBlocks: L2Block[] = [],
277
+ bindings?: LoggerBindings,
244
278
  ): Promise<CheckpointBuilder> {
245
279
  const stateReference = await fork.getStateReference();
246
280
  const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
247
281
 
248
282
  if (existingBlocks.length === 0) {
249
- return this.startCheckpoint(checkpointNumber, constants, l1ToL2Messages, previousCheckpointOutHashes, fork);
283
+ return this.startCheckpoint(
284
+ checkpointNumber,
285
+ constants,
286
+ feeAssetPriceModifier,
287
+ l1ToL2Messages,
288
+ previousCheckpointOutHashes,
289
+ fork,
290
+ bindings,
291
+ );
250
292
  }
251
293
 
252
- log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
294
+ this.log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
253
295
  checkpointNumber,
254
296
  msgCount: l1ToL2Messages.length,
255
297
  existingBlockCount: existingBlocks.length,
256
298
  initialStateReference: stateReference.toInspect(),
257
299
  initialArchiveRoot: bufferToHex(archiveTree.root),
258
300
  constants,
301
+ feeAssetPriceModifier,
259
302
  });
260
303
 
261
304
  const lightweightBuilder = await LightweightCheckpointBuilder.resumeCheckpoint(
262
305
  checkpointNumber,
263
306
  constants,
307
+ feeAssetPriceModifier,
264
308
  l1ToL2Messages,
265
309
  previousCheckpointOutHashes,
266
310
  fork,
267
311
  existingBlocks,
312
+ bindings,
268
313
  );
269
314
 
270
315
  return new CheckpointBuilder(
@@ -274,6 +319,8 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
274
319
  this.contractDataSource,
275
320
  this.dateProvider,
276
321
  this.telemetryClient,
322
+ bindings,
323
+ this.debugLogStore,
277
324
  );
278
325
  }
279
326
 
package/src/config.ts CHANGED
@@ -6,8 +6,8 @@ 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
- import { validatorHASignerConfigMappings } from '@aztec/validator-ha-signer/config';
11
11
 
12
12
  export type { ValidatorClientConfig };
13
13
 
@@ -65,15 +65,17 @@ 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,
75
+ },
76
+ attestToEquivocatedProposals: {
77
+ description: 'Agree to attest to equivocated checkpoint proposals (for testing purposes only)',
78
+ ...booleanConfigHelper(false),
77
79
  },
78
80
  ...validatorHASignerConfigMappings,
79
81
  };