Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/access_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ pub enum Role {
Manager,
Moderator,
Verifier,
/// Emergency guardian: authorized to veto a pending contract WASM
/// upgrade during its timelock window (see `upgrade.rs`). Deliberately
/// separate from `Admin` so upgrade proposals can be checked by a party
/// other than the one proposing them.
Guardian,
}

#[contracttype]
Expand Down
59 changes: 43 additions & 16 deletions src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,7 @@ pub fn emit_proposal_executed(env: &Env, proposal_id: u32, passed: bool) {
// `prop_*` governance topics above so off-chain indexers can separate
// community proposals from privileged admin transactions.

pub fn emit_multisig_configured(
env: &Env,
admin: Address,
signer_count: u32,
threshold: u32,
) {
pub fn emit_multisig_configured(env: &Env, admin: Address, signer_count: u32, threshold: u32) {
env.events().publish(
(symbol_short!("ms_cfg"), admin),
(signer_count, threshold, env.ledger().timestamp()),
Expand Down Expand Up @@ -181,22 +176,14 @@ pub fn emit_multisig_approved(
);
}

pub fn emit_multisig_executed(
env: &Env,
proposal_id: u32,
executor: Address,
) {
pub fn emit_multisig_executed(env: &Env, proposal_id: u32, executor: Address) {
env.events().publish(
(symbol_short!("ms_exec"), proposal_id),
(executor, env.ledger().timestamp()),
);
}

pub fn emit_multisig_cancelled(
env: &Env,
proposal_id: u32,
caller: Address,
) {
pub fn emit_multisig_cancelled(env: &Env, proposal_id: u32, caller: Address) {
env.events().publish(
(symbol_short!("ms_cancl"), proposal_id),
(caller, env.ledger().timestamp()),
Expand Down Expand Up @@ -370,3 +357,43 @@ pub fn emit_vault_claim(env: &Env, claimant: Address, token: Address, amount: i1
(claimant, amount, env.ledger().timestamp()),
);
}

// ── Upgrade Timelock Events ────────────────────────────────────────────────

pub fn emit_upgrade_proposed(
env: &Env,
wasm_hash: soroban_sdk::BytesN<32>,
proposed_by: Address,
ready_at: u64,
) {
env.events().publish(
(symbol_short!("upg_prop"), proposed_by),
(wasm_hash, ready_at, env.ledger().timestamp()),
);
}

pub fn emit_upgrade_executed(env: &Env, wasm_hash: soroban_sdk::BytesN<32>, executed_by: Address) {
env.events().publish(
(symbol_short!("upg_exec"), executed_by),
(wasm_hash, env.ledger().timestamp()),
);
}

pub fn emit_upgrade_vetoed(env: &Env, wasm_hash: soroban_sdk::BytesN<32>, vetoed_by: Address) {
env.events().publish(
(symbol_short!("upg_veto"), vetoed_by),
(wasm_hash, env.ledger().timestamp()),
);
}

pub fn emit_upgrade_timelock_updated(
env: &Env,
old_seconds: u64,
new_seconds: u64,
updated_by: Address,
) {
env.events().publish(
(symbol_short!("upg_tl"), updated_by),
(old_seconds, new_seconds, env.ledger().timestamp()),
);
}
79 changes: 68 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ pub mod multisig;
pub mod pausable;
pub mod reputation;
pub mod storage;
pub mod twap_oracle;
pub mod swap_router;
pub mod twap_oracle;
pub mod upgrade;
pub mod user_profile;
pub mod vault;
pub mod zkp_attestation;
Expand All @@ -24,6 +25,8 @@ mod multisig_test;
mod swap_router_test;
#[cfg(test)]
mod test;
#[cfg(test)]
mod upgrade_test;

use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String, Vec};

Expand Down Expand Up @@ -820,12 +823,8 @@ impl TaskManagerContract {
description: String,
action: multisig::MultisigAction,
) -> u32 {
let proposal_id = multisig::propose(
env.clone(),
proposer.clone(),
description.clone(),
action,
);
let proposal_id =
multisig::propose(env.clone(), proposer.clone(), description.clone(), action);

let threshold = multisig::get_config(&env).threshold;
events::emit_multisig_proposed(&env, proposal_id, proposer, description, threshold);
Expand Down Expand Up @@ -870,10 +869,7 @@ impl TaskManagerContract {
events::emit_multisig_cancelled(&env, proposal_id, caller);
}

pub fn get_multisig_proposal(
env: Env,
proposal_id: u32,
) -> Option<multisig::MultisigProposal> {
pub fn get_multisig_proposal(env: Env, proposal_id: u32) -> Option<multisig::MultisigProposal> {
multisig::get_proposal(&env, proposal_id)
}

Expand Down Expand Up @@ -1133,6 +1129,66 @@ impl TaskManagerContract {
claimant.require_auth();
vault::claim_payroll(&env, claimant, token, payroll_id, amount, proof);
}

// ========================================================================
// Upgrade Timelock & Rollback Guard
// ========================================================================

/// Read the currently configured upgrade timelock delay, in seconds.
pub fn get_upgrade_timelock(env: Env) -> u64 {
upgrade::get_timelock_seconds(&env)
}

/// Reconfigure the upgrade timelock delay. Admin-only; rejects values
/// below `upgrade::MIN_TIMELOCK_SECONDS`.
pub fn set_upgrade_timelock(env: Env, admin: Address, seconds: u64) {
let old_seconds = upgrade::get_timelock_seconds(&env);
let new_seconds = upgrade::set_timelock_seconds(env.clone(), admin.clone(), seconds);
events::emit_upgrade_timelock_updated(&env, old_seconds, new_seconds, admin);
}

/// Propose upgrading the contract to `new_wasm_hash`. Admin-only. Starts
/// the mandatory timelock window; `execute_upgrade` will reject any
/// attempt to apply the upgrade before `ready_at`.
pub fn propose_upgrade(
env: Env,
admin: Address,
new_wasm_hash: soroban_sdk::BytesN<32>,
) -> upgrade::UpgradeProposal {
let proposal = upgrade::propose_upgrade(env.clone(), admin.clone(), new_wasm_hash.clone());
events::emit_upgrade_proposed(&env, new_wasm_hash, admin, proposal.ready_at);
proposal
}

/// Veto the currently pending upgrade proposal. Callable by the admin or
/// any address holding the `Guardian` role during the timelock window.
pub fn veto_upgrade(env: Env, guardian: Address) -> upgrade::UpgradeProposal {
let proposal = upgrade::veto_upgrade(env.clone(), guardian.clone());
events::emit_upgrade_vetoed(&env, proposal.wasm_hash.clone(), guardian);
proposal
}

/// Execute the pending upgrade proposal once its timelock has elapsed.
/// Admin-only. Reverts if called early, or if the proposal was already
/// executed or vetoed.
pub fn execute_upgrade(env: Env, admin: Address) -> soroban_sdk::BytesN<32> {
let wasm_hash = upgrade::execute_upgrade(env.clone(), admin.clone());
events::emit_upgrade_executed(&env, wasm_hash.clone(), admin);
wasm_hash
}

/// Current state of the (single) pending/most-recently-resolved upgrade
/// proposal, if any has ever been created.
pub fn get_pending_upgrade(env: Env) -> Option<upgrade::UpgradeProposal> {
upgrade::get_pending_upgrade(&env)
}

/// Append-only log of WASM hashes this contract has actually been
/// upgraded to. Used to identify a hash to roll back to via
/// `propose_upgrade`.
pub fn get_upgrade_history(env: Env) -> Vec<upgrade::UpgradeHistoryEntry> {
upgrade::get_upgrade_history(&env)
}
}

// ============================================================================
Expand All @@ -1145,6 +1201,7 @@ fn format_role(env: &Env, role: &access_control::Role) -> String {
access_control::Role::Manager => String::from_str(env, "Manager"),
access_control::Role::Moderator => String::from_str(env, "Moderator"),
access_control::Role::Verifier => String::from_str(env, "Verifier"),
access_control::Role::Guardian => String::from_str(env, "Guardian"),
}
}

Expand Down
Loading
Loading