Skip to content

feat: prevent gov update from bricking rollup#22656

Open
just-mitch wants to merge 1 commit intonextfrom
mitch/tmnt-521-rollup-gate-contract
Open

feat: prevent gov update from bricking rollup#22656
just-mitch wants to merge 1 commit intonextfrom
mitch/tmnt-521-rollup-gate-contract

Conversation

@just-mitch
Copy link
Copy Markdown
Collaborator

@just-mitch just-mitch commented Apr 20, 2026

Implements AZIP-2.

The shared theme: governance and rollup-config setters that could mute or strand the escape hatch — or strand validators or rewards — are removed, made one-shot, delay-gated, or rate-limited.

Escape hatch (one-shot): updateEscapeHatchsetEscapeHatch. Reverts on address(0) and on any second call. Once a rollup has an escape hatch, it cannot be replaced or removed; rollups that want no escape hatch simply never call the setter. EscapeHatchUpdatedEscapeHatchSet. New errors ValidatorSelection__EscapeHatchAlreadySet, ValidatorSelection__EscapeHatchCannotBeZero.

Reward distributor — per-rollup earmarking + canonical inheritance: Anyone can subsidize block production on a specific rollup instance, regardless of whether it is canonical.

  • subsidizeRollup(rollup, amount) is permissionless and earmarks ASSET to a specific rollup in specificRollupBalance[rollup] (tracked in totalEarmarkedBalance).
  • ASSET sent to the contract directly forms an unearmarked pool, implicitly available to whichever rollup is canonical at the time.
  • availableTo(rollup) = (balance - totalEarmarked) + specificRollupBalance[rollup] for the canonical rollup; just specificRollupBalance[rollup] otherwise.
  • claim(to, amount) is now _amount <= availableTo(msg.sender). Old (non-canonical) rollups can still drain anything earmarked to them across rotations.
  • recover(fromRollup, to, amount) (was recover(asset, to, amount)) mirrors claim's accounting under owner gating. The non-ASSET path moves to recoverWrongAsset(asset, to, amount), which refuses ASSET so it cannot bypass the accounting.
  • New errors: RewardDistributor__InsufficientAvailable, RewardDistributor__ZeroRollup.

Reward config — addresses immutable post-deployment: setRewardConfig no longer takes a full RewardConfig. It takes MutableRewardConfig, which exposes only sequencerBps and checkpointReward. The rewardDistributor and booster addresses are written exactly once in the constructor (RewardLib.initializeConfig) and immutable thereafter. Rotating either requires redeploying the rollup via Registry.addRollup. The post-deployment writer is RewardLib.updateConfig. RewardConfigUpdated event signature follows.

setProvingCostPerMana — rate-limited: floor of 2 (MIN_PROVING_COST_PER_MANA), 30-day cooldown (first post-init update waived), symmetric 3/2 multiplicative step. FeeStore gains uint64 provingCostLastUpdate. New errors FeeLib__ProvingCostBelowFloor, FeeLib__ProvingCostCooldown, FeeLib__ProvingCostStepExceeded. With 3/2 per 30 days, the value needs ~170 days to move 10× and ~340 days to move 100×.

Staking queue invariants — enforced on every write: assertValidQueueConfig lifted into StakingLib and called from both the constructor and updateStakingQueueConfig. normalFlushSizeMin > 0 and normalFlushSizeQuotient > 0 for the life of the rollup; the path that could close deposits on a running rollup is gone.

Slasher swap — 60-day timelock: setSlasher removed, replaced by queueSetSlasher (owner) → cancelSetSlasher (owner) | finalizeSetSlasher (permissionless). SLASHER_EXECUTION_DELAY = 60 days exceeds the ~38-day withdrawal window so validators who object can exit before the change lands. Queueing while a change is pending overwrites it and resets the timer. New events PendingSlasherQueued, PendingSlasherCancelled. New errors Staking__NoPendingSlasher, Staking__SlasherNotReady. New views getPendingSlasher, getSlasherExecutionDelay.

setLocalEjectionThreshold — removed: mutator gone entirely; reader stays. Threshold is fixed at deploy.

updateManaTarget is deliberately left otherwise unchanged — its worst-case outcomes no longer mute the escape hatch.

Test plan

  • New unit + integration coverage:
    • setEscapeHatchOneShot.t.sol — every transition of the one-shot guard, including zero-then-nonzero.
    • ProvingCostRateLimit.t.sol — floor, step boundaries (up and down), cooldown boundaries, and a 10-step amplification check that bounds (3/2)^10 growth.
    • setSlasher.t.sol — queue/cancel/finalize states, finalize permissionless, overwrite-pending semantics.
    • setLocalEjectionThresholdRemoval.t.sol — selector unreachable post-removal.
    • slash.t.sol — new SlashLocalEjectionTest deploys with a non-zero threshold to exercise the local-ejection path.
    • updateStakingQueueConfig.t.sol — invalid-config reverts.
    • initialize.t.sol — proving-cost floor enforced at construction.
  • Reward distributor: claim.t.sol, recover.t.sol, subsidizeRollup.t.sol cover boundary, multi-rollup isolation, and canonical-rotation semantics. New invariant.t.sol adds a stateful fuzz handler asserting three accounting identities (balance ≥ totalEarmarked, sum of specifics == totalEarmarked, canonical availableTo identity) under random subsidize/claim/recover/donate/rotate sequences.
  • Yarn-project: rollup_cheat_codes.ts gains clearProvingCostCooldown for tests that need to bump proving cost more than once; slash_veto_demo.test.ts deploys the slasher with the correct vetoer up front instead of swapping mid-test (now that setSlasher is gone).

@just-mitch just-mitch force-pushed the mitch/tmnt-521-rollup-gate-contract branch 5 times, most recently from 504903a to a019231 Compare April 20, 2026 19:21
@just-mitch just-mitch changed the title WIP: feat: prevent gov update from bricking rollup feat: prevent gov update from bricking rollup Apr 21, 2026
@just-mitch just-mitch added the ci-full Run all master checks. label Apr 21, 2026
@just-mitch just-mitch force-pushed the mitch/tmnt-521-rollup-gate-contract branch from a019231 to a45292e Compare April 21, 2026 00:25
@just-mitch just-mitch force-pushed the mitch/tmnt-521-rollup-gate-contract branch from 336da25 to 5179bda Compare May 7, 2026 01:15
@just-mitch just-mitch force-pushed the mitch/tmnt-521-rollup-gate-contract branch from 8c89fa4 to 6805039 Compare May 7, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-full Run all master checks.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant