@aztec/validator-client 5.0.0-nightly.20260316 → 5.0.0-nightly.20260317

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.
package/README.md CHANGED
@@ -239,11 +239,11 @@ L1 enforces gas and blob capacity per checkpoint. The node enforces these during
239
239
 
240
240
  Per-block budgets prevent one block from consuming the entire checkpoint budget.
241
241
 
242
- **Proposer**: `computeBlockLimits()` derives budgets at startup as `min(checkpointLimit, ceil(checkpointLimit / maxBlocks * multiplier))`, where `maxBlocks` comes from the timetable and `multiplier` defaults to 2. The multiplier greater than 1 allows early blocks to use more than their even share of the checkpoint budget, since different blocks hit different limit dimensions (L2 gas, DA gas, blob fields) — a strict even split would waste capacity. Operators can override via `SEQ_MAX_L2_BLOCK_GAS` / `SEQ_MAX_DA_BLOCK_GAS` / `SEQ_MAX_TX_PER_BLOCK` (capped at checkpoint limits). Per-block TX limits follow the same derivation pattern when `SEQ_MAX_TX_PER_CHECKPOINT` is set.
242
+ **Proposer**: `computeBlockLimits()` derives budgets at startup as `min(checkpointLimit, ceil(checkpointLimit / maxBlocks * multiplier))`, where `maxBlocks` comes from the timetable and `multiplier` defaults to 1.2. The multiplier greater than 1 allows early blocks to use more than their even share of the checkpoint budget, since different blocks hit different limit dimensions (L2 gas, DA gas, blob fields) — a strict even split would waste capacity. Operators can override via `SEQ_MAX_L2_BLOCK_GAS` / `SEQ_MAX_DA_BLOCK_GAS` / `SEQ_MAX_TX_PER_BLOCK` (capped at checkpoint limits). Per-block TX limits follow the same derivation pattern when `SEQ_MAX_TX_PER_CHECKPOINT` is set.
243
243
 
244
244
  **Validator**: Optionally enforces per-block limits via `VALIDATOR_MAX_L2_BLOCK_GAS`, `VALIDATOR_MAX_DA_BLOCK_GAS`, and `VALIDATOR_MAX_TX_PER_BLOCK`. When set, these are passed to `buildBlock` during re-execution and to `validateCheckpoint` for final validation. When unset, no per-block limit is enforced for that dimension (checkpoint-level protocol limits still apply). These are independent of the `SEQ_` vars so operators can tune proposer and validation limits separately.
245
245
 
246
- **Checkpoint-level capping**: `CheckpointBuilder.capLimitsByCheckpointBudgets()` always runs before tx processing, capping per-block limits by `checkpointBudget - sum(used by prior blocks)` for all three gas dimensions and for transaction count (when `SEQ_MAX_TX_PER_CHECKPOINT` is set). This applies to both proposer and validator paths.
246
+ **Checkpoint-level capping**: `CheckpointBuilder.capLimitsByCheckpointBudgets()` always runs before tx processing, capping per-block limits by the remaining checkpoint budget. When `SEQ_REDISTRIBUTE_CHECKPOINT_BUDGET` is enabled (default: true), the remaining budget is distributed evenly across remaining blocks with the multiplier applied: `min(perBlockLimit, ceil(remainingBudget / remainingBlocks * multiplier), remainingBudget)`. This prevents early blocks from consuming the entire checkpoint budget, producing smoother distribution. When disabled, each block can consume up to the full remaining budget, ie caps by `checkpointBudget - sum(used by prior blocks)`. This applies to all four dimensions (L2 gas, DA gas, blob fields, transaction count). Validators always cap by the total remaining.
247
247
 
248
248
  ### Per-transaction enforcement
249
249
 
@@ -259,7 +259,8 @@ Per-block budgets prevent one block from consuming the entire checkpoint budget.
259
259
  | `SEQ_MAX_DA_BLOCK_GAS` | *auto* | Per-block DA gas. Auto-derived from checkpoint DA limit / maxBlocks * multiplier. |
260
260
  | `SEQ_MAX_TX_PER_BLOCK` | *none* | Per-block tx count. If `SEQ_MAX_TX_PER_CHECKPOINT` is set and per-block is not, derived as `ceil(checkpointLimit / maxBlocks * multiplier)`. |
