Skip to content
Closed
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
10 changes: 10 additions & 0 deletions src/governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ pub fn verify_upgrade_quorum(env: &Env, signers: &Vec<Address>) -> Result<(), Co
.get(&SIGNERS_KEY)
.unwrap_or_else(|| Map::new(env));

let mut valid_count: u32 = 0;
for signer in signers.iter() {
if signer == data.admin || authorized_signers.contains_key(signer.clone()) {
valid_count += 1;
}
}

if valid_count < config.quorum_threshold {
return Err(ContractError::ThresholdNotReached);
}
// Check both legacy count-based and new weight-based quorum
let config = get_governance_config(env);
let multisig_config = get_multisig_config(env);
Expand Down
65 changes: 65 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,41 @@ impl TimeLockedUpgradeContract {
governance::get_ballot(&env, REVOCATION_KEY)
}

/// Return the active governance configuration (quorum threshold).
pub fn get_governance_config(env: Env) -> governance::GovernanceConfig {
governance::get_governance_config(&env)
}

/// Set the governance quorum threshold for WASM upgrade proposals.
/// Requires admin authorization.
pub fn set_governance_config(
env: Env,
admin: Address,
config: governance::GovernanceConfig,
) -> Result<(), ContractError> {
let data = Self::_load_data(&env)?;
if data.admin != admin {
return Err(ContractError::NotAdmin);
}
admin.require_auth();
governance::set_governance_config(&env, &config);
env.events().publish(
(symbol_short!("GVN_CFG_UPD"),),
(admin, config.quorum_threshold),
);
Self::_extend_instance_ttl(&env);
Ok(())
}

/// Return the active governance upgrade proposal, if one exists.
pub fn get_governance_upgrade_proposal(env: Env) -> Option<governance::GovernanceUpgradeProposal> {
env.storage()
.instance()
.get(&crate::governance::GOVERNANCE_UPGRADE_KEY)
}

// --- Core Logic Boilerplate ---

fn _load_data(env: &Env) -> Result<ContractData, ContractError> {
let _ = ensure_schema_version(env);
env.storage().instance().get(&DATA_KEY).ok_or(ContractError::NotInitialized)
Expand All @@ -432,6 +467,11 @@ impl TimeLockedUpgradeContract {
Self::_load_data(&env)
}

fn _load_data(env: &Env) -> Result<ContractData, ContractError> {
env.storage().instance().get(&DATA_KEY).ok_or(ContractError::NotInitialized)
}

pub fn propose_upgrade(env: Env, new_wasm_hash: BytesN<32>, proposer: Address, signers: Vec<Address>, nonce: u64, salt: Bytes, salt_signature: BytesN<32>, sig_expires_at: u64) -> Result<(), ContractError> {
pub fn propose_upgrade(
env: Env, new_wasm_hash: BytesN<32>, proposer: Address,
nonce: u64, salt: Bytes, salt_signature: BytesN<32>, sig_expires_at: u64,
Expand All @@ -443,6 +483,23 @@ impl TimeLockedUpgradeContract {
if data.admin != proposer { return Err(ContractError::NotAdmin); }
proposer.require_auth();
consume_nonce(&env, &proposer, nonce, salt, salt_signature)?;
verify_upgrade_quorum(&env, &signers)?;
let proposal = GovernanceUpgradeProposal {
new_wasm_hash: new_wasm_hash.clone(),
proposer: proposer.clone(),
staged_at: env.ledger().timestamp(),
signers: signers.clone(),
};
env.storage().instance().set(&crate::governance::GOVERNANCE_UPGRADE_KEY, &proposal);
env.events().publish(
(symbol_short!("GV_UPG_PROPOSED"),),
(new_wasm_hash, proposer, signers, env.ledger().timestamp()),
);
let staged = StagedUpgrade {
new_wasm_hash,
proposer,
staged_at: env.ledger().timestamp(),
};
let staged = StagedUpgrade { new_wasm_hash, proposer, staged_at: env.ledger().timestamp() };
env.storage().instance().set(&PENDING_UPGRADE_KEY, &staged);
Ok(())
Expand All @@ -458,6 +515,12 @@ impl TimeLockedUpgradeContract {
if data.admin != executor { return Err(ContractError::NotAdmin); }
executor.require_auth();
consume_nonce(&env, &executor, nonce, salt, signature)?;
let proposal: GovernanceUpgradeProposal = env
.storage()
.instance()
.get(&crate::governance::GOVERNANCE_UPGRADE_KEY)
.ok_or(ContractError::NoPendingUpgrade)?;
verify_upgrade_quorum(&env, &proposal.signers)?;
let pending: StagedUpgrade = env.storage().instance()
let pending: StagedUpgrade = env
.storage()
Expand All @@ -473,6 +536,8 @@ impl TimeLockedUpgradeContract {
// Run post-upgrade diagnostic health checks
Self::_run_post_upgrade_health_check(&env, pre_upgrade_data)?;
env.storage().instance().remove(&PENDING_UPGRADE_KEY);
env.storage().instance().remove(&crate::governance::GOVERNANCE_UPGRADE_KEY);
Self::_extend_instance_ttl(&env);
crate::core::instance::bump_instance_ttl(&env);
Ok(())
}
Expand Down
Loading