Skip to content

feat(sequencer): set block building limits from checkpoint limits#20974

Open
spalladino wants to merge 6 commits intomerge-train/spartanfrom
palla/update-block-build-limits
Open

feat(sequencer): set block building limits from checkpoint limits#20974
spalladino wants to merge 6 commits intomerge-train/spartanfrom
palla/update-block-build-limits

Conversation

@spalladino
Copy link
Contributor

@spalladino spalladino commented Feb 27, 2026

The checkpoint builder now tracks remaining L2 gas, DA gas, and blob fields in a checkpoint while building each block, and forwards them to the public processor. This means that a proposer will not propose blocks that overall exceed checkpoint limits, and validators will properly reject them.

In addition, the proposer defaults the L2 and DA gas limits per block to the checkpoint limits divided by expected number of blocks, times two. This value is still capped by the remaining gas in the checkpoint builder, but means that a proposer will not waste the entire checkpoint gas allocation on the first block.

Fixes A-528

Changes

As described by Claude

  • Derives per-block L2 and DA gas budgets from L1 checkpoint limits (rollupManaLimit, MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT) using the timetable's maxNumberOfBlocks and a configurable multiplier (default: 2) for creating proposals only (not for validation)
  • Moves blob field and gas tracking from the checkpoint proposal job loop into CheckpointBuilder.capLimitsByCheckpointBudgets(), which caps per-block limits by remaining checkpoint budgets for both proposer and validator paths
  • Plumbs maxTxsPerBlock, maxL2BlockGas, maxDABlockGas, and rollupManaLimit through to validator re-execution so limits are enforced during block validation
  • Replaces byte-based maxBlockSizeInBytes with field-based blob limits and a pre-processing blob field estimation (getPrivateTxEffectsSizeInFields)
  • Adds gasPerBlockAllocationMultiplier config (SEQ_GAS_PER_BLOCK_ALLOCATION_MULTIPLIER, default: 2)
  • Makes maxL2BlockGas and maxDABlockGas optional (auto-computed from checkpoint limits when not set)

🤖 Generated with Claude Code

@spalladino spalladino added ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure backport-to-v4 labels Feb 27, 2026
@spalladino spalladino added ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure backport-to-v4 labels Feb 27, 2026
Comment on lines +166 to +200
// Remaining L2 gas (mana)
// IMPORTANT: This assumes mana is computed solely based on L2 gas used in transactions.
// This may change in the future.
const usedMana = sum(existingBlocks.map(b => b.header.totalManaUsed.toNumber()));
const remainingMana = this.config.rollupManaLimit - usedMana;

// Remaining DA gas: DA gas = tx blob fields * DA_GAS_PER_FIELD
// IMPORTANT: This assumes DA gas is computed solely based on the number of blob fields in transactions
// This may change in the future, but we cannot access the actual DA gas used in a block since it's not exposed
// in the L2BlockHeader, so we have to rely on recomputing it here.
const usedDAGas =
sum(existingBlocks.map(b => sum(b.body.txEffects.map(tx => tx.getNumBlobFields())))) * DA_GAS_PER_FIELD;
const remainingDAGas = MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT - usedDAGas;

// Remaining blob fields (block blob fields include both tx data and block-end overhead)
const usedBlobFields = sum(existingBlocks.map(b => b.toBlobFields().length));
const totalBlobCapacity = BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - NUM_CHECKPOINT_END_MARKER_FIELDS;
const isFirstBlock = existingBlocks.length === 0;
const blockEndOverhead = getNumBlockEndBlobFields(isFirstBlock);
const maxBlobFieldsForTxs = totalBlobCapacity - usedBlobFields - blockEndOverhead;

// Cap L2 gas by remaining checkpoint mana
const cappedL2Gas = Math.min(opts.maxBlockGas?.l2Gas ?? remainingMana, remainingMana);

// Cap DA gas by remaining checkpoint DA gas budget
const cappedDAGas = Math.min(opts.maxBlockGas?.daGas ?? remainingDAGas, remainingDAGas);

// Cap blob fields by remaining checkpoint blob capacity
const cappedBlobFields =
opts.maxBlobFields !== undefined ? Math.min(opts.maxBlobFields, maxBlobFieldsForTxs) : maxBlobFieldsForTxs;

return {
maxBlockGas: new Gas(cappedDAGas, cappedL2Gas),
maxBlobFields: cappedBlobFields,
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sirasistant can I ask you to review this part of the code?

spalladino and others added 3 commits February 27, 2026 18:41
… building

During re-execution (validation/proving), the public processor must process
the exact txs from the proposal. Pre-processing skip checks for estimated
blob fields and gas limits are now gated behind a new `isBuildingProposal`
flag on PublicProcessorLimits, which is only set by the sequencer.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@spalladino spalladino removed the request for review from LeilaWang February 27, 2026 22:09
Copy link
Contributor Author

@spalladino spalladino Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sirasistant also the changes on this file


// Default gas limits. Users should use gas estimation, or they will overpay gas fees.
// TODO: consider moving to typescript
// TODO: These are overridden in typescript-land. Remove them from here.
Copy link
Contributor Author

@spalladino spalladino Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't delete them in this PR because I was scared of triggering a change in a vk

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-to-v4 ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant