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
30 changes: 26 additions & 4 deletions governor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ use vortex_common::require_admin;
/// the checkpoint history stays cheap and storage stays predictable.
pub const MAX_CHECKPOINTS: u32 = 64;

/// Persistent entries are bumped once their remaining TTL drops below this
/// many ledgers, extending them back out to `PERSISTENT_TTL_EXTEND_TO`.
pub const PERSISTENT_TTL_THRESHOLD: u32 = 100_000;
/// Target TTL (in ledgers) for persistent entries after a bump.
pub const PERSISTENT_TTL_EXTEND_TO: u32 = 1_000_000;

/// Maximum number of proposals returned by a single `list_proposals` call.
pub const MAX_LIST_LIMIT: u32 = 50;

Expand Down Expand Up @@ -266,7 +272,9 @@ impl Governor {
function,
calldata,
};
env.storage().persistent().set(&Self::proposal_key(&env, id), &proposal);
let proposal_key = Self::proposal_key(&env, id);
env.storage().persistent().set(&proposal_key, &proposal);
Self::bump_persistent_ttl(&env, &proposal_key);
env.storage().instance().set(&Symbol::new(&env, "count"), &(id + 1));
env.events().publish(
(symbol_short!("propose"),),
Expand Down Expand Up @@ -364,7 +372,10 @@ impl Governor {
}
}
env.storage().persistent().set(&vote_key, &power);
env.storage().persistent().set(&Self::proposal_key(&env, proposal_id), &proposal);
Self::bump_persistent_ttl(&env, &vote_key);
let proposal_key = Self::proposal_key(&env, proposal_id);
env.storage().persistent().set(&proposal_key, &proposal);
Self::bump_persistent_ttl(&env, &proposal_key);
env.events().publish(
(symbol_short!("vote"),),
(proposal_id, voter.clone(), support, power),
Expand Down Expand Up @@ -393,7 +404,9 @@ impl Governor {
(proposal.target.clone(), proposal.function.clone(), args, eta).into_val(&env),
);
proposal.eta = eta;
env.storage().persistent().set(&Self::proposal_key(&env, proposal_id), &proposal);
let proposal_key = Self::proposal_key(&env, proposal_id);
env.storage().persistent().set(&proposal_key, &proposal);
Self::bump_persistent_ttl(&env, &proposal_key);
env.events()
.publish((symbol_short!("queue"),), (proposal_id, proposal.eta));
Ok(())
Expand Down Expand Up @@ -426,7 +439,9 @@ impl Governor {
.into_val(&env),
);
proposal.executed = true;
env.storage().persistent().set(&Self::proposal_key(&env, proposal_id), &proposal);
let proposal_key = Self::proposal_key(&env, proposal_id);
env.storage().persistent().set(&proposal_key, &proposal);
Self::bump_persistent_ttl(&env, &proposal_key);
env.events().publish(
(symbol_short!("execute"),),
(proposal_id, proposal.target.clone(), proposal.function.clone()),
Expand Down Expand Up @@ -648,6 +663,13 @@ impl Governor {
}
}

/// Extend the TTL of a persistent entry if it is close to expiring.
fn bump_persistent_ttl(env: &Env, key: &impl soroban_sdk::IntoVal<Env, Val>) {
env.storage()
.persistent()
.extend_ttl(key, PERSISTENT_TTL_THRESHOLD, PERSISTENT_TTL_EXTEND_TO);
}

/// Compute a proposal's deadline, guarding against ledger overflow.
fn deadline(env: &Env, snapshot: u32, config: &GovernorConfig) -> Result<u32, GovernorError> {
let _ = env;
Expand Down
30 changes: 24 additions & 6 deletions timelock/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ const ROLE_KEY: Symbol = symbol_short!("Role");
/// Default grace period after `ready_at` during which an operation may execute.
const DEFAULT_GRACE_PERIOD: u64 = 14 * 24 * 60 * 60;

/// Number of ledgers below which a persistent entry's TTL is bumped on write.
const PERSISTENT_TTL_THRESHOLD: u32 = 100_000;
/// Number of ledgers a persistent entry's TTL is extended to on write.
const PERSISTENT_TTL_EXTEND_TO: u32 = 500_000;

#[contract]
pub struct TimelockController;

Expand Down Expand Up @@ -131,6 +136,19 @@ impl TimelockController {
}
}

// ---------------------------------------------------------------------
// TTL helpers
// ---------------------------------------------------------------------

/// Bump the TTL of a persistent storage key so it does not get archived.
fn bump_persistent_ttl<K: soroban_sdk::IntoVal<Env, Val>>(env: &Env, key: &K) {
env.storage().persistent().extend_ttl(
key,
PERSISTENT_TTL_THRESHOLD,
PERSISTENT_TTL_EXTEND_TO,
);
}

// ---------------------------------------------------------------------
// Role management (must be authorised by the timelock itself)
// ---------------------------------------------------------------------
Expand All @@ -142,9 +160,9 @@ impl TimelockController {

pub fn revoke_role(env: Env, role: Symbol, account: Address) {
Self::require_admin(&env);
env.storage()
.persistent()
.set(&(ROLE_KEY, role, account), &false);
let key = (ROLE_KEY, role, account);
env.storage().persistent().set(&key, &false);
Self::bump_persistent_ttl(&env, &key);
}

pub fn has_role(env: Env, role: Symbol, account: Address) -> bool {
Expand Down Expand Up @@ -239,9 +257,9 @@ impl TimelockController {
executed: false,
};

env.storage()
.persistent()
.set(&(OP_KEY, id.clone()), &operation);
let op_key = (OP_KEY, id.clone());
env.storage().persistent().set(&op_key, &operation);
Self::bump_persistent_ttl(&env, &op_key);

env.events()
.publish((symbol_short!("scheduled"), id.clone()), id.clone());
Expand Down
Loading