261
261
  | `SEQ_MAX_TX_PER_CHECKPOINT` | *none* | Total txs across all blocks in a checkpoint. When set, per-block tx limit is derived from it (unless explicitly overridden) and checkpoint-level capping is enforced. |
262
- | `SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER` | 2 | Multiplier for per-block budget computation. |
262
+ | `SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER` | 1.2 | Multiplier for per-block budget computation. |
263
+ | `SEQ_REDISTRIBUTE_CHECKPOINT_BUDGET` | true | Redistribute remaining checkpoint budget evenly across remaining blocks instead of allowing one block to consume it all. |
263
264
  | `VALIDATOR_MAX_L2_BLOCK_GAS` | *none* | Per-block L2 gas limit for validation. Proposals exceeding this are rejected. |
264
265
  | `VALIDATOR_MAX_DA_BLOCK_GAS` | *none* | Per-block DA gas limit for validation. Proposals exceeding this are rejected. |
265
266
  | `VALIDATOR_MAX_TX_PER_BLOCK` | *none* | Per-block tx count limit for validation. Proposals exceeding this are rejected. |
@@ -34,6 +34,7 @@ export declare class CheckpointBuilder implements ICheckpointBlockBuilder {
34
34
  */
35
35
  buildBlock(pendingTxs: Iterable<Tx> | AsyncIterable<Tx>, blockNumber: BlockNumber, timestamp: bigint, opts?: PublicProcessorLimits & {
36
36
  expectedEndState?: StateReference;
37
+ minValidTxs?: number;
37
38
  }): Promise<BuildBlockInCheckpointResult>;
38
39
  /** Completes the checkpoint and returns it. */
39
40
  completeCheckpoint(): Promise<Checkpoint>;
@@ -73,4 +74,4 @@ export declare class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
73
74
  /** Returns a fork of the world state at the given block number. */
74
75
  getFork(blockNumber: BlockNumber): Promise<MerkleTreeWriteOperations>;
75
76
  }
