Repository navigation
feat(l1): raise the GSE proof-of-possession gas cap to 300k in the v6 payload - #25598
Merged
Merged
Conversation
The GSE verifies each new attester's BLS proof of possession in a sub-call capped at `proofOfPossessionGasLimit` (250k). The verification cost varies per key because `hashToPoint` is a rejection-sampling loop, and since the Osaka modexp repricing (EIP-7883) about 1 in 28.6k honestly generated keys cost more than 250k. Their deposits are refunded and dropped from the entry queue. At 300k that falls to about 1 in 2.5M, and a failing entry can burn at most 50k more gas in a flush. The payload gains an optional last action, `GSE.setProofOfPossessionGasLimit(PROOF_OF_POSSESSION_GAS_LIMIT)`, where zero omits it. It has no ordering dependency on the registration actions, so it goes last and the existing actions keep their positions. The constructor rejects a non-zero value that does not exceed the GSE's current cap. The deploy table sets 300_000 on both mainnet and Sepolia, `verify` reads it back, and the fork simulation asserts the cap after execution. The runbook and the payload doc list the action and the checks before and after execution. Tests cover the action list with and without the action, its calldata, the constructor bound, a full execution through governance, and a mainnet fork test (skipped unless MAINNET_FORK_RPC_URL is set) that checks governance owns the live GSE and can apply the encoded call.
spalladino
marked this pull request as ready for review
October 5, 2026 22:07
spalladino
requested review from
iAmMichaelConnor,
just-mitch and
koenmtb1
as code owners
October 5, 2026 22:07
aminsammara
approved these changes
Oct 6, 2026
This was referenced Oct 6, 2026
spalladino
added a commit
that referenced
this pull request
Oct 8, 2026
Brings the v6 upgrade work onto the `v6` release line: the deploy script, the governance payload, a simulation harness, an operator runbook, and the pinned rollup verifier and protocol constants the deploy script uses. 11 new files; the only existing file touched is `l1-contracts/.gitignore`, which stops ignoring the pinned `ConstantsGen.sol`. Supersedes #25496, which targeted `next`. This is that branch's history plus #25598, cherry-picked onto `v6` and squashed into one commit. `v6` already carries the AZIP-31 stack (#25570–#25573), and `l1-contracts/src` is byte-identical between `v6` and the base the original commits were written against, so nothing needed adapting beyond the reward-override reconciliation described below. ## What it does `DeployRollupForUpgradeV6.s.sol` deploys the HonkVerifier, the Rollup and its sub-contracts, the EscapeHatch and the `V6UpgradePayload`. Every configuration value is a literal in `_config()`, so reviewing that one function is sufficient to review the deployment — no env-var defaults and no `network-defaults.json` fallbacks can change what gets deployed. Mainnet is the literal; Sepolia is a short override list underneath it, so the only thing to check for Sepolia is that list. A chain matching neither reverts rather than silently deploying mainnet values. `verify(address)` reads every value back off the deployed contracts, so a typo in the table fails loudly rather than at the next governance vote. ## Two things worth reviewing closely **The rollup is constructed owned by governance, not transferred afterwards.** The owner argument also becomes the Slasher's immutable `GOVERNANCE`, which can execute any slash payload with no vote and no delay. Constructing with the deploy key would hand that power to the key permanently, because `transferOwnership` moves only the `Ownable` half. That is why the EscapeHatch is built in the script but installed by the payload, and why no owner-gated setup happens during deployment. **The payload acts on the outgoing rollup before registering v6.** It draws the reservation, forwards the earmark, retunes v5's reward split, and only then calls `Registry.addRollup` and `GSE.addRollup` before carrying the entry-queue flush incentive to a fresh FlushRewarder. The distributor resolves the canonical rollup live, so once v6 is canonical the old rollup can no longer reach the pool and a retune would be settling a chain that has already stopped. ## No sequencer reward calculator at launch `sequencerRewardCalculator` is set to `address(0)` on both chains, so every proposer earns the default sequencer share. Under AZIP-31, governance can install a calculator later via `setSequencerRewardCalculator`, which needs no redeploy. ## The verifier and genesis roots are pinned The rollup verifier is a function of the protocol circuits, and the circuits this branch builds differ from the v6 release build, so a verifier built here would reject every proof the v6 nodes produce. The deploy script therefore deploys a pinned verifier, the same mechanism `v5-next` uses for v5: - `l1-contracts/script/deploy/HonkVerifier.sol` is the verifier from the v6 release build at `42fae4eb3feb5a493f2e8a2baf78b4716e323c40` (`VK_HASH 0x1fd4eb1d14be45e05dc78c448eb4f29d7f63e4de95129c4a5e1058cd9837f8a6`), with a provenance comment naming that commit. `DeployRollupForUpgradeV6.s.sol` imports it as `./HonkVerifier.sol` instead of `@generated/HonkVerifier.sol`. - `l1-contracts/src/core/libraries/ConstantsGen.sol` is committed from the same build. - The three genesis roots in `_config()` come from the same build: | Root | Value | | --- | --- | | `vkTreeRoot` | `0x22fff5de6ce590153df4468f7f0b152188a803d91b3c9c648b91d93d5913f219` | | `protocolContractsHash` | `0x0f54271c52865841a77aaa66036eed901aff22fdce9f08e123ec8b205c9854d7` | | `genesisArchiveRoot` | `0x29eb2c527f8d45276430363214e6c8d709ef3f657a3670ebac3179373d41e5c4` | `ConstantsGen.sol` matches what `v6` itself now generates, including the genesis root from the contract address domain separator change (#25624), so the existing test fixtures pass against it. These four move together: re-pin all of them if the release build moves to a commit that changes the protocol circuits or protocol contracts. Because the verifier is pinned, the script deploys from a clean clone with no circuits build; the runbook's Build section has the exact steps, including running `forge script` rather than a whole-project `forge build`. The pin was exercised end to end on an isolated Sepolia network: deployed from a clean clone of this branch, the deployed verifier's bytecode matched the pinned file exactly and not this branch's own build, all contracts verified on Etherscan, and the payload executed through governance and moved all validators to v6. ## Fee asset price `initialEthPerFeeAsset` is set to `5_934_240` on mainnet (0.0000059342 ETH per AZTEC), read from the AZTEC/ETH Uniswap v4 pool `0xce2899b16743cfd5a954d8122d5e07f410305b1aebee39fd73d9f3b9ebf10c2f` at mainnet block 26148995 (2026-10-08). It only sets the starting point the fee oracle moves from. No configuration values remain outstanding. --------- Co-authored-by: Santiago Palladino <santiago@aztec-labs.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an optional action to the V6 upgrade payload that raises the GSE proof-of-possession gas cap from 250,000 to 300,000.
Why
The GSE checks each new validator's BLS proof of possession during the entry-queue flush, in a call capped at
proofOfPossessionGasLimit. If verification does not fit, the deposit is refunded and the validator is not admitted. The cost varies per key, becausehashToPointis a rejection-sampling loop that calls modexp for a square root on some attempts. Osaka's EIP-7883 repriced modexp from 1,338 to 4,016 gas per call, and the 250k cap was not revisited.Measured under Osaka pricing, which is unchanged on this path in the next fork:
The cost of the raise: a deposit whose verification fails can burn at most 50k more gas in a flush. The GSE is shared, so the new cap applies to every instance from execution onward.
Changes
V6UpgradePayload: new immutablePROOF_OF_POSSESSION_GAS_LIMIT(10th constructor argument). 0 adds no action. A non-zero value must be strictly above the GSE's cap at deployment, so a payload cannot lower it. The action isGSE.setProofOfPossessionGasLimit(PROOF_OF_POSSESSION_GAS_LIMIT), placed last so the numbering of existing actions does not change. It depends on no other action.DeployRollupForUpgradeV6.s.sol:Config.proofOfPossessionGasLimit = 300_000in the mainnet table (Sepolia inherits it); passed to the payload and read back in verification.V6UpgradeSimulation: asserts the cap after execution (or unchanged when 0).V6UpgradePayload.mdandV6_UPGRADE_RUNBOOK.md: the new action, config row, pre-flight checks (current cap and GSE owner) and after-execution check. Also adds the missing earmark/retune rows to the payload's action table.Tests
V6UpgradeAtomicity: executes the full payload through governance and assertsproofOfPossessionGasLimit() == 300_000(red before the change:250000 != 300000).test/fork/MainnetV6PopGasLimit.t.sol(skips withoutMAINNET_FORK_RPC_URL): the live GSE is owned by governance, and the action sets 300k.forge test: 1168 passed. Mainnet dry run of the deploy script passes its simulation check.Notes for #25496
test/script/DeployRollupForUpgradeV6.t.solfails when run on its own onpayload-v6(RollupConfiguration: VK_TREE_ROOT is unset). It is unrelated to this PR: it needs the genesisvm.setEnvlines thatDeployRollupForUpgrade.t.solhas.payload-v6will conflict.Fixes A-2299