Skip to content

feat(l1): v6 rollup upgrade deploy script, payload and runbook - #25601

Merged
spalladino merged 13 commits into
v6from
amin/v6-upgrade-payload
Oct 8, 2026
Merged

spalladino merged 13 commits into
v6from
amin/v6-upgrade-payload

Conversation

@aminsammara

@aminsammara aminsammara commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

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 (feat!: bump the contract address domain separator to v3 (#25521) [port to v6] #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.

Deploys the v6 rollup and the governance payload that makes it canonical,
plus a simulation harness and an operator runbook.

Every configuration value is a literal in `DeployRollupForUpgradeV6._config()`,
so reviewing that one file is sufficient to review the deployment: no env-var
defaults, no `network-defaults.json` fallbacks. Mainnet is the literal and
Sepolia 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 or a drift in how the Rollup
consumes it fails loudly rather than at the next governance vote.

Deploy order is itself a safety property. The rollup is constructed owned by
governance, because 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, and `transferOwnership` moves only the Ownable half. The
EscapeHatch is therefore built here but installed by the payload.

The payload acts on the outgoing rollup before making v6 canonical: it draws
the reservation, forwards the earmark, retunes v5's reward split, then
registers v6 with the Registry and GSE and carries the entry-queue flush
incentive across 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 stopped chain.

Sequencer reward policy is left at `address(0)`. v5 carried per-registry ATP
reductions in the rollup; AZIP-28 moved that to a governance-set calculator,
and no production calculator exists yet, so v6 launches paying every proposer
the default share and governance installs one in a follow-up payload.

The GSE proof-of-possession gas cap is raised to 300k, with a mainnet fork
test covering it.

Known outstanding, documented in the runbook: `initialEthPerFeeAsset` is a
point-in-time price that must be refreshed at deploy time, and the three
genesis roots were read off a build at d521f0d and are stale -- the protocol
circuits have moved, so `vkTreeRoot` must be regenerated before any deploy.
`run()` rejects a zero root but not a stale one.

Co-authored-by: Santiago Palladino <santiago@aztec-labs.com>
@aminsammara
aminsammara force-pushed the amin/v6-upgrade-payload branch from bac497d to c4075b8 Compare October 6, 2026 08:09
…RollupForUpgradeV6

Verifier and ConstantsGen copied from the v6 release build at 9f7f5055e989b21a7da3f512e72f6f004ed09909.
vkTreeRoot regenerated from the same build; protocolContractsHash and genesisArchiveRoot confirmed unchanged.
…esis roots

Build section now describes the pinned artifacts and the clean-clone deploy path
(forge script with --use 0.8.30; a whole-project forge build fails on DeployRollupLib's
@generated import). Input table and _config() comment record the roots from 9f7f5055.
…s multiplier

forge script does not fetch lib/, and its prague-based gas estimates are far below
post-fork contract-creation costs; record measured Sepolia gas for each deployment.
…n verification

--use covers compilation but not --verify, which re-reads foundry.toml's solc path.
Deploy command gains --gas-estimate-multiplier and --slow; add the --resume re-verify path.
…nnet

Forge's prague-based limits are correct before the fork, and a 10x multiplier would push
the rollup creation past the EIP-7825 per-transaction cap. Record the post-fork numbers
separately.
The verifier is copied verbatim from the release build and must stay byte-identical to
it; v5-next excludes its pinned verifier the same way.
…e4eb

The verifier is byte-identical (VK_HASH unchanged); only its provenance comment moves.
vkTreeRoot, protocolContractsHash and genesisArchiveRoot all move with the release build,
and ConstantsGen now matches what v6 generates after the address domain separator change.
…iswap pool

5_934_240 (0.0000059342 ETH per AZTEC), read from the AZTEC/ETH Uniswap v4 pool
0xce2899b1... at mainnet block 26148995. Removes the last TODO in the v6 deploy config.
@spalladino
spalladino merged commit edf8e9a into v6 Oct 8, 2026
8 checks passed
@spalladino
spalladino deleted the amin/v6-upgrade-payload branch October 8, 2026 18:09
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.

2 participants