76
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2twb2ludF9idWlsZGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2hlY2twb2ludF9idWlsZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVoRixPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDcEQsT0FBTyxFQUFlLEtBQUssY0FBYyxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBRXZGLE9BQU8sRUFBRSxZQUFZLEVBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUVoRSxPQUFPLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUMxRSxPQUFPLEVBR0wsZUFBZSxFQUVoQixNQUFNLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM5QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdEQsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNqRSxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXJFLE9BQU8sRUFDTCxLQUFLLDRCQUE0QixFQUNqQyxLQUFLLDBCQUEwQixFQUUvQixLQUFLLHVCQUF1QixFQUM1QixLQUFLLG1CQUFtQixFQUN4QixLQUFLLHlCQUF5QixFQUU5QixLQUFLLHFCQUFxQixFQUMxQixLQUFLLHNCQUFzQixFQUM1QixNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxLQUFLLGFBQWEsRUFBcUIsTUFBTSxvQkFBb0IsQ0FBQztBQUUzRSxPQUFPLEVBQUUsS0FBSyx5QkFBeUIsRUFBRSxlQUFlLEVBQUUsY0FBYyxFQUFFLEVBQUUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3ZHLE9BQU8sRUFBRSxLQUFL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
77
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2twb2ludF9idWlsZGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2hlY2twb2ludF9idWlsZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVoRixPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDcEQsT0FBTyxFQUFlLEtBQUssY0FBYyxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBRXZGLE9BQU8sRUFBRSxZQUFZLEVBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUVoRSxPQUFPLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUMxRSxPQUFPLEVBR0wsZUFBZSxFQUVoQixNQUFNLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM5QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdEQsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNqRSxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXJFLE9BQU8sRUFDTCxLQUFLLDRCQUE0QixFQUNqQyxLQUFLLDBCQUEwQixFQUUvQixLQUFLLHVCQUF1QixFQUM1QixLQUFLLG1CQUFtQixFQUV4QixLQUFLLHlCQUF5QixFQUM5QixLQUFLLHFCQUFxQixFQUMxQixLQUFLLHNCQUFzQixFQUM1QixNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxLQUFLLGFBQWEsRUFBcUIsTUFBTSxvQkFBb0IsQ0FBQztBQUUzRSxPQUFPLEVBQUUsS0FBSyx5QkFBeUIsRUFBRSxlQUFlLEVBQUUsY0FBYyxFQUFFLEVBQUUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3ZHLE9BQU8sRUFBRSxLQUFL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
@@ -1 +1 @@
1
- {"version":3,"file":"checkpoint_builder.d.ts","sourceRoot":"","sources":["../src/checkpoint_builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"checkpoint_builder.d.ts","sourceRoot":"","sources":["../src/checkpoint_builder.ts"],"names":[],"mappings":"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"}
@@ -8,11 +8,12 @@ import { createTxValidatorForBlockBuilding, getDefaultAllowedSetupFunctions } fr
8
8
  import { LightweightCheckpointBuilder } from '@aztec/prover-client/light';
9
9
  import { GuardedMerkleTreeOperations, PublicContractsDB, PublicProcessor, createPublicTxSimulatorForBlockBuilding } from '@aztec/simulator/server';
10
10
  import { Gas } from '@aztec/stdlib/gas';
11
- import { FullNodeBlockBuilderConfigKeys, NoValidTxsError } from '@aztec/stdlib/interfaces/server';
11
+ import { FullNodeBlockBuilderConfigKeys, InsufficientValidTxsError } from '@aztec/stdlib/interfaces/server';
12
12
  import { NullDebugLogStore } from '@aztec/stdlib/logs';
13
13
  import { MerkleTreeId } from '@aztec/stdlib/trees';
14
14
  import { GlobalVariables } from '@aztec/stdlib/tx';
15
15
  import { getTelemetryClient } from '@aztec/telemetry-client';
16
+ import { ForkCheckpoint } from '@aztec/world-state';
16
17
  /**
17
18
  * Builder for a single checkpoint. Handles building blocks within the checkpoint
18
19
  * and completing it.
@@ -69,28 +70,40 @@ import { getTelemetryClient } from '@aztec/telemetry-client';
69
70
  ...opts,
70
71
  ...this.capLimitsByCheckpointBudgets(opts)
71
72
  };
72
- const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(()=>processor.process(pendingTxs, cappedOpts, validator));
73
- // Throw if we didn't collect a single valid tx and we're not allowed to build empty blocks
74
- // (only the first block in a checkpoint can be empty)
75
- if (processedTxs.length === 0 && this.checkpointBuilder.getBlockCount() > 0) {
76
- throw new NoValidTxsError(failedTxs);
73
+ // We execute all merkle tree operations on a world state fork checkpoint
74
+ // This enables us to discard all modifications in the event that we fail to successfully process sufficient transactions
75
+ const forkCheckpoint = await ForkCheckpoint.new(this.fork);
76
+ try {
77
+ const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(()=>processor.process(pendingTxs, cappedOpts, validator));
78
+ // Throw before updating state if we don't have enough valid txs
79
+ const minValidTxs = opts.minValidTxs ?? 0;
80
+ if (processedTxs.length < minValidTxs) {
81
+ throw new InsufficientValidTxsError(processedTxs.length, minValidTxs, failedTxs);
82
+ }
83
+ // Commit the fork checkpoint
84
+ await forkCheckpoint.commit();
85
+ // Add block to checkpoint
86
+ const { block } = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
87
+ expectedEndState: opts.expectedEndState
88
+ });
89
+ this.log.debug('Built block within checkpoint', {
90
+ header: block.header.toInspect(),
91
+ processedTxs: processedTxs.map((tx)=>tx.hash.toString()),
92
+ failedTxs: failedTxs.map((tx)=>tx.tx.txHash.toString())
93
+ });
94
+ return {
95
+ block,
96
+ publicProcessorDuration,
97
+ numTxs: processedTxs.length,
98
+ failedTxs,
99
+ usedTxs
100
+ };
101
+ } catch (err) {
102
+ // If we reached the point of committing the checkpoint, this does nothing
103
+ // Otherwise it reverts any changes made to the fork for this failed block
104
+ await forkCheckpoint.revert();
105
+ throw err;
77
106
  }
78
- // Add block to checkpoint
79
- const { block } = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
80
- expectedEndState: opts.expectedEndState
81
- });
82
- this.log.debug('Built block within checkpoint', {
83
- header: block.header.toInspect(),
84
- processedTxs: processedTxs.map((tx)=>tx.hash.toString()),
85
- failedTxs: failedTxs.map((tx)=>tx.tx.txHash.toString())
86
- });
87
- return {
88
- block,
89
- publicProcessorDuration,
90
- numTxs: processedTxs.length,
91
- failedTxs,
92
- usedTxs
93
- };
94
107
  }
95
108
  /** Completes the checkpoint and returns it. */ async completeCheckpoint() {
96
109
  const checkpoint = await this.checkpointBuilder.completeCheckpoint();
@@ -124,18 +137,27 @@ import { getTelemetryClient } from '@aztec/telemetry-client';
124
137
  const isFirstBlock = existingBlocks.length === 0;
125
138
  const blockEndOverhead = getNumBlockEndBlobFields(isFirstBlock);
126
139
  const maxBlobFieldsForTxs = totalBlobCapacity - usedBlobFields - blockEndOverhead;
127
- // Cap L2 gas by remaining checkpoint mana
128
- const cappedL2Gas = Math.min(opts.maxBlockGas?.l2Gas ?? remainingMana, remainingMana);
129
- // Cap DA gas by remaining checkpoint DA gas budget
130
- const cappedDAGas = Math.min(opts.maxBlockGas?.daGas ?? remainingDAGas, remainingDAGas);
131
- // Cap blob fields by remaining checkpoint blob capacity
132
- const cappedBlobFields = opts.maxBlobFields !== undefined ? Math.min(opts.maxBlobFields, maxBlobFieldsForTxs) : maxBlobFieldsForTxs;
133
- // Cap transaction count by remaining checkpoint tx budget
140
+ // When redistributeCheckpointBudget is enabled (default), compute a fair share of remaining budget
141
+ // across remaining blocks scaled by the multiplier, instead of letting one block consume it all.
142
+ const redistribute = this.config.redistributeCheckpointBudget !== false;
143
+ const remainingBlocks = Math.max(1, (this.config.maxBlocksPerCheckpoint ?? 1) - existingBlocks.length);
144
+ const multiplier = this.config.perBlockAllocationMultiplier ?? 1.2;
145
+ // Cap L2 gas by remaining checkpoint mana (with fair share when redistributing)
146
+ const fairShareL2 = redistribute ? Math.ceil(remainingMana / remainingBlocks * multiplier) : Infinity;
147
+ const cappedL2Gas = Math.min(opts.maxBlockGas?.l2Gas ?? Infinity, fairShareL2, remainingMana);
148
+ // Cap DA gas by remaining checkpoint DA gas budget (with fair share when redistributing)
149
+ const fairShareDA = redistribute ? Math.ceil(remainingDAGas / remainingBlocks * multiplier) : Infinity;
150
+ const cappedDAGas = Math.min(opts.maxBlockGas?.daGas ?? remainingDAGas, fairShareDA, remainingDAGas);
151
+ // Cap blob fields by remaining checkpoint blob capacity (with fair share when redistributing)
152
+ const fairShareBlobs = redistribute ? Math.ceil(maxBlobFieldsForTxs / remainingBlocks * multiplier) : Infinity;
153
+ const cappedBlobFields = Math.min(opts.maxBlobFields ?? Infinity, fairShareBlobs, maxBlobFieldsForTxs);
154
+ // Cap transaction count by remaining checkpoint tx budget (with fair share when redistributing)
134
155
  let cappedMaxTransactions;
135
156
  if (this.config.maxTxsPerCheckpoint !== undefined) {
136
157
  const usedTxs = sum(existingBlocks.map((b)=>b.body.txEffects.length));
137
158
  const remainingTxs = Math.max(0, this.config.maxTxsPerCheckpoint - usedTxs);
138
- cappedMaxTransactions = opts.maxTransactions !== undefined ? Math.min(opts.maxTransactions, remainingTxs) : remainingTxs;
159
+ const fairShareTxs = redistribute ? Math.ceil(remainingTxs / remainingBlocks * multiplier) : Infinity;
160
+ cappedMaxTransactions = Math.min(opts.maxTransactions ?? Infinity, fairShareTxs, remainingTxs);
139
161
  } else {
140
162
  cappedMaxTransactions = opts.maxTransactions;
141
163
  }
@@ -105,7 +105,7 @@ export class ValidationService {
105
105
  } else {
106
106
  const error = result.reason;
107
107
  if (error instanceof DutyAlreadySignedError || error instanceof SlashingProtectionError) {
108
- this.log.info(`Attestation for slot ${proposal.slotNumber} by ${attestors[i]} already signed by another High-Availability node`);
108
+ this.log.verbose(`Attestation for slot ${proposal.slotNumber} by ${attestors[i]} already signed by another High-Availability node`);
109
109
  // Continue with remaining attestors
110
110
  } else {
111
111
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/validator-client",
3
- "version": "5.0.0-nightly.20260316",
3
+ "version": "5.0.0-nightly.20260317",
4
4
  "main": "dest/index.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -64,30 +64,30 @@
64
64
  ]
65
65
  },
66
66
  "dependencies": {
67
- "@aztec/blob-client": "5.0.0-nightly.20260316",
68
- "@aztec/blob-lib": "5.0.0-nightly.20260316",
69
- "@aztec/constants": "5.0.0-nightly.20260316",
70
- "@aztec/epoch-cache": "5.0.0-nightly.20260316",
71
- "@aztec/ethereum": "5.0.0-nightly.20260316",
72
- "@aztec/foundation": "5.0.0-nightly.20260316",
73
- "@aztec/node-keystore": "5.0.0-nightly.20260316",
74
- "@aztec/noir-protocol-circuits-types": "5.0.0-nightly.20260316",
75
- "@aztec/p2p": "5.0.0-nightly.20260316",
76
- "@aztec/protocol-contracts": "5.0.0-nightly.20260316",
77
- "@aztec/prover-client": "5.0.0-nightly.20260316",
78
- "@aztec/simulator": "5.0.0-nightly.20260316",
79
- "@aztec/slasher": "5.0.0-nightly.20260316",
80
- "@aztec/stdlib": "5.0.0-nightly.20260316",
81
- "@aztec/telemetry-client": "5.0.0-nightly.20260316",
82
- "@aztec/validator-ha-signer": "5.0.0-nightly.20260316",
67
+ "@aztec/blob-client": "5.0.0-nightly.20260317",
68
+ "@aztec/blob-lib": "5.0.0-nightly.20260317",
69
+ "@aztec/constants": "5.0.0-nightly.20260317",
70
+ "@aztec/epoch-cache": "5.0.0-nightly.20260317",
71
+ "@aztec/ethereum": "5.0.0-nightly.20260317",
72
+ "@aztec/foundation": "5.0.0-nightly.20260317",
73
+ "@aztec/node-keystore": "5.0.0-nightly.20260317",
74
+ "@aztec/noir-protocol-circuits-types": "5.0.0-nightly.20260317",
75
+ "@aztec/p2p": "5.0.0-nightly.20260317",
76
+ "@aztec/protocol-contracts": "5.0.0-nightly.20260317",
77
+ "@aztec/prover-client": "5.0.0-nightly.20260317",
78
+ "@aztec/simulator": "5.0.0-nightly.20260317",
79
+ "@aztec/slasher": "5.0.0-nightly.20260317",
80
+ "@aztec/stdlib": "5.0.0-nightly.20260317",
81
+ "@aztec/telemetry-client": "5.0.0-nightly.20260317",
82
+ "@aztec/validator-ha-signer": "5.0.0-nightly.20260317",
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": "5.0.0-nightly.20260316",
90
- "@aztec/world-state": "5.0.0-nightly.20260316",
89
+ "@aztec/archiver": "5.0.0-nightly.20260317",
90
+ "@aztec/world-state": "5.0.0-nightly.20260317",
91
91
  "@electric-sql/pglite": "^0.3.14",
92
92
  "@jest/globals": "^30.0.0",
93
93
  "@types/jest": "^30.0.0",
@@ -25,8 +25,8 @@ import {
25
25
  FullNodeBlockBuilderConfigKeys,
26
26
  type ICheckpointBlockBuilder,
27
27
  type ICheckpointsBuilder,
28
+ InsufficientValidTxsError,
28
29
  type MerkleTreeWriteOperations,
29
- NoValidTxsError,
30
30
  type PublicProcessorLimits,
31
31
  type WorldStateSynchronizer,
32
32
  } from '@aztec/stdlib/interfaces/server';
@@ -34,6 +34,7 @@ import { type DebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
34
34
  import { MerkleTreeId } from '@aztec/stdlib/trees';
35
35
  import { type CheckpointGlobalVariables, GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
36
36
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
37
+ import { ForkCheckpoint } from '@aztec/world-state';
37
38
 
38
39
  // Re-export for backward compatibility
39
40
  export type { BuildBlockInCheckpointResult } from '@aztec/stdlib/interfaces/server';
@@ -73,7 +74,7 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
73
74
  pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
74
75
  blockNumber: BlockNumber,
75
76
  timestamp: bigint,
76
- opts: PublicProcessorLimits & { expectedEndState?: StateReference } = {},
77
+ opts: PublicProcessorLimits & { expectedEndState?: StateReference; minValidTxs?: number } = {},
77
78
  ): Promise<BuildBlockInCheckpointResult> {
78
79
  const slot = this.checkpointBuilder.constants.slotNumber;
79
80
 
@@ -103,34 +104,47 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
103
104
  ...this.capLimitsByCheckpointBudgets(opts),
104
105
  };
105
106
 
106
- const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() =>
107
- processor.process(pendingTxs, cappedOpts, validator),
108
- );
107
+ // We execute all merkle tree operations on a world state fork checkpoint
108
+ // This enables us to discard all modifications in the event that we fail to successfully process sufficient transactions
109
+ const forkCheckpoint = await ForkCheckpoint.new(this.fork);
109
110
 
110
- // Throw if we didn't collect a single valid tx and we're not allowed to build empty blocks
111
- // (only the first block in a checkpoint can be empty)
112
- if (processedTxs.length === 0 && this.checkpointBuilder.getBlockCount() > 0) {
113
- throw new NoValidTxsError(failedTxs);
111
+ try {
112
+ const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() =>
113
+ processor.process(pendingTxs, cappedOpts, validator),
114
+ );
115
+ // Throw before updating state if we don't have enough valid txs
116
+ const minValidTxs = opts.minValidTxs ?? 0;
117
+ if (processedTxs.length < minValidTxs) {
118
+ throw new InsufficientValidTxsError(processedTxs.length, minValidTxs, failedTxs);
119
+ }
120
+
121
+ // Commit the fork checkpoint
122
+ await forkCheckpoint.commit();
123
+
124
+ // Add block to checkpoint
125
+ const { block } = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
126
+ expectedEndState: opts.expectedEndState,
127
+ });
128
+
129
+ this.log.debug('Built block within checkpoint', {
130
+ header: block.header.toInspect(),
131
+ processedTxs: processedTxs.map(tx => tx.hash.toString()),
132
+ failedTxs: failedTxs.map(tx => tx.tx.txHash.toString()),
133
+ });
134
+
135
+ return {
136
+ block,
137
+ publicProcessorDuration,
138
+ numTxs: processedTxs.length,
139
+ failedTxs,
140
+ usedTxs,
141
+ };
142
+ } catch (err) {
143
+ // If we reached the point of committing the checkpoint, this does nothing
144
+ // Otherwise it reverts any changes made to the fork for this failed block
145
+ await forkCheckpoint.revert();
146
+ throw err;
114
147
  }
115
-
116
- // Add block to checkpoint
117
- const { block } = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
118
- expectedEndState: opts.expectedEndState,
119
- });
120
-
121
- this.log.debug('Built block within checkpoint', {
122
- header: block.header.toInspect(),
123
- processedTxs: processedTxs.map(tx => tx.hash.toString()),
124
- failedTxs: failedTxs.map(tx => tx.tx.txHash.toString()),
125
- });
126
-
127
- return {
128
- block,
129
- publicProcessorDuration,
130
- numTxs: processedTxs.length,
131
- failedTxs,
132
- usedTxs,
133
- };
134
148
  }
