Skip to content

refactor(l1): hold the rollup config in immutables - #25456

Closed
spalladino wants to merge 1 commit into
nextfrom
spl/fi2-b1-rollup-config-immutables
Closed

refactor(l1): hold the rollup config in immutables#25456
spalladino wants to merge 1 commit into
nextfrom
spl/fi2-b1-rollup-config-immutables

Conversation

@spalladino

Copy link
Copy Markdown
Contributor

Pure L1 refactor, no fast-inbox semantics. It moves the rollup's deployment-time configuration out of storage and into immutables to buy back runtime bytecode and gas.

The problem

RollupStore.config held eight values that are fixed at construction: the VK tree root, the protocol contracts hash, the version, the fee asset, the fee asset portal, the epoch proof verifier, the Inbox and the Outbox. Every read of any of them cost a cold or warm SLOAD, on paths that run on every proposal and every epoch proof.

The change

Those eight values become Rollup immutables, assigned directly in the constructor body. A new _getRollupConfig() assembles them into a RollupConfig memory struct, which is threaded down into EpochProofLib, EpochProofExtLib, RewardLib, RewardExtLib and (as the narrower ProposeConfig { IInbox inbox; bool checkBlob; }) ProposeLib. The libraries need the struct because a library cannot read the calling contract's immutables.

The config getters on Rollup also go through _getRollupConfig() rather than reading their own immutable directly. That looks backwards, and is deliberate: each direct immutable read inlines a 32-byte push into Rollup's runtime code, and Rollup sits close to the EIP-170 limit, so sharing one assembly across all of them is about 95 bytes cheaper. In the same spirit, ValidateHeaderArgs is now built inside RollupOperationsExtLib instead of in the Rollup — building a struct that embeds a full ProposedHeader in the Rollup's own code costs several hundred bytes it cannot spare — and checkHeaderWithAttestations's parameters become calldata.

Because immutables can only be assigned in the constructor body, the store setup can no longer live in a helper: _initializeStore is gone and STFLib.initialize now writes only the genesis archive root.

Two follow-on fixes ride along, both consequences of the refactor rather than independent changes. Rollup's link references list EpochProofExtLib, which the generated artifact list in generate-artifacts.sh was missing; and on the TypeScript side (in the labs-patches queue, since the node lives in the labs submodule now) getVkTreeRoot() and getProtocolContractsHash() stop reading raw storage at stfStorageSlot + 3 and + 4 — those slots no longer hold the config — and call the contract getters instead. The stfStorageSlot + 0/1/2 offsets other readers use are unaffected: config sat after tips, archives and tempCheckpointLogs in the struct.

Effect

before after delta
propose (48 validators) 199,366 197,433 −1,933
submitEpochRootProof (48 validators) 991,032 980,213 −10,819
propose (100 validators) 327,774 325,842 −1,932
submitEpochRootProof (100 validators) 1,572,081 1,561,264 −10,817

The three gas artifacts and the partial-epoch-proof report are regenerated accordingly.

Test-side notes

PartialEpochProofGasReporter is etched onto a live Rollup with vm.etch so the gas report runs against that rollup's storage. Immutables live in the code, so the Inbox and Outbox the reporter's own constructor deploys travel with the etched code and would displace the live rollup's; the live ones are now passed into the reporter and substituted back through an override of _getRollupConfig, which is virtual for that reason. RewardLibWrapper holds the fee asset and portal it needs in its own immutables and hands the library a partially populated config.

The deployment-time rollup configuration -- VK tree root, protocol contracts
hash, version, fee asset, fee asset portal, epoch proof verifier, Inbox and
Outbox -- never changes after construction, so it moves out of
`RollupStore.config` storage into `Rollup` immutables. A new `_getRollupConfig`
assembles a `RollupConfig` memory struct and threads it down into the
libraries, which cannot read a contract's immutables themselves.

The config getters route through that one accessor rather than reading their
immutable directly: each direct read inlines a 32-byte push into `Rollup`'s
runtime code, and `Rollup` sits close to the EIP-170 limit, so sharing one
assembly across all of them is about 95 bytes cheaper. `ValidateHeaderArgs` is
likewise assembled inside `RollupOperationsExtLib` instead of in the Rollup,
which saves several hundred more bytes of runtime code.

Immutables must be assigned directly in the constructor body, so the store
setup can no longer be factored out into `_initializeStore`; `STFLib.initialize`
now writes only the genesis archive root.

`Rollup` links against `EpochProofExtLib`, which the generated artifact list was
missing.

Propose drops about 1.9k gas and an epoch proof submission about 10.8k.
@spalladino

Copy link
Copy Markdown
Contributor Author

Closing as redundant: this landed on next via #25314 (perf(l1): hold the rollup config in immutables, commit 2ed7752), which carries the same change — the RollupConfig immutables on RollupCore, _getRollupConfig(), the virtual marker, the PartialEpochProofGasReporter fix, and the TS-side getter change (now labs patch 0011-refactor-ethereum-read-vkTreeRoot-and-protocolContra).

The rest of the stack has been rebased directly onto next: #25457 now bases on next, and #25459#25463#25464#25465 follow from there.

The one piece of this PR not on next was the extra l1_artifacts.test.ts assertion that no library is carried which the rollup does not link against. That was needed here only because this PR swapped ValidatorSelectionLib out for EpochProofExtLib in RollupArtifact.libraries; next keeps both and already pins the forward direction (every link reference has deployable bytecode) in labs patch 0002-fix-link-epoch-proof-library-when-deploying-rollup. Dropped rather than carried forward.

Branch left in place.

@spalladino spalladino closed this Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant