diff --git a/.changelog/unreleased/improvements/3842-split-pos-params.md b/.changelog/unreleased/improvements/3842-split-pos-params.md new file mode 100644 index 0000000000..9ddaa455c4 --- /dev/null +++ b/.changelog/unreleased/improvements/3842-split-pos-params.md @@ -0,0 +1,2 @@ +- Store each PoS parameters at individual sub-key. + ([\#3842](https://github.com/anoma/namada/pull/3842)) \ No newline at end of file diff --git a/crates/proof_of_stake/src/parameters.rs b/crates/proof_of_stake/src/parameters.rs index 1e1032dbe5..2b80abc8bd 100644 --- a/crates/proof_of_stake/src/parameters.rs +++ b/crates/proof_of_stake/src/parameters.rs @@ -13,9 +13,12 @@ use namada_governance::parameters::GovernanceParameters; use namada_macros::BorshDeserializer; #[cfg(feature = "migrations")] use namada_migrations::*; +use namada_state::{Result, StorageRead, StorageWrite}; use serde::Serialize; use thiserror::Error; +use crate::storage_key; + /// Proof-of-Stake system parameters. This includes parameters that are used in /// PoS but are read from other accounts storage (governance). #[derive( @@ -82,6 +85,343 @@ pub struct OwnedPosParams { pub rewards_gain_d: Dec, } +/// Read "max_validator_slots" parameter +pub fn read_max_validator_slots_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_max_validator_slots_key())? + .expect("Required \"max_validator_slots\" param")) +} + +/// Read "pipeline_len" parameter +pub fn read_pipeline_len_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_pipeline_len_key())? + .expect("Required \"pipeline_len\" param")) +} + +/// Read "unbonding_len" parameter +pub fn read_unbonding_len_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_unbonding_len_key())? + .expect("Required \"unbonding_len\" param")) +} + +/// Read "tm_votes_per_token" parameter +pub fn read_tm_votes_per_token_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_tm_votes_per_token_key())? + .expect("Required \"tm_votes_per_token\" param")) +} + +/// Read "block_proposer_reward" parameter +pub fn read_block_proposer_reward_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_block_proposer_reward_key())? + .expect("Required \"block_proposer_reward\" param")) +} + +/// Read "block_vote_reward" parameter +pub fn read_block_vote_reward_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_block_vote_reward_key())? + .expect("Required \"block_vote_reward\" param")) +} + +/// Read "max_inflation_rate" parameter +pub fn read_max_inflation_rate_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_max_inflation_rate_key())? + .expect("Required \"max_inflation_rate\" param")) +} + +/// Read "target_staked_ratio" parameter +pub fn read_target_staked_ratio_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_target_staked_ratio_key())? + .expect("Required \"target_staked_ratio\" param")) +} + +/// Read "duplicate_vote_min_slash_rate" parameter +pub fn read_duplicate_vote_min_slash_rate_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_duplicate_vote_min_slash_rate_key())? + .expect("Required \"duplicate_vote_min_slash_rate\" param")) +} + +/// Read "light_client_attack_min_slash_rate" parameter +pub fn read_light_client_attack_min_slash_rate_param( + storage: &S, +) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_light_client_attack_min_slash_rate_key())? + .expect("Required \"light_client_attack_min_slash_rate\" param")) +} + +/// Read "cubic_slashing_window_length" parameter +pub fn read_cubic_slashing_window_length_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_cubic_slashing_window_length_key())? + .expect("Required \"cubic_slashing_window_length\" param")) +} + +/// Read "validator_stake_threshold" parameter +pub fn read_validator_stake_threshold_param( + storage: &S, +) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_validator_stake_threshold_key())? + .expect("Required \"validator_stake_threshold\" param")) +} + +/// Read "liveness_window_check" parameter +pub fn read_liveness_window_check_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_liveness_window_check_key())? + .expect("Required \"liveness_window_check\" param")) +} + +/// Read "liveness_threshold" parameter +pub fn read_liveness_threshold_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_liveness_threshold_key())? + .expect("Required \"liveness_threshold\" param")) +} + +/// Read "rewards_gain_p" parameter +pub fn read_rewards_gain_p_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_rewards_gain_p_key())? + .expect("Required \"rewards_gain_p\" param")) +} + +/// Read "rewards_gain_d" parameter +pub fn read_rewards_gain_d_param(storage: &S) -> Result +where + S: StorageRead, +{ + Ok(storage + .read(&storage_key::param_rewards_gain_d_key())? + .expect("Required \"rewards_gain_d\" param")) +} + +/// Write "max_validator_slots" parameter +pub fn write_max_validator_slots_param( + storage: &mut S, + value: u64, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_max_validator_slots_key(), value) +} + +/// Write "pipeline_len" parameter +pub fn write_pipeline_len_param(storage: &mut S, value: u64) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_pipeline_len_key(), value) +} + +/// Write "unbonding_len" parameter +pub fn write_unbonding_len_param(storage: &mut S, value: u64) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_unbonding_len_key(), value) +} + +/// Write "tm_votes_per_token" parameter +pub fn write_tm_votes_per_token_param( + storage: &mut S, + value: Dec, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_tm_votes_per_token_key(), value) +} + +/// Write "block_proposer_reward" parameter +pub fn write_block_proposer_reward_param( + storage: &mut S, + value: Dec, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_block_proposer_reward_key(), value) +} + +/// Write "block_vote_reward" parameter +pub fn write_block_vote_reward_param( + storage: &mut S, + value: Dec, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_block_vote_reward_key(), value) +} + +/// Write "max_inflation_rate" parameter +pub fn write_max_inflation_rate_param( + storage: &mut S, + value: Dec, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_max_inflation_rate_key(), value) +} + +/// Write "target_staked_ratio" parameter +pub fn write_target_staked_ratio_param( + storage: &mut S, + value: Dec, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_target_staked_ratio_key(), value) +} + +/// Write "duplicate_vote_min_slash_rate" parameter +pub fn write_duplicate_vote_min_slash_rate_param( + storage: &mut S, + value: Dec, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write( + &storage_key::param_duplicate_vote_min_slash_rate_key(), + value, + ) +} + +/// Write "light_client_attack_min_slash_rate" parameter +pub fn write_light_client_attack_min_slash_rate_param( + storage: &mut S, + value: Dec, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write( + &storage_key::param_light_client_attack_min_slash_rate_key(), + value, + ) +} + +/// Write "cubic_slashing_window_length" parameter +pub fn write_cubic_slashing_window_length_param( + storage: &mut S, + value: u64, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write( + &storage_key::param_cubic_slashing_window_length_key(), + value, + ) +} + +/// Write "validator_stake_threshold" parameter +pub fn write_validator_stake_threshold_param( + storage: &mut S, + value: token::Amount, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_validator_stake_threshold_key(), value) +} + +/// Write "liveness_window_check" parameter +pub fn write_liveness_window_check_param( + storage: &mut S, + value: u64, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_liveness_window_check_key(), value) +} + +/// Write "liveness_threshold" parameter +pub fn write_liveness_threshold_param( + storage: &mut S, + value: Dec, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_liveness_threshold_key(), value) +} + +/// Write "rewards_gain_p" parameter +pub fn write_rewards_gain_p_param(storage: &mut S, value: Dec) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_rewards_gain_p_key(), value) +} + +/// Write "rewards_gain_d" parameter +pub fn write_rewards_gain_d_param(storage: &mut S, value: Dec) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + storage.write(&storage_key::param_rewards_gain_d_key(), value) +} + impl Default for OwnedPosParams { fn default() -> Self { Self { diff --git a/crates/proof_of_stake/src/storage.rs b/crates/proof_of_stake/src/storage.rs index a3d0a61cea..848ddbf60a 100644 --- a/crates/proof_of_stake/src/storage.rs +++ b/crates/proof_of_stake/src/storage.rs @@ -14,6 +14,27 @@ use namada_core::token; use namada_systems::governance; use crate::lazy_map::NestedSubKey; +pub use crate::parameters::{ + read_block_proposer_reward_param, read_block_vote_reward_param, + read_cubic_slashing_window_length_param, + read_duplicate_vote_min_slash_rate_param, + read_light_client_attack_min_slash_rate_param, + read_liveness_threshold_param, read_liveness_window_check_param, + read_max_inflation_rate_param, read_max_validator_slots_param, + read_pipeline_len_param, read_rewards_gain_d_param, + read_rewards_gain_p_param, read_target_staked_ratio_param, + read_tm_votes_per_token_param, read_unbonding_len_param, + read_validator_stake_threshold_param, write_block_proposer_reward_param, + write_block_vote_reward_param, write_cubic_slashing_window_length_param, + write_duplicate_vote_min_slash_rate_param, + write_light_client_attack_min_slash_rate_param, + write_liveness_threshold_param, write_liveness_window_check_param, + write_max_inflation_rate_param, write_max_validator_slots_param, + write_pipeline_len_param, write_rewards_gain_d_param, + write_rewards_gain_p_param, write_target_staked_ratio_param, + write_tm_votes_per_token_param, write_unbonding_len_param, + write_validator_stake_threshold_param, +}; use crate::storage_key::consensus_keys_key; use crate::types::{ BelowCapacityValidatorSets, BondId, Bonds, CommissionRates, @@ -265,9 +286,45 @@ pub fn read_owned_pos_params(storage: &S) -> Result where S: StorageRead, { - Ok(storage - .read(&storage_key::params_key())? - .expect("PosParams should always exist in storage after genesis")) + let max_validator_slots = read_max_validator_slots_param(storage)?; + let pipeline_len = read_pipeline_len_param(storage)?; + let unbonding_len = read_unbonding_len_param(storage)?; + let tm_votes_per_token = read_tm_votes_per_token_param(storage)?; + let block_proposer_reward = read_block_proposer_reward_param(storage)?; + let block_vote_reward = read_block_vote_reward_param(storage)?; + let max_inflation_rate = read_max_inflation_rate_param(storage)?; + let target_staked_ratio = read_target_staked_ratio_param(storage)?; + let duplicate_vote_min_slash_rate = + read_duplicate_vote_min_slash_rate_param(storage)?; + let light_client_attack_min_slash_rate = + read_light_client_attack_min_slash_rate_param(storage)?; + let cubic_slashing_window_length = + read_cubic_slashing_window_length_param(storage)?; + let validator_stake_threshold = + read_validator_stake_threshold_param(storage)?; + let liveness_window_check = read_liveness_window_check_param(storage)?; + let liveness_threshold = read_liveness_threshold_param(storage)?; + let rewards_gain_p = read_rewards_gain_p_param(storage)?; + let rewards_gain_d = read_rewards_gain_d_param(storage)?; + + Ok(OwnedPosParams { + max_validator_slots, + pipeline_len, + unbonding_len, + tm_votes_per_token, + block_proposer_reward, + block_vote_reward, + max_inflation_rate, + target_staked_ratio, + duplicate_vote_min_slash_rate, + light_client_attack_min_slash_rate, + cubic_slashing_window_length, + validator_stake_threshold, + liveness_window_check, + liveness_threshold, + rewards_gain_p, + rewards_gain_d, + }) } /// Read PoS parameters @@ -305,8 +362,52 @@ pub fn write_pos_params( where S: StorageRead + StorageWrite, { - let key = storage_key::params_key(); - storage.write(&key, params) + let OwnedPosParams { + max_validator_slots, + pipeline_len, + unbonding_len, + tm_votes_per_token, + block_proposer_reward, + block_vote_reward, + max_inflation_rate, + target_staked_ratio, + duplicate_vote_min_slash_rate, + light_client_attack_min_slash_rate, + cubic_slashing_window_length, + validator_stake_threshold, + liveness_window_check, + liveness_threshold, + rewards_gain_p, + rewards_gain_d, + } = params; + + write_max_validator_slots_param(storage, *max_validator_slots)?; + write_pipeline_len_param(storage, *pipeline_len)?; + write_unbonding_len_param(storage, *unbonding_len)?; + write_tm_votes_per_token_param(storage, *tm_votes_per_token)?; + write_block_proposer_reward_param(storage, *block_proposer_reward)?; + write_block_vote_reward_param(storage, *block_vote_reward)?; + write_max_inflation_rate_param(storage, *max_inflation_rate)?; + write_target_staked_ratio_param(storage, *target_staked_ratio)?; + write_duplicate_vote_min_slash_rate_param( + storage, + *duplicate_vote_min_slash_rate, + )?; + write_light_client_attack_min_slash_rate_param( + storage, + *light_client_attack_min_slash_rate, + )?; + write_cubic_slashing_window_length_param( + storage, + *cubic_slashing_window_length, + )?; + write_validator_stake_threshold_param(storage, *validator_stake_threshold)?; + write_liveness_window_check_param(storage, *liveness_window_check)?; + write_liveness_threshold_param(storage, *liveness_threshold)?; + write_rewards_gain_p_param(storage, *rewards_gain_p)?; + write_rewards_gain_d_param(storage, *rewards_gain_d)?; + + Ok(()) } /// Get the validator address given the raw hash of the Tendermint consensus key diff --git a/crates/proof_of_stake/src/storage_key.rs b/crates/proof_of_stake/src/storage_key.rs index 57db98a2df..7e240abe07 100644 --- a/crates/proof_of_stake/src/storage_key.rs +++ b/crates/proof_of_stake/src/storage_key.rs @@ -2,11 +2,33 @@ use namada_core::address::Address; use namada_core::storage::DbKeySeg; +use namada_macros::StorageKeys; use super::ADDRESS; use crate::types::BondId; use crate::{epoched, lazy_map, lazy_vec, Epoch, Key, KeySeg}; +#[derive(StorageKeys)] +struct Params { + proposal: &'static str, + max_validator_slots: &'static str, + pipeline_len: &'static str, + unbonding_len: &'static str, + tm_votes_per_token: &'static str, + block_proposer_reward: &'static str, + block_vote_reward: &'static str, + max_inflation_rate: &'static str, + target_staked_ratio: &'static str, + duplicate_vote_min_slash_rate: &'static str, + light_client_attack_min_slash_rate: &'static str, + cubic_slashing_window_length: &'static str, + validator_stake_threshold: &'static str, + liveness_window_check: &'static str, + liveness_threshold: &'static str, + rewards_gain_p: &'static str, + rewards_gain_d: &'static str, +} + const PARAMS_STORAGE_KEY: &str = "params"; const VALIDATOR_ADDRESSES_KEY: &str = "validator_addresses"; #[allow(missing_docs)] @@ -71,16 +93,144 @@ pub fn is_pos_key(key: &Key) -> bool { } } -/// Storage key for PoS parameters. -pub fn params_key() -> Key { +/// Storage key prefix for PoS parameters. +/// +/// Each param is stored in a key derived for `Params`. +pub fn params_prefix() -> Key { Key::from(ADDRESS.to_db_key()) .push(&PARAMS_STORAGE_KEY.to_owned()) .expect("Cannot obtain a storage key") } +/// Proposal param storage key +pub fn param_proposal_key() -> Key { + params_prefix() + .push(&Params::VALUES.proposal.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Max validator slots param storage key +pub fn param_max_validator_slots_key() -> Key { + params_prefix() + .push(&Params::VALUES.max_validator_slots.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Pipeline length param storage key +pub fn param_pipeline_len_key() -> Key { + params_prefix() + .push(&Params::VALUES.pipeline_len.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Unbonding length param storage key +pub fn param_unbonding_len_key() -> Key { + params_prefix() + .push(&Params::VALUES.unbonding_len.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Tendermint votes per token param storage key +pub fn param_tm_votes_per_token_key() -> Key { + params_prefix() + .push(&Params::VALUES.tm_votes_per_token.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Block proposer reward param storage key +pub fn param_block_proposer_reward_key() -> Key { + params_prefix() + .push(&Params::VALUES.block_proposer_reward.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Block vote reward param storage key +pub fn param_block_vote_reward_key() -> Key { + params_prefix() + .push(&Params::VALUES.block_vote_reward.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Max inflation rate param storage key +pub fn param_max_inflation_rate_key() -> Key { + params_prefix() + .push(&Params::VALUES.max_inflation_rate.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Target staked ratio param storage key +pub fn param_target_staked_ratio_key() -> Key { + params_prefix() + .push(&Params::VALUES.target_staked_ratio.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Duplicate vote minimum slash rate param storage key +pub fn param_duplicate_vote_min_slash_rate_key() -> Key { + params_prefix() + .push(&Params::VALUES.duplicate_vote_min_slash_rate.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Light client attack minimum slash rate param storage key +pub fn param_light_client_attack_min_slash_rate_key() -> Key { + params_prefix() + .push(&Params::VALUES.light_client_attack_min_slash_rate.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Cubic slashing window length param storage key +pub fn param_cubic_slashing_window_length_key() -> Key { + params_prefix() + .push(&Params::VALUES.cubic_slashing_window_length.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Validator stake threshold param storage key +pub fn param_validator_stake_threshold_key() -> Key { + params_prefix() + .push(&Params::VALUES.validator_stake_threshold.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Liveness window check param storage key +pub fn param_liveness_window_check_key() -> Key { + params_prefix() + .push(&Params::VALUES.liveness_window_check.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Liveness threshold param storage key +pub fn param_liveness_threshold_key() -> Key { + params_prefix() + .push(&Params::VALUES.liveness_threshold.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Rewards gain P param storage key +pub fn param_rewards_gain_p_key() -> Key { + params_prefix() + .push(&Params::VALUES.rewards_gain_p.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Rewards gain D param storage key +pub fn param_rewards_gain_d_key() -> Key { + params_prefix() + .push(&Params::VALUES.rewards_gain_d.to_owned()) + .expect("Cannot obtain a storage key") +} + /// Is storage key for PoS parameters? pub fn is_params_key(key: &Key) -> bool { - matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] if addr == &ADDRESS && key == PARAMS_STORAGE_KEY) + matches!( + &key.segments[..], + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(key), + DbKeySeg::StringSeg(_sub_key), + ] if addr == &ADDRESS && key == PARAMS_STORAGE_KEY + ) } /// Storage key prefix for validator data.