135
149
 
136
150
  /** Completes the checkpoint and returns it. */
@@ -178,23 +192,31 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
178
192
  const blockEndOverhead = getNumBlockEndBlobFields(isFirstBlock);
179
193
  const maxBlobFieldsForTxs = totalBlobCapacity - usedBlobFields - blockEndOverhead;
180
194
 
181
- // Cap L2 gas by remaining checkpoint mana
182
- const cappedL2Gas = Math.min(opts.maxBlockGas?.l2Gas ?? remainingMana, remainingMana);
195
+ // When redistributeCheckpointBudget is enabled (default), compute a fair share of remaining budget
196
+ // across remaining blocks scaled by the multiplier, instead of letting one block consume it all.
197
+ const redistribute = this.config.redistributeCheckpointBudget !== false;
198
+ const remainingBlocks = Math.max(1, (this.config.maxBlocksPerCheckpoint ?? 1) - existingBlocks.length);
199
+ const multiplier = this.config.perBlockAllocationMultiplier ?? 1.2;
200
+
201
+ // Cap L2 gas by remaining checkpoint mana (with fair share when redistributing)
202
+ const fairShareL2 = redistribute ? Math.ceil((remainingMana / remainingBlocks) * multiplier) : Infinity;
203
+ const cappedL2Gas = Math.min(opts.maxBlockGas?.l2Gas ?? Infinity, fairShareL2, remainingMana);
183
204
 
