feat: add upgrade timelock, guardian veto, and rollback history - #64
Merged
bakarezainab merged 1 commit intoJul 29, 2026
Merged
Conversation
Add a mandatory timelock state machine gating contract WASM upgrades: propose_upgrade stores a pending proposal with a configurable delay (default 48h, 1h safety floor), execute_upgrade rejects early calls and only swaps the WASM once the timelock has elapsed, and veto_upgrade lets the admin or an emergency guardian cancel a pending proposal before it can execute. The guardian is modeled as a new access_control::Role::Guardian on the existing role registry rather than a parallel role system. Every applied upgrade is appended to an on-chain history log so a rollback is just a propose_upgrade call with a previously recorded hash, going through the same timelock and veto protections as any other upgrade. Closes LatterFixxx#37
3 tasks
3 tasks
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.
Summary
Implements a mandatory timelock state machine for contract WASM upgrades, per the acceptance criteria in #37.
propose_upgrade(new_wasm_hash)(admin-only) stores a single pending proposal withready_at = now + timelock.execute_upgrade()reverts if called beforeready_at, or if the proposal was already executed or vetoed. Only one proposal can be in flight at a time.set_upgrade_timelock(admin-only), defaulting to 48 hours, with a 1-hour safety floor so it can't be configured down to (near) zero.veto_upgrade: cancels the pending proposal during its timelock window. Callable by the contract admin (self-correction of a mistaken proposal) or any address holding the newRole::Guardian(the actual emergency-stop path against a malicious/compromised admin-issued proposal).access_controlrole registry —Role::Guardianis a new variant, installed the normal way viagrant_role(admin, guardian_address, Role::Guardian).get_upgrade_history), capped at the most recent 50 entries. There's no separate "rollback" bypass — rolling back means callingpropose_upgradeagain with a previously recorded hash, going through the exact same timelock + veto flow as any forward upgrade, since a rollback deserves no less scrutiny than a forward upgrade. Design rationale is documented in theupgrade.rsmodule doc comment.New module:
src/upgrade.rs, wired intoTaskManagerContractinsrc/lib.rsaspropose_upgrade,execute_upgrade,veto_upgrade,get_upgrade_timelock,set_upgrade_timelock,get_pending_upgrade,get_upgrade_history. New events insrc/events.rs(upg_prop,upg_exec,upg_veto,upg_tl).Test plan
New
src/upgrade_test.rs(15 tests, all passing):propose_upgradesetsPendingstatus with the correctready_atpropose_upgraderejects non-admin callerspropose_upgraderejects a second proposal while one is still pendingexecute_upgraderejects premature execution (immediately after proposing, and one second beforeready_at)execute_upgradesucceeds once the timelock has elapsed, applies the WASM swap, and records a history entryexecute_upgraderejects non-admin callersexecute_upgraderejects a proposal that's already executedveto_upgradeby a guardian blocks execution even after the timelock would have elapsedveto_upgradealso works for admin self-correctionveto_upgraderejects unauthorized (non-admin, non-guardian) callersveto_upgraderejects when there's no pending proposalset_upgrade_timelockchanges the delay applied to future proposalsset_upgrade_timelockrejects values below the 1-hour safety floorset_upgrade_timelockrejects non-admin callersVerified from the contract crate dir:
cargo test— 78 passed (63 pre-existing + 15 new), 0 failedcargo clippy --all-targets -- -D warnings— cleancargo fmt --checkon the touched files (upgrade.rs,upgrade_test.rs,access_control.rs,events.rs,lib.rs) — cleanNote:
cargo fmt --checkon the full crate surfaces pre-existing formatting drift inmultisig.rs,multisig_test.rs,test.rs, andtwap_oracle.rsunrelated to this change — left untouched as out of scope.Closes #37