diff --git a/.changelog/unreleased/improvements/1944-tune-storage-past-epochs.md b/.changelog/unreleased/improvements/1944-tune-storage-past-epochs.md new file mode 100644 index 0000000000..9ddda02759 --- /dev/null +++ b/.changelog/unreleased/improvements/1944-tune-storage-past-epochs.md @@ -0,0 +1,2 @@ +- New implementation and parameters for purging old epochs for Epoched validator + data in storage. ([\#1944](https://github.com/anoma/namada/pull/1944)) \ No newline at end of file diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 7ec1c49d59..4e4086313f 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -6,8 +6,6 @@ use namada::core::ledger::governance::cli::offline::{ use namada::core::ledger::governance::cli::onchain::{ DefaultProposal, PgfFundingProposal, PgfStewardProposal, ProposalVote, }; -use namada::ledger::pos; -use namada::proof_of_stake::parameters::PosParams; use namada::proto::Tx; use namada::types::address::{Address, ImplicitAddress}; use namada::types::dec::Dec; @@ -469,11 +467,7 @@ pub async fn submit_init_validator<'a>( ) .unwrap(); - let key = pos::params_key(); - let pos_params: PosParams = - rpc::query_storage_value(namada.client(), &key) - .await - .expect("Pos parameter should be defined."); + let pos_params = rpc::query_pos_parameters(namada.client()).await; display_line!(namada.io(), ""); display_line!( diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 63a431fca2..9d112dd0d8 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -7,7 +7,7 @@ use derivative::Derivative; use namada::core::ledger::governance::parameters::GovernanceParameters; use namada::core::ledger::pgf::parameters::PgfParameters; use namada::ledger::parameters::EpochDuration; -use namada::ledger::pos::{Dec, GenesisValidator, PosParams}; +use namada::ledger::pos::{Dec, GenesisValidator, OwnedPosParams}; use namada::types::address::Address; use namada::types::chain::ProposalBytes; use namada::types::key::dkg_session_keys::DkgPublicKey; @@ -31,7 +31,7 @@ pub mod genesis_config { use namada::core::ledger::governance::parameters::GovernanceParameters; use namada::core::ledger::pgf::parameters::PgfParameters; use namada::ledger::parameters::EpochDuration; - use namada::ledger::pos::{Dec, GenesisValidator, PosParams}; + use namada::ledger::pos::{Dec, GenesisValidator, OwnedPosParams}; use namada::types::address::Address; use namada::types::chain::ProposalBytes; use namada::types::key::dkg_session_keys::DkgPublicKey; @@ -338,6 +338,12 @@ pub mod genesis_config { .unwrap() .to_public_key() .unwrap(), + protocol_key: config + .protocol_public_key + .as_ref() + .unwrap() + .to_public_key() + .unwrap(), eth_cold_key: config .eth_cold_key .as_ref() @@ -372,12 +378,6 @@ pub mod genesis_config { .unwrap() .to_public_key() .unwrap(), - protocol_key: config - .protocol_public_key - .as_ref() - .unwrap() - .to_public_key() - .unwrap(), dkg_public_key: config .dkg_public_key .as_ref() @@ -665,7 +665,7 @@ pub mod genesis_config { validator_stake_threshold, } = pos_params; - let pos_params = PosParams { + let pos_params = OwnedPosParams { max_validator_slots, pipeline_len, unbonding_len, @@ -738,7 +738,7 @@ pub struct Genesis { pub established_accounts: Vec, pub implicit_accounts: Vec, pub parameters: Parameters, - pub pos_params: PosParams, + pub pos_params: OwnedPosParams, pub gov_params: GovernanceParameters, pub pgf_params: PgfParameters, // Ethereum bridge config @@ -774,9 +774,6 @@ pub struct Validator { /// this key on a transaction signature. /// Note that this is distinct from consensus key used in the PoS system. pub account_key: common::PublicKey, - /// Public key associated with validator account used for signing protocol - /// transactions - pub protocol_key: common::PublicKey, /// The public DKG session key used during the DKG protocol pub dkg_public_key: DkgPublicKey, /// These tokens are not staked and hence do not contribute to the @@ -938,6 +935,7 @@ pub fn genesis(num_validators: u64) -> Genesis { address, tokens: token::Amount::native_whole(200_000), consensus_key: consensus_keypair.ref_to(), + protocol_key: protocol_keypair.ref_to(), commission_rate: Dec::new(5, 2).expect("This can't fail"), max_commission_rate_change: Dec::new(1, 2) .expect("This can't fail"), @@ -945,7 +943,6 @@ pub fn genesis(num_validators: u64) -> Genesis { eth_hot_key: eth_bridge_keypair.ref_to(), }, account_key: account_keypair.ref_to(), - protocol_key: protocol_keypair.ref_to(), dkg_public_key: dkg_keypair.public(), non_staked_balance: token::Amount::native_whole(100_000), // TODO replace with https://github.com/anoma/namada/issues/25) @@ -971,6 +968,7 @@ pub fn genesis(num_validators: u64) -> Genesis { address, tokens: token::Amount::native_whole(200_000), consensus_key: consensus_keypair.ref_to(), + protocol_key: protocol_keypair.ref_to(), commission_rate: Dec::new(5, 2).expect("This can't fail"), max_commission_rate_change: Dec::new(1, 2) .expect("This can't fail"), @@ -978,7 +976,6 @@ pub fn genesis(num_validators: u64) -> Genesis { eth_hot_key: eth_bridge_keypair.ref_to(), }, account_key: account_keypair.ref_to(), - protocol_key: protocol_keypair.ref_to(), dkg_public_key: dkg_keypair.public(), non_staked_balance: token::Amount::native_whole(100_000), // TODO replace with https://github.com/anoma/namada/issues/25) @@ -1109,7 +1106,7 @@ pub fn genesis(num_validators: u64) -> Genesis { implicit_accounts, token_accounts, parameters, - pos_params: PosParams::default(), + pos_params: OwnedPosParams::default(), gov_params: GovernanceParameters::default(), pgf_params: PgfParameters::default(), ethereum_bridge_params: Some(EthereumBridgeConfig { diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index fd34935692..c106d71738 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -100,6 +100,7 @@ where namada_proof_of_stake::read_pos_params(&self.wl_storage)?; namada_proof_of_stake::copy_validator_sets_and_positions( &mut self.wl_storage, + &pos_params, current_epoch, current_epoch + pos_params.pipeline_len, )?; @@ -107,10 +108,6 @@ where &mut self.wl_storage, current_epoch, )?; - namada_proof_of_stake::purge_validator_sets_for_old_epoch( - &mut self.wl_storage, - current_epoch, - )?; } // Invariant: Has to be applied before `record_slashes_from_evidence` @@ -2639,8 +2636,8 @@ mod test_finalize_block { ..Default::default() }); let mut params = read_pos_params(&shell.wl_storage).unwrap(); - params.unbonding_len = 4; - write_pos_params(&mut shell.wl_storage, params.clone())?; + params.owned.unbonding_len = 4; + write_pos_params(&mut shell.wl_storage, ¶ms.owned)?; let validator_set: Vec = read_consensus_validator_set_addresses_with_stake( @@ -3029,9 +3026,9 @@ mod test_finalize_block { ..Default::default() }); let mut params = read_pos_params(&shell.wl_storage).unwrap(); - params.unbonding_len = 4; - params.max_validator_slots = 4; - write_pos_params(&mut shell.wl_storage, params.clone())?; + params.owned.unbonding_len = 4; + params.owned.max_validator_slots = 4; + write_pos_params(&mut shell.wl_storage, ¶ms.owned)?; // Slash pool balance let nam_address = shell.wl_storage.storage.native_token.clone(); @@ -3830,6 +3827,180 @@ mod test_finalize_block { Ok(()) } + #[test] + fn test_purge_validator_information() -> storage_api::Result<()> { + // Setup the network with pipeline_len = 2, unbonding_len = 4 + let num_validators = 4_u64; + let (mut shell, _recv, _, _) = setup_with_cfg(SetupCfg { + last_height: 0, + num_validators, + ..Default::default() + }); + let mut params = read_pos_params(&shell.wl_storage).unwrap(); + params.owned.unbonding_len = 4; + // params.owned.max_validator_slots = 3; + // params.owned.validator_stake_threshold = token::Amount::zero(); + write_pos_params(&mut shell.wl_storage, ¶ms.owned)?; + + let max_proposal_period = params.max_proposal_period; + let default_past_epochs = 2; + let consensus_val_set_len = max_proposal_period + default_past_epochs; + + let consensus_val_set = + namada_proof_of_stake::consensus_validator_set_handle(); + // let below_cap_val_set = + // namada_proof_of_stake::below_capacity_validator_set_handle(); + let validator_positions = + namada_proof_of_stake::validator_set_positions_handle(); + let all_validator_addresses = + namada_proof_of_stake::validator_addresses_handle(); + + let consensus_set: Vec = + read_consensus_validator_set_addresses_with_stake( + &shell.wl_storage, + Epoch::default(), + ) + .unwrap() + .into_iter() + .collect(); + let val1 = consensus_set[0].clone(); + let pkh1 = get_pkh_from_address( + &shell.wl_storage, + ¶ms, + val1.address, + Epoch::default(), + ); + + // Finalize block 1 + next_block_for_inflation(&mut shell, pkh1.clone(), vec![], None); + + let votes = get_default_true_votes(&shell.wl_storage, Epoch::default()); + assert!(!votes.is_empty()); + + let check_is_data = |storage: &WlStorage<_, _>, + start: Epoch, + end: Epoch| { + for ep in Epoch::iter_bounds_inclusive(start, end) { + assert!(!consensus_val_set.at(&ep).is_empty(storage).unwrap()); + // assert!(!below_cap_val_set.at(&ep).is_empty(storage). + // unwrap()); + assert!( + !validator_positions.at(&ep).is_empty(storage).unwrap() + ); + assert!( + !all_validator_addresses.at(&ep).is_empty(storage).unwrap() + ); + } + }; + + // Check that there is validator data for epochs 0 - pipeline_len + check_is_data(&shell.wl_storage, Epoch(0), Epoch(params.pipeline_len)); + + // Advance to epoch `default_past_epochs` + let mut current_epoch = Epoch(0); + for _ in 0..default_past_epochs { + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + } + assert_eq!(shell.wl_storage.storage.block.epoch.0, default_past_epochs); + assert_eq!(current_epoch.0, default_past_epochs); + + check_is_data( + &shell.wl_storage, + Epoch(0), + Epoch(params.pipeline_len + default_past_epochs), + ); + + // Advance one more epoch, which should purge the data for epoch 0 in + // everything except the consensus validator set + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + assert_eq!(current_epoch.0, default_past_epochs + 1); + + check_is_data( + &shell.wl_storage, + Epoch(1), + Epoch(params.pipeline_len + default_past_epochs + 1), + ); + assert!( + !consensus_val_set + .at(&Epoch(0)) + .is_empty(&shell.wl_storage) + .unwrap() + ); + assert!( + validator_positions + .at(&Epoch(0)) + .is_empty(&shell.wl_storage) + .unwrap() + ); + assert!( + all_validator_addresses + .at(&Epoch(0)) + .is_empty(&shell.wl_storage) + .unwrap() + ); + + // Advance to the epoch `consensus_val_set_len` + 1 + loop { + assert!( + !consensus_val_set + .at(&Epoch(0)) + .is_empty(&shell.wl_storage) + .unwrap() + ); + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + if current_epoch.0 == consensus_val_set_len + 1 { + break; + } + } + + assert!( + consensus_val_set + .at(&Epoch(0)) + .is_empty(&shell.wl_storage) + .unwrap() + ); + + // Advance one more epoch + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + for ep in Epoch::default().iter_range(2) { + assert!( + consensus_val_set + .at(&ep) + .is_empty(&shell.wl_storage) + .unwrap() + ); + } + for ep in Epoch::iter_bounds_inclusive( + Epoch(2), + current_epoch + params.pipeline_len, + ) { + assert!( + !consensus_val_set + .at(&ep) + .is_empty(&shell.wl_storage) + .unwrap() + ); + } + + Ok(()) + } + fn get_default_true_votes(storage: &S, epoch: Epoch) -> Vec where S: StorageRead, diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 60b46369f7..84226c531d 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::hash::Hash; use namada::ledger::parameters::{self, Parameters}; -use namada::ledger::pos::{staking_token_address, PosParams}; +use namada::ledger::pos::{staking_token_address, OwnedPosParams}; use namada::ledger::storage::traits::StorageHasher; use namada::ledger::storage::{DBIter, DB}; use namada::ledger::storage_api::token::{ @@ -394,10 +394,6 @@ where ) .unwrap(); - self.wl_storage - .write(&protocol_pk_key(addr), &validator.protocol_key) - .expect("Unable to set genesis user protocol public key"); - self.wl_storage .write( &dkg_session_keys::dkg_pk_key(addr), @@ -412,7 +408,7 @@ where &mut self, staking_token: &Address, validators: Vec, - pos_params: &PosParams, + pos_params: &OwnedPosParams, ) -> Result { let mut response = response::InitChain::default(); // PoS system depends on epoch being initialized. Write the total diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs b/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs index 7af066dc58..f817b4cd9f 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs @@ -274,7 +274,6 @@ mod test_bp_vote_extensions { #[cfg(not(feature = "abcipp"))] use namada::core::ledger::eth_bridge::storage::bridge_pool::get_key_from_hash; use namada::ledger::pos::PosQueries; - use namada::ledger::storage_api::StorageWrite; use namada::proof_of_stake::types::{ Position as ValidatorPosition, WeightedValidator, }; @@ -321,18 +320,12 @@ mod test_bp_vote_extensions { ) .expect("Test failed"); - // register Bertha's protocol key - let pk_key = protocol_pk_key(&bertha_address()); - shell - .wl_storage - .write_bytes(&pk_key, bertha_keypair().ref_to().serialize_to_vec()) - .expect("Test failed."); - // change pipeline length to 1 let mut params = shell.wl_storage.pos_queries().get_pos_params(); - params.pipeline_len = 1; + params.owned.pipeline_len = 1; let consensus_key = gen_keypair(); + let protocol_key = bertha_keypair(); let hot_key = gen_secp256k1_keypair(); let cold_key = gen_secp256k1_keypair(); @@ -341,6 +334,7 @@ mod test_bp_vote_extensions { params: ¶ms, address: &bertha_address(), consensus_key: &consensus_key.ref_to(), + protocol_key: &protocol_key.ref_to(), eth_hot_key: &hot_key.ref_to(), eth_cold_key: &cold_key.ref_to(), current_epoch: 0.into(), diff --git a/benches/lib.rs b/benches/lib.rs index b4b285f6db..15f7ac85f8 100644 --- a/benches/lib.rs +++ b/benches/lib.rs @@ -275,10 +275,8 @@ impl BenchShell { } pub fn advance_epoch(&mut self) { - let pipeline_len = - proof_of_stake::read_pos_params(&self.inner.wl_storage) - .unwrap() - .pipeline_len; + let params = + proof_of_stake::read_pos_params(&self.inner.wl_storage).unwrap(); self.wl_storage.storage.block.epoch = self.wl_storage.storage.block.epoch.next(); @@ -286,8 +284,9 @@ impl BenchShell { proof_of_stake::copy_validator_sets_and_positions( &mut self.wl_storage, + ¶ms, current_epoch, - current_epoch + pipeline_len, + current_epoch + params.pipeline_len, ) .unwrap(); } diff --git a/core/src/ledger/storage_api/governance.rs b/core/src/ledger/storage_api/governance.rs index e03be937b5..ab4ad27b0b 100644 --- a/core/src/ledger/storage_api/governance.rs +++ b/core/src/ledger/storage_api/governance.rs @@ -245,9 +245,7 @@ where let min_proposal_voting_period: u64 = storage.read(&key)?.expect("Parameter should be definied."); - let key = governance_keys::get_max_proposal_period_key(); - let max_proposal_period: u64 = - storage.read(&key)?.expect("Parameter should be definied."); + let max_proposal_period: u64 = get_max_proposal_period(storage)?; Ok(GovernanceParameters { min_proposal_fund, @@ -258,3 +256,14 @@ where min_proposal_grace_epochs, }) } + +/// Get governance "max_proposal_period" parameter +pub fn get_max_proposal_period(storage: &S) -> storage_api::Result +where + S: storage_api::StorageRead, +{ + let key = governance_keys::get_max_proposal_period_key(); + let max_proposal_period: u64 = + storage.read(&key)?.expect("Parameter should be defined."); + Ok(max_proposal_period) +} diff --git a/core/src/types/storage.rs b/core/src/types/storage.rs index b4c6a595dd..2f6af267de 100644 --- a/core/src/types/storage.rs +++ b/core/src/types/storage.rs @@ -1036,11 +1036,12 @@ impl Epoch { /// overflow occurred. #[must_use = "this returns the result of the operation, without modifying \ the original"] - pub fn checked_sub(self, rhs: Epoch) -> Option { - if rhs.0 > self.0 { + pub fn checked_sub(self, rhs: impl Into) -> Option { + let Epoch(rhs) = rhs.into(); + if rhs > self.0 { None } else { - Some(Self(self.0 - rhs.0)) + Some(Self(self.0 - rhs)) } } diff --git a/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs b/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs index 9cb4377cef..a5dda2d206 100644 --- a/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs +++ b/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs @@ -235,7 +235,7 @@ mod test_apply_bp_roots_to_storage { use namada_core::types::storage::Key; use namada_core::types::vote_extensions::bridge_pool_roots; use namada_core::types::voting_power::FractionalVotingPower; - use namada_proof_of_stake::parameters::PosParams; + use namada_proof_of_stake::parameters::OwnedPosParams; use namada_proof_of_stake::write_pos_params; use super::*; @@ -754,11 +754,11 @@ mod test_apply_bp_roots_to_storage { ); // update the pos params - let params = PosParams { + let params = OwnedPosParams { pipeline_len: 1, ..Default::default() }; - write_pos_params(&mut wl_storage, params).expect("Test failed"); + write_pos_params(&mut wl_storage, ¶ms).expect("Test failed"); // insert validators 2 and 3 at epoch 1 test_utils::append_validators_to_storage( diff --git a/ethereum_bridge/src/protocol/transactions/votes.rs b/ethereum_bridge/src/protocol/transactions/votes.rs index c3a82bd370..cc029e28f5 100644 --- a/ethereum_bridge/src/protocol/transactions/votes.rs +++ b/ethereum_bridge/src/protocol/transactions/votes.rs @@ -189,7 +189,7 @@ mod tests { use namada_core::types::storage::BlockHeight; use namada_core::types::{address, token}; - use namada_proof_of_stake::parameters::PosParams; + use namada_proof_of_stake::parameters::OwnedPosParams; use namada_proof_of_stake::write_pos_params; use super::*; @@ -321,11 +321,11 @@ mod tests { ); // update the pos params - let params = PosParams { + let params = OwnedPosParams { pipeline_len: 1, ..Default::default() }; - write_pos_params(&mut wl_storage, params).expect("Test failed"); + write_pos_params(&mut wl_storage, ¶ms).expect("Test failed"); // insert validators 2 and 3 at epoch 1 test_utils::append_validators_to_storage( diff --git a/ethereum_bridge/src/protocol/transactions/votes/update.rs b/ethereum_bridge/src/protocol/transactions/votes/update.rs index 928bb4d7f8..a98be1859d 100644 --- a/ethereum_bridge/src/protocol/transactions/votes/update.rs +++ b/ethereum_bridge/src/protocol/transactions/votes/update.rs @@ -263,6 +263,7 @@ mod tests { votes, total_stake, } = self; + let keys = vote_tallies::Keys::from(event); let seen_voting_power: token::Amount = votes .iter() diff --git a/ethereum_bridge/src/test_utils.rs b/ethereum_bridge/src/test_utils.rs index 96b5abf240..cc5370360d 100644 --- a/ethereum_bridge/src/test_utils.rs +++ b/ethereum_bridge/src/test_utils.rs @@ -6,6 +6,7 @@ use std::num::NonZeroU64; use borsh_ext::BorshSerializeExt; use namada_core::ledger::eth_bridge::storage::bridge_pool::get_key_from_hash; use namada_core::ledger::eth_bridge::storage::whitelist; +use namada_core::ledger::governance::parameters::GovernanceParameters; use namada_core::ledger::storage::mockdb::MockDBWriteBatch; use namada_core::ledger::storage::testing::{TestStorage, TestWlStorage}; use namada_core::ledger::storage_api::token::credit_tokens; @@ -17,7 +18,7 @@ use namada_core::types::keccak::KeccakHash; use namada_core::types::key::{self, protocol_pk_key, RefTo}; use namada_core::types::storage::{BlockHeight, Key}; use namada_core::types::token; -use namada_proof_of_stake::parameters::PosParams; +use namada_proof_of_stake::parameters::OwnedPosParams; use namada_proof_of_stake::pos_queries::PosQueries; use namada_proof_of_stake::types::GenesisValidator; use namada_proof_of_stake::{ @@ -199,6 +200,7 @@ pub fn init_storage_with_validators( .map(|(address, tokens)| { let keys = TestValidatorKeys::generate(); let consensus_key = keys.consensus.ref_to(); + let protocol_key = keys.protocol.ref_to(); let eth_cold_key = keys.eth_gov.ref_to(); let eth_hot_key = keys.eth_bridge.ref_to(); all_keys.insert(address.clone(), keys); @@ -206,6 +208,7 @@ pub fn init_storage_with_validators( address, tokens, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, commission_rate: Dec::new(5, 2).unwrap(), @@ -214,9 +217,11 @@ pub fn init_storage_with_validators( }) .collect(); + let gov_params = GovernanceParameters::default(); + gov_params.init_storage(wl_storage).unwrap(); namada_proof_of_stake::init_genesis( wl_storage, - &PosParams::default(), + &OwnedPosParams::default(), validators.into_iter(), 0.into(), ) @@ -271,6 +276,7 @@ pub fn append_validators_to_storage( let keys = TestValidatorKeys::generate(); let consensus_key = &keys.consensus.ref_to(); + let protocol_key = &&keys.protocol.ref_to(); let eth_cold_key = &keys.eth_gov.ref_to(); let eth_hot_key = &keys.eth_bridge.ref_to(); @@ -279,6 +285,7 @@ pub fn append_validators_to_storage( params: ¶ms, address: &validator, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, current_epoch, diff --git a/proof_of_stake/src/epoched.rs b/proof_of_stake/src/epoched.rs index 0d29affbdd..796827de1f 100644 --- a/proof_of_stake/src/epoched.rs +++ b/proof_of_stake/src/epoched.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use std::fmt::Debug; use std::marker::PhantomData; -use std::ops; +use std::{cmp, ops}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api; @@ -26,7 +26,7 @@ pub const LAST_UPDATE_SUB_KEY: &str = "last_update"; pub const OLDEST_EPOCH_SUB_KEY: &str = "oldest_epoch"; /// Default number of past epochs to keep. -const DEFAULT_NUM_PAST_EPOCHS: u64 = 2; +pub const DEFAULT_NUM_PAST_EPOCHS: u64 = 2; /// Discrete epoched data handle pub struct Epoched { @@ -168,7 +168,7 @@ where /// kept is dropped. If the oldest stored epoch is not already /// associated with some value, the latest value from the dropped /// values, if any, is associated with it. - fn update_data( + pub fn update_data( &self, storage: &mut S, params: &PosParams, @@ -183,11 +183,10 @@ where (last_update, oldest_epoch) { let oldest_to_keep = current_epoch - .0 .checked_sub(PastEpochs::value(params)) .unwrap_or_default(); - if oldest_epoch.0 < oldest_to_keep { - let diff = oldest_to_keep - oldest_epoch.0; + if oldest_epoch < oldest_to_keep { + let diff = u64::from(oldest_to_keep - oldest_epoch); // Go through the epochs before the expected oldest epoch and // keep the latest one tracing::debug!( @@ -266,12 +265,9 @@ where } fn sub_past_epochs(params: &PosParams, epoch: Epoch) -> Epoch { - Epoch( - epoch - .0 - .checked_sub(PastEpochs::value(params)) - .unwrap_or_default(), - ) + epoch + .checked_sub(PastEpochs::value(params)) + .unwrap_or_default() } fn get_oldest_epoch_storage_key(&self) -> storage::Key { @@ -325,7 +321,7 @@ where NestedMap::open(key) } - /// Initialize new nested data at the given epoch offset. + /// Initialize new nested data at the given epoch. pub fn init( &self, storage: &mut S, @@ -335,7 +331,8 @@ where S: StorageWrite + StorageRead, { let key = self.get_last_update_storage_key(); - storage.write(&key, epoch) + storage.write(&key, epoch)?; + self.set_oldest_epoch(storage, epoch) } fn get_last_update_storage_key(&self) -> storage::Key { @@ -368,6 +365,100 @@ where let key = self.get_last_update_storage_key(); storage.write(&key, current_epoch) } + + fn get_oldest_epoch_storage_key(&self) -> storage::Key { + self.storage_prefix + .push(&OLDEST_EPOCH_SUB_KEY.to_owned()) + .unwrap() + } + + fn get_oldest_epoch( + &self, + storage: &S, + ) -> storage_api::Result> + where + S: StorageRead, + { + let key = self.get_oldest_epoch_storage_key(); + storage.read(&key) + } + + fn set_oldest_epoch( + &self, + storage: &mut S, + new_oldest_epoch: Epoch, + ) -> storage_api::Result<()> + where + S: StorageRead + StorageWrite, + { + let key = self.get_oldest_epoch_storage_key(); + storage.write(&key, new_oldest_epoch) + } + + fn sub_past_epochs(params: &PosParams, epoch: Epoch) -> Epoch { + epoch + .checked_sub(PastEpochs::value(params)) + .unwrap_or_default() + } + + /// Update data by removing old epochs + // TODO: should we consider more complex handling of empty epochs in the + // data below? + pub fn update_data( + &self, + storage: &mut S, + params: &PosParams, + current_epoch: Epoch, + ) -> storage_api::Result<()> + where + S: StorageRead + StorageWrite, + { + let last_update = self.get_last_update(storage)?; + let oldest_epoch = self.get_oldest_epoch(storage)?; + if let (Some(last_update), Some(oldest_epoch)) = + (last_update, oldest_epoch) + { + let oldest_to_keep = current_epoch + .checked_sub(PastEpochs::value(params)) + .unwrap_or_default(); + if oldest_epoch < oldest_to_keep { + let diff = u64::from(oldest_to_keep - oldest_epoch); + // Go through the epochs before the expected oldest epoch and + // keep the latest one + tracing::debug!( + "Trimming nested epoched data in epoch {current_epoch}, \ + last updated at {last_update}." + ); + let data_handler = self.get_data_handler(); + // Remove data before the new oldest epoch, keep the latest + // value + for epoch in oldest_epoch.iter_range(diff) { + let was_data = data_handler.remove_all(storage, &epoch)?; + if was_data { + tracing::debug!( + "Removed inner map data at epoch {epoch}" + ); + } else { + tracing::debug!("WARNING: was no data in {epoch}"); + } + } + let new_oldest_epoch = + Self::sub_past_epochs(params, current_epoch); + + // if !data_handler.contains(storage, &new_oldest_epoch)? { + // panic!("WARNING: no data existing in + // {new_oldest_epoch}"); } + self.set_oldest_epoch(storage, new_oldest_epoch)?; + + // Update the epoch of the last update to the current epoch + let key = self.get_last_update_storage_key(); + storage.write(&key, current_epoch)?; + return Ok(()); + } + } + + Ok(()) + } } impl @@ -531,11 +622,10 @@ where (last_update, oldest_epoch) { let oldest_to_keep = current_epoch - .0 .checked_sub(PastEpochs::value(params)) .unwrap_or_default(); - if oldest_epoch.0 < oldest_to_keep { - let diff = oldest_to_keep - oldest_epoch.0; + if oldest_epoch < oldest_to_keep { + let diff = u64::from(oldest_to_keep - oldest_epoch); // Go through the epochs before the expected oldest epoch and // sum them into it tracing::debug!( @@ -634,12 +724,9 @@ where } fn sub_past_epochs(params: &PosParams, epoch: Epoch) -> Epoch { - Epoch( - epoch - .0 - .checked_sub(PastEpochs::value(params)) - .unwrap_or_default(), - ) + epoch + .checked_sub(PastEpochs::value(params)) + .unwrap_or_default() } fn get_oldest_epoch_storage_key(&self) -> storage::Key { @@ -810,6 +897,29 @@ impl EpochOffset for OffsetSlashProcessingLen { } } +/// Offset at the slash processing delay plus the default num past epochs. +#[derive( + Debug, + Clone, + BorshDeserialize, + BorshSerialize, + BorshSchema, + PartialEq, + Eq, + PartialOrd, + Ord, +)] +pub struct OffsetSlashProcessingLenPlus; +impl EpochOffset for OffsetSlashProcessingLenPlus { + fn value(params: &PosParams) -> u64 { + params.slash_processing_epoch_offset() + DEFAULT_NUM_PAST_EPOCHS + } + + fn dyn_offset() -> DynEpochOffset { + DynEpochOffset::SlashProcessingLenPlus + } +} + /// Maximum offset. #[derive( Debug, @@ -833,6 +943,106 @@ impl EpochOffset for OffsetMaxU64 { } } +/// Offset at max proposal period. +#[derive( + Debug, + Clone, + BorshDeserialize, + BorshSerialize, + BorshSchema, + PartialEq, + Eq, + PartialOrd, + Ord, +)] +pub struct OffsetMaxProposalPeriod; +impl EpochOffset for OffsetMaxProposalPeriod { + fn value(params: &PosParams) -> u64 { + params.max_proposal_period + } + + fn dyn_offset() -> DynEpochOffset { + DynEpochOffset::MaxProposalPeriod + } +} + +/// Offset at the max proposal period, plus the default num past epochs. +#[derive( + Debug, + Clone, + BorshDeserialize, + BorshSerialize, + BorshSchema, + PartialEq, + Eq, + PartialOrd, + Ord, +)] +pub struct OffsetMaxProposalPeriodPlus; +impl EpochOffset for OffsetMaxProposalPeriodPlus { + fn value(params: &PosParams) -> u64 { + params.max_proposal_period + DEFAULT_NUM_PAST_EPOCHS + } + + fn dyn_offset() -> DynEpochOffset { + DynEpochOffset::MaxProposalPeriodPlus + } +} + +/// Offset at the larger of the slash processing length and the max proposal +/// period. +#[derive( + Debug, + Clone, + BorshDeserialize, + BorshSerialize, + BorshSchema, + PartialEq, + Eq, + PartialOrd, + Ord, +)] +pub struct OffsetMaxProposalPeriodOrSlashProcessingLen; +impl EpochOffset for OffsetMaxProposalPeriodOrSlashProcessingLen { + fn value(params: &PosParams) -> u64 { + cmp::max( + params.slash_processing_epoch_offset(), + params.max_proposal_period, + ) + } + + fn dyn_offset() -> DynEpochOffset { + DynEpochOffset::MaxProposalPeriodOrSlashProcessingLen + } +} + +/// Offset at the larger of the slash processing length and the max proposal +/// period, plus the default num past epochs. +#[derive( + Debug, + Clone, + BorshDeserialize, + BorshSerialize, + BorshSchema, + PartialEq, + Eq, + PartialOrd, + Ord, +)] +pub struct OffsetMaxProposalPeriodOrSlashProcessingLenPlus; +impl EpochOffset for OffsetMaxProposalPeriodOrSlashProcessingLenPlus { + fn value(params: &PosParams) -> u64 { + cmp::max( + params.slash_processing_epoch_offset(), + params.max_proposal_period, + ) + DEFAULT_NUM_PAST_EPOCHS + } + + fn dyn_offset() -> DynEpochOffset { + DynEpochOffset::MaxProposalPeriodOrSlashProcessingLenPlus + } +} + /// Offset length dynamic choice. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum DynEpochOffset { @@ -851,6 +1061,17 @@ pub enum DynEpochOffset { /// Offset at slash processing delay (unbonding + /// cubic_slashing_window + 1). SlashProcessingLen, + /// Offset at slash processing delay plus the defaul num past epochs + SlashProcessingLenPlus, + /// Offset at the max proposal period + MaxProposalPeriod, + /// Offset at the max proposal period plus the default num past epochs + MaxProposalPeriodPlus, + /// Offset at the larger of max proposal period or slash processing delay + MaxProposalPeriodOrSlashProcessingLen, + /// Offset at the larger of max proposal period or slash processing delay, + /// plus the default num past epochs + MaxProposalPeriodOrSlashProcessingLenPlus, /// Offset of the max u64 value MaxU64, } @@ -860,7 +1081,7 @@ pub enum DynEpochOffset { pub trait EpochOffset: Debug + Clone + BorshDeserialize + BorshSerialize + BorshSchema { - /// Find the value of a given offset from PoS parameters. + /// Find the value of a given offset from PoS and Gov parameters. fn value(params: &PosParams) -> u64; /// Convert to [`DynEpochOffset`] fn dyn_offset() -> DynEpochOffset; @@ -1176,6 +1397,8 @@ mod test { fn init_storage() -> storage_api::Result { let mut s = TestWlStorage::default(); + let gov_params = namada_core::ledger::governance::parameters::GovernanceParameters::default(); + gov_params.init_storage(&mut s)?; crate::init_genesis( &mut s, &PosParams::default(), @@ -1183,6 +1406,7 @@ mod test { address: established_address_1(), tokens: token::Amount::native_whole(1_000), consensus_key: key::testing::keypair_1().to_public(), + protocol_key: key::testing::keypair_2().to_public(), eth_hot_key: key::testing::keypair_3().to_public(), eth_cold_key: key::testing::keypair_3().to_public(), commission_rate: Dec::new(1, 1).expect("Dec creation failed"), diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 0fadd6c728..bb5795a80c 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -35,16 +35,16 @@ use namada_core::ledger::storage_api::collections::lazy_map::{ }; use namada_core::ledger::storage_api::collections::{LazyCollection, LazySet}; use namada_core::ledger::storage_api::{ - self, token, ResultExt, StorageRead, StorageWrite, + self, governance, token, ResultExt, StorageRead, StorageWrite, }; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::dec::Dec; use namada_core::types::key::{ - common, tm_consensus_key_raw_hash, PublicKeyTmRawHash, + common, protocol_pk_key, tm_consensus_key_raw_hash, PublicKeyTmRawHash, }; pub use namada_core::types::storage::{Epoch, Key, KeySeg}; use once_cell::unsync::Lazy; -pub use parameters::PosParams; +pub use parameters::{OwnedPosParams, PosParams}; use rewards::PosRewardsCalculator; use storage::{ bonds_for_source_prefix, bonds_prefix, consensus_keys_key, @@ -67,8 +67,8 @@ use types::{ Slashes, TotalConsensusStakes, TotalDeltas, TotalRedelegatedBonded, TotalRedelegatedUnbonded, UnbondDetails, Unbonds, ValidatorAddresses, ValidatorConsensusKeys, ValidatorDeltas, ValidatorEthColdKeys, - ValidatorEthHotKeys, ValidatorPositionAddresses, ValidatorSetPositions, - ValidatorSetUpdate, ValidatorState, ValidatorStates, + ValidatorEthHotKeys, ValidatorPositionAddresses, ValidatorProtocolKeys, + ValidatorSetPositions, ValidatorSetUpdate, ValidatorState, ValidatorStates, ValidatorTotalUnbonded, VoteInfo, WeightedValidator, }; @@ -86,12 +86,6 @@ pub fn staking_token_address(storage: &impl StorageRead) -> Address { .expect("Must be able to read native token address") } -/// Number of epochs below the current epoch for which full validator sets are -/// stored -const STORE_VALIDATOR_SETS_LEN: u64 = 2; - -// ---- Storage handles ---- - /// Get the storage handle to the epoched consensus validator set pub fn consensus_validator_set_handle() -> ConsensusValidatorSets { let key = storage::consensus_validator_set_key(); @@ -113,6 +107,14 @@ pub fn validator_consensus_key_handle( ValidatorConsensusKeys::open(key) } +/// Get the storage handle to a PoS validator's protocol key key. +pub fn validator_protocol_key_handle( + validator: &Address, +) -> ValidatorProtocolKeys { + let key = protocol_pk_key(validator); + ValidatorProtocolKeys::open(key) +} + /// Get the storage handle to a PoS validator's eth hot key. pub fn validator_eth_hot_key_handle( validator: &Address, @@ -292,10 +294,10 @@ pub fn delegator_redelegated_unbonds_handle( DelegatorRedelegatedUnbonded::open(key) } -/// Init genesis +/// Init genesis. Requires that the governance parameters are initialized. pub fn init_genesis( storage: &mut S, - params: &PosParams, + params: &OwnedPosParams, validators: impl Iterator + Clone, current_epoch: namada_core::types::storage::Epoch, ) -> storage_api::Result<()> @@ -303,7 +305,8 @@ where S: StorageRead + StorageWrite, { tracing::debug!("Initializing PoS genesis"); - write_pos_params(storage, params.clone())?; + write_pos_params(storage, params)?; + let params = read_non_pos_owned_params(storage, params.clone())?; let mut total_bonded = token::Amount::zero(); consensus_validator_set_handle().init(storage, current_epoch)?; @@ -315,6 +318,7 @@ where address, tokens, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, commission_rate, @@ -331,7 +335,7 @@ where // validator data insert_validator_into_validator_set( storage, - params, + ¶ms, &address, tokens, current_epoch, @@ -354,6 +358,11 @@ where consensus_key, current_epoch, )?; + validator_protocol_key_handle(&address).init_at_genesis( + storage, + protocol_key, + current_epoch, + )?; validator_eth_hot_key_handle(&address).init_at_genesis( storage, eth_hot_key, @@ -401,7 +410,12 @@ where token::credit_tokens(storage, &staking_token, &ADDRESS, total_bonded)?; // Copy the genesis validator set into the pipeline epoch as well for epoch in (current_epoch.next()).iter_range(params.pipeline_len) { - copy_validator_sets_and_positions(storage, current_epoch, epoch)?; + copy_validator_sets_and_positions( + storage, + ¶ms, + current_epoch, + epoch, + )?; } tracing::debug!("Genesis initialized"); @@ -414,16 +428,33 @@ pub fn read_pos_params(storage: &S) -> storage_api::Result where S: StorageRead, { - storage + let params = storage .read(¶ms_key()) .transpose() - .expect("PosParams should always exist in storage after genesis") + .expect("PosParams should always exist in storage after genesis")?; + read_non_pos_owned_params(storage, params) +} + +/// Read non-PoS-owned parameters to add them to `OwnedPosParams` to construct +/// `PosParams`. +pub fn read_non_pos_owned_params( + storage: &S, + owned: OwnedPosParams, +) -> storage_api::Result +where + S: StorageRead, +{ + let max_proposal_period = governance::get_max_proposal_period(storage)?; + Ok(PosParams { + owned, + max_proposal_period, + }) } /// Write PoS parameters pub fn write_pos_params( storage: &mut S, - params: PosParams, + params: &OwnedPosParams, ) -> storage_api::Result<()> where S: StorageRead + StorageWrite, @@ -1408,6 +1439,7 @@ where /// Validator sets and positions copying into a future epoch pub fn copy_validator_sets_and_positions( storage: &mut S, + params: &PosParams, current_epoch: Epoch, target_epoch: Epoch, ) -> storage_api::Result<()> @@ -1470,6 +1502,9 @@ where .at(&val_stake) .insert(storage, val_position, val_address)?; } + // Purge consensus and below-capacity validator sets + consensus_validator_set.update_data(storage, params, current_epoch)?; + below_capacity_validator_set.update_data(storage, params, current_epoch)?; // Copy validator positions let mut positions = HashMap::::default(); @@ -1488,6 +1523,13 @@ where } validator_set_positions_handle.set_last_update(storage, current_epoch)?; + // Purge old epochs of validator positions + validator_set_positions_handle.update_data( + storage, + params, + current_epoch, + )?; + // Copy set of all validator addresses let mut all_validators = HashSet::
::default(); let validator_addresses_handle = validator_addresses_handle(); @@ -1503,6 +1545,9 @@ where debug_assert!(!was_in); } + // Purge old epochs of all validator addresses + validator_addresses_handle.update_data(storage, params, current_epoch)?; + Ok(()) } @@ -1547,27 +1592,6 @@ where total_consensus_stake_key_handle().set(storage, total, epoch, 0) } -/// Purge the validator sets from the epochs older than the current epoch minus -/// `STORE_VALIDATOR_SETS_LEN` -pub fn purge_validator_sets_for_old_epoch( - storage: &mut S, - epoch: Epoch, -) -> storage_api::Result<()> -where - S: StorageRead + StorageWrite, -{ - if Epoch(STORE_VALIDATOR_SETS_LEN) < epoch { - let old_epoch = epoch - STORE_VALIDATOR_SETS_LEN - 1; - consensus_validator_set_handle() - .get_data_handler() - .remove_all(storage, &old_epoch)?; - below_capacity_validator_set_handle() - .get_data_handler() - .remove_all(storage, &old_epoch)?; - } - Ok(()) -} - /// Read the position of the validator in the subset of validators that have the /// same bonded stake. This information is held in its own epoched structure in /// addition to being inside the validator sets. @@ -2095,7 +2119,7 @@ struct FoldRedelegatedBondsResult { // `def foldAndSlashRedelegatedBondsMap` fn fold_and_slash_redelegated_bonds( storage: &S, - params: &PosParams, + params: &OwnedPosParams, redelegated_unbonds: &EagerRedelegatedBondsMap, start_epoch: Epoch, list_slashes: &[Slash], @@ -2146,7 +2170,7 @@ where /// - `amount` - the amount of slashable tokens. // `def applyListSlashes` fn apply_list_slashes( - params: &PosParams, + params: &OwnedPosParams, slashes: &[Slash], amount: token::Amount, ) -> token::Amount { @@ -2166,7 +2190,7 @@ fn apply_list_slashes( /// that a set of slashes may have been previously applied. // `def computeSlashableAmount` fn compute_slashable_amount( - params: &PosParams, + params: &OwnedPosParams, slash: &Slash, amount: token::Amount, computed_slashes: &BTreeMap, @@ -2599,7 +2623,7 @@ fn get_slashed_amount( // `def computeAmountAfterSlashingUnbond` fn compute_amount_after_slashing_unbond( storage: &S, - params: &PosParams, + params: &OwnedPosParams, unbonds: &BTreeMap, redelegated_unbonds: &EagerRedelegatedUnbonds, slashes: Vec, @@ -2654,7 +2678,7 @@ where // `def computeAmountAfterSlashingWithdraw` fn compute_amount_after_slashing_withdraw( storage: &S, - params: &PosParams, + params: &OwnedPosParams, unbonds_and_redelegated_unbonds: &BTreeMap< (Epoch, Epoch), (token::Amount, EagerRedelegatedBondsMap), @@ -2725,6 +2749,8 @@ pub struct BecomeValidator<'a, S> { pub address: &'a Address, /// The validator's consensus key, used by Tendermint. pub consensus_key: &'a common::PublicKey, + /// The validator's protocol key. + pub protocol_key: &'a common::PublicKey, /// The validator's Ethereum bridge cold key. pub eth_cold_key: &'a common::PublicKey, /// The validator's Ethereum bridge hot key. @@ -2749,6 +2775,7 @@ where params, address, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, current_epoch, @@ -2779,6 +2806,12 @@ where current_epoch, params.pipeline_len, )?; + validator_protocol_key_handle(address).set( + storage, + protocol_key.clone(), + current_epoch, + params.pipeline_len, + )?; validator_eth_hot_key_handle(address).set( storage, eth_hot_key.clone(), @@ -3979,10 +4012,10 @@ fn make_unbond_details( if slash.epoch >= start && slash.epoch < withdraw - .checked_sub(Epoch( + .checked_sub( params.unbonding_len + params.cubic_slashing_window_length, - )) + ) .unwrap_or_default() { let cur_rate = slash_rates_by_epoch.entry(slash.epoch).or_default(); @@ -4449,6 +4482,9 @@ where *cur_rate = cmp::min(Dec::one(), *cur_rate + slash_rate); } + // Update the epochs of enqueued slashes in storage + enqueued_slashes_handle().update_data(storage, ¶ms, current_epoch)?; + // `resultSlashing` let mut map_validator_slash: EagerRedelegatedBondsMap = BTreeMap::new(); for (validator, slash_rate) in eager_validator_slash_rates { @@ -4633,7 +4669,7 @@ where #[allow(clippy::too_many_arguments)] fn slash_validator_redelegation( storage: &S, - params: &PosParams, + params: &OwnedPosParams, src_validator: &Address, current_epoch: Epoch, outgoing_redelegations: &NestedMap>, @@ -4685,7 +4721,7 @@ where #[allow(clippy::too_many_arguments)] fn slash_redelegation( storage: &S, - params: &PosParams, + params: &OwnedPosParams, amount: token::Amount, bond_start: Epoch, redel_bond_start: Epoch, @@ -4811,7 +4847,7 @@ where // `def slashValidator` fn slash_validator( storage: &S, - params: &PosParams, + params: &OwnedPosParams, validator: &Address, slash_rate: Dec, current_epoch: Epoch, @@ -4936,7 +4972,7 @@ where /// - `redelegated_bonds` fn compute_bond_at_epoch( storage: &S, - params: &PosParams, + params: &OwnedPosParams, validator: &Address, epoch: Epoch, start: Epoch, @@ -4984,7 +5020,7 @@ where #[allow(clippy::too_many_arguments)] fn compute_slash_bond_at_epoch( storage: &S, - params: &PosParams, + params: &OwnedPosParams, validator: &Address, epoch: Epoch, infraction_epoch: Epoch, @@ -5348,3 +5384,21 @@ where Ok(()) } + +/// Init PoS genesis wrapper helper that also initializes gov params that are +/// used in PoS with default values. +#[cfg(any(test, feature = "testing"))] +pub fn test_init_genesis( + storage: &mut S, + owned: OwnedPosParams, + validators: impl Iterator + Clone, + current_epoch: namada_core::types::storage::Epoch, +) -> storage_api::Result +where + S: StorageRead + StorageWrite, +{ + let gov_params = namada_core::ledger::governance::parameters::GovernanceParameters::default(); + gov_params.init_storage(storage)?; + crate::init_genesis(storage, &owned, validators, current_epoch)?; + crate::read_non_pos_owned_params(storage, owned) +} diff --git a/proof_of_stake/src/parameters.rs b/proof_of_stake/src/parameters.rs index 1fe0b33ed3..0c173c9261 100644 --- a/proof_of_stake/src/parameters.rs +++ b/proof_of_stake/src/parameters.rs @@ -1,16 +1,28 @@ //! Proof-of-Stake system parameters use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::ledger::governance::parameters::GovernanceParameters; use namada_core::types::dec::Dec; use namada_core::types::storage::Epoch; use namada_core::types::token; use namada_core::types::uint::Uint; use thiserror::Error; -/// Proof-of-Stake system parameters, set at genesis and can only be changed via -/// governance +/// Proof-of-Stake system parameters. This includes parameters that are used in +/// PoS but are read from other accounts storage (governance). #[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct PosParams { + /// PoS-owned params + pub owned: OwnedPosParams, + /// Governance param - Maximum proposal voting period in epochs. + /// This param is stored in governance. + pub max_proposal_period: u64, +} + +/// Proof-of-Stake system parameters owned by the PoS address, set at genesis +/// and can only be changed via governance +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +pub struct OwnedPosParams { /// A maximum number of consensus validators pub max_validator_slots: u64, /// Any change applied during an epoch `n` will become active at the @@ -49,6 +61,17 @@ pub struct PosParams { } impl Default for PosParams { + fn default() -> Self { + let owned = OwnedPosParams::default(); + let gov = GovernanceParameters::default(); + Self { + owned, + max_proposal_period: gov.max_proposal_period, + } + } +} + +impl Default for OwnedPosParams { fn default() -> Self { Self { max_validator_slots: 100, @@ -102,7 +125,7 @@ const MAX_TOTAL_VOTING_POWER: i64 = i64::MAX / 8; /// Assuming token amount is `u64` in micro units. const TOKEN_MAX_AMOUNT: u64 = u64::MAX / TOKENS_PER_NAM; -impl PosParams { +impl OwnedPosParams { /// Validate PoS parameters values. Returns an empty list if the values are /// valid. #[must_use] @@ -204,6 +227,24 @@ impl PosParams { infraction_epoch + self.slash_processing_epoch_offset(); redel_start < processing_epoch && infraction_epoch < redel_end } + + /// A test helper to add the default gov params to PoS params. + #[cfg(any(test, feature = "testing"))] + pub fn with_default_gov_params(self) -> PosParams { + let gov = GovernanceParameters::default(); + PosParams { + owned: self, + max_proposal_period: gov.max_proposal_period, + } + } +} + +impl std::ops::Deref for PosParams { + type Target = OwnedPosParams; + + fn deref(&self) -> &Self::Target { + &self.owned + } } #[cfg(test)] @@ -245,8 +286,8 @@ pub mod testing { unbonding_len in pipeline_len + 1..pipeline_len + 8, pipeline_len in Just(pipeline_len), tm_votes_per_token in 1..10_001_i128) - -> PosParams { - PosParams { + -> OwnedPosParams { + OwnedPosParams { max_validator_slots, pipeline_len, unbonding_len, diff --git a/proof_of_stake/src/pos_queries.rs b/proof_of_stake/src/pos_queries.rs index a629afce73..b694897aa8 100644 --- a/proof_of_stake/src/pos_queries.rs +++ b/proof_of_stake/src/pos_queries.rs @@ -1,7 +1,6 @@ //! Storage API for querying data about Proof-of-stake related //! data. This includes validator and epoch related data. -use borsh::BorshDeserialize; use namada_core::ledger::parameters::storage::get_max_proposal_bytes_key; use namada_core::ledger::parameters::EpochDuration; use namada_core::ledger::storage::WlStorage; @@ -172,16 +171,17 @@ where pk: &key::common::PublicKey, epoch: Option, ) -> Result { - let pk_bytes = - borsh::to_vec(pk).expect("Serializing public key should not fail"); + let params = crate::read_pos_params(self.wl_storage) + .expect("Failed to fetch Pos params"); let epoch = epoch .unwrap_or_else(|| self.wl_storage.storage.get_current_epoch().0); self.get_consensus_validators(Some(epoch)) .iter() .find(|validator| { - let pk_key = key::protocol_pk_key(&validator.address); - match self.wl_storage.storage.read(&pk_key) { - Ok((Some(bytes), _)) => bytes == pk_bytes, + let protocol_keys = + crate::validator_protocol_key_handle(&validator.address); + match protocol_keys.get(self.wl_storage, epoch, ¶ms) { + Ok(Some(key)) => key == *pk, _ => false, } }) @@ -194,26 +194,24 @@ where address: &Address, epoch: Option, ) -> Result<(token::Amount, key::common::PublicKey)> { + let params = crate::read_pos_params(self.wl_storage) + .expect("Failed to fetch Pos params"); let epoch = epoch .unwrap_or_else(|| self.wl_storage.storage.get_current_epoch().0); self.get_consensus_validators(Some(epoch)) .iter() .find(|validator| address == &validator.address) .map(|validator| { - let protocol_pk_key = key::protocol_pk_key(&validator.address); - // TODO: rewrite this, to use `StorageRead::read` - let bytes = self - .wl_storage - .storage - .read(&protocol_pk_key) - .expect("Validator should have public protocol key") - .0 - .expect("Validator should have public protocol key"); - let protocol_pk: key::common::PublicKey = - BorshDeserialize::deserialize(&mut bytes.as_ref()).expect( - "Protocol public key in storage should be \ - deserializable", + let protocol_keys = + crate::validator_protocol_key_handle(&validator.address); + let protocol_pk = protocol_keys + .get(self.wl_storage, epoch, ¶ms) + .unwrap() + .expect( + "Protocol public key should be set in storage after \ + genesis.", ); + (validator.bonded_stake, protocol_pk) }) .ok_or_else(|| Error::NotValidatorAddress(address.clone(), epoch)) diff --git a/proof_of_stake/src/tests.rs b/proof_of_stake/src/tests.rs index 8c2ce7fbc4..caac96c540 100644 --- a/proof_of_stake/src/tests.rs +++ b/proof_of_stake/src/tests.rs @@ -38,8 +38,9 @@ use proptest::test_runner::Config; // `tracing` logs from tests use test_log::test; +use crate::epoched::DEFAULT_NUM_PAST_EPOCHS; use crate::parameters::testing::arb_pos_params; -use crate::parameters::PosParams; +use crate::parameters::{OwnedPosParams, PosParams}; use crate::types::{ into_tm_voting_power, BondDetails, BondId, BondsAndUnbondsDetails, ConsensusValidator, EagerRedelegatedBondsMap, GenesisValidator, Position, @@ -58,39 +59,38 @@ use crate::{ find_bonds_to_remove, find_validator_by_raw_hash, fold_and_slash_redelegated_bonds, get_num_consensus_validators, init_genesis, insert_validator_into_validator_set, is_validator, - process_slashes, purge_validator_sets_for_old_epoch, - read_below_capacity_validator_set_addresses_with_stake, + process_slashes, read_below_capacity_validator_set_addresses_with_stake, read_below_threshold_validator_set_addresses, read_consensus_validator_set_addresses_with_stake, read_total_stake, read_validator_deltas_value, read_validator_stake, slash, slash_redelegation, slash_validator, slash_validator_redelegation, - staking_token_address, store_total_consensus_stake, total_bonded_handle, - total_deltas_handle, total_unbonded_handle, unbond_handle, unbond_tokens, - unjail_validator, update_validator_deltas, update_validator_set, - validator_consensus_key_handle, validator_incoming_redelegations_handle, + staking_token_address, store_total_consensus_stake, test_init_genesis, + total_bonded_handle, total_deltas_handle, total_unbonded_handle, + unbond_handle, unbond_tokens, unjail_validator, update_validator_deltas, + update_validator_set, validator_consensus_key_handle, + validator_incoming_redelegations_handle, validator_outgoing_redelegations_handle, validator_set_positions_handle, validator_set_update_tendermint, validator_slashes_handle, validator_state_handle, validator_total_redelegated_bonded_handle, validator_total_redelegated_unbonded_handle, withdraw_tokens, write_validator_address_raw_hash, BecomeValidator, EagerRedelegatedUnbonds, FoldRedelegatedBondsResult, ModifiedRedelegation, RedelegationError, - STORE_VALIDATOR_SETS_LEN, }; proptest! { - // Generate arb valid input for `test_init_genesis_aux` + // Generate arb valid input for `test_test_init_genesis_aux` #![proptest_config(Config { cases: 100, .. Config::default() })] #[test] - fn test_init_genesis( + fn test_test_init_genesis( (pos_params, genesis_validators) in arb_params_and_genesis_validators(Some(5), 1..10), start_epoch in (0_u64..1000).prop_map(Epoch), ) { - test_init_genesis_aux(pos_params, start_epoch, genesis_validators) + test_test_init_genesis_aux(pos_params, start_epoch, genesis_validators) } } @@ -230,7 +230,7 @@ proptest! { fn arb_params_and_genesis_validators( num_max_validator_slots: Option, val_size: Range, -) -> impl Strategy)> { +) -> impl Strategy)> { let params = arb_pos_params(num_max_validator_slots); params.prop_flat_map(move |params| { let validators = arb_genesis_validators( @@ -242,7 +242,7 @@ fn arb_params_and_genesis_validators( } fn test_slashes_with_unbonding_params() --> impl Strategy, u64)> { +-> impl Strategy, u64)> { let params = arb_pos_params(Some(5)); params.prop_flat_map(|params| { let unbond_delay = 0..(params.slash_processing_epoch_offset() * 2); @@ -254,8 +254,8 @@ fn test_slashes_with_unbonding_params() } /// Test genesis initialization -fn test_init_genesis_aux( - params: PosParams, +fn test_test_init_genesis_aux( + params: OwnedPosParams, start_epoch: Epoch, mut validators: Vec, ) { @@ -267,8 +267,13 @@ fn test_init_genesis_aux( s.storage.block.epoch = start_epoch; validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); - init_genesis(&mut s, ¶ms, validators.clone().into_iter(), start_epoch) - .unwrap(); + let params = test_init_genesis( + &mut s, + params, + validators.clone().into_iter(), + start_epoch, + ) + .unwrap(); let mut bond_details = bonds_and_unbonds(&s, None, None).unwrap(); assert!(bond_details.iter().all(|(_id, details)| { @@ -336,7 +341,7 @@ fn test_init_genesis_aux( /// Test bonding /// NOTE: copy validator sets each time we advance the epoch -fn test_bonds_aux(params: PosParams, validators: Vec) { +fn test_bonds_aux(params: OwnedPosParams, validators: Vec) { // This can be useful for debugging: // params.pipeline_len = 2; // params.unbonding_len = 4; @@ -346,9 +351,9 @@ fn test_bonds_aux(params: PosParams, validators: Vec) { // Genesis let start_epoch = s.storage.block.epoch; let mut current_epoch = s.storage.block.epoch; - init_genesis( + let params = test_init_genesis( &mut s, - ¶ms, + params, validators.clone().into_iter(), current_epoch, ) @@ -876,7 +881,7 @@ fn test_bonds_aux(params: PosParams, validators: Vec) { /// Test validator initialization. fn test_become_validator_aux( - params: PosParams, + params: OwnedPosParams, new_validator: Address, new_validator_consensus_key: SecretKey, validators: Vec, @@ -890,9 +895,9 @@ fn test_become_validator_aux( // Genesis let mut current_epoch = dbg!(s.storage.block.epoch); - init_genesis( + let params = test_init_genesis( &mut s, - ¶ms, + params, validators.clone().into_iter(), current_epoch, ) @@ -923,6 +928,8 @@ fn test_become_validator_aux( // Initialize the validator account let consensus_key = new_validator_consensus_key.to_public(); + let protocol_sk = common_sk_from_simple_seed(0); + let protocol_key = protocol_sk.to_public(); let eth_hot_key = key::common::PublicKey::Secp256k1( key::testing::gen_keypair::().ref_to(), ); @@ -934,6 +941,7 @@ fn test_become_validator_aux( params: ¶ms, address: &new_validator, consensus_key: &consensus_key, + protocol_key: &protocol_key, eth_cold_key: ð_cold_key, eth_hot_key: ð_hot_key, current_epoch, @@ -1023,7 +1031,7 @@ fn test_become_validator_aux( } fn test_slashes_with_unbonding_aux( - mut params: PosParams, + mut params: OwnedPosParams, validators: Vec, unbond_delay: u64, ) { @@ -1049,9 +1057,9 @@ fn test_slashes_with_unbonding_aux( // Genesis // let start_epoch = s.storage.block.epoch; let mut current_epoch = s.storage.block.epoch; - init_genesis( + let params = test_init_genesis( &mut s, - ¶ms, + params, validators.clone().into_iter(), current_epoch, ) @@ -1203,7 +1211,7 @@ fn test_validator_raw_hash() { fn test_validator_sets() { let mut s = TestWlStorage::default(); // Only 3 consensus validator slots - let params = PosParams { + let params = OwnedPosParams { max_validator_slots: 3, ..Default::default() }; @@ -1220,38 +1228,6 @@ fn test_validator_sets() { res }; - // A helper to insert a non-genesis validator - let insert_validator = |s: &mut TestWlStorage, - addr, - pk: &PublicKey, - stake: token::Amount, - epoch: Epoch| { - insert_validator_into_validator_set( - s, - ¶ms, - addr, - stake, - epoch, - params.pipeline_len, - ) - .unwrap(); - - update_validator_deltas( - s, - addr, - stake.change(), - epoch, - params.pipeline_len, - ) - .unwrap(); - - // Set their consensus key (needed for - // `validator_set_update_tendermint` fn) - validator_consensus_key_handle(addr) - .set(s, pk.clone(), epoch, params.pipeline_len) - .unwrap(); - }; - // Create genesis validators let ((val1, pk1), stake1) = (gen_validator(), token::Amount::native_whole(1)); @@ -1278,14 +1254,18 @@ fn test_validator_sets() { let start_epoch = Epoch::default(); let epoch = start_epoch; - init_genesis( + let protocol_sk_1 = common_sk_from_simple_seed(0); + let protocol_sk_2 = common_sk_from_simple_seed(1); + + let params = test_init_genesis( &mut s, - ¶ms, + params, [ GenesisValidator { address: val1.clone(), tokens: stake1, consensus_key: pk1.clone(), + protocol_key: protocol_sk_1.to_public(), eth_hot_key: key::common::PublicKey::Secp256k1( key::testing::gen_keypair::() .ref_to(), @@ -1302,6 +1282,7 @@ fn test_validator_sets() { address: val2.clone(), tokens: stake2, consensus_key: pk2.clone(), + protocol_key: protocol_sk_2.to_public(), eth_hot_key: key::common::PublicKey::Secp256k1( key::testing::gen_keypair::() .ref_to(), @@ -1320,6 +1301,38 @@ fn test_validator_sets() { ) .unwrap(); + // A helper to insert a non-genesis validator + let insert_validator = |s: &mut TestWlStorage, + addr, + pk: &PublicKey, + stake: token::Amount, + epoch: Epoch| { + insert_validator_into_validator_set( + s, + ¶ms, + addr, + stake, + epoch, + params.pipeline_len, + ) + .unwrap(); + + update_validator_deltas( + s, + addr, + stake.change(), + epoch, + params.pipeline_len, + ) + .unwrap(); + + // Set their consensus key (needed for + // `validator_set_update_tendermint` fn) + validator_consensus_key_handle(addr) + .set(s, pk.clone(), epoch, params.pipeline_len) + .unwrap(); + }; + // Advance to EPOCH 1 // // We cannot call `get_tendermint_set_updates` for the genesis state as @@ -1850,16 +1863,17 @@ fn test_validator_sets() { ); assert_eq!(tm_updates[1], ValidatorSetUpdate::Deactivated(pk4)); - // Check that the validator sets were purged for the old epochs + // Check that the below-capacity validator set was purged for the old epochs + // but that the consensus_validator_set was not let last_epoch = epoch; for e in Epoch::iter_bounds_inclusive( start_epoch, last_epoch - .sub_or_default(Epoch(STORE_VALIDATOR_SETS_LEN)) + .sub_or_default(Epoch(DEFAULT_NUM_PAST_EPOCHS)) .sub_or_default(Epoch(1)), ) { assert!( - consensus_validator_set_handle() + !consensus_validator_set_handle() .at(&e) .is_empty(&s) .unwrap() @@ -1883,7 +1897,7 @@ fn test_validator_sets() { fn test_validator_sets_swap() { let mut s = TestWlStorage::default(); // Only 2 consensus validator slots - let params = PosParams { + let params = OwnedPosParams { max_validator_slots: 2, // Set the stake threshold to 0 so no validators are in the // below-threshold set @@ -1892,6 +1906,7 @@ fn test_validator_sets_swap() { tm_votes_per_token: Dec::new(1, 1).expect("Dec creation failed"), ..Default::default() }; + let addr_seed = "seed"; let mut address_gen = EstablishedAddressGen::new(addr_seed); let mut sk_seed = 0; @@ -1905,38 +1920,6 @@ fn test_validator_sets_swap() { res }; - // A helper to insert a non-genesis validator - let insert_validator = |s: &mut TestWlStorage, - addr, - pk: &PublicKey, - stake: token::Amount, - epoch: Epoch| { - insert_validator_into_validator_set( - s, - ¶ms, - addr, - stake, - epoch, - params.pipeline_len, - ) - .unwrap(); - - update_validator_deltas( - s, - addr, - stake.change(), - epoch, - params.pipeline_len, - ) - .unwrap(); - - // Set their consensus key (needed for - // `validator_set_update_tendermint` fn) - validator_consensus_key_handle(addr) - .set(s, pk.clone(), epoch, params.pipeline_len) - .unwrap(); - }; - // Start with two genesis validators, one with 1 voting power and other 0 let epoch = Epoch::default(); // 1M voting power @@ -1952,14 +1935,18 @@ fn test_validator_sets_swap() { println!("val2: {val2}, {pk2}, {}", stake2.to_string_native()); println!("val3: {val3}, {pk3}, {}", stake3.to_string_native()); - init_genesis( + let protocol_sk_1 = common_sk_from_simple_seed(0); + let protocol_sk_2 = common_sk_from_simple_seed(1); + + let params = test_init_genesis( &mut s, - ¶ms, + params, [ GenesisValidator { address: val1, tokens: stake1, consensus_key: pk1, + protocol_key: protocol_sk_1.to_public(), eth_hot_key: key::common::PublicKey::Secp256k1( key::testing::gen_keypair::() .ref_to(), @@ -1976,6 +1963,7 @@ fn test_validator_sets_swap() { address: val2.clone(), tokens: stake2, consensus_key: pk2, + protocol_key: protocol_sk_2.to_public(), eth_hot_key: key::common::PublicKey::Secp256k1( key::testing::gen_keypair::() .ref_to(), @@ -1994,6 +1982,38 @@ fn test_validator_sets_swap() { ) .unwrap(); + // A helper to insert a non-genesis validator + let insert_validator = |s: &mut TestWlStorage, + addr, + pk: &PublicKey, + stake: token::Amount, + epoch: Epoch| { + insert_validator_into_validator_set( + s, + ¶ms, + addr, + stake, + epoch, + params.pipeline_len, + ) + .unwrap(); + + update_validator_deltas( + s, + addr, + stake.change(), + epoch, + params.pipeline_len, + ) + .unwrap(); + + // Set their consensus key (needed for + // `validator_set_update_tendermint` fn) + validator_consensus_key_handle(addr) + .set(s, pk.clone(), epoch, params.pipeline_len) + .unwrap(); + }; + // Advance to EPOCH 1 let epoch = advance_epoch(&mut s, ¶ms); let pipeline_epoch = epoch + params.pipeline_len; @@ -2147,11 +2167,12 @@ fn advance_epoch(s: &mut TestWlStorage, params: &PosParams) -> Epoch { store_total_consensus_stake(s, current_epoch).unwrap(); copy_validator_sets_and_positions( s, + params, current_epoch, current_epoch + params.pipeline_len, ) .unwrap(); - purge_validator_sets_for_old_epoch(s, current_epoch).unwrap(); + // purge_validator_sets_for_old_epoch(s, current_epoch).unwrap(); // process_slashes(s, current_epoch).unwrap(); // dbg!(current_epoch); current_epoch @@ -2188,6 +2209,9 @@ fn arb_genesis_validators( let consensus_sk = common_sk_from_simple_seed(seed); let consensus_key = consensus_sk.to_public(); + let protocol_sk = common_sk_from_simple_seed(seed); + let protocol_key = protocol_sk.to_public(); + let eth_hot_key = key::common::PublicKey::Secp256k1( key::testing::gen_keypair::( ) @@ -2206,6 +2230,7 @@ fn arb_genesis_validators( address, tokens, consensus_key, + protocol_key, eth_hot_key, eth_cold_key, commission_rate, @@ -2224,7 +2249,7 @@ fn arb_genesis_validators( } fn test_unjail_validator_aux( - params: PosParams, + params: OwnedPosParams, mut validators: Vec, ) { println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); @@ -2246,9 +2271,9 @@ fn test_unjail_validator_aux( // Genesis let mut current_epoch = s.storage.block.epoch; - init_genesis( + let params = test_init_genesis( &mut s, - ¶ms, + params, validators.clone().into_iter(), current_epoch, ) @@ -2605,7 +2630,7 @@ fn test_compute_modified_redelegation() { #[test] fn test_compute_bond_at_epoch() { let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { pipeline_len: 2, unbonding_len: 4, cubic_slashing_window_length: 1, @@ -2755,7 +2780,7 @@ fn test_compute_bond_at_epoch() { #[test] fn test_compute_slash_bond_at_epoch() { let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { pipeline_len: 2, unbonding_len: 4, cubic_slashing_window_length: 1, @@ -3018,7 +3043,7 @@ fn test_compute_new_redelegated_unbonds() { #[test] fn test_apply_list_slashes() { let init_epoch = Epoch(2); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; @@ -3066,7 +3091,7 @@ fn test_apply_list_slashes() { #[test] fn test_compute_slashable_amount() { let init_epoch = Epoch(2); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; @@ -3123,7 +3148,7 @@ fn test_compute_slashable_amount() { #[test] fn test_fold_and_slash_redelegated_bonds() { let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; @@ -3222,7 +3247,7 @@ fn test_fold_and_slash_redelegated_bonds() { #[test] fn test_slash_redelegation() { let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; @@ -3410,7 +3435,7 @@ fn test_slash_redelegation() { #[test] fn test_slash_validator_redelegation() { let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; @@ -3587,7 +3612,7 @@ fn test_slash_validator_redelegation() { #[test] fn test_slash_validator() { let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; @@ -3996,7 +4021,7 @@ fn test_slash_validator() { #[test] fn compute_amount_after_slashing_unbond_test() { let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; @@ -4114,7 +4139,7 @@ fn compute_amount_after_slashing_unbond_test() { #[test] fn compute_amount_after_slashing_withdraw_test() { let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; @@ -4275,10 +4300,14 @@ fn test_simple_redelegation_aux( let dest_validator = validators[1].address.clone(); let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; + let all_params = PosParams { + owned: params.clone(), + ..Default::default() + }; // Genesis let mut current_epoch = storage.storage.block.epoch; @@ -4317,7 +4346,7 @@ fn test_simple_redelegation_aux( ); for _ in 0..5 { - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); } @@ -4360,11 +4389,11 @@ fn test_simple_redelegation_aux( dbg!(&bonds, &bonds_dest, &unbonds, &tot_bonds, &tot_unbonds); // Advance three epochs - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); // Redelegate in epoch 3 @@ -4512,11 +4541,11 @@ fn test_simple_redelegation_aux( assert_eq!(redelegated, amount_redelegate); // Advance three epochs - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); // Unbond in epoch 5 from dest_validator @@ -4641,7 +4670,7 @@ fn test_simple_redelegation_aux( // Advance to withdrawal epoch loop { - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); if current_epoch == unbond_end { break; @@ -4686,12 +4715,16 @@ fn test_redelegation_with_slashing_aux( let dest_validator = validators[1].address.clone(); let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, // Avoid empty consensus set by removing the threshold validator_stake_threshold: token::Amount::zero(), ..Default::default() }; + let all_params = PosParams { + owned: params.clone(), + ..Default::default() + }; // Genesis let mut current_epoch = storage.storage.block.epoch; @@ -4712,7 +4745,7 @@ fn test_redelegation_with_slashing_aux( .unwrap(); for _ in 0..5 { - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); } @@ -4755,11 +4788,11 @@ fn test_redelegation_with_slashing_aux( dbg!(&bonds, &bonds_dest, &unbonds, &tot_bonds, &tot_unbonds); // Advance three epochs - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); // Redelegate in epoch 8 @@ -4907,11 +4940,11 @@ fn test_redelegation_with_slashing_aux( assert_eq!(redelegated, amount_redelegate); // Advance three epochs - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); // Unbond in epoch 11 from dest_validator @@ -4998,13 +5031,13 @@ fn test_redelegation_with_slashing_aux( ); // Advance one epoch - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); // Discover evidence slash( &mut storage, - ¶ms, + &all_params, current_epoch, init_epoch + 2 * params.pipeline_len, 0u64, @@ -5053,7 +5086,7 @@ fn test_redelegation_with_slashing_aux( // Advance to withdrawal epoch loop { - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); if current_epoch == unbond_end { break; @@ -5094,10 +5127,14 @@ fn test_chain_redelegations_aux(mut validators: Vec) { let _init_stake_dest_2 = validators[2].tokens; let mut storage = TestWlStorage::default(); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; + let all_params = PosParams { + owned: params.clone(), + ..Default::default() + }; // Genesis let mut current_epoch = storage.storage.block.epoch; @@ -5131,7 +5168,7 @@ fn test_chain_redelegations_aux(mut validators: Vec) { let bond_start = current_epoch + params.pipeline_len; // Advance one epoch - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); // Redelegate in epoch 1 to dest_validator @@ -5244,9 +5281,9 @@ fn test_chain_redelegations_aux(mut validators: Vec) { assert_eq!(src_total_unbonded, redel_amount_1); // Attempt to redelegate in epoch 3 to dest_validator - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); let redel_amount_2: token::Amount = 23.into(); @@ -5265,7 +5302,7 @@ fn test_chain_redelegations_aux(mut validators: Vec) { let epoch_can_redel = redel_end.prev() + params.slash_processing_epoch_offset(); loop { - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); if current_epoch == epoch_can_redel.prev() { break; @@ -5284,7 +5321,7 @@ fn test_chain_redelegations_aux(mut validators: Vec) { assert!(redel_att.is_err()); // Advance one more epoch - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); // Redelegate from dest_validator to dest_validator_2 now @@ -5556,10 +5593,14 @@ fn test_from_sm_case_1() { fn test_overslashing_aux(mut validators: Vec) { assert_eq!(validators.len(), 4); - let params = PosParams { + let params = OwnedPosParams { unbonding_len: 4, ..Default::default() }; + let all_params = PosParams { + owned: params.clone(), + ..Default::default() + }; let offending_stake = token::Amount::native_whole(110); let other_stake = token::Amount::native_whole(100); @@ -5609,14 +5650,14 @@ fn test_overslashing_aux(mut validators: Vec) { // Advance to pipeline epoch for _ in 0..params.pipeline_len { - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); } assert_eq!(delegation_epoch, current_epoch); // Find a misbehavior committed in epoch 0 slash( &mut storage, - ¶ms, + &all_params, current_epoch, self_bond_epoch, 0_u64, @@ -5629,7 +5670,7 @@ fn test_overslashing_aux(mut validators: Vec) { // Find a misbehavior committed in current epoch slash( &mut storage, - ¶ms, + &all_params, current_epoch, delegation_epoch, 0_u64, @@ -5646,7 +5687,7 @@ fn test_overslashing_aux(mut validators: Vec) { // Advance to processing epoch 1 loop { - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); if current_epoch == processing_epoch_1 { break; @@ -5663,11 +5704,11 @@ fn test_overslashing_aux(mut validators: Vec) { // Check that the proper amount was slashed let epoch = current_epoch.next(); let validator_stake = - read_validator_stake(&storage, ¶ms, &validator, epoch).unwrap(); + read_validator_stake(&storage, &all_params, &validator, epoch).unwrap(); let exp_validator_stake = offending_stake - exp_slashed_1 + amount_del; assert_eq!(validator_stake, exp_validator_stake); - let total_stake = read_total_stake(&storage, ¶ms, epoch).unwrap(); + let total_stake = read_total_stake(&storage, &all_params, epoch).unwrap(); let exp_total_stake = offending_stake - exp_slashed_1 + amount_del + 3 * other_stake; assert_eq!(total_stake, exp_total_stake); @@ -5683,7 +5724,7 @@ fn test_overslashing_aux(mut validators: Vec) { // Advance to processing epoch 2 loop { - current_epoch = advance_epoch(&mut storage, ¶ms); + current_epoch = advance_epoch(&mut storage, &all_params); super::process_slashes(&mut storage, current_epoch).unwrap(); if current_epoch == processing_epoch_2 { break; @@ -5704,11 +5745,11 @@ fn test_overslashing_aux(mut validators: Vec) { let epoch = current_epoch.next(); let validator_stake = - read_validator_stake(&storage, ¶ms, &validator, epoch).unwrap(); + read_validator_stake(&storage, &all_params, &validator, epoch).unwrap(); let exp_validator_stake = amount_del - exp_slashed_from_delegation; assert_eq!(validator_stake, exp_validator_stake); - let total_stake = read_total_stake(&storage, ¶ms, epoch).unwrap(); + let total_stake = read_total_stake(&storage, &all_params, epoch).unwrap(); let exp_total_stake = amount_del - exp_slashed_from_delegation + 3 * other_stake; assert_eq!(total_stake, exp_total_stake); diff --git a/proof_of_stake/src/tests/state_machine.rs b/proof_of_stake/src/tests/state_machine.rs index e9c4db1b3a..52add87695 100644 --- a/proof_of_stake/src/tests/state_machine.rs +++ b/proof_of_stake/src/tests/state_machine.rs @@ -165,6 +165,7 @@ enum Transition { InitValidator { address: Address, consensus_key: PublicKey, + protocol_key: PublicKey, eth_cold_key: PublicKey, eth_hot_key: PublicKey, commission_rate: Dec, @@ -216,9 +217,9 @@ impl StateMachineTest for ConcretePosState { .collect::>() ); let mut s = TestWlStorage::default(); - crate::init_genesis( + crate::test_init_genesis( &mut s, - &initial_state.params, + initial_state.params.owned.clone(), initial_state.genesis_validators.clone().into_iter(), initial_state.epoch, ) @@ -254,6 +255,7 @@ impl StateMachineTest for ConcretePosState { Transition::InitValidator { address, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, commission_rate, @@ -267,6 +269,7 @@ impl StateMachineTest for ConcretePosState { params: ¶ms, address: &address, consensus_key: &consensus_key, + protocol_key: &protocol_key, eth_cold_key: ð_cold_key, eth_hot_key: ð_hot_key, current_epoch, @@ -1979,6 +1982,7 @@ impl ReferenceStateMachine for AbstractPosState { tracing::debug!("\nInitializing abstract state machine"); arb_params_and_genesis_validators(Some(8), 8..10) .prop_map(|(params, genesis_validators)| { + let params = params.with_default_gov_params(); let epoch = Epoch::default(); let mut state = Self { epoch, @@ -2013,6 +2017,7 @@ impl ReferenceStateMachine for AbstractPosState { address, tokens, consensus_key: _, + protocol_key: _, eth_cold_key: _, eth_hot_key: _, commission_rate: _, @@ -2156,6 +2161,7 @@ impl ReferenceStateMachine for AbstractPosState { 1 => ( address::testing::arb_established_address(), key::testing::arb_common_keypair(), + key::testing::arb_common_keypair(), key::testing::arb_common_secp256k1_keypair(), key::testing::arb_common_secp256k1_keypair(), arb_rate(), @@ -2165,6 +2171,7 @@ impl ReferenceStateMachine for AbstractPosState { |( addr, consensus_key, + protocol_key, eth_hot_key, eth_cold_key, commission_rate, @@ -2173,6 +2180,7 @@ impl ReferenceStateMachine for AbstractPosState { Transition::InitValidator { address: Address::Established(addr), consensus_key: consensus_key.to_public(), + protocol_key: protocol_key.to_public(), eth_hot_key: eth_hot_key.to_public(), eth_cold_key: eth_cold_key.to_public(), commission_rate, @@ -2311,6 +2319,7 @@ impl ReferenceStateMachine for AbstractPosState { Transition::InitValidator { address, consensus_key: _, + protocol_key: _, eth_cold_key: _, eth_hot_key: _, commission_rate: _, @@ -2743,6 +2752,7 @@ impl ReferenceStateMachine for AbstractPosState { Transition::InitValidator { address, consensus_key: _, + protocol_key: _, eth_cold_key: _, eth_hot_key: _, commission_rate: _, diff --git a/proof_of_stake/src/tests/state_machine_v2.rs b/proof_of_stake/src/tests/state_machine_v2.rs index ce2e0e6817..02df1b39a0 100644 --- a/proof_of_stake/src/tests/state_machine_v2.rs +++ b/proof_of_stake/src/tests/state_machine_v2.rs @@ -1875,6 +1875,8 @@ enum Transition { #[derivative(Debug = "ignore")] consensus_key: PublicKey, #[derivative(Debug = "ignore")] + protocol_key: PublicKey, + #[derivative(Debug = "ignore")] eth_cold_key: PublicKey, #[derivative(Debug = "ignore")] eth_hot_key: PublicKey, @@ -1981,6 +1983,7 @@ impl StateMachineTest for ConcretePosState { Transition::InitValidator { address, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, commission_rate, @@ -1994,6 +1997,7 @@ impl StateMachineTest for ConcretePosState { params: ¶ms, address: &address, consensus_key: &consensus_key, + protocol_key: &protocol_key, eth_cold_key: ð_cold_key, eth_hot_key: ð_hot_key, current_epoch, @@ -3570,7 +3574,10 @@ impl ReferenceStateMachine for AbstractPosState { let epoch = Epoch::default(); let mut state = Self { epoch, - params, + params: PosParams { + owned: params, + ..Default::default() + }, genesis_validators: genesis_validators .into_iter() // Sorted by stake to fill in the consensus set first @@ -3592,6 +3599,7 @@ impl ReferenceStateMachine for AbstractPosState { address, tokens, consensus_key: _, + protocol_key: _, eth_cold_key: _, eth_hot_key: _, commission_rate: _, @@ -3712,6 +3720,7 @@ impl ReferenceStateMachine for AbstractPosState { 1 => ( address::testing::arb_established_address(), key::testing::arb_common_keypair(), + key::testing::arb_common_keypair(), key::testing::arb_common_secp256k1_keypair(), key::testing::arb_common_secp256k1_keypair(), arb_rate(), @@ -3721,6 +3730,7 @@ impl ReferenceStateMachine for AbstractPosState { |( addr, consensus_key, + protocol_key, eth_hot_key, eth_cold_key, commission_rate, @@ -3729,6 +3739,7 @@ impl ReferenceStateMachine for AbstractPosState { Transition::InitValidator { address: Address::Established(addr), consensus_key: consensus_key.to_public(), + protocol_key: protocol_key.to_public(), eth_hot_key: eth_hot_key.to_public(), eth_cold_key: eth_cold_key.to_public(), commission_rate, @@ -3871,6 +3882,7 @@ impl ReferenceStateMachine for AbstractPosState { Transition::InitValidator { address, consensus_key: _, + protocol_key: _, eth_cold_key: _, eth_hot_key: _, commission_rate: _, @@ -4214,6 +4226,7 @@ impl ReferenceStateMachine for AbstractPosState { Transition::InitValidator { address, consensus_key: _, + protocol_key: _, eth_cold_key: _, eth_hot_key: _, commission_rate: _, diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index 842073144a..aa4edf1bcc 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -24,7 +24,8 @@ pub use rev_order::ReverseOrdTokenAmount; use crate::parameters::PosParams; -// TODO: add this to the spec +// TODO: review the offsets for each epoched type!! + /// Stored positions of validators in validator sets pub type ValidatorSetPositions = crate::epoched::NestedEpoched< LazyMap, @@ -32,8 +33,6 @@ pub type ValidatorSetPositions = crate::epoched::NestedEpoched< crate::epoched::OffsetDefaultNumPastEpochs, >; -// TODO: check the offsets for each epoched type!! - /// Epoched validator's consensus key. pub type ValidatorConsensusKeys = crate::epoched::Epoched< common::PublicKey, @@ -41,18 +40,25 @@ pub type ValidatorConsensusKeys = crate::epoched::Epoched< crate::epoched::OffsetDefaultNumPastEpochs, >; +/// Epoched validator's protocol key. +pub type ValidatorProtocolKeys = crate::epoched::Epoched< + common::PublicKey, + crate::epoched::OffsetPipelineLen, + crate::epoched::OffsetMaxProposalPeriodPlus, +>; + /// Epoched validator's eth hot key. pub type ValidatorEthHotKeys = crate::epoched::Epoched< common::PublicKey, crate::epoched::OffsetPipelineLen, - crate::epoched::OffsetDefaultNumPastEpochs, + crate::epoched::OffsetMaxProposalPeriodPlus, >; /// Epoched validator's eth cold key. pub type ValidatorEthColdKeys = crate::epoched::Epoched< common::PublicKey, crate::epoched::OffsetPipelineLen, - crate::epoched::OffsetDefaultNumPastEpochs, + crate::epoched::OffsetMaxProposalPeriodPlus, >; /// Epoched validator's state. @@ -77,7 +83,7 @@ pub type BelowCapacityValidatorSet = pub type ConsensusValidatorSets = crate::epoched::NestedEpoched< ConsensusValidatorSet, crate::epoched::OffsetPipelineLen, - crate::epoched::OffsetDefaultNumPastEpochs, + crate::epoched::OffsetMaxProposalPeriodPlus, >; /// Epoched below-capacity validator sets. @@ -87,7 +93,7 @@ pub type BelowCapacityValidatorSets = crate::epoched::NestedEpoched< crate::epoched::OffsetDefaultNumPastEpochs, >; -/// Epoched total consensus validator stake +/// Epoched total consensus validator set stake pub type TotalConsensusStakes = crate::epoched::Epoched< Amount, crate::epoched::OffsetZero, @@ -98,14 +104,14 @@ pub type TotalConsensusStakes = crate::epoched::Epoched< pub type ValidatorDeltas = crate::epoched::EpochedDelta< token::Change, crate::epoched::OffsetUnbondingLen, - crate::epoched::OffsetSlashProcessingLen, + crate::epoched::OffsetMaxProposalPeriodOrSlashProcessingLenPlus, >; /// Epoched total deltas. pub type TotalDeltas = crate::epoched::EpochedDelta< token::Change, crate::epoched::OffsetUnbondingLen, - crate::epoched::OffsetSlashProcessingLen, + crate::epoched::OffsetMaxProposalPeriodOrSlashProcessingLenPlus, >; /// Epoched validator commission rate @@ -123,7 +129,7 @@ pub type Bonds = crate::epoched::EpochedDelta< >; /// An epoched lazy set of all known active validator addresses (consensus, -/// below-capacity, jailed) +/// below-capacity, below-threshold, jailed) pub type ValidatorAddresses = crate::epoched::NestedEpoched< LazySet
, crate::epoched::OffsetPipelineLen, @@ -141,7 +147,7 @@ pub type ValidatorSlashes = NestedMap; pub type EpochedSlashes = crate::epoched::NestedEpoched< ValidatorSlashes, crate::epoched::OffsetUnbondingLen, - crate::epoched::OffsetSlashProcessingLen, + crate::epoched::OffsetSlashProcessingLenPlus, >; /// Epoched validator's unbonds @@ -311,6 +317,8 @@ pub struct GenesisValidator { pub tokens: token::Amount, /// A public key used for signing validator's consensus actions pub consensus_key: common::PublicKey, + /// A public key used for signing protocol transactions + pub protocol_key: common::PublicKey, /// An Eth bridge governance public key pub eth_cold_key: common::PublicKey, /// An Eth bridge hot signing public key used for validator set updates and diff --git a/shared/src/ledger/native_vp/ibc/mod.rs b/shared/src/ledger/native_vp/ibc/mod.rs index f370e79823..dabe468c27 100644 --- a/shared/src/ledger/native_vp/ibc/mod.rs +++ b/shared/src/ledger/native_vp/ibc/mod.rs @@ -257,6 +257,9 @@ pub fn get_dummy_genesis_validator() let consensus_sk = common_sk_from_simple_seed(0); let consensus_key = consensus_sk.to_public(); + let protocol_sk = common_sk_from_simple_seed(1); + let protocol_key = protocol_sk.to_public(); + let commission_rate = Dec::new(1, 1).expect("expected 0.1 to be a valid decimal"); let max_commission_rate_change = @@ -278,6 +281,7 @@ pub fn get_dummy_genesis_validator() address, tokens, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, commission_rate, @@ -293,6 +297,7 @@ mod tests { use borsh_ext::BorshSerializeExt; use namada_core::ledger::gas::TxGasMeter; + use namada_core::ledger::governance::parameters::GovernanceParameters; use prost::Message; use sha2::Digest; @@ -409,6 +414,8 @@ mod tests { // initialize the storage ibc::init_genesis_storage(&mut wl_storage); + let gov_params = GovernanceParameters::default(); + gov_params.init_storage(&mut wl_storage).unwrap(); pos::init_genesis_storage( &mut wl_storage, &PosParams::default(), diff --git a/shared/src/ledger/pos/mod.rs b/shared/src/ledger/pos/mod.rs index a61a721e4f..d47e5cc884 100644 --- a/shared/src/ledger/pos/mod.rs +++ b/shared/src/ledger/pos/mod.rs @@ -11,7 +11,7 @@ pub use namada_core::types::dec::Dec; pub use namada_core::types::key::common; pub use namada_core::types::token; pub use namada_proof_of_stake; -pub use namada_proof_of_stake::parameters::PosParams; +pub use namada_proof_of_stake::parameters::{OwnedPosParams, PosParams}; pub use namada_proof_of_stake::pos_queries::*; pub use namada_proof_of_stake::storage::*; pub use namada_proof_of_stake::{staking_token_address, types}; @@ -42,7 +42,7 @@ pub fn into_tm_voting_power( /// Initialize storage in the genesis block. pub fn init_genesis_storage( storage: &mut S, - params: &PosParams, + params: &OwnedPosParams, validators: impl Iterator + Clone, current_epoch: Epoch, ) where diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index c294367f1c..f3ab55d83e 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -53,7 +53,6 @@ use namada::ibc_proto::google::protobuf::Any; use namada::ledger::events::EventType; use namada::ledger::ibc::storage::*; use namada::ledger::parameters::{storage as param_storage, EpochDuration}; -use namada::ledger::pos::{self, PosParams}; use namada::ledger::queries::RPC; use namada::ledger::storage::ics23_specs::ibc_proof_specs; use namada::ledger::storage::traits::Sha256Hasher; @@ -64,7 +63,7 @@ use namada::types::key::PublicKey; use namada::types::storage::{BlockHeight, Key}; use namada::types::token::Amount; use namada_apps::client::rpc::{ - query_storage_value, query_storage_value_bytes, + query_pos_parameters, query_storage_value, query_storage_value_bytes, }; use namada_apps::client::utils::id_from_pk; use namada_apps::config::ethereum_bridge; @@ -249,11 +248,8 @@ fn make_client_state(test: &Test, height: Height) -> TmClientState { let ledger_address = TendermintAddress::from_str(&rpc).unwrap(); let client = HttpClient::new(ledger_address).unwrap(); - let key = pos::params_key(); - let pos_params = test - .async_runtime() - .block_on(query_storage_value::(&client, &key)) - .unwrap(); + let pos_params = + test.async_runtime().block_on(query_pos_parameters(&client)); let pipeline_len = pos_params.pipeline_len; let key = param_storage::get_epoch_duration_storage_key(); diff --git a/tests/src/native_vp/pos.rs b/tests/src/native_vp/pos.rs index 3715804b66..233d999861 100644 --- a/tests/src/native_vp/pos.rs +++ b/tests/src/native_vp/pos.rs @@ -95,8 +95,8 @@ //! - add slashes //! - add rewards -use namada::ledger::pos::namada_proof_of_stake::init_genesis; -use namada::proof_of_stake::parameters::PosParams; +use namada::proof_of_stake::parameters::{OwnedPosParams, PosParams}; +use namada::proof_of_stake::test_init_genesis as init_genesis; use namada::proof_of_stake::types::GenesisValidator; use namada::types::storage::Epoch; @@ -106,9 +106,9 @@ use crate::tx::tx_host_env; /// parameters. pub fn init_pos( genesis_validators: &[GenesisValidator], - params: &PosParams, + params: &OwnedPosParams, start_epoch: Epoch, -) { +) -> PosParams { tx_host_env::init(); tx_host_env::with(|tx_env| { @@ -130,9 +130,9 @@ pub fn init_pos( // .storage // .init_genesis(params, genesis_validators.iter(), start_epoch) // .unwrap(); - init_genesis( + let params = init_genesis( &mut tx_env.wl_storage, - params, + params.clone(), genesis_validators.iter().cloned(), start_epoch, ) @@ -140,7 +140,8 @@ pub fn init_pos( // Commit changes in WL to genesis state tx_env.commit_genesis(); - }); + params + }) } #[cfg(test)] @@ -334,6 +335,7 @@ mod tests { // We're starting from an empty state let state = vec![]; let epoch = Epoch(epoch); + let params = params.with_default_gov_params(); arb_valid_pos_action(&state).prop_map(move |valid_action| { Self { epoch, diff --git a/tests/src/vm_host_env/ibc.rs b/tests/src/vm_host_env/ibc.rs index 25f8d09fcf..583c58ada3 100644 --- a/tests/src/vm_host_env/ibc.rs +++ b/tests/src/vm_host_env/ibc.rs @@ -84,6 +84,7 @@ use namada::types::time::DurationSecs; use namada::types::token::{self, Amount, DenominatedAmount}; use namada::vm::{wasm, WasmCacheRwAccess}; use namada_core::ledger::gas::TxGasMeter; +use namada_core::ledger::governance::parameters::GovernanceParameters; use namada_test_utils::TestWasms; use namada_tx_prelude::borsh_ext::BorshSerializeExt; @@ -211,6 +212,8 @@ pub fn init_storage() -> (Address, Address) { tx_host_env::with(|env| { ibc::init_genesis_storage(&mut env.wl_storage); + let gov_params = GovernanceParameters::default(); + gov_params.init_storage(&mut env.wl_storage).unwrap(); pos::init_genesis_storage( &mut env.wl_storage, &PosParams::default(), diff --git a/tx_prelude/src/proof_of_stake.rs b/tx_prelude/src/proof_of_stake.rs index 36584a4d19..1f365f1b9c 100644 --- a/tx_prelude/src/proof_of_stake.rs +++ b/tx_prelude/src/proof_of_stake.rs @@ -115,8 +115,6 @@ impl Ctx { &account_keys, threshold, )?; - let protocol_pk_key = key::protocol_pk_key(&validator_address); - self.write(&protocol_pk_key, &protocol_key)?; let dkg_pk_key = key::dkg_session_keys::dkg_pk_key(&validator_address); self.write(&dkg_pk_key, &dkg_key)?; let eth_cold_key = key::common::PublicKey::Secp256k1(eth_cold_key); @@ -128,6 +126,7 @@ impl Ctx { params: ¶ms, address: &validator_address, consensus_key: &consensus_key, + protocol_key: &protocol_key, eth_cold_key: ð_cold_key, eth_hot_key: ð_hot_key, current_epoch, diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index bfd088b32a..6b7263ce9b 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -17,7 +17,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { mod tests { use std::collections::BTreeSet; - use namada::ledger::pos::{PosParams, PosVP}; + use namada::ledger::pos::{OwnedPosParams, PosVP}; use namada::proof_of_stake::types::{GenesisValidator, WeightedValidator}; use namada::proof_of_stake::{ bond_handle, read_consensus_validator_set_addresses_with_stake, @@ -65,10 +65,10 @@ mod tests { initial_stake: token::Amount, bond: transaction::pos::Bond, key: key::common::SecretKey, - pos_params: PosParams, + pos_params: OwnedPosParams, ) -> TxResult { // Remove the validator stake threshold for simplicity - let pos_params = PosParams { + let pos_params = OwnedPosParams { validator_stake_threshold: token::Amount::zero(), ..pos_params }; @@ -77,6 +77,7 @@ mod tests { let is_delegation = matches!(&bond.source, Some(source) if *source != bond.validator); let consensus_key = key::testing::keypair_1().ref_to(); + let protocol_key = key::testing::keypair_2().ref_to(); let commission_rate = Dec::new(5, 2).expect("Cannot fail"); let max_commission_rate_change = Dec::new(1, 2).expect("Cannot fail"); let eth_cold_key = key::testing::keypair_3().ref_to(); @@ -86,13 +87,15 @@ mod tests { address: bond.validator.clone(), tokens: initial_stake, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, commission_rate, max_commission_rate_change, }]; - init_pos(&genesis_validators[..], &pos_params, Epoch(0)); + let pos_params = + init_pos(&genesis_validators[..], &pos_params, Epoch(0)); let native_token = tx_host_env::with(|tx_env| { if let Some(source) = &bond.source { diff --git a/wasm/wasm_source/src/tx_change_validator_commission.rs b/wasm/wasm_source/src/tx_change_validator_commission.rs index ac7f827ccf..0923797e36 100644 --- a/wasm/wasm_source/src/tx_change_validator_commission.rs +++ b/wasm/wasm_source/src/tx_change_validator_commission.rs @@ -19,7 +19,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { mod tests { use std::cmp; - use namada::ledger::pos::{PosParams, PosVP}; + use namada::ledger::pos::{OwnedPosParams, PosVP}; use namada::proof_of_stake::types::GenesisValidator; use namada::proof_of_stake::validator_commission_rate_handle; use namada::types::dec::{Dec, POS_DECIMAL_PRECISION}; @@ -64,9 +64,11 @@ mod tests { max_change: Dec, commission_change: transaction::pos::CommissionChange, key: key::common::SecretKey, - pos_params: PosParams, + pos_params: OwnedPosParams, ) -> TxResult { let consensus_key = key::testing::keypair_1().ref_to(); + let protocol_key = key::testing::keypair_2().ref_to(); + let eth_hot_key = key::common::PublicKey::Secp256k1( key::testing::gen_keypair::().ref_to(), ); @@ -77,13 +79,15 @@ mod tests { address: commission_change.validator.clone(), tokens: token::Amount::from_uint(1_000_000, 0).unwrap(), consensus_key, + protocol_key, commission_rate: initial_rate, max_commission_rate_change: max_change, eth_hot_key, eth_cold_key, }]; - init_pos(&genesis_validators[..], &pos_params, Epoch(0)); + let pos_params = + init_pos(&genesis_validators[..], &pos_params, Epoch(0)); let tx_code = vec![]; let tx_data = commission_change.serialize_to_vec(); diff --git a/wasm/wasm_source/src/tx_redelegate.rs b/wasm/wasm_source/src/tx_redelegate.rs index 338e0205d7..adae605a81 100644 --- a/wasm/wasm_source/src/tx_redelegate.rs +++ b/wasm/wasm_source/src/tx_redelegate.rs @@ -21,7 +21,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { mod tests { use std::collections::BTreeSet; - use namada::ledger::pos::{PosParams, PosVP}; + use namada::ledger::pos::{OwnedPosParams, PosVP}; use namada::proof_of_stake::types::{GenesisValidator, WeightedValidator}; use namada::proof_of_stake::{ bond_handle, read_consensus_validator_set_addresses_with_stake, @@ -68,10 +68,10 @@ mod tests { initial_stake: token::Amount, redelegation: transaction::pos::Redelegation, key: key::common::SecretKey, - pos_params: PosParams, + pos_params: OwnedPosParams, ) -> TxResult { // Remove the validator stake threshold for simplicity - let pos_params = PosParams { + let pos_params = OwnedPosParams { validator_stake_threshold: token::Amount::zero(), ..pos_params }; @@ -79,6 +79,7 @@ mod tests { let consensus_key_1 = key::testing::keypair_1().ref_to(); let consensus_key_2 = key::testing::keypair_2().ref_to(); + let protocol_key = key::testing::keypair_2().ref_to(); let eth_cold_key = key::testing::keypair_3().ref_to(); let eth_hot_key = key::testing::keypair_4().ref_to(); let commission_rate = Dec::new(5, 2).expect("Cannot fail"); @@ -89,6 +90,7 @@ mod tests { address: redelegation.src_validator.clone(), tokens: token::Amount::zero(), consensus_key: consensus_key_1, + protocol_key: protocol_key.clone(), eth_cold_key: eth_cold_key.clone(), eth_hot_key: eth_hot_key.clone(), commission_rate, @@ -98,6 +100,7 @@ mod tests { address: redelegation.dest_validator.clone(), tokens: token::Amount::zero(), consensus_key: consensus_key_2, + protocol_key, eth_cold_key, eth_hot_key, commission_rate, @@ -105,7 +108,8 @@ mod tests { }, ]; - init_pos(&genesis_validators[..], &pos_params, Epoch(0)); + let pos_params = + init_pos(&genesis_validators[..], &pos_params, Epoch(0)); let native_token = tx_host_env::with(|tx_env| { let native_token = tx_env.wl_storage.storage.native_token.clone(); diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 713d2d42a8..7f66b7a338 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -24,7 +24,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { mod tests { use std::collections::BTreeSet; - use namada::ledger::pos::{PosParams, PosVP}; + use namada::ledger::pos::{OwnedPosParams, PosVP}; use namada::proof_of_stake::types::{GenesisValidator, WeightedValidator}; use namada::proof_of_stake::{ bond_handle, read_consensus_validator_set_addresses_with_stake, @@ -70,10 +70,10 @@ mod tests { initial_stake: token::Amount, unbond: transaction::pos::Unbond, key: key::common::SecretKey, - pos_params: PosParams, + pos_params: OwnedPosParams, ) -> TxResult { // Remove the validator stake threshold for simplicity - let pos_params = PosParams { + let pos_params = OwnedPosParams { validator_stake_threshold: token::Amount::zero(), ..pos_params }; @@ -83,6 +83,8 @@ mod tests { &unbond.source, Some(source) if *source != unbond.validator); let consensus_key = key::testing::keypair_1().ref_to(); + let protocol_key = key::testing::keypair_2().ref_to(); + let eth_cold_key = key::testing::keypair_3().ref_to(); let eth_hot_key = key::testing::keypair_4().ref_to(); let commission_rate = Dec::new(5, 2).expect("Cannot fail"); @@ -98,13 +100,15 @@ mod tests { initial_stake }, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, commission_rate, max_commission_rate_change, }]; - init_pos(&genesis_validators[..], &pos_params, Epoch(0)); + let pos_params = + init_pos(&genesis_validators[..], &pos_params, Epoch(0)); let native_token = tx_host_env::with(|tx_env| { let native_token = tx_env.wl_storage.storage.native_token.clone(); diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index 807d0b530a..5bedf44c42 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -20,7 +20,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use namada::ledger::pos::{PosParams, PosVP}; + use namada::ledger::pos::{OwnedPosParams, PosVP}; use namada::proof_of_stake::types::GenesisValidator; use namada::proof_of_stake::unbond_handle; use namada::types::dec::Dec; @@ -69,10 +69,10 @@ mod tests { unbonded_amount: token::Amount, withdraw: transaction::pos::Withdraw, key: key::common::SecretKey, - pos_params: PosParams, + pos_params: OwnedPosParams, ) -> TxResult { // Remove the validator stake threshold for simplicity - let pos_params = PosParams { + let pos_params = OwnedPosParams { validator_stake_threshold: token::Amount::zero(), ..pos_params }; @@ -80,6 +80,8 @@ mod tests { let is_delegation = matches!( &withdraw.source, Some(source) if *source != withdraw.validator); let consensus_key = key::testing::keypair_1().ref_to(); + let protocol_key = key::testing::keypair_2().ref_to(); + let eth_cold_key = key::testing::keypair_3().ref_to(); let eth_hot_key = key::testing::keypair_4().ref_to(); let commission_rate = Dec::new(5, 2).expect("Cannot fail"); @@ -96,13 +98,15 @@ mod tests { initial_stake }, consensus_key, + protocol_key, eth_cold_key, eth_hot_key, commission_rate, max_commission_rate_change, }]; - init_pos(&genesis_validators[..], &pos_params, Epoch(0)); + let pos_params = + init_pos(&genesis_validators[..], &pos_params, Epoch(0)); let native_token = tx_host_env::with(|tx_env| { let native_token = tx_env.wl_storage.storage.native_token.clone(); diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 215dccf421..c51650b752 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -391,6 +391,7 @@ mod tests { let validator = address::testing::established_address_3(); let initial_stake = token::Amount::from_uint(10_098_123, 0).unwrap(); let consensus_key = key::testing::keypair_2().ref_to(); + let protocol_key = key::testing::keypair_1().ref_to(); let eth_cold_key = key::testing::keypair_3().ref_to(); let eth_hot_key = key::testing::keypair_4().ref_to(); let commission_rate = Dec::new(5, 2).unwrap(); @@ -400,6 +401,7 @@ mod tests { address: validator.clone(), tokens: initial_stake, consensus_key, + protocol_key, commission_rate, max_commission_rate_change, eth_hot_key, @@ -470,6 +472,7 @@ mod tests { let validator = address::testing::established_address_3(); let initial_stake = token::Amount::from_uint(10_098_123, 0).unwrap(); let consensus_key = key::testing::keypair_2().ref_to(); + let protocol_key = key::testing::keypair_1().ref_to(); let commission_rate = Dec::new(5, 2).unwrap(); let max_commission_rate_change = Dec::new(1, 2).unwrap(); @@ -477,6 +480,7 @@ mod tests { address: validator.clone(), tokens: initial_stake, consensus_key, + protocol_key, commission_rate, max_commission_rate_change, eth_hot_key: key::common::PublicKey::Secp256k1( diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index a334576b53..5283b5e8db 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -418,6 +418,7 @@ mod tests { let validator = address::testing::established_address_3(); let initial_stake = token::Amount::from_uint(10_098_123, 0).unwrap(); let consensus_key = key::testing::keypair_2().ref_to(); + let protocol_key = key::testing::keypair_1().ref_to(); let eth_cold_key = key::testing::keypair_3().ref_to(); let eth_hot_key = key::testing::keypair_4().ref_to(); let commission_rate = Dec::new(5, 2).unwrap(); @@ -427,6 +428,7 @@ mod tests { address: validator.clone(), tokens: initial_stake, consensus_key, + protocol_key, commission_rate, max_commission_rate_change, eth_hot_key, @@ -495,6 +497,7 @@ mod tests { let validator = address::testing::established_address_3(); let initial_stake = token::Amount::from_uint(10_098_123, 0).unwrap(); let consensus_key = key::testing::keypair_2().ref_to(); + let protocol_key = key::testing::keypair_1().ref_to(); let commission_rate = Dec::new(5, 2).unwrap(); let max_commission_rate_change = Dec::new(1, 2).unwrap(); @@ -502,6 +505,7 @@ mod tests { address: validator.clone(), tokens: initial_stake, consensus_key, + protocol_key, commission_rate, max_commission_rate_change, eth_hot_key: key::common::PublicKey::Secp256k1( diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index f929a8a0d1..1c53daa4f1 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -191,7 +191,7 @@ fn validate_tx( #[cfg(test)] mod tests { use address::testing::arb_non_internal_address; - use namada::ledger::pos::{GenesisValidator, PosParams}; + use namada::ledger::pos::{GenesisValidator, OwnedPosParams}; use namada::proto::{Code, Data, Signature}; use namada::types::dec::Dec; use namada::types::storage::Epoch; @@ -421,10 +421,11 @@ mod tests { #[test] fn test_unsigned_pos_action_rejected() { // Init PoS genesis - let pos_params = PosParams::default(); + let pos_params = OwnedPosParams::default(); let validator = address::testing::established_address_3(); let initial_stake = token::Amount::from_uint(10_098_123, 0).unwrap(); let consensus_key = key::testing::keypair_2().ref_to(); + let protocol_key = key::testing::keypair_1().ref_to(); let eth_cold_key = key::testing::keypair_3().ref_to(); let eth_hot_key = key::testing::keypair_4().ref_to(); let commission_rate = Dec::new(5, 2).unwrap(); @@ -434,6 +435,7 @@ mod tests { address: validator.clone(), tokens: initial_stake, consensus_key, + protocol_key, commission_rate, max_commission_rate_change, eth_hot_key, @@ -504,10 +506,11 @@ mod tests { #[test] fn test_signed_pos_action_accepted() { // Init PoS genesis - let pos_params = PosParams::default(); + let pos_params = OwnedPosParams::default(); let validator = address::testing::established_address_3(); let initial_stake = token::Amount::from_uint(10_098_123, 0).unwrap(); let consensus_key = key::testing::keypair_2().ref_to(); + let protocol_key = key::testing::keypair_1().ref_to(); let commission_rate = Dec::new(5, 2).unwrap(); let max_commission_rate_change = Dec::new(1, 2).unwrap(); @@ -515,6 +518,7 @@ mod tests { address: validator.clone(), tokens: initial_stake, consensus_key, + protocol_key, commission_rate, max_commission_rate_change, eth_hot_key: key::common::PublicKey::Secp256k1(