184
- // Cap DA gas by remaining checkpoint DA gas budget
185
- const cappedDAGas = Math.min(opts.maxBlockGas?.daGas ?? remainingDAGas, remainingDAGas);
205
+ // Cap DA gas by remaining checkpoint DA gas budget (with fair share when redistributing)
206
+ const fairShareDA = redistribute ? Math.ceil((remainingDAGas / remainingBlocks) * multiplier) : Infinity;
207
+ const cappedDAGas = Math.min(opts.maxBlockGas?.daGas ?? remainingDAGas, fairShareDA, remainingDAGas);
186
208
 
187
- // Cap blob fields by remaining checkpoint blob capacity
188
- const cappedBlobFields =
189
- opts.maxBlobFields !== undefined ? Math.min(opts.maxBlobFields, maxBlobFieldsForTxs) : maxBlobFieldsForTxs;
209
+ // Cap blob fields by remaining checkpoint blob capacity (with fair share when redistributing)
210
+ const fairShareBlobs = redistribute ? Math.ceil((maxBlobFieldsForTxs / remainingBlocks) * multiplier) : Infinity;
211
+ const cappedBlobFields = Math.min(opts.maxBlobFields ?? Infinity, fairShareBlobs, maxBlobFieldsForTxs);
190
212
 
191
- // Cap transaction count by remaining checkpoint tx budget
213
+ // Cap transaction count by remaining checkpoint tx budget (with fair share when redistributing)
192
214
  let cappedMaxTransactions: number | undefined;
