Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

116 changes: 0 additions & 116 deletions yarn-project/sequencer-client/src/client/sequencer-client.test.ts

This file was deleted.

113 changes: 27 additions & 86 deletions yarn-project/sequencer-client/src/client/sequencer-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,10 @@ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
import { L1Metrics, type TelemetryClient } from '@aztec/telemetry-client';
import { FullNodeCheckpointsBuilder, NodeKeystoreAdapter, type ValidatorClient } from '@aztec/validator-client';

import {
DefaultSequencerConfig,
type SequencerClientConfig,
getPublisherConfigFromSequencerConfig,
} from '../config.js';
import { type SequencerClientConfig, getPublisherConfigFromSequencerConfig } from '../config.js';
import { GlobalVariableBuilder } from '../global_variable_builder/index.js';
import { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
import { Sequencer, type SequencerConfig } from '../sequencer/index.js';
import { SequencerTimetable } from '../sequencer/timetable.js';

/**
* Encapsulates the full sequencer and publisher.
Expand Down Expand Up @@ -160,12 +155,7 @@ export class SequencerClient {
const l1PublishingTimeBasedOnChain = isAnvilTestChain(config.l1ChainId) ? 1 : ethereumSlotDuration;
const l1PublishingTime = config.l1PublishingTime ?? l1PublishingTimeBasedOnChain;

const { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock, maxBlocksPerCheckpoint } = computeBlockLimits(
config,
rollupManaLimit,
l1PublishingTime,
log,
);
const { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock } = capPerBlockLimits(config, rollupManaLimit, log);

const l1Constants = { l1GenesisTime, slotDuration: Number(slotDuration), ethereumSlotDuration, rollupManaLimit };

Expand All @@ -183,7 +173,7 @@ export class SequencerClient {
deps.dateProvider,
epochCache,
rollupContract,
{ ...config, l1PublishingTime, maxL2BlockGas, maxDABlockGas, maxTxsPerBlock, maxBlocksPerCheckpoint },
{ ...config, l1PublishingTime, maxL2BlockGas, maxDABlockGas, maxTxsPerBlock },
telemetryClient,
log,
);
Expand Down Expand Up @@ -248,88 +238,39 @@ export class SequencerClient {
}

/**
* Computes per-block L2 gas, DA gas, and TX count budgets based on the L1 rollup limits and the timetable.
* If the user explicitly set a limit, it is capped at the corresponding checkpoint limit.
* Otherwise, derives it as (checkpointLimit / maxBlocks) * multiplier, capped at the checkpoint limit.
* Caps operator-provided per-block limits at checkpoint-level limits.
* Returns undefined for any limit the operator didn't set — the checkpoint builder handles redistribution.
*/
export function computeBlockLimits(
function capPerBlockLimits(
config: SequencerClientConfig,
rollupManaLimit: number,
l1PublishingTime: number,
log: ReturnType<typeof createLogger>,
): { maxL2BlockGas: number; maxDABlockGas: number; maxTxsPerBlock: number; maxBlocksPerCheckpoint: number } {
const maxNumberOfBlocks = new SequencerTimetable({
ethereumSlotDuration: config.ethereumSlotDuration,
aztecSlotDuration: config.aztecSlotDuration,
l1PublishingTime,
p2pPropagationTime: config.attestationPropagationTime,
blockDurationMs: config.blockDurationMs,
enforce: config.enforceTimeTable ?? DefaultSequencerConfig.enforceTimeTable,
}).maxNumberOfBlocks;

const multiplier = config.perBlockAllocationMultiplier ?? DefaultSequencerConfig.perBlockAllocationMultiplier;

// Compute maxL2BlockGas
let maxL2BlockGas: number;
if (config.maxL2BlockGas !== undefined) {
if (config.maxL2BlockGas > rollupManaLimit) {
log.warn(
`Provided MAX_L2_BLOCK_GAS ${config.maxL2BlockGas} exceeds L1 rollup mana limit ${rollupManaLimit} (capping)`,
);
maxL2BlockGas = rollupManaLimit;
} else {
maxL2BlockGas = config.maxL2BlockGas;
}
} else {
maxL2BlockGas = Math.min(rollupManaLimit, Math.ceil((rollupManaLimit / maxNumberOfBlocks) * multiplier));
): { maxL2BlockGas: number | undefined; maxDABlockGas: number | undefined; maxTxsPerBlock: number | undefined } {
let maxL2BlockGas = config.maxL2BlockGas;
if (maxL2BlockGas !== undefined && maxL2BlockGas > rollupManaLimit) {
log.warn(`Provided MAX_L2_BLOCK_GAS ${maxL2BlockGas} exceeds rollup mana limit ${rollupManaLimit} (capping)`);
maxL2BlockGas = rollupManaLimit;
}

// Compute maxDABlockGas
const daCheckpointLimit = MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT;
let maxDABlockGas: number;
if (config.maxDABlockGas !== undefined) {
if (config.maxDABlockGas > daCheckpointLimit) {
log.warn(
`Provided MAX_DA_BLOCK_GAS ${config.maxDABlockGas} exceeds DA checkpoint limit ${daCheckpointLimit} (capping)`,
);
maxDABlockGas = daCheckpointLimit;
} else {
maxDABlockGas = config.maxDABlockGas;
}
} else {
maxDABlockGas = Math.min(daCheckpointLimit, Math.ceil((daCheckpointLimit / maxNumberOfBlocks) * multiplier));
let maxDABlockGas = config.maxDABlockGas;
if (maxDABlockGas !== undefined && maxDABlockGas > MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT) {
log.warn(
`Provided MAX_DA_BLOCK_GAS ${maxDABlockGas} exceeds DA checkpoint limit ${MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT} (capping)`,
);
maxDABlockGas = MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT;
}

// Compute maxTxsPerBlock
const defaultMaxTxsPerBlock = 32;
let maxTxsPerBlock: number;
if (config.maxTxsPerBlock !== undefined) {
if (config.maxTxsPerCheckpoint !== undefined && config.maxTxsPerBlock > config.maxTxsPerCheckpoint) {
log.warn(
`Provided MAX_TX_PER_BLOCK ${config.maxTxsPerBlock} exceeds MAX_TX_PER_CHECKPOINT ${config.maxTxsPerCheckpoint} (capping)`,
);
maxTxsPerBlock = config.maxTxsPerCheckpoint;
} else {
maxTxsPerBlock = config.maxTxsPerBlock;
}
} else if (config.maxTxsPerCheckpoint !== undefined) {
maxTxsPerBlock = Math.min(
config.maxTxsPerCheckpoint,
Math.ceil((config.maxTxsPerCheckpoint / maxNumberOfBlocks) * multiplier),
let maxTxsPerBlock = config.maxTxsPerBlock;
if (
maxTxsPerBlock !== undefined &&
config.maxTxsPerCheckpoint !== undefined &&
maxTxsPerBlock > config.maxTxsPerCheckpoint
) {
log.warn(
`Provided MAX_TX_PER_BLOCK ${maxTxsPerBlock} exceeds MAX_TX_PER_CHECKPOINT ${config.maxTxsPerCheckpoint} (capping)`,
);
} else {
maxTxsPerBlock = defaultMaxTxsPerBlock;
maxTxsPerBlock = config.maxTxsPerCheckpoint;
}

log.info(`Computed block limits L2=${maxL2BlockGas} DA=${maxDABlockGas} maxTxs=${maxTxsPerBlock}`, {
maxL2BlockGas,
maxDABlockGas,
maxTxsPerBlock,
rollupManaLimit,
daCheckpointLimit,
maxNumberOfBlocks,
multiplier,
});

return { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock, maxBlocksPerCheckpoint: maxNumberOfBlocks };
return { maxL2BlockGas, maxDABlockGas, maxTxsPerBlock };
}
3 changes: 0 additions & 3 deletions yarn-project/sequencer-client/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,6 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
'Redistribute remaining checkpoint budget evenly across remaining blocks instead of allowing a single block to consume the entire remaining budget.',
...booleanConfigHelper(DefaultSequencerConfig.redistributeCheckpointBudget),
},
maxBlocksPerCheckpoint: {
description: 'Computed max number of blocks per checkpoint from timetable.',
},
coinbase: {
env: 'COINBASE',
parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import type { L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
import { GasFees } from '@aztec/stdlib/gas';
import type {
BlockBuilderOptions,
MerkleTreeWriteOperations,
PublicProcessorLimits,
ResolvedSequencerConfig,
WorldStateSynchronizer,
} from '@aztec/stdlib/interfaces/server';
Expand Down Expand Up @@ -80,7 +80,7 @@ class TimingAwareMockCheckpointBuilder extends MockCheckpointBuilder {
pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
blockNumber: BlockNumber,
timestamp: bigint,
opts: PublicProcessorLimits,
opts: BlockBuilderOptions,
): Promise<BuildBlockInCheckpointResult> {
const startTime = this.getSecondsIntoSlot();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ import { type Checkpoint, validateCheckpoint } from '@aztec/stdlib/checkpoint';
import { getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
import { Gas } from '@aztec/stdlib/gas';
import {
type BlockBuilderOptions,
InsufficientValidTxsError,
type PublicProcessorLimits,
type ResolvedSequencerConfig,
type WorldStateSynchronizer,
} from '@aztec/stdlib/interfaces/server';
Expand Down Expand Up @@ -270,7 +270,8 @@ export class CheckpointProposalJob implements Traceable {
this.setStateFn(SequencerState.ASSEMBLING_CHECKPOINT, this.slot);
const checkpoint = await checkpointBuilder.completeCheckpoint();

// Final validation round for the checkpoint before we propose it, just for safety
// Final validation: per-block limits are only checked if the operator set them explicitly.
// Otherwise, checkpoint-level budgets were already enforced by the redistribution logic.
try {
validateCheckpoint(checkpoint, {
rollupManaLimit: this.l1Constants.rollupManaLimit,
Expand Down Expand Up @@ -574,11 +575,11 @@ export class CheckpointProposalJob implements Traceable {
);
this.setStateFn(SequencerState.CREATING_BLOCK, this.slot);

// Per-block limits derived at startup by computeBlockLimits(), further capped
// Per-block limits are operator overrides (from SEQ_MAX_L2_BLOCK_GAS etc.) further capped
// by remaining checkpoint-level budgets inside CheckpointBuilder before each block is built.
// minValidTxs is passed into the builder so it can reject the block *before* updating state.
const minValidTxs = forceCreate ? 0 : (this.config.minValidTxsPerBlock ?? minTxs);
const blockBuilderOptions: PublicProcessorLimits & { minValidTxs?: number } = {
const blockBuilderOptions: BlockBuilderOptions = {
maxTransactions: this.config.maxTxsPerBlock,
maxBlockGas:
this.config.maxL2BlockGas !== undefined || this.config.maxDABlockGas !== undefined
Expand All @@ -587,6 +588,8 @@ export class CheckpointProposalJob implements Traceable {
deadline: buildDeadline,
isBuildingProposal: true,
minValidTxs,
maxBlocksPerCheckpoint: this.timetable.maxNumberOfBlocks,
perBlockAllocationMultiplier: this.config.perBlockAllocationMultiplier,
};

// Actually build the block by executing txs. The builder throws InsufficientValidTxsError
Expand Down Expand Up @@ -657,7 +660,7 @@ export class CheckpointProposalJob implements Traceable {
pendingTxs: AsyncIterable<Tx>,
blockNumber: BlockNumber,
blockTimestamp: bigint,
blockBuilderOptions: PublicProcessorLimits & { minValidTxs?: number },
blockBuilderOptions: BlockBuilderOptions,
) {
try {
const workTimer = new Timer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { unfreeze } from '@aztec/foundation/types';
import { L2Block } from '@aztec/stdlib/block';
import { Checkpoint } from '@aztec/stdlib/checkpoint';
import type {
BlockBuilderOptions,
FullNodeBlockBuilderConfig,
ICheckpointBlockBuilder,
ICheckpointsBuilder,
MerkleTreeWriteOperations,
PublicProcessorLimits,
} from '@aztec/stdlib/interfaces/server';
import { CheckpointHeader } from '@aztec/stdlib/rollup';
import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
Expand All @@ -32,7 +32,7 @@ export class MockCheckpointBuilder implements ICheckpointBlockBuilder {
public buildBlockCalls: Array<{
blockNumber: BlockNumber;
timestamp: bigint;
opts: PublicProcessorLimits & { minValidTxs?: number };
opts: BlockBuilderOptions;
}> = [];
/** Track all consumed transaction hashes across buildBlock calls */
public consumedTxHashes: Set<string> = new Set();
Expand Down Expand Up @@ -74,7 +74,7 @@ export class MockCheckpointBuilder implements ICheckpointBlockBuilder {
pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
blockNumber: BlockNumber,
timestamp: bigint,
opts: PublicProcessorLimits & { minValidTxs?: number },
opts: BlockBuilderOptions,
): Promise<BuildBlockInCheckpointResult> {
this.buildBlockCalls.push({ blockNumber, timestamp, opts });

Expand Down
4 changes: 2 additions & 2 deletions yarn-project/slasher/src/watchers/epoch_prune_watcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ describe('EpochPruneWatcher', () => {
[tx],
block.header.globalVariables.blockNumber,
block.header.globalVariables.timestamp,
{},
{ isBuildingProposal: false, minValidTxs: 0 },
);
});

Expand Down Expand Up @@ -246,7 +246,7 @@ describe('EpochPruneWatcher', () => {
[tx],
blockFromL1.header.globalVariables.blockNumber,
blockFromL1.header.globalVariables.timestamp,
{},
{ isBuildingProposal: false, minValidTxs: 0 },
);
});
});
Expand Down
Loading
Loading