193
215
  if (this.config.maxTxsPerCheckpoint !== undefined) {
194
216
  const usedTxs = sum(existingBlocks.map(b => b.body.txEffects.length));
195
217
  const remainingTxs = Math.max(0, this.config.maxTxsPerCheckpoint - usedTxs);
196
- cappedMaxTransactions =
197
- opts.maxTransactions !== undefined ? Math.min(opts.maxTransactions, remainingTxs) : remainingTxs;
218
+ const fairShareTxs = redistribute ? Math.ceil((remainingTxs / remainingBlocks) * multiplier) : Infinity;
219
+ cappedMaxTransactions = Math.min(opts.maxTransactions ?? Infinity, fairShareTxs, remainingTxs);
198
220
  } else {
199
221
  cappedMaxTransactions = opts.maxTransactions;
200
222
  }
@@ -177,7 +177,7 @@ export class ValidationService {
177
177
  } else {
178
178
  const error = result.reason;
179
179
  if (error instanceof DutyAlreadySignedError || error instanceof SlashingProtectionError) {
180
- this.log.info(
180
+ this.log.verbose(
181
181
  `Attestation for slot ${proposal.slotNumber} by ${attestors[i]} already signed by another High-Availability node`,
182
182
  );
183
183
  // Continue with remaining attestors