From 4ac196d4f52157350513d52932c56ecafb25a2a5 Mon Sep 17 00:00:00 2001 From: brentstone Date: Wed, 8 Feb 2023 18:13:09 -0500 Subject: [PATCH 01/48] inflation and rewards modules --- proof_of_stake/src/rewards.rs | 85 ++++++++++++++++++++ shared/src/ledger/inflation.rs | 141 +++++++++++++++++++++++++++++++++ shared/src/ledger/mod.rs | 1 + 3 files changed, 227 insertions(+) diff --git a/proof_of_stake/src/rewards.rs b/proof_of_stake/src/rewards.rs index e69de29bb2d..9b021af6130 100644 --- a/proof_of_stake/src/rewards.rs +++ b/proof_of_stake/src/rewards.rs @@ -0,0 +1,85 @@ +//! PoS rewards + +use rust_decimal::Decimal; +use rust_decimal_macros::dec; +use thiserror::Error; + +/// Errors during rewards calculation +#[derive(Debug, Error)] +pub enum RewardsError { + /// number of votes is less than the threshold of 2/3 + #[error( + "Insufficient votes, needed at least 2/3 of the total bonded stake" + )] + InsufficentVotes, + /// rewards coefficients are not set + #[error("Rewards coefficients are not properly set.")] + CoeffsNotSet, +} + +/// Holds coefficients for the three different ways to get PoS rewards +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +pub struct PosRewards { + pub proposer_coeff: Decimal, + pub signer_coeff: Decimal, + pub active_val_coeff: Decimal, +} + +/// Holds relevant PoS parameters and is used to calculate the coefficients for +/// the rewards +#[derive(Debug, Copy, Clone)] +pub struct PosRewardsCalculator { + proposer_param: Decimal, + signer_param: Decimal, + signing_stake: u64, + total_stake: u64, +} + +impl PosRewardsCalculator { + /// Instantiate a new PosRewardsCalculator + pub fn new( + proposer_param: Decimal, + signer_param: Decimal, + signing_stake: u64, + total_stake: u64, + ) -> Self { + Self { + proposer_param, + signer_param, + signing_stake, + total_stake, + } + } + + /// Calculate the reward coefficients + pub fn get_reward_coeffs(&self) -> Result { + // TODO: think about possibility of u64 overflow + let votes_needed = self.get_min_required_votes(); + if self.signing_stake < votes_needed { + return Err(RewardsError::InsufficentVotes); + } + + // Logic for determining the coefficients + // TODO: error handling to ensure proposer_coeff is > 0? + let proposer_coeff = self.proposer_param + * Decimal::from(self.signing_stake - votes_needed) + / Decimal::from(self.total_stake) + + dec!(0.01); + let signer_coeff = self.signer_param; + let active_val_coeff = dec!(1.0) - proposer_coeff - signer_coeff; + + let coeffs = PosRewards { + proposer_coeff, + signer_coeff, + active_val_coeff, + }; + + Ok(coeffs) + } + + /// Implement as ceiling (2/3) * validator set stake + fn get_min_required_votes(&self) -> u64 { + ((2 * self.total_stake) + 3 - 1) / 3 + } +} diff --git a/shared/src/ledger/inflation.rs b/shared/src/ledger/inflation.rs index e69de29bb2d..f3301c4d7dd 100644 --- a/shared/src/ledger/inflation.rs +++ b/shared/src/ledger/inflation.rs @@ -0,0 +1,141 @@ +//! General inflation system that will be used to process rewards for +//! proof-of-stake, providing liquity to shielded asset pools, and public goods +//! funding. + +use rust_decimal::prelude::ToPrimitive; +use rust_decimal::Decimal; +use rust_decimal_macros::dec; + +use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; +use crate::types::address::Address; +use crate::types::token; + +/// The domains of inflation +pub enum RewardsType { + /// Proof-of-stake rewards + Staking, + /// Rewards for locking tokens in the multi-asset shielded pool + Masp, + /// Rewards for public goods funding (PGF) + PubGoodsFunding, +} + +/// Holds the PD controller values that should be updated in storage +#[allow(missing_docs)] +pub struct ValsToUpdate { + pub locked_ratio: Decimal, + pub inflation: u64, +} + +/// PD controller used to dynamically adjust the rewards rates +#[derive(Debug, Clone)] +pub struct RewardsController { + locked_tokens: token::Amount, + total_tokens: token::Amount, + locked_ratio_target: Decimal, + locked_ratio_last: Decimal, + max_reward_rate: Decimal, + last_inflation_amount: token::Amount, + p_gain_nom: Decimal, + d_gain_nom: Decimal, + epochs_per_year: u64, +} + +impl RewardsController { + /// Initialize a new PD controller + #[allow(clippy::too_many_arguments)] + pub fn new( + locked_tokens: token::Amount, + total_tokens: token::Amount, + locked_ratio_target: Decimal, + locked_ratio_last: Decimal, + max_reward_rate: Decimal, + last_inflation_amount: token::Amount, + p_gain_nom: Decimal, + d_gain_nom: Decimal, + epochs_per_year: u64, + ) -> Self { + Self { + locked_tokens, + total_tokens, + locked_ratio_target, + locked_ratio_last, + max_reward_rate, + last_inflation_amount, + p_gain_nom, + d_gain_nom, + epochs_per_year, + } + } + + /// Calculate a new rewards rate + pub fn run( + Self { + locked_tokens, + total_tokens, + locked_ratio_target, + locked_ratio_last, + max_reward_rate, + last_inflation_amount, + p_gain_nom, + d_gain_nom, + epochs_per_year, + }: &Self, + ) -> ValsToUpdate { + let locked: Decimal = u64::from(*locked_tokens).into(); + let total: Decimal = u64::from(*total_tokens).into(); + let epochs_py: Decimal = (*epochs_per_year).into(); + + let locked_ratio = locked / total; + let max_inflation = total * max_reward_rate / epochs_py; + let p_gain = p_gain_nom * max_inflation; + let d_gain = d_gain_nom * max_inflation; + + let error = locked_ratio_target - locked_ratio; + let delta_error = locked_ratio_last - locked_ratio; + let control_val = p_gain * error - d_gain * delta_error; + + let last_inflation_amount = Decimal::from(*last_inflation_amount); + let inflation = if last_inflation_amount + control_val > max_inflation { + max_inflation + } else if last_inflation_amount + control_val > dec!(0.0) { + last_inflation_amount + control_val + } else { + dec!(0.0) + }; + let inflation: u64 = inflation.to_u64().unwrap(); + + ValsToUpdate { + locked_ratio, + inflation, + } + } +} + +/// Function that allows the protocol to mint some number of tokens of a desired +/// type to a destination address TODO: think of error cases that must be +/// handled. +pub fn mint_tokens( + storage: &mut S, + target: &Address, + token: &Address, + amount: token::Amount, +) -> storage_api::Result<()> +where + S: StorageWrite + StorageRead, +{ + let dest_key = token::balance_key(token, target); + let mut dest_bal: token::Amount = + storage.read(&dest_key)?.unwrap_or_default(); + dest_bal.receive(&amount); + storage.write(&dest_key, dest_bal)?; + + // Update the total supply of the tokens in storage + let mut total_tokens: token::Amount = storage + .read(&token::total_supply_key(token))? + .unwrap_or_default(); + total_tokens.receive(&amount); + storage.write(&token::total_supply_key(token), total_tokens)?; + + Ok(()) +} diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index 73f39dda05d..16cd09c841e 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -3,6 +3,7 @@ pub mod eth_bridge; pub mod events; pub mod ibc; +pub mod inflation; pub mod masp; pub mod native_vp; pub mod pos; From 0e8b2015926fff533bdd75bae8e53c766ac41fd9 Mon Sep 17 00:00:00 2001 From: brentstone Date: Wed, 8 Feb 2023 18:16:56 -0500 Subject: [PATCH 02/48] storage types and keys for inflation --- apps/src/lib/node/ledger/shell/mod.rs | 3 + .../node/ledger/shims/abcipp_shim_types.rs | 39 +++++- core/src/types/token.rs | 13 ++ proof_of_stake/src/parameters.rs | 8 +- proof_of_stake/src/storage.rs | 132 +++++++++++++++++- proof_of_stake/src/types.rs | 21 ++- shared/src/ledger/pos/mod.rs | 7 +- 7 files changed, 216 insertions(+), 7 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 6b4b05b5ade..ce6127396fc 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -967,6 +967,9 @@ mod test_utils { }, byzantine_validators: vec![], txs: vec![], + #[cfg(feature = "abcipp")] + proposer_address: vec![], + votes: vec![], } } } diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs index cb3145f0e27..45f4eb13dd0 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs @@ -15,7 +15,7 @@ pub mod shim { ResponseCheckTx, ResponseCommit, ResponseEcho, ResponseEndBlock, ResponseFlush, ResponseInfo, ResponseInitChain, ResponseListSnapshots, ResponseLoadSnapshotChunk, ResponseOfferSnapshot, - ResponsePrepareProposal, ResponseQuery, + ResponsePrepareProposal, ResponseQuery, VoteInfo as TendermintVoteInfo, }; #[cfg(feature = "abcipp")] use tendermint_proto_abcipp::abci::{ @@ -28,6 +28,7 @@ pub mod shim { ResponseFlush, ResponseInfo, ResponseInitChain, ResponseListSnapshots, ResponseLoadSnapshotChunk, ResponseOfferSnapshot, ResponsePrepareProposal, ResponseQuery, ResponseVerifyVoteExtension, + VoteInfo as TendermintVoteInfo, }; use thiserror::Error; @@ -193,6 +194,7 @@ pub mod shim { pub mod request { use std::convert::TryFrom; + use namada::ledger::pos::types::VoteInfo; #[cfg(not(feature = "abcipp"))] use namada::tendermint_proto::abci::RequestBeginBlock; use namada::types::hash::Hash; @@ -205,6 +207,8 @@ pub mod shim { Misbehavior as Evidence, RequestFinalizeBlock, }; + use super::TendermintVoteInfo; + pub struct VerifyHeader; pub struct RevertProposal; @@ -216,11 +220,15 @@ pub mod shim { pub result: super::response::TxResult, } + #[derive(Debug, Clone)] pub struct FinalizeBlock { pub hash: BlockHash, pub header: Header, pub byzantine_validators: Vec, pub txs: Vec, + #[cfg(feature = "abcipp")] + pub proposer_address: Vec, + pub votes: Vec, } #[cfg(feature = "abcipp")] @@ -238,10 +246,30 @@ pub mod shim { }, byzantine_validators: req.byzantine_validators, txs: vec![], + #[cfg(feature = "abcipp")] + proposer_address: req.proposer_address, + votes: req + .decided_last_commit + .unwrap() + .votes + .iter() + .map(|tm_vote_info| { + vote_info_to_tendermint(tm_vote_info.clone()) + }) + .collect(), } } } + fn vote_info_to_tendermint(info: TendermintVoteInfo) -> VoteInfo { + let val_info = info.validator.clone().unwrap(); + VoteInfo { + validator_address: info.validator.unwrap().address, + validator_vp: val_info.power as u64, + signed_last_block: info.signed_last_block, + } + } + #[cfg(not(feature = "abcipp"))] impl From for FinalizeBlock { fn from(req: RequestBeginBlock) -> FinalizeBlock { @@ -260,6 +288,15 @@ pub mod shim { }, byzantine_validators: req.byzantine_validators, txs: vec![], + votes: req + .last_commit_info + .unwrap() + .votes + .iter() + .map(|tm_vote_info| { + vote_info_to_tendermint(tm_vote_info.clone()) + }) + .collect(), } } } diff --git a/core/src/types/token.rs b/core/src/types/token.rs index d95d944b8c4..d77176eca99 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -297,6 +297,7 @@ pub const TX_KEY_PREFIX: &str = "tx-"; pub const CONVERSION_KEY_PREFIX: &str = "conv"; /// Key segment prefix for pinned shielded transactions pub const PIN_KEY_PREFIX: &str = "pin-"; +const TOTAL_SUPPLY_STORAGE_KEY: &str = "total_supply"; /// Obtain a storage key for user's balance. pub fn balance_key(token_addr: &Address, owner: &Address) -> Key { @@ -370,6 +371,18 @@ pub fn is_masp_key(key: &Key) -> bool { || key.starts_with(PIN_KEY_PREFIX))) } +/// Storage key for total supply of a token +pub fn total_supply_key(token_address: &Address) -> Key { + Key::from(token_address.to_db_key()) + .push(&TOTAL_SUPPLY_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for total supply of a specific token? +pub fn is_total_supply_key(key: &Key, token_address: &Address) -> bool { + matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] if addr == token_address && key == TOTAL_SUPPLY_STORAGE_KEY) +} + /// Check if the given storage key is multitoken balance key for the given /// token. If it is, returns the sub prefix and the owner. pub fn is_multitoken_balance_key<'a>( diff --git a/proof_of_stake/src/parameters.rs b/proof_of_stake/src/parameters.rs index 93886e1d987..14229710899 100644 --- a/proof_of_stake/src/parameters.rs +++ b/proof_of_stake/src/parameters.rs @@ -33,10 +33,10 @@ pub struct PosParams { pub max_inflation_rate: Decimal, /// Target ratio of staked NAM tokens to total NAM tokens pub target_staked_ratio: Decimal, - /// Portion of validator's stake that should be slashed on a duplicate + /// Fraction of validator's stake that should be slashed on a duplicate /// vote. pub duplicate_vote_min_slash_rate: Decimal, - /// Portion of validator's stake that should be slashed on a light client + /// Fraction of validator's stake that should be slashed on a light client /// attack. pub light_client_attack_min_slash_rate: Decimal, } @@ -113,6 +113,10 @@ impl PosParams { // Check maximum total voting power cannot get larger than what // Tendermint allows + // + // TODO: decide if this is still a check we want to do (in its current + // state with our latest voting power conventions, it will fail + // always) let max_total_voting_power = Decimal::from(self.max_validator_slots) * self.tm_votes_per_token * Decimal::from(TOKEN_MAX_AMOUNT); diff --git a/proof_of_stake/src/storage.rs b/proof_of_stake/src/storage.rs index 7dd282fd823..3f7c0a04db9 100644 --- a/proof_of_stake/src/storage.rs +++ b/proof_of_stake/src/storage.rs @@ -6,17 +6,22 @@ use namada_core::types::storage::{DbKeySeg, Epoch, Key, KeySeg}; use super::ADDRESS; use crate::epoched::LAZY_MAP_SUB_KEY; -pub use crate::types::*; +pub use crate::types::*; // TODO: not sure why this needs to be public const PARAMS_STORAGE_KEY: &str = "params"; const VALIDATOR_STORAGE_PREFIX: &str = "validator"; const VALIDATOR_ADDRESS_RAW_HASH: &str = "address_raw_hash"; const VALIDATOR_CONSENSUS_KEY_STORAGE_KEY: &str = "consensus_key"; const VALIDATOR_STATE_STORAGE_KEY: &str = "state"; -const VALIDATOR_DELTAS_STORAGE_KEY: &str = "validator_deltas"; +const VALIDATOR_DELTAS_STORAGE_KEY: &str = "deltas"; const VALIDATOR_COMMISSION_RATE_STORAGE_KEY: &str = "commission_rate"; const VALIDATOR_MAX_COMMISSION_CHANGE_STORAGE_KEY: &str = "max_commission_rate_change"; +const VALIDATOR_SELF_REWARDS_PRODUCT_KEY: &str = "validator_rewards_product"; +const VALIDATOR_DELEGATION_REWARDS_PRODUCT_KEY: &str = + "delegation_rewards_product"; +const VALIDATOR_LAST_KNOWN_PRODUCT_EPOCH_KEY: &str = + "last_known_rewards_product_epoch"; const SLASHES_PREFIX: &str = "slash"; const BOND_STORAGE_KEY: &str = "bond"; const UNBOND_STORAGE_KEY: &str = "unbond"; @@ -26,6 +31,10 @@ const NUM_CONSENSUS_VALIDATORS_STORAGE_KEY: &str = "num_consensus"; const BELOW_CAPACITY_VALIDATOR_SET_STORAGE_KEY: &str = "below_capacity"; const TOTAL_DELTAS_STORAGE_KEY: &str = "total_deltas"; const VALIDATOR_SET_POSITIONS_KEY: &str = "validator_set_positions"; +const LAST_BLOCK_PROPOSER_STORAGE_KEY: &str = "last_block_proposer"; +const CURRENT_BLOCK_PROPOSER_STORAGE_KEY: &str = "current_block_proposer"; +const CONSENSUS_VALIDATOR_SET_ACCUMULATOR_STORAGE_KEY: &str = + "validator_rewards_accumulator"; /// Is the given key a PoS storage key? pub fn is_pos_key(key: &Key) -> bool { @@ -158,6 +167,85 @@ pub fn is_validator_max_commission_rate_change_key( } } +/// Storage key for validator's self rewards products. +pub fn validator_self_rewards_product_key(validator: &Address) -> Key { + validator_prefix(validator) + .push(&VALIDATOR_SELF_REWARDS_PRODUCT_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for validator's self rewards products? +pub fn is_validator_self_rewards_product_key(key: &Key) -> Option<&Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + DbKeySeg::AddressSeg(validator), + DbKeySeg::StringSeg(key), + ] if addr == &ADDRESS + && prefix == VALIDATOR_STORAGE_PREFIX + && key == VALIDATOR_SELF_REWARDS_PRODUCT_KEY => + { + Some(validator) + } + _ => None, + } +} + +/// Storage key for validator's delegation rewards products. +pub fn validator_delegation_rewards_product_key(validator: &Address) -> Key { + validator_prefix(validator) + .push(&VALIDATOR_DELEGATION_REWARDS_PRODUCT_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for validator's delegation rewards products? +pub fn is_validator_delegation_rewards_product_key( + key: &Key, +) -> Option<&Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + DbKeySeg::AddressSeg(validator), + DbKeySeg::StringSeg(key), + ] if addr == &ADDRESS + && prefix == VALIDATOR_STORAGE_PREFIX + && key == VALIDATOR_DELEGATION_REWARDS_PRODUCT_KEY => + { + Some(validator) + } + _ => None, + } +} + +/// Storage key for validator's last known rewards product epoch. +pub fn validator_last_known_product_epoch_key(validator: &Address) -> Key { + validator_prefix(validator) + .push(&VALIDATOR_LAST_KNOWN_PRODUCT_EPOCH_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for validator's last known rewards product epoch? +pub fn is_validator_last_known_product_epoch_key( + key: &Key, +) -> Option<&Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + DbKeySeg::AddressSeg(validator), + DbKeySeg::StringSeg(key), + ] if addr == &ADDRESS + && prefix == VALIDATOR_STORAGE_PREFIX + && key == VALIDATOR_LAST_KNOWN_PRODUCT_EPOCH_KEY => + { + Some(validator) + } + _ => None, + } +} + /// Storage key for validator's consensus key. pub fn validator_state_key(validator: &Address) -> Key { validator_prefix(validator) @@ -427,6 +515,46 @@ pub fn is_total_deltas_key(key: &Key) -> Option<&String> { } } +/// Storage key for block proposer address of the previous block. +pub fn last_block_proposer_key() -> Key { + Key::from(ADDRESS.to_db_key()) + .push(&LAST_BLOCK_PROPOSER_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for block proposer address of the previous block? +pub fn is_last_block_proposer_key(key: &Key) -> bool { + matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] if addr == &ADDRESS && key == LAST_BLOCK_PROPOSER_STORAGE_KEY) +} + +/// Storage key for block proposer address of the current block. +pub fn current_block_proposer_key() -> Key { + Key::from(ADDRESS.to_db_key()) + .push(&CURRENT_BLOCK_PROPOSER_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for block proposer address of the current block? +pub fn is_current_block_proposer_key(key: &Key) -> bool { + matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] if addr == &ADDRESS && key == CURRENT_BLOCK_PROPOSER_STORAGE_KEY) +} + +/// Storage key for the consensus validator set rewards accumulator. +pub fn consensus_validator_rewards_accumulator_key() -> Key { + Key::from(ADDRESS.to_db_key()) + .push(&CONSENSUS_VALIDATOR_SET_ACCUMULATOR_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for the consensus validator set? +pub fn is_consensus_validator_set_accumulator_key(key: &Key) -> bool { + matches!(&key.segments[..], [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(key), + ] if addr == &ADDRESS + && key == CONSENSUS_VALIDATOR_SET_ACCUMULATOR_STORAGE_KEY) +} + /// Get validator address from bond key pub fn get_validator_address_from_bond(key: &Key) -> Option
{ match key.get_at(3) { diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index a00ece88792..ed3c61eedae 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -141,6 +141,13 @@ pub struct CommissionPair { pub max_commission_change_per_epoch: Decimal, } +/// Epoched rewards products +pub type RewardsProducts = LazyMap; + +/// Consensus validator rewards accumulator (for tracking the fractional block +/// rewards owed over the course of an epoch) +pub type RewardsAccumulator = LazyMap; + // -------------------------------------------------------------------------------------------- /// A genesis validator definition. @@ -334,7 +341,7 @@ pub struct Slash { pub epoch: Epoch, /// Block height at which the slashable event occurred. pub block_height: u64, - /// A type of slashsable event. + /// A type of slashable event. pub r#type: SlashType, } @@ -362,6 +369,18 @@ pub enum SlashType { LightClientAttack, } +/// VoteInfo inspired from tendermint +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +pub struct VoteInfo { + /// the first 20 bytes of the validator public key hash (SHA-256) taken + /// from tendermint + pub validator_address: Vec, + /// validator voting power + pub validator_vp: u64, + /// was the validator signature was included in the last block? + pub signed_last_block: bool, +} + /// Bonds and unbonds with all details (slashes and rewards, if any) /// grouped by their bond IDs. pub type BondsAndUnbondsDetails = HashMap; diff --git a/shared/src/ledger/pos/mod.rs b/shared/src/ledger/pos/mod.rs index 5e4a1a064d4..22a681be01d 100644 --- a/shared/src/ledger/pos/mod.rs +++ b/shared/src/ledger/pos/mod.rs @@ -13,7 +13,7 @@ pub use namada_proof_of_stake::types; use rust_decimal::Decimal; pub use vp::PosVP; -use crate::types::address::{Address, InternalAddress}; +use crate::types::address::{self, Address, InternalAddress}; use crate::types::storage::Epoch; /// Address of the PoS account implemented as a native VP @@ -23,6 +23,11 @@ pub const ADDRESS: Address = Address::Internal(InternalAddress::PoS); pub const SLASH_POOL_ADDRESS: Address = Address::Internal(InternalAddress::PosSlashPool); +/// Address of the staking token (NAM) +pub fn staking_token_address() -> Address { + address::nam() +} + /// Calculate voting power in the tendermint context (which is stored as i64) /// from the number of tokens pub fn into_tm_voting_power( From 94449a005d2027082bc53ed696a2c5692525f2e5 Mon Sep 17 00:00:00 2001 From: brentstone Date: Wed, 8 Feb 2023 18:23:44 -0500 Subject: [PATCH 03/48] log block rewards with accumulator LazyMaps and apply inflation with rewards products --- .../lib/node/ledger/shell/finalize_block.rs | 376 +++++++++++++++- apps/src/lib/node/ledger/shell/init_chain.rs | 39 +- apps/src/lib/node/ledger/shims/abcipp_shim.rs | 57 ++- core/src/types/storage.rs | 2 +- proof_of_stake/src/lib.rs | 422 +++++++++++++----- vp_prelude/src/token.rs | 25 +- 6 files changed, 791 insertions(+), 130 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index e37f7bb27bb..414f33b5862 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -1,11 +1,32 @@ //! Implementation of the `FinalizeBlock` ABCI++ method for the Shell -use namada::ledger::pos::namada_proof_of_stake; -use namada::ledger::pos::types::into_tm_voting_power; +use std::collections::HashMap; + +use namada::ledger::inflation::{self, RewardsController}; +use namada::ledger::parameters::storage as params_storage; +use namada::ledger::pos::types::{ + decimal_mult_u64, into_tm_voting_power, VoteInfo, +}; +use namada::ledger::pos::{ + namada_proof_of_stake, staking_token_address, ADDRESS as POS_ADDRESS, +}; use namada::ledger::protocol; use namada::ledger::storage_api::StorageRead; -use namada::types::storage::{BlockHash, BlockResults, Header}; -use namada::types::token::Amount; +#[cfg(feature = "abcipp")] +use namada::proof_of_stake::find_validator_by_raw_hash; +use namada::proof_of_stake::{ + delegator_rewards_products_handle, read_current_block_proposer_address, + read_last_block_proposer_address, read_pos_params, read_total_stake, + read_validator_stake, rewards_accumulator_handle, + validator_commission_rate_handle, validator_rewards_products_handle, + write_last_block_proposer_address, +}; +use namada::types::address::Address; +#[cfg(feature = "abcipp")] +use namada::types::key::{tm_consensus_key_raw_hash, tm_raw_hash_to_string}; +use namada::types::storage::{BlockHash, BlockResults, Epoch, Header}; +use namada::types::token::{total_supply_key, Amount}; +use rust_decimal::prelude::Decimal; use super::governance::execute_governance_proposals; use super::*; @@ -46,10 +67,12 @@ where self.gas_meter.reset(); let mut response = shim::response::FinalizeBlock::default(); - // begin the next block and check if a new epoch began + + // Begin the new block and check if a new epoch has begun let (height, new_epoch) = self.update_state(req.header, req.hash, req.byzantine_validators); + let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); let current_epoch = self.wl_storage.storage.block.epoch; if new_epoch { @@ -361,6 +384,105 @@ where self.update_epoch(&mut response); } + // Read the block proposer of the previously committed block in storage + // (n-1 if we are in the process of finalizing n right now). + match read_last_block_proposer_address(&self.wl_storage)? { + Some(proposer_address) => { + println!("FOUND LAST BLOCK PROPOSER"); + if new_epoch { + println!("APPLYING INFLATION"); + self.apply_inflation( + current_epoch, + &proposer_address, + &req.votes, + )?; + } else { + // TODO: watch out because this is likely not using the + // proper block proposer address + println!("LOGGING BLOCK REWARDS (NOT NEW EPOCH)"); + namada_proof_of_stake::log_block_rewards( + &mut self.wl_storage, + current_epoch, + &proposer_address, + &req.votes, + ) + .unwrap(); + } + #[cfg(feature = "abcipp")] + { + // TODO: better error handling that converts + // storage_api::Error -> shell::Error + let tm_raw_hash_string = + tm_raw_hash_to_string(req.proposer_address); + let native_proposer_address = find_validator_by_raw_hash( + &self.wl_storage, + tm_raw_hash_string, + ) + .unwrap() + .expect( + "Unable to find native validator address of block \ + proposer from tendermint raw hash", + ); + write_last_block_proposer_address( + &mut self.wl_storage, + native_proposer_address, + )?; + } + + #[cfg(not(feature = "abcipp"))] + { + let cur_proposer = + read_current_block_proposer_address(&self.wl_storage)? + .expect( + "Should have found the current block proposer \ + address", + ); + write_last_block_proposer_address( + &mut self.wl_storage, + cur_proposer, + )?; + } + } + None => { + println!("NO BLOCK PROPOSER FOUND SET IN STORAGE"); + #[cfg(feature = "abcipp")] + if req.votes.is_empty() && !req.proposer_address.is_empty() { + // Get proposer address from storage based on the consensus + // key hash + let tm_raw_hash_string = + tm_raw_hash_to_string(req.proposer_address); + let native_proposer_address = find_validator_by_raw_hash( + &self.wl_storage, + tm_raw_hash_string, + ) + .unwrap() + .expect( + "Unable to find native validator address of block \ + proposer from tendermint raw hash", + ); + write_last_block_proposer_address( + &mut self.wl_storage, + native_proposer_address, + )?; + } + #[cfg(not(feature = "abcipp"))] + { + let proposer = + read_current_block_proposer_address(&self.wl_storage)?; + // .expect( + // "Current block proposer should always be set in \ + // ProcessProposal", + // ); + if let Some(proposer) = proposer { + write_last_block_proposer_address( + &mut self.wl_storage, + proposer, + )?; + } + } + } + } + let _ = self .gas_meter .finalize_transaction() @@ -454,6 +576,246 @@ where }, ); } + + /// Calculate the new inflation rate, mint the new tokens to the PoS + /// account, then update the reward products of the validators. This is + /// executed while finalizing the first block of a new epoch and is applied + /// with respect to the previous epoch. + fn apply_inflation( + &mut self, + current_epoch: Epoch, + proposer_address: &Address, + votes: &[VoteInfo], + ) -> Result<()> { + let last_epoch = current_epoch - 1; + // Get input values needed for the PD controller for PoS and MASP. + // Run the PD controllers to calculate new rates. + // + // MASP is included below just for some completeness. + + // Calculate the fractional block rewards for the previous block (final + // block of the previous epoch), which also gives the final + // accumulator value updates + namada_proof_of_stake::log_block_rewards( + &mut self.wl_storage, + last_epoch, + proposer_address, + votes, + )?; + + // TODO: review if the appropriate epoch is being used (last vs now) + let params = read_pos_params(&self.wl_storage)?; + + // Read from Parameters storage + let epochs_per_year: u64 = self + .read_storage_key(¶ms_storage::get_epochs_per_year_key()) + .expect("Epochs per year should exist in storage"); + let pos_p_gain_nom: Decimal = self + .read_storage_key(¶ms_storage::get_pos_gain_p_key()) + .expect("PoS P-gain factor should exist in storage"); + let pos_d_gain_nom: Decimal = self + .read_storage_key(¶ms_storage::get_pos_gain_d_key()) + .expect("PoS D-gain factor should exist in storage"); + + let pos_last_staked_ratio: Decimal = self + .read_storage_key(¶ms_storage::get_staked_ratio_key()) + .expect("PoS staked ratio should exist in storage"); + let pos_last_inflation_amount: u64 = self + .read_storage_key(¶ms_storage::get_pos_inflation_amount_key()) + .expect("PoS inflation rate should exist in storage"); + // Read from PoS storage + let total_tokens = self + .read_storage_key(&total_supply_key(&staking_token_address())) + .expect("Total NAM balance should exist in storage"); + let pos_locked_supply = + read_total_stake(&self.wl_storage, ¶ms, last_epoch)?; + let pos_locked_ratio_target = params.target_staked_ratio; + let pos_max_inflation_rate = params.max_inflation_rate; + + // TODO: properly fetch these values (arbitrary for now) + let masp_locked_supply: Amount = Amount::default(); + let masp_locked_ratio_target = Decimal::new(5, 1); + let masp_locked_ratio_last = Decimal::new(5, 1); + let masp_max_inflation_rate = Decimal::new(2, 1); + let masp_last_inflation_rate = Decimal::new(12, 2); + let masp_p_gain = Decimal::new(1, 1); + let masp_d_gain = Decimal::new(1, 1); + + // Run rewards PD controller + let pos_controller = inflation::RewardsController::new( + pos_locked_supply, + total_tokens, + pos_locked_ratio_target, + pos_last_staked_ratio, + pos_max_inflation_rate, + token::Amount::from(pos_last_inflation_amount), + pos_p_gain_nom, + pos_d_gain_nom, + epochs_per_year, + ); + let _masp_controller = inflation::RewardsController::new( + masp_locked_supply, + total_tokens, + masp_locked_ratio_target, + masp_locked_ratio_last, + masp_max_inflation_rate, + token::Amount::from(masp_last_inflation_rate), + masp_p_gain, + masp_d_gain, + epochs_per_year, + ); + + // Run the rewards controllers + let new_pos_vals = RewardsController::run(&pos_controller); + // let new_masp_vals = RewardsController::run(&_masp_controller); + + // Mint tokens to the PoS account for the last epoch's inflation + let pos_minted_tokens = new_pos_vals.inflation; + inflation::mint_tokens( + &mut self.wl_storage, + &POS_ADDRESS, + &staking_token_address(), + Amount::from(pos_minted_tokens), + )?; + + // For each consensus validator, update the rewards products + // + // TODO: update implementation using lazy DS and be more + // memory-efficient + + // Get the number of blocks in the last epoch + let first_block_of_last_epoch = self + .wl_storage + .storage + .block + .pred_epochs + .first_block_heights[last_epoch.0 as usize] + .0; + let num_blocks_in_last_epoch = if first_block_of_last_epoch == 0 { + self.wl_storage.storage.block.height.0 + - first_block_of_last_epoch + - 1 + } else { + self.wl_storage.storage.block.height.0 - first_block_of_last_epoch + }; + + // Read the rewards accumulator, which was last updated when finalizing + // the previous block + // TODO: can/should this be optimized? Since we are reading and writing + // to the accumulator storage earlier in apply_inflation + + // TODO: think about changing the reward to Decimal + let mut reward_tokens_remaining = pos_minted_tokens; + let mut new_rewards_products: HashMap = + HashMap::new(); + + for acc in rewards_accumulator_handle().iter(&self.wl_storage)? { + let (address, value) = acc?; + + // Get reward token amount for this validator + let fractional_claim = + value / Decimal::from(num_blocks_in_last_epoch); + let reward = decimal_mult_u64(fractional_claim, pos_minted_tokens); + + // Get validator data at the last epoch + let stake = read_validator_stake( + &self.wl_storage, + ¶ms, + &address, + last_epoch, + )? + .map(Decimal::from) + .unwrap_or_default(); + let last_rewards_product = + validator_rewards_products_handle(&address) + .get(&self.wl_storage, &last_epoch)? + .unwrap_or(Decimal::ONE); + let last_delegation_product = + delegator_rewards_products_handle(&address) + .get(&self.wl_storage, &last_epoch)? + .unwrap_or(Decimal::ONE); + let commission_rate = validator_commission_rate_handle(&address) + .get(&self.wl_storage, last_epoch, ¶ms)? + .expect("Should be able to find validator commission rate"); + + let new_product = last_rewards_product + * (Decimal::ONE + Decimal::from(reward) / stake); + let new_delegation_product = last_delegation_product + * (Decimal::ONE + + (Decimal::ONE - commission_rate) * Decimal::from(reward) + / stake); + new_rewards_products + .insert(address, (new_product, new_delegation_product)); + reward_tokens_remaining -= reward; + } + for ( + address, + (new_validator_reward_product, new_delegator_reward_product), + ) in new_rewards_products + { + validator_rewards_products_handle(&address).insert( + &mut self.wl_storage, + last_epoch, + new_validator_reward_product, + )?; + delegator_rewards_products_handle(&address).insert( + &mut self.wl_storage, + last_epoch, + new_delegator_reward_product, + )?; + } + + // TODO: Figure out how to deal with round-off to a whole number of + // tokens. May be tricky. TODO: Storing reward products + // as a Decimal suggests that no round-off should be done here, + // TODO: perhaps only upon withdrawal. But by truncating at + // withdrawal, may leave tokens in TDOD: the PoS account + // that are not accounted for. Is this an issue? + if reward_tokens_remaining > 0 { + // TODO: do something here? + } + + // Write new rewards parameters that will be used for the inflation of + // the current new epoch + self.wl_storage + .storage + .write( + ¶ms_storage::get_pos_inflation_amount_key(), + new_pos_vals + .inflation + .try_to_vec() + .expect("encode new reward rate"), + ) + .expect("unable to encode new reward rate (Decimal)"); + self.wl_storage + .storage + .write( + ¶ms_storage::get_staked_ratio_key(), + new_pos_vals + .locked_ratio + .try_to_vec() + .expect("encode new locked ratio"), + ) + .expect("unable to encode new locked ratio (Decimal)"); + + // Delete the accumulators from storage + // TODO: may want better way to implement this (for lazy PoS in general) + let addresses_to_drop: HashSet
= rewards_accumulator_handle() + .iter(&self.wl_storage)? + .map(|a| a.unwrap().0) + .collect(); + for address in addresses_to_drop.into_iter() { + rewards_accumulator_handle() + .remove(&mut self.wl_storage, &address)?; + } + + // self.wl_storage + // .storage + // .delete(&consensus_validator_rewards_accumulator_key()) + // .unwrap(); + + Ok(()) + } } /// We test the failure cases of [`finalize_block`]. The happy flows @@ -847,6 +1209,7 @@ mod test_finalize_block { let mut add_proposal = |proposal_id, vote| { let validator = shell.mode.get_validator_address().unwrap().clone(); shell.proposal_data.insert(proposal_id); + let proposal = InitProposalData { id: Some(proposal_id), content: vec![], @@ -856,11 +1219,13 @@ mod test_finalize_block { grace_epoch: Epoch::default().next(), proposal_code: None, }; + storage_api::governance::init_proposal( &mut shell.wl_storage, proposal, ) .unwrap(); + let vote = VoteProposalData { id: proposal_id, vote, @@ -872,6 +1237,7 @@ mod test_finalize_block { storage_api::governance::vote_proposal(&mut shell.wl_storage, vote) .unwrap(); }; + // Add a proposal to be accepted and one to be rejected. add_proposal(0, ProposalVote::Yay); add_proposal(1, ProposalVote::Nay); diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index fec78643069..1a833c89c91 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -4,10 +4,13 @@ use std::hash::Hash; #[cfg(not(feature = "mainnet"))] use namada::core::ledger::testnet_pow; +use namada::ledger::parameters::storage::get_staked_ratio_key; use namada::ledger::parameters::Parameters; -use namada::ledger::pos::into_tm_voting_power; +use namada::ledger::pos::{into_tm_voting_power, staking_token_address}; use namada::ledger::storage_api::StorageWrite; use namada::types::key::*; +use namada::types::token::total_supply_key; +use rust_decimal::Decimal; #[cfg(not(feature = "dev"))] use sha2::{Digest, Sha256}; @@ -225,6 +228,8 @@ where } // Initialize genesis implicit + // TODO: verify if we can get the total initial token supply from simply + // looping over the set of implicit accounts for genesis::ImplicitAccount { public_key } in genesis.implicit_accounts { let address: address::Address = (&public_key).into(); @@ -233,6 +238,7 @@ where } // Initialize genesis token accounts + let mut total_nam_balance = token::Amount::default(); for genesis::TokenAccount { address, vp_code_path, @@ -267,6 +273,9 @@ where .unwrap(); for (owner, amount) in balances { + if address == staking_token_address() { + total_nam_balance += amount; + } self.wl_storage .write(&token::balance_key(&address, &owner), amount) .unwrap(); @@ -274,6 +283,7 @@ where } // Initialize genesis validator accounts + let mut total_staked_nam_tokens = token::Amount::default(); for validator in &genesis.validators { let vp_code = vp_code_cache.get_or_insert_with( validator.validator_vp_code_path.clone(), @@ -308,7 +318,12 @@ where self.wl_storage .write(&pk_key, &validator.account_key) .expect("Unable to set genesis user public key"); - // Account balance (tokens no staked in PoS) + + // Balances + total_staked_nam_tokens += validator.pos_data.tokens; + total_nam_balance += + validator.pos_data.tokens + validator.non_staked_balance; + // Account balance (tokens not staked in PoS) self.wl_storage .write( &token::balance_key( @@ -330,7 +345,9 @@ where .expect("Unable to set genesis user public DKG session key"); } - // PoS system depends on epoch being initialized + // PoS system depends on epoch being initialized. Write the total + // genesis staking token balance to storage after + // initialization. let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); pos::init_genesis_storage( &mut self.wl_storage, @@ -342,6 +359,22 @@ where .map(|validator| validator.pos_data), current_epoch, ); + self.wl_storage + .write( + &total_supply_key(&staking_token_address()), + total_nam_balance, + ) + .expect("unable to set total NAM balance in storage"); + + // Set the ratio of staked to total NAM tokens in the parameters storage + self.wl_storage + .write( + &get_staked_ratio_key(), + Decimal::from(total_staked_nam_tokens) + / Decimal::from(total_nam_balance), + ) + .expect("unable to set staked ratio of NAM in storage"); + ibc::init_genesis_storage(&mut self.wl_storage.storage); // Set the initial validator set diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim.rs b/apps/src/lib/node/ledger/shims/abcipp_shim.rs index 74a56a4ddcc..4b77a07235b 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim.rs @@ -5,10 +5,15 @@ use std::pin::Pin; use std::task::{Context, Poll}; use futures::future::FutureExt; +use namada::proof_of_stake::{ + find_validator_by_raw_hash, write_current_block_proposer_address, +}; use namada::types::address::Address; #[cfg(not(feature = "abcipp"))] use namada::types::hash::Hash; #[cfg(not(feature = "abcipp"))] +use namada::types::key::tm_raw_hash_to_string; +#[cfg(not(feature = "abcipp"))] use namada::types::storage::BlockHash; #[cfg(not(feature = "abcipp"))] use namada::types::transaction::hash_tx; @@ -90,18 +95,49 @@ impl AbcippShim { pub fn run(mut self) { while let Ok((req, resp_sender)) = self.shell_recv.recv() { let resp = match req { - Req::ProcessProposal(proposal) => self - .service - .call(Request::ProcessProposal(proposal)) - .map_err(Error::from) - .and_then(|res| match res { - Response::ProcessProposal(resp) => { - Ok(Resp::ProcessProposal((&resp).into())) + Req::ProcessProposal(proposal) => { + #[cfg(not(feature = "abcipp"))] + { + println!("\nRECEIVED REQUEST PROCESSPROPOSAL"); + if !proposal.proposer_address.is_empty() { + let tm_raw_hash_string = tm_raw_hash_to_string( + proposal.proposer_address.clone(), + ); + let native_proposer_address = + find_validator_by_raw_hash( + &self.service.wl_storage, + tm_raw_hash_string, + ) + .unwrap() + .expect( + "Unable to find native validator address \ + of block proposer from tendermint raw \ + hash", + ); + println!( + "BLOCK PROPOSER (PROCESSPROPOSAL): {}", + native_proposer_address + ); + write_current_block_proposer_address( + &mut self.service.wl_storage, + native_proposer_address, + ) + .unwrap(); } - _ => unreachable!(), - }), + } + self.service + .call(Request::ProcessProposal(proposal)) + .map_err(Error::from) + .and_then(|res| match res { + Response::ProcessProposal(resp) => { + Ok(Resp::ProcessProposal((&resp).into())) + } + _ => unreachable!(), + }) + } #[cfg(feature = "abcipp")] Req::FinalizeBlock(block) => { + println!("RECEIVED REQUEST FINALIZEBLOCK"); let unprocessed_txs = block.txs.clone(); let processing_results = self.service.process_txs(&block.txs); @@ -126,17 +162,20 @@ impl AbcippShim { } #[cfg(not(feature = "abcipp"))] Req::BeginBlock(block) => { + println!("RECEIVED REQUEST BEGINBLOCK"); // we save this data to be forwarded to finalize later self.begin_block_request = Some(block); Ok(Resp::BeginBlock(Default::default())) } #[cfg(not(feature = "abcipp"))] Req::DeliverTx(tx) => { + println!("RECEIVED REQUEST DELIVERTX"); self.delivered_txs.push(tx.tx); Ok(Resp::DeliverTx(Default::default())) } #[cfg(not(feature = "abcipp"))] Req::EndBlock(_) => { + println!("RECEIVED REQUEST ENDBLOCK"); let processing_results = self.service.process_txs(&self.delivered_txs); let mut txs = Vec::with_capacity(self.delivered_txs.len()); diff --git a/core/src/types/storage.rs b/core/src/types/storage.rs index eefb30e508e..9655a7e107f 100644 --- a/core/src/types/storage.rs +++ b/core/src/types/storage.rs @@ -1045,7 +1045,7 @@ pub struct Epochs { first_known_epoch: Epoch, /// The block heights of the first block of each known epoch. /// Invariant: the values must be sorted in ascending order. - first_block_heights: Vec, + pub first_block_heights: Vec, } impl Default for Epochs { diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 869708655a7..3512ae294c2 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -15,6 +15,7 @@ pub mod btree_set; pub mod epoched; pub mod parameters; +pub mod rewards; pub mod storage; pub mod types; // pub mod validation; @@ -42,28 +43,29 @@ pub use namada_core::types::storage::Epoch; use namada_core::types::token; use once_cell::unsync::Lazy; use parameters::PosParams; +use rewards::PosRewardsCalculator; use rust_decimal::Decimal; use storage::{ - bonds_for_source_prefix, bonds_prefix, get_validator_address_from_bond, - into_tm_voting_power, is_bond_key, is_unbond_key, is_validator_slashes_key, + bonds_for_source_prefix, bonds_prefix, current_block_proposer_key, + get_validator_address_from_bond, into_tm_voting_power, is_bond_key, + is_unbond_key, is_validator_slashes_key, last_block_proposer_key, mult_amount, mult_change_to_amount, num_consensus_validators_key, params_key, slashes_prefix, unbonds_for_source_prefix, unbonds_prefix, validator_address_raw_hash_key, validator_max_commission_rate_change_key, BondDetails, BondsAndUnbondsDetail, BondsAndUnbondsDetails, - ReverseOrdTokenAmount, UnbondDetails, WeightedValidator, + ReverseOrdTokenAmount, RewardsAccumulator, UnbondDetails, }; use thiserror::Error; use types::{ - BelowCapacityValidatorSet, BelowCapacityValidatorSets, Bonds, - CommissionRates, ConsensusValidator, ConsensusValidatorSet, - ConsensusValidatorSets, GenesisValidator, Position, Slash, SlashType, - Slashes, TotalDeltas, Unbonds, ValidatorConsensusKeys, ValidatorDeltas, + decimal_mult_i128, decimal_mult_u64, BelowCapacityValidatorSet, + BelowCapacityValidatorSets, BondId, Bonds, CommissionRates, + ConsensusValidator, ConsensusValidatorSet, ConsensusValidatorSets, + GenesisValidator, Position, RewardsProducts, Slash, SlashType, Slashes, + TotalDeltas, Unbonds, ValidatorConsensusKeys, ValidatorDeltas, ValidatorPositionAddresses, ValidatorSetPositions, ValidatorSetUpdate, - ValidatorState, ValidatorStates, + ValidatorState, ValidatorStates, VoteInfo, WeightedValidator, }; -use crate::types::{decimal_mult_i128, decimal_mult_u64, BondId}; - /// Address of the PoS account implemented as a native VP pub const ADDRESS: Address = Address::Internal(InternalAddress::PoS); @@ -83,6 +85,13 @@ pub enum GenesisError { VotingPowerOverflow(TryFromIntError), } +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum InflationError { + #[error("Error")] + Error, +} + #[allow(missing_docs)] #[derive(Error, Debug)] pub enum BecomeValidatorError { @@ -199,6 +208,12 @@ impl From for storage_api::Error { } } +impl From for storage_api::Error { + fn from(err: InflationError) -> Self { + Self::new(err) + } +} + /// Get the storage handle to the epoched consensus validator set pub fn consensus_validator_set_handle() -> ConsensusValidatorSets { let key = storage::consensus_validator_set_key(); @@ -280,6 +295,30 @@ pub fn validator_slashes_handle(validator: &Address) -> Slashes { Slashes::open(key) } +/// Get the storage handle to the rewards accumulator for the consensus +/// validators in a given epoch +pub fn rewards_accumulator_handle() -> RewardsAccumulator { + let key = storage::consensus_validator_rewards_accumulator_key(); + RewardsAccumulator::open(key) +} + +/// Get the storage handle to a validator's self rewards products +pub fn validator_rewards_products_handle( + validator: &Address, +) -> RewardsProducts { + let key = storage::validator_self_rewards_product_key(validator); + RewardsProducts::open(key) +} + +/// Get the storage handle to the delegator rewards products associated with a +/// particular validator +pub fn delegator_rewards_products_handle( + validator: &Address, +) -> RewardsProducts { + let key = storage::validator_delegation_rewards_product_key(validator); + RewardsProducts::open(key) +} + /// new init genesis pub fn init_genesis( storage: &mut S, @@ -294,6 +333,7 @@ where write_pos_params(storage, params.clone())?; let mut total_bonded = token::Amount::default(); + let mut total_balance = token::Amount::default(); consensus_validator_set_handle().init(storage, current_epoch)?; below_capacity_validator_set_handle().init(storage, current_epoch)?; validator_set_positions_handle().init(storage, current_epoch)?; @@ -349,6 +389,9 @@ where current_epoch, )?; } + // TODO: figure out why I had this here in #714 or if its right here + total_balance += total_bonded; + // Write total deltas to storage total_deltas_handle().init_at_genesis( storage, @@ -470,6 +513,52 @@ where storage.write(&key, new_num) } +/// Read current block proposer address. +pub fn read_current_block_proposer_address( + storage: &S, +) -> storage_api::Result> +where + S: StorageRead, +{ + let key = current_block_proposer_key(); + storage.read(&key) +} + +/// Write current block proposer address. +pub fn write_current_block_proposer_address( + storage: &mut S, + address: Address, +) -> storage_api::Result<()> +where + S: StorageRead + StorageWrite, +{ + let key = current_block_proposer_key(); + storage.write(&key, address) +} + +/// Read last block proposer address. +pub fn read_last_block_proposer_address( + storage: &S, +) -> storage_api::Result> +where + S: StorageRead, +{ + let key = last_block_proposer_key(); + storage.read(&key) +} + +/// Write last block proposer address. +pub fn write_last_block_proposer_address( + storage: &mut S, + address: Address, +) -> storage_api::Result<()> +where + S: StorageRead + StorageWrite, +{ + let key = last_block_proposer_key(); + storage.write(&key, address) +} + /// Read PoS validator's delta value. pub fn read_validator_delta_value( storage: &S, @@ -1128,8 +1217,6 @@ where below_cap_in_mem.insert((stake, position), address); } - dbg!(&consensus_in_mem); - for ((val_stake, val_position), val_address) in consensus_in_mem.into_iter() { consensus_validator_set @@ -1137,11 +1224,6 @@ where .at(&val_stake) .insert(storage, val_position, val_address)?; } - println!("NEW VALIDATOR SET SHOULD BE INSERTED:"); - dbg!(read_consensus_validator_set_addresses( - storage, - target_epoch - )?); for ((val_stake, val_position), val_address) in below_cap_in_mem.into_iter() { @@ -1833,7 +1915,9 @@ where Ok((total, total_active)) } -/// NEW: adapting `PosBase::validator_set_update for lazy storage +/// Communicate imminent validator set updates to Tendermint. This function is +/// called two blocks before the start of a new epoch becuase Tendermint +/// validator updates become active two blocks after the updates are submitted. pub fn validator_set_update_tendermint( storage: &S, params: &PosParams, @@ -1842,20 +1926,12 @@ pub fn validator_set_update_tendermint( ) where S: StorageRead, { - let current_epoch: Epoch = current_epoch; - let current_epoch_u64: u64 = current_epoch.into(); - - let previous_epoch: Option = if current_epoch_u64 == 0 { - None - } else { - Some(Epoch::from(current_epoch_u64 - 1)) - }; + let next_epoch: Epoch = current_epoch.next(); let cur_consensus_validators = + consensus_validator_set_handle().at(&next_epoch); + let prev_consensus_validators = consensus_validator_set_handle().at(¤t_epoch); - let prev_consensus_validators = previous_epoch.map(|previous_epoch| { - consensus_validator_set_handle().at(&previous_epoch) - }); let consensus_validators = cur_consensus_validators .iter(storage) @@ -1869,64 +1945,54 @@ pub fn validator_set_update_tendermint( address, ) = validator.unwrap(); - println!( - "CONSENSUS VALIDATOR ADDRESS {}, CUR_STAKE {}\n", - address, cur_stake - ); - // Check if the validator was consensus in the previous epoch with // the same stake - if prev_consensus_validators.is_some() { - if let Some(prev_epoch) = previous_epoch { - // Look up previous state and prev and current voting powers - let prev_state = validator_state_handle(&address) - .get(storage, prev_epoch, params) - .unwrap(); - let prev_tm_voting_power = Lazy::new(|| { - let prev_validator_stake = - validator_deltas_handle(&address) - .get_sum(storage, prev_epoch, params) - .unwrap() - .map(token::Amount::from_change) - .unwrap_or_default(); - into_tm_voting_power( - params.tm_votes_per_token, - prev_validator_stake, - ) - }); - let cur_tm_voting_power = Lazy::new(|| { - into_tm_voting_power( - params.tm_votes_per_token, - cur_stake, - ) - }); - - // If its was in `Consensus` before and voting power has not - // changed, skip the update - if matches!(prev_state, Some(ValidatorState::Consensus)) - && *prev_tm_voting_power == *cur_tm_voting_power - { - tracing::debug!( - "skipping validator update, {address} is in \ - consensus set but voting power hasn't changed" - ); - return None; - } + // Look up previous state and prev and current voting powers + if !prev_consensus_validators.is_empty(storage).unwrap() { + let prev_state = validator_state_handle(&address) + .get(storage, current_epoch, params) + .unwrap(); + let prev_tm_voting_power = Lazy::new(|| { + let prev_validator_stake = + validator_deltas_handle(&address) + .get_sum(storage, current_epoch, params) + .unwrap() + .map(token::Amount::from_change) + .unwrap_or_default(); + into_tm_voting_power( + params.tm_votes_per_token, + prev_validator_stake, + ) + }); + let cur_tm_voting_power = Lazy::new(|| { + into_tm_voting_power(params.tm_votes_per_token, cur_stake) + }); + + // If it was in `Consensus` before and voting power has not + // changed, skip the update + if matches!(prev_state, Some(ValidatorState::Consensus)) + && *prev_tm_voting_power == *cur_tm_voting_power + { + tracing::debug!( + "skipping validator update, {address} is in consensus \ + set but voting power hasn't changed" + ); + return None; + } - // If both previous and current voting powers are 0, skip - // update - if *prev_tm_voting_power == 0 && *cur_tm_voting_power == 0 { - tracing::info!( - "skipping validator update, {address} is in \ - consensus set but without voting power" - ); - return None; - } + // If both previous and current voting powers are 0, skip + // update + if *prev_tm_voting_power == 0 && *cur_tm_voting_power == 0 { + tracing::info!( + "skipping validator update, {address} is in consensus \ + set but without voting power" + ); + return None; } } - println!("\nMAKING A CONSENSUS VALIDATOR CHANGE"); + let consensus_key = validator_consensus_key_handle(&address) - .get(storage, current_epoch, params) + .get(storage, next_epoch, params) .unwrap() .unwrap(); Some(ValidatorSetUpdate::Consensus(ConsensusValidator { @@ -1935,7 +2001,10 @@ pub fn validator_set_update_tendermint( })) }); let cur_below_capacity_validators = + below_capacity_validator_set_handle().at(&next_epoch); + let prev_below_capacity_vals = below_capacity_validator_set_handle().at(¤t_epoch); + let below_capacity_validators = cur_below_capacity_validators .iter(storage) .unwrap() @@ -1954,31 +2023,26 @@ pub fn validator_set_update_tendermint( address, cur_stake ); - let prev_below_capacity_vals = - below_capacity_validator_set_handle() - .at(&previous_epoch.unwrap()); if !prev_below_capacity_vals.is_empty(storage).unwrap() { - if let Some(prev_epoch) = previous_epoch { - // Look up the previous state - let prev_state = validator_state_handle(&address) - .get(storage, prev_epoch, params) - .unwrap(); - // If the `prev_state.is_none()`, it's a new validator that - // is `BelowCapacity`, so no update is needed. If it - // previously was `BelowCapacity` there's no update needed - // either. - if !matches!(prev_state, Some(ValidatorState::Consensus)) { - tracing::debug!( - "skipping validator update, {address} is not and \ - wasn't previously in consensus set" - ); - return None; - } + // Look up the previous state + let prev_state = validator_state_handle(&address) + .get(storage, current_epoch, params) + .unwrap(); + // If the `prev_state.is_none()`, it's a new validator that + // is `BelowCapacity`, so no update is needed. If it + // previously was `BelowCapacity` there's no update needed + // either. + if !matches!(prev_state, Some(ValidatorState::Consensus)) { + tracing::debug!( + "skipping validator update, {address} is not and \ + wasn't previously in consensus set" + ); + return None; } } let consensus_key = validator_consensus_key_handle(&address) - .get(storage, current_epoch, params) + .get(storage, next_epoch, params) .unwrap() .unwrap(); Some(ValidatorSetUpdate::Deactivated(consensus_key)) @@ -2423,3 +2487,159 @@ fn make_unbond_details( slashed_amount, } } + +/// Tally a running sum of the fracton of rewards owed to each validator in +/// the consensus set. This is used to keep track of the rewards due to each +/// consensus validator over the lifetime of an epoch. +pub fn log_block_rewards( + storage: &mut S, + epoch: impl Into, + proposer_address: &Address, + votes: &[VoteInfo], +) -> storage_api::Result<()> +where + S: StorageRead + StorageWrite, +{ + // TODO: all values collected here need to be consistent with the same + // block that the voting info corresponds to, which is the + // previous block from the current one we are in. + + // The votes correspond to the last committed block (n-1 if we are + // finalizing block n) + + let epoch: Epoch = epoch.into(); + let params = read_pos_params(storage)?; + let consensus_validators = consensus_validator_set_handle().at(&epoch); + // dbg!(&epoch); + + // let validator_set = self.read_validator_set(); + // dbg!(&validator_set); + // let validators = validator_set.get(epoch).unwrap(); + // let pos_params = self.read_pos_params(); + + // println!( + // "VALIDATOR SET OF EPOCH {} LAST UPDATE = {}, LEN = {}:", + // epoch, + // validator_set.last_update(), + // validator_set.data.len() + // ); + // for val in &validators.active { + // println!("STAKE: {}, ADDRESS: {}", val.bonded_stake, val.address); + // let ck = self + // .read_validator_consensus_key(&val.address) + // .unwrap() + // .get(epoch) + // .unwrap() + // .to_owned(); + // let hash_string1 = tm_consensus_key_raw_hash(&ck); + // let bytes1 = HEXUPPER.decode(hash_string1.as_bytes()).unwrap(); + // dbg!(bytes1); + // } + // dbg!(votes); + + // Get total stake of the consensus validator set + // TODO: this will need to account for rewards products? + let mut total_consensus_stake = 0_u64; + for validator in consensus_validators.iter(storage)? { + let ( + NestedSubKey::Data { + key: amount, + nested_sub_key: _, + }, + _address, + ) = validator?; + total_consensus_stake += u64::from(amount); + } + + // Get set of signing validator addresses and the combined stake of + // these signers + let mut signer_set: HashSet
= HashSet::new(); + let mut total_signing_stake: u64 = 0; + for vote in votes.iter() { + if !vote.signed_last_block { + continue; + } + let tm_raw_hash_string = + hex::encode_upper(vote.validator_address.clone()); + let native_address = + find_validator_by_raw_hash(storage, tm_raw_hash_string)?.expect( + "Unable to read native address of validator from tendermint \ + raw hash", + ); + + signer_set.insert(native_address.clone()); + total_signing_stake += vote.validator_vp; + + // Ensure TM stake updates properly with a debug_assert + let stake_from_deltas = + read_validator_stake(storage, ¶ms, &native_address, epoch)? + .unwrap_or_default(); + debug_assert_eq!( + stake_from_deltas, + token::Amount::from(vote.validator_vp) + ); + } + + // Get the block rewards coefficients (proposing, signing/voting, + // consensus set status) + let consensus_stake: Decimal = total_consensus_stake.into(); + let signing_stake: Decimal = total_signing_stake.into(); + let rewards_calculator = PosRewardsCalculator::new( + params.block_proposer_reward, + params.block_vote_reward, + total_signing_stake, + total_consensus_stake, + ); + let coeffs = match rewards_calculator.get_reward_coeffs() { + Ok(coeffs) => coeffs, + Err(_) => return Err(InflationError::Error.into()), + }; + + // println!( + // "TOTAL SIGNING STAKE (LOGGING BLOCK REWARDS) = {}", + // signing_stake + // ); + + // Compute the fractional block rewards for each consensus validator and + // update the reward accumulators + let mut values: HashMap = HashMap::new(); + for validator in consensus_validators.iter(storage)? { + let ( + NestedSubKey::Data { + key: stake, + nested_sub_key: _, + }, + address, + ) = validator?; + + let mut rewards_frac = Decimal::default(); + let stake: Decimal = u64::from(stake).into(); + // println!( + // "NAMADA VALIDATOR STAKE (LOGGING BLOCK REWARDS) OF EPOCH {} = + // {}", epoch, stake + // ); + + // Proposer reward + if address == *proposer_address { + rewards_frac += coeffs.proposer_coeff; + } + // Signer reward + if signer_set.contains(&address) { + let signing_frac = stake / signing_stake; + rewards_frac += coeffs.signer_coeff * signing_frac; + } + // Consensus validator reward + rewards_frac += coeffs.active_val_coeff * (stake / consensus_stake); + + // Update the rewards accumulator + let prev = rewards_accumulator_handle() + .get(storage, &address)? + .unwrap_or_default(); + values.insert(address, prev + rewards_frac); + } + for (address, value) in values.into_iter() { + rewards_accumulator_handle().insert(storage, address, value)?; + } + + Ok(()) +} diff --git a/vp_prelude/src/token.rs b/vp_prelude/src/token.rs index 0785fbf97d7..53c9f583234 100644 --- a/vp_prelude/src/token.rs +++ b/vp_prelude/src/token.rs @@ -15,21 +15,24 @@ use super::*; pub fn vp( ctx: &Ctx, token: &Address, - keys_changed: &BTreeSet, + keys_touched: &BTreeSet, verifiers: &BTreeSet
, ) -> VpResult { let mut change: Change = 0; - for key in keys_changed.iter() { - let owner: Option<&Address> = - match token::is_multitoken_balance_key(token, key) { - Some((_, o)) => Some(o), - None => token::is_balance_key(token, key), - }; - match owner { + for key in keys_touched.iter() { + match token::is_balance_key(token, key) { None => { - // Unknown changes to this address space are disallowed, but - // unknown changes anywhere else are permitted - if key.segments.get(0) == Some(&token.to_db_key()) { + if token::is_total_supply_key(key, token) { + // check if total supply is changed, which it should never + // be from a tx + let total_pre: Amount = ctx.read_pre(key)?.unwrap(); + let total_post: Amount = ctx.read_post(key)?.unwrap(); + if total_pre != total_post { + return reject(); + } + } else if key.segments.get(0) == Some(&token.to_db_key()) { + // Unknown changes to this address space are disallowed, but + // unknown changes anywhere else are permitted return reject(); } } From 92609cab14fef19be12c246fb778b94ed11e2655 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 10:42:39 -0500 Subject: [PATCH 04/48] trigger 2-block countdown to new epoch for Tendermint --- .../lib/node/ledger/shell/finalize_block.rs | 6 +- core/src/ledger/storage/mod.rs | 80 ++++++++++++++++++- core/src/types/time.rs | 8 ++ 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 414f33b5862..8898681abbb 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -73,6 +73,10 @@ where self.update_state(req.header, req.hash, req.byzantine_validators); let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); + let update_for_tendermint = + self.wl_storage.storage.epoch_update_tracker.0 + && self.wl_storage.storage.epoch_update_tracker.1 == 2; + let current_epoch = self.wl_storage.storage.block.epoch; if new_epoch { @@ -380,7 +384,7 @@ where tracing::info!("{}", stats); tracing::info!("{}", stats.format_tx_executed()); - if new_epoch { + if update_for_tendermint { self.update_epoch(&mut response); } diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index e2ac4da2351..f50a2e4df55 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -82,6 +82,8 @@ where pub next_epoch_min_start_time: DateTimeUtc, /// The current established address generator pub address_gen: EstablishedAddressGen, + /// Epoch update info + pub epoch_update_tracker: (bool, u32), /// The shielded transaction index pub tx_index: TxIndex, /// The currently saved conversion state @@ -343,6 +345,7 @@ where address_gen: EstablishedAddressGen::new( "Privacy is a function of liberty.", ), + epoch_update_tracker: (false, 0), tx_index: TxIndex::default(), conversion_state: ConversionState::default(), #[cfg(feature = "ferveo-tpke")] @@ -720,9 +723,28 @@ where let (parameters, _gas) = parameters::read(self).expect("Couldn't read protocol parameters"); - // Check if the current epoch is over - let new_epoch = height >= self.next_epoch_min_start_height + // Check if the new epoch minimum start height and start time have been + // fulfilled. If so, queue the next epoch to start two blocks + // into the future so as to align validator set updates + etc with + // tendermint. This is because tendermint has a two block delay + // to validator changes. + let current_epoch_duration_satisfied = height + >= self.next_epoch_min_start_height && time >= self.next_epoch_min_start_time; + + if current_epoch_duration_satisfied { + if !self.epoch_update_tracker.0 { + self.epoch_update_tracker = (true, 2); + } else { + self.epoch_update_tracker = + (true, self.epoch_update_tracker.1 - 1); + } + } else if self.epoch_update_tracker.0 { + self.epoch_update_tracker.0 = false + } + let new_epoch = + self.epoch_update_tracker.0 && self.epoch_update_tracker.1 == 0; + if new_epoch { // Begin a new epoch self.block.epoch = self.block.epoch.next(); @@ -865,6 +887,7 @@ pub mod testing { address_gen: EstablishedAddressGen::new( "Test address generator seed", ), + epoch_update_tracker: (false, 0), tx_index: TxIndex::default(), conversion_state: ConversionState::default(), #[cfg(feature = "ferveo-tpke")] @@ -995,7 +1018,25 @@ mod tests { epoch_duration.min_duration, ) { + // Update will now be enqueued for 2 blocks in the future + assert_eq!(storage.block.epoch, epoch_before); + assert!(storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,2); + + let block_height = block_height + 1; + let block_time = block_time + Duration::seconds(1); + storage.update_epoch(block_height, block_time).unwrap(); + assert_eq!(storage.block.epoch, epoch_before); + assert!(storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,1); + + let block_height = block_height + 1; + let block_time = block_time + Duration::seconds(1); + storage.update_epoch(block_height, block_time).unwrap(); assert_eq!(storage.block.epoch, epoch_before.next()); + assert!(storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,0); + assert_eq!(storage.next_epoch_min_start_height, block_height + epoch_duration.min_num_of_blocks); assert_eq!(storage.next_epoch_min_start_time, @@ -1007,6 +1048,8 @@ mod tests { storage.block.pred_epochs.get_epoch(block_height), Some(epoch_before.next())); } else { + assert!(!storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,0); assert_eq!(storage.block.epoch, epoch_before); assert_eq!( storage.block.pred_epochs.get_epoch(BlockHeight(block_height.0 - 1)), @@ -1041,19 +1084,50 @@ mod tests { // satisfied storage.update_epoch(height_before_update, time_before_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before); + assert!(!storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,0); storage.update_epoch(height_of_update, time_before_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before); + assert!(!storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,0); storage.update_epoch(height_before_update, time_of_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before); + assert!(!storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,0); - // Update should happen at this or after this height and time + // Update should be enqueued for 2 blocks in the future starting at or after this height and time + storage.update_epoch(height_of_update, time_of_update).unwrap(); + assert_eq!(storage.block.epoch, epoch_before); + assert!(storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,2); + + // Increment the block height and time to simulate new blocks now + let height_of_update = height_of_update + 1; + let time_of_update = time_of_update + Duration::seconds(1); + storage.update_epoch(height_of_update, time_of_update).unwrap(); + assert_eq!(storage.block.epoch, epoch_before); + assert!(storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,1); + + let height_of_update = height_of_update + 1; + let time_of_update = time_of_update + Duration::seconds(1); storage.update_epoch(height_of_update, time_of_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before.next()); + assert!(storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,0); // The next epoch's minimum duration should change assert_eq!(storage.next_epoch_min_start_height, height_of_update + parameters.epoch_duration.min_num_of_blocks); assert_eq!(storage.next_epoch_min_start_time, time_of_update + parameters.epoch_duration.min_duration); + + // Increment the block height and time once more to make sure things reset + let height_of_update = height_of_update + 1; + let time_of_update = time_of_update + Duration::seconds(1); + storage.update_epoch(height_of_update, time_of_update).unwrap(); + assert_eq!(storage.block.epoch, epoch_before.next()); + assert!(!storage.epoch_update_tracker.0); + assert_eq!(storage.epoch_update_tracker.1,0); } } } diff --git a/core/src/types/time.rs b/core/src/types/time.rs index 7288d88bab5..72f7510e0be 100644 --- a/core/src/types/time.rs +++ b/core/src/types/time.rs @@ -132,6 +132,14 @@ impl Add for DateTimeUtc { } } +impl Add for DateTimeUtc { + type Output = DateTimeUtc; + + fn add(self, rhs: Duration) -> Self::Output { + (self.0 + rhs).into() + } +} + impl Sub for DateTimeUtc { type Output = DateTimeUtc; From 33ce31ff2c812b367bca428be0a0705fe61c5f42 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 10:45:09 -0500 Subject: [PATCH 05/48] configure genesis to set up any number of validators --- apps/src/lib/config/genesis.rs | 45 +++++++++++++++++-- apps/src/lib/node/ledger/mod.rs | 4 +- .../lib/node/ledger/shell/finalize_block.rs | 10 ++--- apps/src/lib/node/ledger/shell/init_chain.rs | 3 +- apps/src/lib/node/ledger/shell/mod.rs | 31 ++++++++----- .../lib/node/ledger/shell/process_proposal.rs | 38 +++++++++------- 6 files changed, 92 insertions(+), 39 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 0dbb385208c..461728a97ed 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -875,7 +875,7 @@ pub fn genesis(base_dir: impl AsRef, chain_id: &ChainId) -> Genesis { genesis_config::read_genesis_config(path) } #[cfg(feature = "dev")] -pub fn genesis() -> Genesis { +pub fn genesis(num_validators: u64) -> Genesis { use namada::types::address; use rust_decimal_macros::dec; @@ -888,6 +888,9 @@ pub fn genesis() -> Genesis { // NOTE When the validator's key changes, tendermint must be reset with // `namada reset` command. To generate a new validator, use the // `tests::gen_genesis_validator` below. + let mut validators = Vec::::new(); + + // Use hard-coded keys for the first validator to avoid breaking other code let consensus_keypair = wallet::defaults::validator_keypair(); let account_keypair = wallet::defaults::validator_keypair(); let address = wallet::defaults::validator_address(); @@ -908,6 +911,37 @@ pub fn genesis() -> Genesis { validator_vp_code_path: vp_user_path.into(), validator_vp_sha256: Default::default(), }; + validators.push(validator); + + // Add other validators with randomly generated keys if needed + for _ in 0..(num_validators - 1) { + let consensus_keypair: common::SecretKey = + testing::gen_keypair::() + .try_to_sk() + .unwrap(); + let account_keypair = consensus_keypair.clone(); + let address = address::gen_established_address("validator account"); + let (protocol_keypair, dkg_keypair) = + wallet::defaults::validator_keys(); + let validator = Validator { + pos_data: GenesisValidator { + address, + tokens: token::Amount::whole(200_000), + consensus_key: consensus_keypair.ref_to(), + commission_rate: dec!(0.05), + max_commission_rate_change: dec!(0.01), + }, + account_key: account_keypair.ref_to(), + protocol_key: protocol_keypair.ref_to(), + dkg_public_key: dkg_keypair.public(), + non_staked_balance: token::Amount::whole(100_000), + // TODO replace with https://github.com/anoma/namada/issues/25) + validator_vp_code_path: vp_user_path.into(), + validator_vp_sha256: Default::default(), + }; + validators.push(validator); + } + let parameters = Parameters { epoch_duration: EpochDuration { min_num_of_blocks: 10, @@ -960,7 +994,7 @@ pub fn genesis() -> Genesis { }]; let default_user_tokens = token::Amount::whole(1_000_000); let default_key_tokens = token::Amount::whole(1_000); - let balances: HashMap = HashMap::from_iter([ + let mut balances: HashMap = HashMap::from_iter([ // established accounts' balances (wallet::defaults::albert_address(), default_user_tokens), (wallet::defaults::bertha_address(), default_user_tokens), @@ -980,8 +1014,11 @@ pub fn genesis() -> Genesis { christel.public_key.as_ref().unwrap().into(), default_key_tokens, ), - ((&validator.account_key).into(), default_key_tokens), ]); + for validator in &validators { + balances.insert((&validator.account_key).into(), default_key_tokens); + } + let token_accounts = address::tokens() .into_keys() .map(|address| TokenAccount { @@ -993,7 +1030,7 @@ pub fn genesis() -> Genesis { .collect(); Genesis { genesis_time: DateTimeUtc::now(), - validators: vec![validator], + validators, established_accounts: vec![albert, bertha, christel, masp], implicit_accounts, token_accounts, diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index 7bf950cc8d3..5e92c9879de 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -90,7 +90,7 @@ impl Shell { match req { Request::InitChain(init) => { tracing::debug!("Request InitChain"); - self.init_chain(init).map(Response::InitChain) + self.init_chain(init, 1).map(Response::InitChain) } Request::Info(_) => Ok(Response::Info(self.last_state())), Request::Query(query) => Ok(Response::Query(self.query(query))), @@ -446,7 +446,7 @@ fn start_abci_broadcaster_shell( #[cfg(not(feature = "dev"))] let genesis = genesis::genesis(&config.shell.base_dir, &config.chain_id); #[cfg(feature = "dev")] - let genesis = genesis::genesis(); + let genesis = genesis::genesis(1); let (shell, abci_service) = AbcippShim::new( config, wasm_dir, diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 8898681abbb..52809e28992 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -850,7 +850,7 @@ mod test_finalize_block { /// not appear in the queue of txs to be decrypted #[test] fn test_process_proposal_rejected_wrapper_tx() { - let (mut shell, _) = setup(); + let (mut shell, _) = setup(1); let keypair = gen_keypair(); let mut processed_txs = vec![]; let mut valid_wrappers = vec![]; @@ -939,7 +939,7 @@ mod test_finalize_block { /// proposal #[test] fn test_process_proposal_rejected_decrypted_tx() { - let (mut shell, _) = setup(); + let (mut shell, _) = setup(1); let keypair = gen_keypair(); let raw_tx = Tx::new( "wasm_code".as_bytes().to_owned(), @@ -993,7 +993,7 @@ mod test_finalize_block { /// but the tx result contains the appropriate error code. #[test] fn test_undecryptable_returns_error_code() { - let (mut shell, _) = setup(); + let (mut shell, _) = setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); let pubkey = EncryptionKey::default(); @@ -1052,7 +1052,7 @@ mod test_finalize_block { /// decrypted txs are de-queued. #[test] fn test_mixed_txs_queued_in_correct_order() { - let (mut shell, _) = setup(); + let (mut shell, _) = setup(1); let keypair = gen_keypair(); let mut processed_txs = vec![]; let mut valid_txs = vec![]; @@ -1194,7 +1194,7 @@ mod test_finalize_block { /// the DB. #[test] fn test_finalize_doesnt_commit_db() { - let (mut shell, _) = setup(); + let (mut shell, _) = setup(1); // Update epoch duration to make sure we go through couple epochs let epoch_duration = EpochDuration { diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 1a833c89c91..c77c902c9a2 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -31,6 +31,7 @@ where pub fn init_chain( &mut self, init: request::InitChain, + num_validators: u64, ) -> Result { let mut response = response::InitChain::default(); let (current_chain_id, _) = self.wl_storage.storage.get_chain_id(); @@ -56,7 +57,7 @@ where ); } #[cfg(feature = "dev")] - let genesis = genesis::genesis(); + let genesis = genesis::genesis(num_validators); let ts: protobuf::Timestamp = init.time.expect("Missing genesis time"); let initial_height = init diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index ce6127396fc..f5bd0d2540b 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -878,9 +878,13 @@ mod test_utils { } /// Forward a InitChain request and expect a success - pub fn init_chain(&mut self, req: RequestInitChain) { + pub fn init_chain( + &mut self, + req: RequestInitChain, + #[cfg(feature = "dev")] num_validators: u64, + ) { self.shell - .init_chain(req) + .init_chain(req, num_validators) .expect("Test shell failed to initialize"); } @@ -941,16 +945,21 @@ mod test_utils { /// Start a new test shell and initialize it. Returns the shell paired with /// a broadcast receiver, which will receives any protocol txs sent by the /// shell. - pub(super) fn setup() -> (TestShell, UnboundedReceiver>) { + pub(super) fn setup( + num_validators: u64, + ) -> (TestShell, UnboundedReceiver>) { let (mut test, receiver) = TestShell::new(); - test.init_chain(RequestInitChain { - time: Some(Timestamp { - seconds: 0, - nanos: 0, - }), - chain_id: ChainId::default().to_string(), - ..Default::default() - }); + test.init_chain( + RequestInitChain { + time: Some(Timestamp { + seconds: 0, + nanos: 0, + }), + chain_id: ChainId::default().to_string(), + ..Default::default() + }, + num_validators, + ); (test, receiver) } diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 11deec9e13f..7e55da3cea2 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -593,14 +593,17 @@ mod test_process_proposal { #[test] fn test_invalid_hash_commitment() { let (mut shell, _) = TestShell::new(); - shell.init_chain(RequestInitChain { - time: Some(Timestamp { - seconds: 0, - nanos: 0, - }), - chain_id: ChainId::default().to_string(), - ..Default::default() - }); + shell.init_chain( + RequestInitChain { + time: Some(Timestamp { + seconds: 0, + nanos: 0, + }), + chain_id: ChainId::default().to_string(), + ..Default::default() + }, + 1, + ); let keypair = crate::wallet::defaults::daewon_keypair(); let tx = Tx::new( @@ -649,14 +652,17 @@ mod test_process_proposal { #[test] fn test_undecryptable() { let (mut shell, _) = TestShell::new(); - shell.init_chain(RequestInitChain { - time: Some(Timestamp { - seconds: 0, - nanos: 0, - }), - chain_id: ChainId::default().to_string(), - ..Default::default() - }); + shell.init_chain( + RequestInitChain { + time: Some(Timestamp { + seconds: 0, + nanos: 0, + }), + chain_id: ChainId::default().to_string(), + ..Default::default() + }, + 1, + ); let keypair = crate::wallet::defaults::daewon_keypair(); let pubkey = EncryptionKey::default(); // not valid tx bytes From efffafd61fbb3a79648be8cea642a22b062444e5 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 10:47:19 -0500 Subject: [PATCH 06/48] tests for inflation --- .../lib/node/ledger/shell/finalize_block.rs | 287 +++++++++++++++++- tests/src/e2e/ledger_tests.rs | 154 ++++++++++ 2 files changed, 440 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 52809e28992..648cccc14c3 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -826,11 +826,21 @@ where /// are covered by the e2e tests. #[cfg(test)] mod test_finalize_block { - use std::collections::BTreeMap; + use std::collections::{BTreeMap, BTreeSet}; use std::str::FromStr; + use data_encoding::HEXUPPER; use namada::ledger::parameters::EpochDuration; use namada::ledger::storage_api; + // use data_encoding::HEXUPPER; + use namada::proof_of_stake::btree_set::BTreeSetShims; + use namada::proof_of_stake::types::WeightedValidator; + use namada::proof_of_stake::{ + read_consensus_validator_set_addresses_with_stake, + rewards_accumulator_handle, validator_consensus_key_handle, + validator_rewards_products_handle, + write_current_block_proposer_address, + }; use namada::types::governance::ProposalVote; use namada::types::storage::Epoch; use namada::types::time::DurationSecs; @@ -838,6 +848,7 @@ mod test_finalize_block { InitProposalData, VoteProposalData, }; use namada::types::transaction::{EncryptionKey, Fee, WrapperTx, MIN_FEE}; + use rust_decimal_macros::dec; use super::*; use crate::node::ledger::shell::test_utils::*; @@ -1285,4 +1296,278 @@ mod test_finalize_block { last_storage_state = store_block_state(&shell); } } + + /// A unit test for PoS inflationary rewards + #[test] + fn test_inflation_accounting() { + // GENERAL IDEA OF THE TEST: + // For the duration of an epoch, choose some number of times for each of + // 4 genesis validators to propose a block and choose some arbitrary + // voting distribution for each block. After each call of + // finalize_block, check the validator rewards accumulators to ensure + // that the proper inflation is being applied for each validator. Can + // also check that the last and current block proposers are being stored + // properly. At the end of the epoch, check that the validator rewards + // products are appropriately updated. + + let (mut shell, _) = setup(4); + + let mut validator_set: BTreeSet = + read_consensus_validator_set_addresses_with_stake( + &shell.wl_storage, + Epoch::default(), + ) + .unwrap() + .into_iter() + .collect(); + + let params = read_pos_params(&shell.wl_storage).unwrap(); + + let val1 = validator_set.pop_first_shim().unwrap(); + let val2 = validator_set.pop_first_shim().unwrap(); + let val3 = validator_set.pop_first_shim().unwrap(); + let val4 = validator_set.pop_first_shim().unwrap(); + + let get_pkh = |address, epoch| { + let ck = validator_consensus_key_handle(&address) + .get(&shell.wl_storage, epoch, ¶ms) + .unwrap() + .unwrap(); + let hash_string = tm_consensus_key_raw_hash(&ck); + HEXUPPER.decode(hash_string.as_bytes()).unwrap() + }; + + let pkh1 = get_pkh(val1.address.clone(), Epoch::default()); + let pkh2 = get_pkh(val2.address.clone(), Epoch::default()); + let pkh3 = get_pkh(val3.address.clone(), Epoch::default()); + let pkh4 = get_pkh(val4.address.clone(), Epoch::default()); + + // All validators sign blocks initially + let votes = vec![ + VoteInfo { + validator_address: pkh1.clone(), + validator_vp: u64::from(val1.bonded_stake), + signed_last_block: true, + }, + VoteInfo { + validator_address: pkh2.clone(), + validator_vp: u64::from(val2.bonded_stake), + signed_last_block: true, + }, + VoteInfo { + validator_address: pkh3.clone(), + validator_vp: u64::from(val3.bonded_stake), + signed_last_block: true, + }, + VoteInfo { + validator_address: pkh4.clone(), + validator_vp: u64::from(val4.bonded_stake), + signed_last_block: true, + }, + ]; + + let rewards_prod_1 = validator_rewards_products_handle(&val1.address); + let rewards_prod_2 = validator_rewards_products_handle(&val2.address); + let rewards_prod_3 = validator_rewards_products_handle(&val3.address); + let rewards_prod_4 = validator_rewards_products_handle(&val4.address); + + let is_decimal_equal_enough = + |target: Decimal, to_compare: Decimal| -> bool { + // also return false if to_compare > target since this should + // never happen for the use cases + if to_compare < target { + let tolerance = Decimal::new(1, 9); + let res = Decimal::ONE - to_compare / target; + res < tolerance + } else { + to_compare == target + } + }; + + // NOTE: Want to manually set the block proposer and the vote + // information in a FinalizeBlock object. In non-abcipp mode, + // the block proposer is written in ProcessProposal, so need to + // manually do it here let proposer_address = pkh1.clone(); + + // FINALIZE BLOCK 1. Tell Namada that val1 is the block proposer. We + // won't receive votes from TM since we receive votes at a 1-block + // delay, so votes will be empty here + next_block_for_inflation(&mut shell, &val1.address, vec![]); + assert!( + rewards_accumulator_handle() + .is_empty(&shell.wl_storage) + .unwrap() + ); + + // FINALIZE BLOCK 2. Tell Namada that val1 is the block proposer. + // Include votes that correspond to block 1. Make val2 the next block's + // proposer. + next_block_for_inflation(&mut shell, &val2.address, votes.clone()); + assert!(rewards_prod_1.is_empty(&shell.wl_storage).unwrap()); + assert!(rewards_prod_2.is_empty(&shell.wl_storage).unwrap()); + assert!(rewards_prod_3.is_empty(&shell.wl_storage).unwrap()); + assert!(rewards_prod_4.is_empty(&shell.wl_storage).unwrap()); + assert!( + !rewards_accumulator_handle() + .is_empty(&shell.wl_storage) + .unwrap() + ); + // Val1 was the proposer, so its reward should be larger than all + // others, which should themselves all be equal + let acc_sum = get_rewards_sum(&shell.wl_storage); + assert!(is_decimal_equal_enough(Decimal::ONE, acc_sum)); + let acc = get_rewards_acc(&shell.wl_storage); + assert_eq!(acc.get(&val2.address), acc.get(&val3.address)); + assert_eq!(acc.get(&val2.address), acc.get(&val4.address)); + assert!( + acc.get(&val1.address).cloned().unwrap() + > acc.get(&val2.address).cloned().unwrap() + ); + + // FINALIZE BLOCK 3, with val1 as proposer for the next block. + next_block_for_inflation(&mut shell, &val1.address, votes); + assert!(rewards_prod_1.is_empty(&shell.wl_storage).unwrap()); + assert!(rewards_prod_2.is_empty(&shell.wl_storage).unwrap()); + assert!(rewards_prod_3.is_empty(&shell.wl_storage).unwrap()); + assert!(rewards_prod_4.is_empty(&shell.wl_storage).unwrap()); + // Val2 was the proposer for this block, so its rewards accumulator + // should be the same as val1 now. Val3 and val4 should be equal as + // well. + let acc_sum = get_rewards_sum(&shell.wl_storage); + assert!(is_decimal_equal_enough(Decimal::TWO, acc_sum)); + let acc = get_rewards_acc(&shell.wl_storage); + assert_eq!(acc.get(&val1.address), acc.get(&val2.address)); + assert_eq!(acc.get(&val3.address), acc.get(&val4.address)); + assert!( + acc.get(&val1.address).cloned().unwrap() + > acc.get(&val3.address).cloned().unwrap() + ); + + // Now we don't receive a vote from val4. + let votes = vec![ + VoteInfo { + validator_address: pkh1, + validator_vp: u64::from(val1.bonded_stake), + signed_last_block: true, + }, + VoteInfo { + validator_address: pkh2, + validator_vp: u64::from(val2.bonded_stake), + signed_last_block: true, + }, + VoteInfo { + validator_address: pkh3, + validator_vp: u64::from(val3.bonded_stake), + signed_last_block: true, + }, + VoteInfo { + validator_address: pkh4, + validator_vp: u64::from(val4.bonded_stake), + signed_last_block: false, + }, + ]; + + // FINALIZE BLOCK 4. The next block proposer will be val1. Only val1, + // val2, and val3 vote on this block. + next_block_for_inflation(&mut shell, &val1.address, votes.clone()); + assert!(rewards_prod_1.is_empty(&shell.wl_storage).unwrap()); + assert!(rewards_prod_2.is_empty(&shell.wl_storage).unwrap()); + assert!(rewards_prod_3.is_empty(&shell.wl_storage).unwrap()); + assert!(rewards_prod_4.is_empty(&shell.wl_storage).unwrap()); + let acc_sum = get_rewards_sum(&shell.wl_storage); + assert!(is_decimal_equal_enough(dec!(3), acc_sum)); + let acc = get_rewards_acc(&shell.wl_storage); + assert!( + acc.get(&val1.address).cloned().unwrap() + > acc.get(&val2.address).cloned().unwrap() + ); + assert!( + acc.get(&val2.address).cloned().unwrap() + > acc.get(&val3.address).cloned().unwrap() + ); + assert!( + acc.get(&val3.address).cloned().unwrap() + > acc.get(&val4.address).cloned().unwrap() + ); + + // Advance to the start of epoch 1. Val1 is the only block proposer for + // the rest of the epoch. Val4 does not vote for the rest of the epoch. + let height_of_next_epoch = + shell.wl_storage.storage.next_epoch_min_start_height; + let current_height = 4_u64; + assert_eq!(current_height, shell.wl_storage.storage.block.height.0); + + for _ in current_height..height_of_next_epoch.0 + 2 { + dbg!( + get_rewards_acc(&shell.wl_storage), + get_rewards_sum(&shell.wl_storage), + ); + next_block_for_inflation(&mut shell, &val1.address, votes.clone()); + } + assert!( + rewards_accumulator_handle() + .is_empty(&shell.wl_storage) + .unwrap() + ); + let rp1 = rewards_prod_1 + .get(&shell.wl_storage, &Epoch::default()) + .unwrap() + .unwrap(); + let rp2 = rewards_prod_2 + .get(&shell.wl_storage, &Epoch::default()) + .unwrap() + .unwrap(); + let rp3 = rewards_prod_3 + .get(&shell.wl_storage, &Epoch::default()) + .unwrap() + .unwrap(); + let rp4 = rewards_prod_4 + .get(&shell.wl_storage, &Epoch::default()) + .unwrap() + .unwrap(); + assert!(rp1 > rp2); + assert!(rp2 > rp3); + assert!(rp3 > rp4); + } + + fn get_rewards_acc(storage: &S) -> HashMap + where + S: StorageRead, + { + rewards_accumulator_handle() + .iter(storage) + .unwrap() + .map(|elem| elem.unwrap()) + .collect::>() + } + + fn get_rewards_sum(storage: &S) -> Decimal + where + S: StorageRead, + { + let acc = get_rewards_acc(storage); + if acc.is_empty() { + Decimal::ZERO + } else { + acc.iter().fold(Decimal::default(), |sum, elm| sum + *elm.1) + } + } + + fn next_block_for_inflation( + shell: &mut TestShell, + next_proposer: &Address, + votes: Vec, + ) { + write_current_block_proposer_address( + &mut shell.wl_storage, + next_proposer.clone(), + ) + .unwrap(); + let req = FinalizeBlock { + votes, + ..Default::default() + }; + shell.finalize_block(req).unwrap(); + shell.commit(); + } } diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 94371035469..6d53755af11 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -1972,7 +1972,161 @@ fn pos_bonds() -> Result<()> { let mut client = run!(test, Bin::Client, tx_args, Some(40))?; client.exp_string("Transaction is valid.")?; client.assert_success(); + Ok(()) +} + +/// TODO +#[test] +fn pos_rewards() -> Result<()> { + let test = setup::network( + |genesis| { + let parameters = ParametersConfig { + min_num_of_blocks: 3, + epochs_per_year: 31_536_000, + max_expected_time_per_block: 1, + ..genesis.parameters + }; + let pos_params = PosParamsConfig { + pipeline_len: 2, + unbonding_len: 4, + ..genesis.pos_params + }; + let genesis = GenesisConfig { + parameters, + pos_params, + ..genesis + }; + setup::set_validators(3, genesis, default_port_offset) + }, + None, + )?; + + println!("\nFINISHED SETUP\n"); + + // 1. Run 3 genesis validator ledger nodes + let mut validator_0 = + run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; + validator_0.exp_string("Namada ledger node started")?; + validator_0.exp_string("This node is a validator")?; + + let mut validator_1 = + run_as!(test, Who::Validator(1), Bin::Node, &["ledger"], Some(40))?; + validator_1.exp_string("Namada ledger node started")?; + validator_1.exp_string("This node is a validator")?; + + let mut validator_2 = + run_as!(test, Who::Validator(2), Bin::Node, &["ledger"], Some(40))?; + validator_2.exp_string("Namada ledger node started")?; + validator_2.exp_string("This node is a validator")?; + + let bg_validator_0 = validator_0.background(); + let bg_validator_1 = validator_1.background(); + let bg_validator_2 = validator_2.background(); + + let validator_zero_rpc = get_actor_rpc(&test, &Who::Validator(0)); + let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(1)); + let validator_two_rpc = get_actor_rpc(&test, &Who::Validator(2)); + println!("\nDBG0\n"); + // Submit a delegation from Bertha to validator-0 + let tx_args = vec![ + "bond", + "--validator", + "validator-0", + "--source", + BERTHA, + "--amount", + "10000.0", + "--gas-amount", + "0", + "--gas-limit", + "0", + "--gas-token", + NAM, + "--ledger-address", + &validator_zero_rpc, + ]; + println!("\nDBG1\n"); + + let mut client = run!(test, Bin::Client, tx_args, Some(40))?; + client.exp_string("Transaction is valid.")?; + client.assert_success(); + + // Check that all validator nodes processed the tx with same result + let validator_0 = bg_validator_0.foreground(); + let validator_1 = bg_validator_1.foreground(); + let validator_2 = bg_validator_2.foreground(); + + // let expected_result = "all VPs accepted transaction"; + // validator_0.exp_string(expected_result)?; + // validator_1.exp_string(expected_result)?; + // validator_2.exp_string(expected_result)?; + + let _bg_validator_0 = validator_0.background(); + let _bg_validator_1 = validator_1.background(); + let _bg_validator_2 = validator_2.background(); + + // Let validator-1 self-bond + let tx_args = vec![ + "bond", + "--validator", + "validator-1", + "--amount", + "30000.0", + "--gas-amount", + "0", + "--gas-limit", + "0", + "--gas-token", + NAM, + "--ledger-address", + &validator_one_rpc, + ]; + let mut client = + run_as!(test, Who::Validator(1), Bin::Client, tx_args, Some(40))?; + client.exp_string("Transaction is valid.")?; + client.assert_success(); + + // Let validator-2 self-bond + let tx_args = vec![ + "bond", + "--validator", + "validator-2", + "--amount", + "25000.0", + "--gas-amount", + "0", + "--gas-limit", + "0", + "--gas-token", + NAM, + "--ledger-address", + &validator_two_rpc, + ]; + let mut client = + run_as!(test, Who::Validator(2), Bin::Client, tx_args, Some(40))?; + client.exp_string("Transaction is valid.")?; + client.assert_success(); + println!("\nDBG7\n"); + // Wait some epochs + let epoch = get_epoch(&test, &validator_zero_rpc)?; + let wait_epoch = epoch + 4_u64; + println!( + "Current epoch: {}, earliest epoch for withdrawal: {}", + epoch, wait_epoch + ); + + let start = Instant::now(); + let loop_timeout = Duration::new(40, 0); + loop { + if Instant::now().duration_since(start) > loop_timeout { + panic!("Timed out waiting for epoch: {}", wait_epoch); + } + let epoch = get_epoch(&test, &validator_zero_rpc)?; + if dbg!(epoch) >= wait_epoch { + break; + } + } Ok(()) } From 11fffaed45f1ef0ed5f7b14ffeff75bf63608003 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 10:58:39 -0500 Subject: [PATCH 07/48] Cargo lock and tomls --- Cargo.lock | 4 +++ proof_of_stake/Cargo.toml | 4 +++ shared/Cargo.toml | 1 + wasm/Cargo.lock | 37 +++++++++++++++++++++------ wasm_for_tests/wasm_source/Cargo.lock | 37 +++++++++++++++++++++------ 5 files changed, 67 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8aba5b5646b..734c39b2703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3655,6 +3655,7 @@ dependencies = [ "pwasm-utils", "rayon", "rust_decimal", + "rust_decimal_macros", "serde_json", "sha2 0.9.9", "tempfile", @@ -3844,12 +3845,15 @@ name = "namada_proof_of_stake" version = "0.14.0" dependencies = [ "borsh", + "data-encoding", "derivative", + "hex", "namada_core", "once_cell", "proptest", "rust_decimal", "rust_decimal_macros", + "tendermint-proto 0.23.5", "test-log", "thiserror", "tracing 0.1.37", diff --git a/proof_of_stake/Cargo.toml b/proof_of_stake/Cargo.toml index afd8e444752..0f4a0f14d62 100644 --- a/proof_of_stake/Cargo.toml +++ b/proof_of_stake/Cargo.toml @@ -20,13 +20,17 @@ testing = ["proptest"] namada_core = {path = "../core", default-features = false} borsh = "0.9.1" derivative = "2.2.0" +hex = "0.4.3" once_cell = "1.8.0" # A fork with state machine testing proptest = {git = "https://github.com/heliaxdev/proptest", branch = "tomas/sm", optional = true} rust_decimal = { version = "1.26.1", features = ["borsh"] } rust_decimal_macros = "1.26.1" +tendermint-proto = {git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9"} thiserror = "1.0.30" tracing = "0.1.30" +data-encoding = "2.3.2" + [dev-dependencies] namada_core = {path = "../core", features = ["testing"]} diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 91e8fee219e..4a5ee3a1e76 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -106,6 +106,7 @@ prost = "0.9.0" pwasm-utils = {git = "https://github.com/heliaxdev/wasm-utils", tag = "v0.20.0", features = ["sign_ext"], optional = true} rayon = {version = "=1.5.3", optional = true} rust_decimal = "1.26.1" +rust_decimal_macros = "1.26.1" serde_json = "1.0.62" sha2 = "0.9.3" # We switch off "blake2b" because it cannot be compiled to wasm diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 3d3d7d82807..369c9aa78d5 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -1926,7 +1926,7 @@ dependencies = [ "subtle-encoding", "tendermint", "tendermint-light-client-verifier", - "tendermint-proto", + "tendermint-proto 0.23.6", "tendermint-testgen", "time", "tracing", @@ -1942,7 +1942,7 @@ dependencies = [ "prost", "prost-types", "serde", - "tendermint-proto", + "tendermint-proto 0.23.6", "tonic", ] @@ -1988,7 +1988,7 @@ dependencies = [ "tendermint", "tendermint-light-client", "tendermint-light-client-verifier", - "tendermint-proto", + "tendermint-proto 0.23.6", "tendermint-rpc", "thiserror", "tiny-bip39", @@ -2481,11 +2481,12 @@ dependencies = [ "pwasm-utils", "rayon", "rust_decimal", + "rust_decimal_macros", "serde_json", "sha2 0.9.9", "tempfile", "tendermint", - "tendermint-proto", + "tendermint-proto 0.23.6", "thiserror", "tracing", "wasmer", @@ -2533,7 +2534,7 @@ dependencies = [ "sha2 0.9.9", "sparse-merkle-tree", "tendermint", - "tendermint-proto", + "tendermint-proto 0.23.6", "thiserror", "tonic-build", "tracing", @@ -2554,12 +2555,15 @@ name = "namada_proof_of_stake" version = "0.14.0" dependencies = [ "borsh", + "data-encoding", "derivative", + "hex", "namada_core", "once_cell", "proptest", "rust_decimal", "rust_decimal_macros", + "tendermint-proto 0.23.5", "thiserror", "tracing", ] @@ -2596,7 +2600,7 @@ dependencies = [ "tempfile", "tendermint", "tendermint-config", - "tendermint-proto", + "tendermint-proto 0.23.6", "tendermint-rpc", "test-log", "tokio", @@ -4113,7 +4117,7 @@ dependencies = [ "signature", "subtle", "subtle-encoding", - "tendermint-proto", + "tendermint-proto 0.23.6", "time", "zeroize", ] @@ -4164,6 +4168,23 @@ dependencies = [ "time", ] +[[package]] +name = "tendermint-proto" +version = "0.23.5" +source = "git+https://github.com/heliaxdev/tendermint-rs?rev=95c52476bc37927218374f94ac8e2a19bd35bec9#95c52476bc37927218374f94ac8e2a19bd35bec9" +dependencies = [ + "bytes", + "flex-error", + "num-derive", + "num-traits", + "prost", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + [[package]] name = "tendermint-proto" version = "0.23.6" @@ -4204,7 +4225,7 @@ dependencies = [ "subtle-encoding", "tendermint", "tendermint-config", - "tendermint-proto", + "tendermint-proto 0.23.6", "thiserror", "time", "tokio", diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 1b290303cda..4f8a7e98a31 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -1926,7 +1926,7 @@ dependencies = [ "subtle-encoding", "tendermint", "tendermint-light-client-verifier", - "tendermint-proto", + "tendermint-proto 0.23.6", "tendermint-testgen", "time", "tracing", @@ -1942,7 +1942,7 @@ dependencies = [ "prost", "prost-types", "serde", - "tendermint-proto", + "tendermint-proto 0.23.6", "tonic", ] @@ -1988,7 +1988,7 @@ dependencies = [ "tendermint", "tendermint-light-client", "tendermint-light-client-verifier", - "tendermint-proto", + "tendermint-proto 0.23.6", "tendermint-rpc", "thiserror", "tiny-bip39", @@ -2481,11 +2481,12 @@ dependencies = [ "pwasm-utils", "rayon", "rust_decimal", + "rust_decimal_macros", "serde_json", "sha2 0.9.9", "tempfile", "tendermint", - "tendermint-proto", + "tendermint-proto 0.23.6", "thiserror", "tracing", "wasmer", @@ -2533,7 +2534,7 @@ dependencies = [ "sha2 0.9.9", "sparse-merkle-tree", "tendermint", - "tendermint-proto", + "tendermint-proto 0.23.6", "thiserror", "tonic-build", "tracing", @@ -2554,12 +2555,15 @@ name = "namada_proof_of_stake" version = "0.14.0" dependencies = [ "borsh", + "data-encoding", "derivative", + "hex", "namada_core", "once_cell", "proptest", "rust_decimal", "rust_decimal_macros", + "tendermint-proto 0.23.5", "thiserror", "tracing", ] @@ -2596,7 +2600,7 @@ dependencies = [ "tempfile", "tendermint", "tendermint-config", - "tendermint-proto", + "tendermint-proto 0.23.6", "tendermint-rpc", "test-log", "tokio", @@ -4106,7 +4110,7 @@ dependencies = [ "signature", "subtle", "subtle-encoding", - "tendermint-proto", + "tendermint-proto 0.23.6", "time", "zeroize", ] @@ -4157,6 +4161,23 @@ dependencies = [ "time", ] +[[package]] +name = "tendermint-proto" +version = "0.23.5" +source = "git+https://github.com/heliaxdev/tendermint-rs?rev=95c52476bc37927218374f94ac8e2a19bd35bec9#95c52476bc37927218374f94ac8e2a19bd35bec9" +dependencies = [ + "bytes", + "flex-error", + "num-derive", + "num-traits", + "prost", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + [[package]] name = "tendermint-proto" version = "0.23.6" @@ -4197,7 +4218,7 @@ dependencies = [ "subtle-encoding", "tendermint", "tendermint-config", - "tendermint-proto", + "tendermint-proto 0.23.6", "thiserror", "time", "tokio", From 074027b461a870d62b05023f30c13b6e23a045c3 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 11:03:11 -0500 Subject: [PATCH 08/48] update comments and documentation --- core/src/ledger/parameters/mod.rs | 1 + proof_of_stake/src/epoched.rs | 11 +---------- tests/src/e2e/ledger_tests.rs | 2 +- tests/src/e2e/setup.rs | 2 +- tests/src/native_vp/pos.rs | 31 ++++++++++++++----------------- 5 files changed, 18 insertions(+), 29 deletions(-) diff --git a/core/src/ledger/parameters/mod.rs b/core/src/ledger/parameters/mod.rs index 7cb8174f0bf..fef582a33a8 100644 --- a/core/src/ledger/parameters/mod.rs +++ b/core/src/ledger/parameters/mod.rs @@ -526,6 +526,7 @@ where decode(value.ok_or(ReadError::ParametersMissing)?) .map_err(ReadError::StorageTypeError)?; + // read max expected block time let max_expected_time_per_block_key = storage::get_max_expected_time_per_block_key(); let (value, gas_time) = storage diff --git a/proof_of_stake/src/epoched.rs b/proof_of_stake/src/epoched.rs index 3091c9daa56..9a6fecccd31 100644 --- a/proof_of_stake/src/epoched.rs +++ b/proof_of_stake/src/epoched.rs @@ -451,19 +451,10 @@ where None => Ok(None), Some(last_update) => { let data_handler = self.get_data_handler(); - // dbg!(data_handler.get(storage, &Epoch(0))); - // dbg!(data_handler.get(storage, &Epoch(1))); - // dbg!(data_handler.get(storage, &Epoch(2))); - // dbg!(data_handler.len(storage)?); - - // let mut it = data_handler.iter(storage)?; - // dbg!(it.next()); - // dbg!(it.next()); - // drop(it); let future_most_epoch = last_update + FutureEpochs::value(params); - // dbg!(future_most_epoch); + // Epoch can be a lot greater than the epoch where // a value is recorded, we check the upper bound // epoch of the LazyMap data diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 6d53755af11..113a42acabd 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -1815,7 +1815,7 @@ fn pos_bonds() -> Result<()> { let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - // 2. Submit a self-bond for the gepnesis validator + // 2. Submit a self-bond for the genesis validator let tx_args = vec![ "bond", "--validator", diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index daa4db4dd79..39438b92db3 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -179,7 +179,7 @@ pub fn network( format!("{}:{}", std::file!(), std::line!()), )?; - // Get the generated chain_id` from result of the last command + // Get the generated chain_id from result of the last command let (unread, matched) = init_network.exp_regex(r"Derived chain ID: .*\n")?; let chain_id_raw = diff --git a/tests/src/native_vp/pos.rs b/tests/src/native_vp/pos.rs index d3e3773f47a..d6e0b6bd0ed 100644 --- a/tests/src/native_vp/pos.rs +++ b/tests/src/native_vp/pos.rs @@ -37,28 +37,23 @@ //! `testing::PosStorageChange`. //! //! - Bond: Requires a validator account in the state (the `#{validator}` -//! segments in the keys below). Some of the storage change are optional, -//! which depends on whether the bond increases voting power of the validator. +//! segments in the keys below). //! - `#{PoS}/bond/#{owner}/#{validator}` -//! - `#{PoS}/total_voting_power` (optional) -//! - `#{PoS}/validator_set` (optional) -//! - `#{PoS}/validator/#{validator}/total_deltas` -//! - `#{PoS}/validator/#{validator}/voting_power` (optional) +//! - `#{PoS}/total_deltas` +//! - `#{PoS}/validator_set` +//! - `#{PoS}/validator/#{validator}/deltas` //! - `#{staking_token}/balance/#{PoS}` //! //! //! - Unbond: Requires a bond in the state (the `#{owner}` and `#{validator}` //! segments in the keys below must be the owner and a validator of an //! existing bond). The bond's total amount must be greater or equal to the -//! amount that is being unbonded. Some of the storage changes are optional, -//! which depends on whether the unbonding decreases voting power of the -//! validator. +//! amount that is being unbonded. //! - `#{PoS}/bond/#{owner}/#{validator}` -//! - `#{PoS}/total_voting_power` (optional) //! - `#{PoS}/unbond/#{owner}/#{validator}` -//! - `#{PoS}/validator_set` (optional) -//! - `#{PoS}/validator/#{validator}/total_deltas` -//! - `#{PoS}/validator/#{validator}/voting_power` (optional) +//! - `#{PoS}/total_deltas` +//! - `#{PoS}/validator_set` +//! - `#{PoS}/validator/#{validator}/deltas` //! //! - Withdraw: Requires a withdrawable unbond in the state (the `#{owner}` and //! `#{validator}` segments in the keys below must be the owner and a @@ -67,13 +62,14 @@ //! - `#{staking_token}/balance/#{PoS}` //! //! - Init validator: No state requirements. -//! - `#{PoS}/address_raw_hash/{raw_hash}` (the raw_hash is the validator's -//! address in Tendermint) +//! - `#{PoS}/validator/#{validator}/address_raw_hash` (the raw_hash is the +//! validator's address in Tendermint) //! - `#{PoS}/validator_set` //! - `#{PoS}/validator/#{validator}/consensus_key` //! - `#{PoS}/validator/#{validator}/state` -//! - `#{PoS}/validator/#{validator}/total_deltas` -//! - `#{PoS}/validator/#{validator}/voting_power` +//! - `#{PoS}/validator/#{validator}/deltas` +//! - `#{PoS}/validator/#{validator}/commission_rate` +//! - `#{PoS}/validator/#{validator}/max_commission_rate_change` //! //! //! ## Invalidating transitions @@ -97,6 +93,7 @@ //! - add more invalid PoS changes //! - add arb invalid storage changes //! - add slashes +//! - add rewards use namada::ledger::pos::namada_proof_of_stake::init_genesis; use namada::proof_of_stake::parameters::PosParams; From b030fd8a1f88498f8377f051aaaeae11703cddf7 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 11:06:16 -0500 Subject: [PATCH 09/48] WIP - current changes, debug printouts, etc stuff --- apps/src/lib/client/tx.rs | 2 +- .../lib/node/ledger/shell/finalize_block.rs | 13 ++++++- apps/src/lib/node/ledger/shell/init_chain.rs | 2 + genesis/e2e-tests-single-node.toml | 2 +- tests/src/e2e/ledger_tests.rs | 14 +++---- wasm/checksums.json | 38 +++++++++---------- 6 files changed, 42 insertions(+), 29 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 0014833ca88..933c8dbf2f2 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -2407,7 +2407,7 @@ pub async fn submit_unbond(ctx: Context, args: args::Unbond) { let data = pos::Unbond { validator: validator.clone(), amount: args.amount, - source, + source: Some(bond_source.clone()), }; let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 648cccc14c3..89d8ee13721 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -63,7 +63,13 @@ where &mut self, req: shim::request::FinalizeBlock, ) -> Result { - // reset gas meter before we start + println!( + "\nFINALIZE BLOCK {} - NUM TXS = {}", + self.wl_storage.storage.block.height + 1, + req.txs.len() + ); + + // Reset the gas meter before we start self.gas_meter.reset(); let mut response = shim::response::FinalizeBlock::default(); @@ -77,6 +83,11 @@ where self.wl_storage.storage.epoch_update_tracker.0 && self.wl_storage.storage.epoch_update_tracker.1 == 2; + println!( + "BLOCK HEIGHT {} AND EPOCH {}, NEW EPOCH = {}", + height, current_epoch, new_epoch + ); + let current_epoch = self.wl_storage.storage.block.epoch; if new_epoch { diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index c77c902c9a2..654c32ce3dd 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -360,6 +360,8 @@ where .map(|validator| validator.pos_data), current_epoch, ); + println!("TOTAL NAM BALANCE = {}", total_nam_balance); + println!("TOTAL STAKED NAM BALANCE = {}\n", total_staked_nam_tokens); self.wl_storage .write( &total_supply_key(&staking_token_address()), diff --git a/genesis/e2e-tests-single-node.toml b/genesis/e2e-tests-single-node.toml index 91f51507891..73194ac9547 100644 --- a/genesis/e2e-tests-single-node.toml +++ b/genesis/e2e-tests-single-node.toml @@ -36,7 +36,7 @@ Bertha = 1000000 Christel = 1000000 "Christel.public_key" = 100 Daewon = 1000000 -faucet = 9223372036854 +faucet = 9223372036 "faucet.public_key" = 100 "validator-0.public_key" = 100 diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 113a42acabd..d27fb25db4b 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -1821,7 +1821,7 @@ fn pos_bonds() -> Result<()> { "--validator", "validator-0", "--amount", - "100", + "10000.0", "--gas-amount", "0", "--gas-limit", @@ -1844,7 +1844,7 @@ fn pos_bonds() -> Result<()> { "--source", BERTHA, "--amount", - "200", + "5000.0", "--gas-amount", "0", "--gas-limit", @@ -1864,7 +1864,7 @@ fn pos_bonds() -> Result<()> { "--validator", "validator-0", "--amount", - "51", + "5100.0", "--gas-amount", "0", "--gas-limit", @@ -1876,7 +1876,7 @@ fn pos_bonds() -> Result<()> { ]; let mut client = run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; - client.exp_string("Amount 51 withdrawable starting from epoch ")?; + client.exp_string("Amount 5100 withdrawable starting from epoch ")?; client.assert_success(); // 5. Submit an unbond of the delegation @@ -1887,7 +1887,7 @@ fn pos_bonds() -> Result<()> { "--source", BERTHA, "--amount", - "32", + "3200.", "--gas-amount", "0", "--gas-limit", @@ -1898,7 +1898,7 @@ fn pos_bonds() -> Result<()> { &validator_one_rpc, ]; let mut client = run!(test, Bin::Client, tx_args, Some(40))?; - let expected = "Amount 32 withdrawable starting from epoch "; + let expected = "Amount 3200 withdrawable starting from epoch "; let (_unread, matched) = client.exp_regex(&format!("{expected}.*\n"))?; let epoch_raw = matched .trim() @@ -1920,7 +1920,7 @@ fn pos_bonds() -> Result<()> { epoch, delegation_withdrawable_epoch ); let start = Instant::now(); - let loop_timeout = Duration::new(20, 0); + let loop_timeout = Duration::new(40, 0); loop { if Instant::now().duration_since(start) > loop_timeout { panic!( diff --git a/wasm/checksums.json b/wasm/checksums.json index 0b0fcc38125..8bc0f653f3d 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.4461350dfdd62e2339c3598d397deb082b6929d262e74a9c17cedecdec37a82b.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.0c2b17241055cc64a8a4b2fa6037467988fd8225efb935811af0a05e8986b9bf.wasm", - "tx_ibc.wasm": "tx_ibc.6f145a44b918adefcc4b845ff8ac485c06c3cc84e12c17bcba86346ecf654933.wasm", - "tx_init_account.wasm": "tx_init_account.da0d005c0b44df80cd2fd286bfbeac9049a3bb2d9b153d0eae90f14129de396a.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.d24f1f0e66ec16c791710f6a4ec9b9c203e9584927b2554020682427a5845fce.wasm", - "tx_init_validator.wasm": "tx_init_validator.9b12230c792dfc841b6df1ae115bacd4c673c70fbc21f2153437edab1a774f46.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.fa346fb4cff11182d137b7f702a98bde2d2c487323d2786fa0519b048f23a08b.wasm", - "tx_transfer.wasm": "tx_transfer.76b6662c2a46e88161c6d28ef1b9c28cd1099388f309962986f12f84db4a309e.wasm", - "tx_unbond.wasm": "tx_unbond.0e172de011029b3d8cbbc801e0dc79a36b14f50862499e9f61d33309d9f7b0f6.wasm", - "tx_update_vp.wasm": "tx_update_vp.4f8e976b9693dc9de243daa7b67b65f1128a1c80816f6ccdd794d28440354668.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.43e81fe4d0d9dd115f85234c49ab65b931012d129bdbfed908bb38486f4d246a.wasm", - "tx_withdraw.wasm": "tx_withdraw.df4a51c5b4ae8422b08dd8875ee9d48743f9c2b59f2405ffa76807b7fb4ec854.wasm", - "vp_implicit.wasm": "vp_implicit.72284d90dd9935f5014a8b20f84ed3792fc199a5c3d42b1df426af91adf57e2b.wasm", - "vp_masp.wasm": "vp_masp.ae02c5d2ed5d9ea5302729e29dac2ca6beb8ae771f55672e56706ef3b93840f2.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.ae294e23903c9f8d44c402bddd7d9266280450661c197233eddfe0978bcea049.wasm", - "vp_token.wasm": "vp_token.d9cada902a86fdb55f346438eb5970357b7dbcbbdbf3b7579a20eb702676dde3.wasm", - "vp_user.wasm": "vp_user.2682693d7d6b2a0001452bb3393334ab02bb1776cdca1af04e669999c3763150.wasm", - "vp_validator.wasm": "vp_validator.2efe83475d379123cf26bf7bdb8089e626edb8a8b47a5fc1d8529ed2e20875ce.wasm" -} \ No newline at end of file + "tx_bond.wasm": "tx_bond.d721788c7523275d9ecfbad72a133d3d0e7f437614dd0acaf6e256311e1fbac0.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.c2f8774276c84660c1dd0ce6a35538fdf33071bfa21b6cc96b8aa6e255899375.wasm", + "tx_ibc.wasm": "tx_ibc.7c85fcd12b81a7c49ccd265ca0606370ef5a349161010fea06852f6a7b537812.wasm", + "tx_init_account.wasm": "tx_init_account.7476d8b93ddfe8eeb7c74f7432f4a4f3346f120c58bce26eb03df817bb97cafa.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.f695ba92c0667b2575a3650f7ae403736691b0c7e340d7a4f11e0f8f1733368d.wasm", + "tx_init_validator.wasm": "tx_init_validator.991f79a941f3b04f7938a29ecdd0df11d0e94d8a3a43723f5455c58c36b9b781.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.0e0ae581558b1f08392eaecbb0f57a83646a0f88bbeca51e93a67a7a5fd9a693.wasm", + "tx_transfer.wasm": "tx_transfer.bd02d028d7d712ce8433da5327beb390a401fc14c425180542ae09858bc35967.wasm", + "tx_unbond.wasm": "tx_unbond.90d4ca02ed055405751305af028c8562d7cfdaa12be2a74b1c77bc183abd7ce6.wasm", + "tx_update_vp.wasm": "tx_update_vp.b9cb2b4b366de849385ff511b30bc5c9a49651007a7183ca46b8eaa43269e02c.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.f3ca9b0f6a4bee8c7f5f76fae84a2137861718b043d51ecfe33f0a004bb7debe.wasm", + "tx_withdraw.wasm": "tx_withdraw.d56bb112763e08ccc67b30005eeca14ec7f92f415c66232a7f46c5290c9a4909.wasm", + "vp_implicit.wasm": "vp_implicit.e4580c1644008b2721066ee57a32961f539c824f76b39df4246ffe0f112fc1f6.wasm", + "vp_masp.wasm": "vp_masp.a8d288a9b5d20026077103d4aec48ceb79cb964eb7170ba5e6becdda50bb4201.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.c6b4b606805ea213f9000a1872e55963710aafb1c24c17a685c26570dcafaa20.wasm", + "vp_token.wasm": "vp_token.ddc30091200e1dc28c1fd359017c87c822b733341cae268811694ce3d84e1186.wasm", + "vp_user.wasm": "vp_user.945dcf0c530ced6237c28ab9d0f4896e0d409ec7e6075dedc3b531cdd920e6fe.wasm", + "vp_validator.wasm": "vp_validator.fa04ed85352df83008afccadf058a3dc30956d0646f32221b458d6ea6d14b915.wasm" +} From a41952f2a0c72ba9d5baeb2d273b940c5d14189b Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 12:26:42 -0500 Subject: [PATCH 10/48] fix e2e test ibc --- vp_prelude/src/token.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/vp_prelude/src/token.rs b/vp_prelude/src/token.rs index 53c9f583234..7d0a695b20a 100644 --- a/vp_prelude/src/token.rs +++ b/vp_prelude/src/token.rs @@ -20,7 +20,12 @@ pub fn vp( ) -> VpResult { let mut change: Change = 0; for key in keys_touched.iter() { - match token::is_balance_key(token, key) { + let owner: Option<&Address> = token::is_balance_key(token, key) + .or_else(|| { + token::is_multitoken_balance_key(token, key).map(|a| a.1) + }); + + match owner { None => { if token::is_total_supply_key(key, token) { // check if total supply is changed, which it should never From a13f673316ce86a7e6f5b9948ba7b53d88a7e644 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 14:40:05 -0500 Subject: [PATCH 11/48] upgrade total token supply tracking and balance tracking at genesis --- apps/src/lib/node/ledger/shell/init_chain.rs | 58 ++++++++++---------- core/src/ledger/storage_api/token.rs | 21 ++++++- proof_of_stake/src/lib.rs | 3 - 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 654c32ce3dd..f95a6d6bae8 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -7,9 +7,11 @@ use namada::core::ledger::testnet_pow; use namada::ledger::parameters::storage::get_staked_ratio_key; use namada::ledger::parameters::Parameters; use namada::ledger::pos::{into_tm_voting_power, staking_token_address}; +use namada::ledger::storage_api::token::{ + credit_tokens, read_balance, read_total_supply, +}; use namada::ledger::storage_api::StorageWrite; use namada::types::key::*; -use namada::types::token::total_supply_key; use rust_decimal::Decimal; #[cfg(not(feature = "dev"))] use sha2::{Digest, Sha256}; @@ -239,7 +241,6 @@ where } // Initialize genesis token accounts - let mut total_nam_balance = token::Amount::default(); for genesis::TokenAccount { address, vp_code_path, @@ -274,17 +275,12 @@ where .unwrap(); for (owner, amount) in balances { - if address == staking_token_address() { - total_nam_balance += amount; - } - self.wl_storage - .write(&token::balance_key(&address, &owner), amount) + credit_tokens(&mut self.wl_storage, &address, &owner, amount) .unwrap(); } } // Initialize genesis validator accounts - let mut total_staked_nam_tokens = token::Amount::default(); for validator in &genesis.validators { let vp_code = vp_code_cache.get_or_insert_with( validator.validator_vp_code_path.clone(), @@ -321,19 +317,15 @@ where .expect("Unable to set genesis user public key"); // Balances - total_staked_nam_tokens += validator.pos_data.tokens; - total_nam_balance += - validator.pos_data.tokens + validator.non_staked_balance; // Account balance (tokens not staked in PoS) - self.wl_storage - .write( - &token::balance_key( - &self.wl_storage.storage.native_token, - addr, - ), - validator.non_staked_balance, - ) - .expect("Unable to set genesis balance"); + credit_tokens( + &mut self.wl_storage, + &staking_token_address(), + addr, + validator.non_staked_balance, + ) + .unwrap(); + self.wl_storage .write(&protocol_pk_key(addr), &validator.protocol_key) .expect("Unable to set genesis user protocol public key"); @@ -360,21 +352,27 @@ where .map(|validator| validator.pos_data), current_epoch, ); - println!("TOTAL NAM BALANCE = {}", total_nam_balance); - println!("TOTAL STAKED NAM BALANCE = {}\n", total_staked_nam_tokens); - self.wl_storage - .write( - &total_supply_key(&staking_token_address()), - total_nam_balance, - ) - .expect("unable to set total NAM balance in storage"); + + let total_nam = + read_total_supply(&self.wl_storage, &staking_token_address()) + .unwrap(); + // At this stage in the chain genesis, the PoS address balance is the + // same as the number of staked tokens + let total_staked_nam = read_balance( + &self.wl_storage, + &staking_token_address(), + &address::POS, + ) + .unwrap(); + + println!("READ TOTAL NAM BALANCE = {}", total_nam); + println!("READ TOTAL STAKED NAM BALANCE = {}\n", total_staked_nam); // Set the ratio of staked to total NAM tokens in the parameters storage self.wl_storage .write( &get_staked_ratio_key(), - Decimal::from(total_staked_nam_tokens) - / Decimal::from(total_nam_balance), + Decimal::from(total_staked_nam) / Decimal::from(total_nam), ) .expect("unable to set staked ratio of NAM in storage"); diff --git a/core/src/ledger/storage_api/token.rs b/core/src/ledger/storage_api/token.rs index 3ee3b84f352..dcc55b43455 100644 --- a/core/src/ledger/storage_api/token.rs +++ b/core/src/ledger/storage_api/token.rs @@ -20,6 +20,19 @@ where Ok(balance) } +/// Read the total network supply of a given token. +pub fn read_total_supply( + storage: &S, + token: &Address, +) -> storage_api::Result +where + S: StorageRead, +{ + let key = token::total_supply_key(token); + let balance = storage.read::(&key)?.unwrap_or_default(); + Ok(balance) +} + /// Transfer `token` from `src` to `dest`. Returns an `Err` if `src` has /// insufficient balance or if the transfer the `dest` would overflow (This can /// only happen if the total supply does't fit in `token::Amount`). @@ -68,5 +81,11 @@ where { let key = token::balance_key(token, dest); let new_balance = read_balance(storage, token, dest)? + amount; - storage.write(&key, new_balance) + storage.write(&key, new_balance)?; + + let total_supply_key = token::total_supply_key(token); + let current_supply = storage + .read::(&total_supply_key)? + .unwrap_or_default(); + storage.write(&total_supply_key, current_supply + amount) } diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 3512ae294c2..15bad58aeaa 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -333,7 +333,6 @@ where write_pos_params(storage, params.clone())?; let mut total_bonded = token::Amount::default(); - let mut total_balance = token::Amount::default(); consensus_validator_set_handle().init(storage, current_epoch)?; below_capacity_validator_set_handle().init(storage, current_epoch)?; validator_set_positions_handle().init(storage, current_epoch)?; @@ -389,8 +388,6 @@ where current_epoch, )?; } - // TODO: figure out why I had this here in #714 or if its right here - total_balance += total_bonded; // Write total deltas to storage total_deltas_handle().init_at_genesis( From 6b0dc7fbf5b7f7eece6b44ce667541faccb45fd3 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 17:45:12 -0500 Subject: [PATCH 12/48] fix e2e::ledger_tests::proposal_submission --- tests/src/e2e/ledger_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index d27fb25db4b..395bb3a1f02 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -2417,7 +2417,7 @@ fn proposal_submission() -> Result<()> { let parameters = ParametersConfig { epochs_per_year: epochs_per_year_from_min_duration(1), max_proposal_bytes: Default::default(), - min_num_of_blocks: 1, + min_num_of_blocks: 2, max_expected_time_per_block: 1, vp_whitelist: Some(get_all_wasms_hashes( &working_dir, From 6fb79a93f04927443d7b2b723fba7afbdf5b5a24 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 20:58:35 -0500 Subject: [PATCH 13/48] fix e2e::ledger_tests::pos_init_validator error --- proof_of_stake/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 15bad58aeaa..0a5a8aaf104 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -2553,7 +2553,7 @@ where let mut signer_set: HashSet
= HashSet::new(); let mut total_signing_stake: u64 = 0; for vote in votes.iter() { - if !vote.signed_last_block { + if !vote.signed_last_block || vote.validator_vp == 0 { continue; } let tm_raw_hash_string = @@ -2609,6 +2609,14 @@ where address, ) = validator?; + // TODO: + // When below-threshold validator set is added, this shouldn't be needed + // anymore since some minimal stake will be required to be in at least + // the consensus set + if stake == token::Amount::default() { + continue; + } + let mut rewards_frac = Decimal::default(); let stake: Decimal = u64::from(stake).into(); // println!( From 5b818a254a76a3eea6c52dd6f28963d76890a664 Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 21 Feb 2023 00:50:22 +0100 Subject: [PATCH 14/48] fix e2e::ledger_tests::double_signing_gets_slashed --- apps/src/lib/node/ledger/shims/abcipp_shim.rs | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim.rs b/apps/src/lib/node/ledger/shims/abcipp_shim.rs index 4b77a07235b..f45a95a3066 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim.rs @@ -96,35 +96,7 @@ impl AbcippShim { while let Ok((req, resp_sender)) = self.shell_recv.recv() { let resp = match req { Req::ProcessProposal(proposal) => { - #[cfg(not(feature = "abcipp"))] - { - println!("\nRECEIVED REQUEST PROCESSPROPOSAL"); - if !proposal.proposer_address.is_empty() { - let tm_raw_hash_string = tm_raw_hash_to_string( - proposal.proposer_address.clone(), - ); - let native_proposer_address = - find_validator_by_raw_hash( - &self.service.wl_storage, - tm_raw_hash_string, - ) - .unwrap() - .expect( - "Unable to find native validator address \ - of block proposer from tendermint raw \ - hash", - ); - println!( - "BLOCK PROPOSER (PROCESSPROPOSAL): {}", - native_proposer_address - ); - write_current_block_proposer_address( - &mut self.service.wl_storage, - native_proposer_address, - ) - .unwrap(); - } - } + println!("\nRECEIVED REQUEST PROCESSPROPOSAL"); self.service .call(Request::ProcessProposal(proposal)) .map_err(Error::from) @@ -163,6 +135,33 @@ impl AbcippShim { #[cfg(not(feature = "abcipp"))] Req::BeginBlock(block) => { println!("RECEIVED REQUEST BEGINBLOCK"); + if let Some(header) = block.header.clone() { + if !header.proposer_address.is_empty() { + let tm_raw_hash_string = tm_raw_hash_to_string( + header.proposer_address.clone(), + ); + let native_proposer_address = + find_validator_by_raw_hash( + &self.service.wl_storage, + tm_raw_hash_string, + ) + .unwrap() + .expect( + "Unable to find native validator address \ + of block proposer from tendermint raw \ + hash", + ); + println!( + "BLOCK PROPOSER (BEGINBLOCK): {}", + native_proposer_address + ); + write_current_block_proposer_address( + &mut self.service.wl_storage, + native_proposer_address, + ) + .unwrap(); + } + } // we save this data to be forwarded to finalize later self.begin_block_request = Some(block); Ok(Resp::BeginBlock(Default::default())) From 59421c29aa3939ae68f199921154b11d09967d41 Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 21 Feb 2023 01:28:48 +0100 Subject: [PATCH 15/48] clean up documentation and print-outs --- .../lib/node/ledger/shell/finalize_block.rs | 6 -- proof_of_stake/src/lib.rs | 71 +------------------ tests/src/e2e/ledger_tests.rs | 36 +--------- 3 files changed, 2 insertions(+), 111 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 89d8ee13721..01050a034f1 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -403,18 +403,13 @@ where // (n-1 if we are in the process of finalizing n right now). match read_last_block_proposer_address(&self.wl_storage)? { Some(proposer_address) => { - println!("FOUND LAST BLOCK PROPOSER"); if new_epoch { - println!("APPLYING INFLATION"); self.apply_inflation( current_epoch, &proposer_address, &req.votes, )?; } else { - // TODO: watch out because this is likely not using the - // proper block proposer address - println!("LOGGING BLOCK REWARDS (NOT NEW EPOCH)"); namada_proof_of_stake::log_block_rewards( &mut self.wl_storage, current_epoch, @@ -459,7 +454,6 @@ where } } None => { - println!("NO BLOCK PROPOSER FOUND SET IN STORAGE"); #[cfg(feature = "abcipp")] if req.votes.is_empty() && !req.proposer_address.is_empty() { // Get proposer address from storage based on the consensus diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 0a5a8aaf104..157ed7895c3 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -582,7 +582,6 @@ pub fn read_validator_stake( where S: StorageRead, { - // println!("\nREAD VALIDATOR STAKE AT EPOCH {}", epoch); let handle = validator_deltas_handle(validator); let amount = handle .get_sum(storage, epoch, params)? @@ -811,7 +810,6 @@ where S: StorageRead + StorageWrite, { let amount = amount.change(); - println!("BONDING TOKEN AMOUNT {}\n", amount); let params = read_pos_params(storage)?; let pipeline_epoch = current_epoch + params.pipeline_len; if let Some(source) = source { @@ -853,26 +851,16 @@ where // TODO: ensure that this method of checking if the bond exists works if !bond_handle.get_data_handler().is_empty(storage)? { - println!("BOND EXISTS TO BEGIN WITH\n"); let cur_remain = bond_handle .get_delta_val(storage, current_epoch + offset, ¶ms)? .unwrap_or_default(); - // println!( - // "Bond remain at offset epoch {}: {}\n", - // current_epoch + offset, - // cur_remain - // ); bond_handle.set(storage, cur_remain + amount, current_epoch, offset)?; } else { - println!("BOND DOESNT EXIST YET\n"); bond_handle.init(storage, amount, current_epoch, offset)?; } - println!("\nUPDATING VALIDATOR SET NOW\n"); - // Update the validator set update_validator_set(storage, ¶ms, validator, amount, current_epoch)?; - println!("UPDATING VALIDATOR DELTAS NOW\n"); // Update the validator and total deltas update_validator_deltas( @@ -893,7 +881,6 @@ where source, &ADDRESS, )?; - println!("END BOND_TOKENS\n"); Ok(()) } @@ -1012,9 +999,6 @@ where return Ok(()); } let epoch = current_epoch + params.pipeline_len; - println!( - "Update epoch for validator set: {epoch}, validator: {validator}\n" - ); let consensus_validator_set = consensus_validator_set_handle(); let below_capacity_validator_set = below_capacity_validator_set_handle(); @@ -1026,8 +1010,6 @@ where let tokens_pre = read_validator_stake(storage, params, validator, epoch)? .unwrap_or_default(); - // println!("VALIDATOR STAKE BEFORE UPDATE: {}\n", tokens_pre); - let tokens_post = tokens_pre.change() + token_change; // TODO: handle overflow or negative vals perhaps with TryFrom let tokens_post = token::Amount::from_change(tokens_post); @@ -1047,7 +1029,6 @@ where let consensus_vals_pre = consensus_val_handle.at(&tokens_pre); if consensus_vals_pre.contains(storage, &position)? { - println!("\nTARGET VALIDATOR IS CONSENSUS\n"); // It's initially consensus let val_address = consensus_vals_pre.get(storage, &position)?; assert!(val_address.is_some()); @@ -1061,7 +1042,6 @@ where )?; if tokens_post < max_below_capacity_validator_amount { - println!("NEED TO SWAP VALIDATORS\n"); // Place the validator into the below-capacity set and promote the // lowest position max below-capacity validator. @@ -1091,7 +1071,6 @@ where validator, )?; } else { - println!("VALIDATOR REMAINS IN CONSENSUS SET\n"); // The current validator should remain in the consensus set - place // it into a new position insert_validator_into_set( @@ -1364,12 +1343,6 @@ where S: StorageRead + StorageWrite, { let next_position = find_next_position(handle, storage)?; - println!( - "Inserting validator {} into position {:?} at epoch {}\n", - address.clone(), - next_position.clone(), - epoch.clone() - ); handle.insert(storage, next_position, address.clone())?; validator_set_positions_handle().at(epoch).insert( storage, @@ -1391,14 +1364,8 @@ where S: StorageRead + StorageWrite, { let amount = amount.change(); - println!("UNBONDING TOKEN AMOUNT {amount} at epoch {current_epoch}\n"); let params = read_pos_params(storage)?; let pipeline_epoch = current_epoch + params.pipeline_len; - println!( - "Current validator stake at pipeline: {}", - read_validator_stake(storage, ¶ms, validator, pipeline_epoch)? - .unwrap_or_default() - ); if let Some(source) = source { if source != validator @@ -1527,7 +1494,6 @@ where // ) // } - println!("Updating validator set for unbonding"); // Update the validator set at the pipeline offset update_validator_set(storage, ¶ms, validator, -amount, current_epoch)?; @@ -1633,7 +1599,6 @@ pub fn withdraw_tokens( where S: StorageRead + StorageWrite, { - // println!("WITHDRAWING TOKENS IN EPOCH {current_epoch}\n"); let params = read_pos_params(storage)?; let source = source.unwrap_or(validator); @@ -1648,7 +1613,6 @@ where // TODO: use `find_unbonds` let unbond_iter = unbond_handle.iter(storage)?; for unbond in unbond_iter { - // println!("\nUNBOND ITER\n"); let ( NestedSubKey::Data { key: withdraw_epoch, @@ -1693,7 +1657,6 @@ where // Remove the unbond data from storage for (withdraw_epoch, start_epoch) in unbonds_to_remove { - // println!("Remove ({}, {}) from unbond\n", end_epoch, start_epoch); unbond_handle .at(&withdraw_epoch) .remove(storage, &start_epoch)?; @@ -2008,17 +1971,11 @@ pub fn validator_set_update_tendermint( .filter_map(|validator| { let ( NestedSubKey::Data { - key: cur_stake, + key: _, nested_sub_key: _, }, address, ) = validator.unwrap(); - let cur_stake = token::Amount::from(cur_stake); - - println!( - "BELOW-CAPACITY VALIDATOR ADDRESS {}, STAKE {}\n", - address, cur_stake - ); if !prev_below_capacity_vals.is_empty(storage).unwrap() { // Look up the previous state @@ -2507,32 +2464,6 @@ where let epoch: Epoch = epoch.into(); let params = read_pos_params(storage)?; let consensus_validators = consensus_validator_set_handle().at(&epoch); - // dbg!(&epoch); - - // let validator_set = self.read_validator_set(); - // dbg!(&validator_set); - // let validators = validator_set.get(epoch).unwrap(); - // let pos_params = self.read_pos_params(); - - // println!( - // "VALIDATOR SET OF EPOCH {} LAST UPDATE = {}, LEN = {}:", - // epoch, - // validator_set.last_update(), - // validator_set.data.len() - // ); - // for val in &validators.active { - // println!("STAKE: {}, ADDRESS: {}", val.bonded_stake, val.address); - // let ck = self - // .read_validator_consensus_key(&val.address) - // .unwrap() - // .get(epoch) - // .unwrap() - // .to_owned(); - // let hash_string1 = tm_consensus_key_raw_hash(&ck); - // let bytes1 = HEXUPPER.decode(hash_string1.as_bytes()).unwrap(); - // dbg!(bytes1); - // } - // dbg!(votes); // Get total stake of the consensus validator set // TODO: this will need to account for rewards products? diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 395bb3a1f02..f35be5898d4 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -2001,8 +2001,6 @@ fn pos_rewards() -> Result<()> { None, )?; - println!("\nFINISHED SETUP\n"); - // 1. Run 3 genesis validator ledger nodes let mut validator_0 = run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; @@ -2026,7 +2024,7 @@ fn pos_rewards() -> Result<()> { let validator_zero_rpc = get_actor_rpc(&test, &Who::Validator(0)); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(1)); let validator_two_rpc = get_actor_rpc(&test, &Who::Validator(2)); - println!("\nDBG0\n"); + // Submit a delegation from Bertha to validator-0 let tx_args = vec![ "bond", @@ -2045,7 +2043,6 @@ fn pos_rewards() -> Result<()> { "--ledger-address", &validator_zero_rpc, ]; - println!("\nDBG1\n"); let mut client = run!(test, Bin::Client, tx_args, Some(40))?; client.exp_string("Transaction is valid.")?; @@ -2106,7 +2103,6 @@ fn pos_rewards() -> Result<()> { run_as!(test, Who::Validator(2), Bin::Client, tx_args, Some(40))?; client.exp_string("Transaction is valid.")?; client.assert_success(); - println!("\nDBG7\n"); // Wait some epochs let epoch = get_epoch(&test, &validator_zero_rpc)?; @@ -2455,8 +2451,6 @@ fn proposal_submission() -> Result<()> { let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - println!("\nDELEGATING SOME TOKENS\n"); - // 1.1 Delegate some token let tx_args = vec![ "bond", @@ -2479,8 +2473,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("Transaction is valid.")?; client.assert_success(); - println!("\nSUBMIT VALID PROPOSAL FROM ALBERT\n"); - // 2. Submit valid proposal let albert = find_address(&test, ALBERT)?; let valid_proposal_json_path = prepare_proposal_data(&test, albert); @@ -2497,8 +2489,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("Transaction is valid.")?; client.assert_success(); - println!("\nQUERY ALBERT'S VALID PROPOSAL\n"); - // 3. Query the proposal let proposal_query_args = vec![ "query-proposal", @@ -2512,8 +2502,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("Proposal: 0")?; client.assert_success(); - println!("\nQUERY ALBERT TOKENS\n"); - // 4. Query token balance proposal author (submitted funds) let query_balance_args = vec![ "balance", @@ -2529,8 +2517,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("NAM: 999500")?; client.assert_success(); - println!("\nQUERY GOV ADDRESS TOKENS\n"); - // 5. Query token balance governance let query_balance_args = vec![ "balance", @@ -2546,8 +2532,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("NAM: 500")?; client.assert_success(); - println!("\nSUBMIT INVALID PROPOSAL FROM ALBERT\n"); - // 6. Submit an invalid proposal // proposal is invalid due to voting_end_epoch - voting_start_epoch < 3 let albert = find_address(&test, ALBERT)?; @@ -2607,8 +2591,6 @@ fn proposal_submission() -> Result<()> { )?; client.assert_failure(); - println!("\nCHECK INVALID PROPOSAL WAS NOT ACCEPTED\n"); - // 7. Check invalid proposal was not accepted let proposal_query_args = vec![ "query-proposal", @@ -2622,8 +2604,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("No valid proposal was found with id 1")?; client.assert_success(); - println!("\nQUERY ALBERT TOKENS\n"); - // 8. Query token balance (funds shall not be submitted) let query_balance_args = vec![ "balance", @@ -2639,8 +2619,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("NAM: 999500")?; client.assert_success(); - println!("\nSEND YAY VOTE FROM VALIDATOR-0\n"); - // 9. Send a yay vote from a validator let mut epoch = get_epoch(&test, &validator_one_rpc).unwrap(); while epoch.0 <= 13 { @@ -2670,8 +2648,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("Transaction is valid.")?; client.assert_success(); - println!("\nSEND NAY VOTE FROM BERTHA\n"); - let submit_proposal_vote_delagator = vec![ "vote-proposal", "--proposal-id", @@ -2689,8 +2665,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("Transaction is valid.")?; client.assert_success(); - println!("\nSEND YAY VOTE FROM ALBERT\n"); - // 10. Send a yay vote from a non-validator/non-delegator user let submit_proposal_vote = vec![ "vote-proposal", @@ -2717,8 +2691,6 @@ fn proposal_submission() -> Result<()> { epoch = get_epoch(&test, &validator_one_rpc).unwrap(); } - println!("\nQUERY PROPOSAL AND CHECK RESULT\n"); - let query_proposal = vec![ "query-proposal-result", "--proposal-id", @@ -2738,8 +2710,6 @@ fn proposal_submission() -> Result<()> { epoch = get_epoch(&test, &validator_one_rpc).unwrap(); } - println!("\nQUERY ALBERT TOKENS\n"); - let query_balance_args = vec![ "balance", "--owner", @@ -2754,8 +2724,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("NAM: 1000000")?; client.assert_success(); - println!("\nQUERY GOV ADDRESS TOKENS\n"); - // 13. Check if governance funds are 0 let query_balance_args = vec![ "balance", @@ -2771,8 +2739,6 @@ fn proposal_submission() -> Result<()> { client.exp_string("NAM: 0")?; client.assert_success(); - println!("\nQUERY PROTOCOL PARAMS\n"); - // // 14. Query parameters let query_protocol_parameters = vec![ "query-protocol-parameters", From 27c44f4af35b1da29593e023b86948716227a08b Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 21 Feb 2023 14:49:31 +0100 Subject: [PATCH 16/48] redo block proposer storage in abciplus mode --- .../lib/node/ledger/shell/finalize_block.rs | 94 +++++-------------- apps/src/lib/node/ledger/shell/mod.rs | 1 - apps/src/lib/node/ledger/shims/abcipp_shim.rs | 32 ------- .../node/ledger/shims/abcipp_shim_types.rs | 3 +- proof_of_stake/src/lib.rs | 39 ++------ proof_of_stake/src/storage.rs | 13 --- 6 files changed, 35 insertions(+), 147 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 01050a034f1..ca77a8c0786 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -12,10 +12,8 @@ use namada::ledger::pos::{ }; use namada::ledger::protocol; use namada::ledger::storage_api::StorageRead; -#[cfg(feature = "abcipp")] -use namada::proof_of_stake::find_validator_by_raw_hash; use namada::proof_of_stake::{ - delegator_rewards_products_handle, read_current_block_proposer_address, + delegator_rewards_products_handle, find_validator_by_raw_hash, read_last_block_proposer_address, read_pos_params, read_total_stake, read_validator_stake, rewards_accumulator_handle, validator_commission_rate_handle, validator_rewards_products_handle, @@ -88,8 +86,6 @@ where height, current_epoch, new_epoch ); - let current_epoch = self.wl_storage.storage.block.epoch; - if new_epoch { namada::ledger::storage::update_allowed_conversions( &mut self.wl_storage, @@ -403,6 +399,7 @@ where // (n-1 if we are in the process of finalizing n right now). match read_last_block_proposer_address(&self.wl_storage)? { Some(proposer_address) => { + println!("FOUND LAST BLOCK PROPOSER"); if new_epoch { self.apply_inflation( current_epoch, @@ -418,43 +415,24 @@ where ) .unwrap(); } - #[cfg(feature = "abcipp")] - { - // TODO: better error handling that converts - // storage_api::Error -> shell::Error - let tm_raw_hash_string = - tm_raw_hash_to_string(req.proposer_address); - let native_proposer_address = find_validator_by_raw_hash( - &self.wl_storage, - tm_raw_hash_string, - ) - .unwrap() - .expect( - "Unable to find native validator address of block \ - proposer from tendermint raw hash", - ); - write_last_block_proposer_address( - &mut self.wl_storage, - native_proposer_address, - )?; - } - - #[cfg(not(feature = "abcipp"))] - { - let cur_proposer = - read_current_block_proposer_address(&self.wl_storage)? - .expect( - "Should have found the current block proposer \ - address", - ); - write_last_block_proposer_address( - &mut self.wl_storage, - cur_proposer, - )?; - } + let tm_raw_hash_string = + tm_raw_hash_to_string(req.proposer_address); + let native_proposer_address = find_validator_by_raw_hash( + &self.wl_storage, + tm_raw_hash_string, + )? + .expect( + "Unable to find native validator address of block \ + proposer from tendermint raw hash", + ); + write_last_block_proposer_address( + &mut self.wl_storage, + native_proposer_address, + )?; } None => { - #[cfg(feature = "abcipp")] + println!("CANT FIND LAST BLOCK PROPOSER"); + if req.votes.is_empty() && !req.proposer_address.is_empty() { // Get proposer address from storage based on the consensus // key hash @@ -474,21 +452,6 @@ where native_proposer_address, )?; } - #[cfg(not(feature = "abcipp"))] - { - let proposer = - read_current_block_proposer_address(&self.wl_storage)?; - // .expect( - // "Current block proposer should always be set in \ - // ProcessProposal", - // ); - if let Some(proposer) = proposer { - write_last_block_proposer_address( - &mut self.wl_storage, - proposer, - )?; - } - } } } @@ -844,7 +807,6 @@ mod test_finalize_block { read_consensus_validator_set_addresses_with_stake, rewards_accumulator_handle, validator_consensus_key_handle, validator_rewards_products_handle, - write_current_block_proposer_address, }; use namada::types::governance::ProposalVote; use namada::types::storage::Epoch; @@ -1397,7 +1359,7 @@ mod test_finalize_block { // FINALIZE BLOCK 1. Tell Namada that val1 is the block proposer. We // won't receive votes from TM since we receive votes at a 1-block // delay, so votes will be empty here - next_block_for_inflation(&mut shell, &val1.address, vec![]); + next_block_for_inflation(&mut shell, pkh1.clone(), vec![]); assert!( rewards_accumulator_handle() .is_empty(&shell.wl_storage) @@ -1407,7 +1369,7 @@ mod test_finalize_block { // FINALIZE BLOCK 2. Tell Namada that val1 is the block proposer. // Include votes that correspond to block 1. Make val2 the next block's // proposer. - next_block_for_inflation(&mut shell, &val2.address, votes.clone()); + next_block_for_inflation(&mut shell, pkh2.clone(), votes.clone()); assert!(rewards_prod_1.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_2.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_3.is_empty(&shell.wl_storage).unwrap()); @@ -1430,7 +1392,7 @@ mod test_finalize_block { ); // FINALIZE BLOCK 3, with val1 as proposer for the next block. - next_block_for_inflation(&mut shell, &val1.address, votes); + next_block_for_inflation(&mut shell, pkh1.clone(), votes); assert!(rewards_prod_1.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_2.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_3.is_empty(&shell.wl_storage).unwrap()); @@ -1451,7 +1413,7 @@ mod test_finalize_block { // Now we don't receive a vote from val4. let votes = vec![ VoteInfo { - validator_address: pkh1, + validator_address: pkh1.clone(), validator_vp: u64::from(val1.bonded_stake), signed_last_block: true, }, @@ -1474,7 +1436,7 @@ mod test_finalize_block { // FINALIZE BLOCK 4. The next block proposer will be val1. Only val1, // val2, and val3 vote on this block. - next_block_for_inflation(&mut shell, &val1.address, votes.clone()); + next_block_for_inflation(&mut shell, pkh1.clone(), votes.clone()); assert!(rewards_prod_1.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_2.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_3.is_empty(&shell.wl_storage).unwrap()); @@ -1507,7 +1469,7 @@ mod test_finalize_block { get_rewards_acc(&shell.wl_storage), get_rewards_sum(&shell.wl_storage), ); - next_block_for_inflation(&mut shell, &val1.address, votes.clone()); + next_block_for_inflation(&mut shell, pkh1.clone(), votes.clone()); } assert!( rewards_accumulator_handle() @@ -1560,15 +1522,11 @@ mod test_finalize_block { fn next_block_for_inflation( shell: &mut TestShell, - next_proposer: &Address, + proposer_address: Vec, votes: Vec, ) { - write_current_block_proposer_address( - &mut shell.wl_storage, - next_proposer.clone(), - ) - .unwrap(); let req = FinalizeBlock { + proposer_address, votes, ..Default::default() }; diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index f5bd0d2540b..9d40d20760b 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -976,7 +976,6 @@ mod test_utils { }, byzantine_validators: vec![], txs: vec![], - #[cfg(feature = "abcipp")] proposer_address: vec![], votes: vec![], } diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim.rs b/apps/src/lib/node/ledger/shims/abcipp_shim.rs index f45a95a3066..6d007aff722 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim.rs @@ -5,15 +5,10 @@ use std::pin::Pin; use std::task::{Context, Poll}; use futures::future::FutureExt; -use namada::proof_of_stake::{ - find_validator_by_raw_hash, write_current_block_proposer_address, -}; use namada::types::address::Address; #[cfg(not(feature = "abcipp"))] use namada::types::hash::Hash; #[cfg(not(feature = "abcipp"))] -use namada::types::key::tm_raw_hash_to_string; -#[cfg(not(feature = "abcipp"))] use namada::types::storage::BlockHash; #[cfg(not(feature = "abcipp"))] use namada::types::transaction::hash_tx; @@ -135,33 +130,6 @@ impl AbcippShim { #[cfg(not(feature = "abcipp"))] Req::BeginBlock(block) => { println!("RECEIVED REQUEST BEGINBLOCK"); - if let Some(header) = block.header.clone() { - if !header.proposer_address.is_empty() { - let tm_raw_hash_string = tm_raw_hash_to_string( - header.proposer_address.clone(), - ); - let native_proposer_address = - find_validator_by_raw_hash( - &self.service.wl_storage, - tm_raw_hash_string, - ) - .unwrap() - .expect( - "Unable to find native validator address \ - of block proposer from tendermint raw \ - hash", - ); - println!( - "BLOCK PROPOSER (BEGINBLOCK): {}", - native_proposer_address - ); - write_current_block_proposer_address( - &mut self.service.wl_storage, - native_proposer_address, - ) - .unwrap(); - } - } // we save this data to be forwarded to finalize later self.begin_block_request = Some(block); Ok(Resp::BeginBlock(Default::default())) diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs index 45f4eb13dd0..f78bcc5339c 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs @@ -226,7 +226,6 @@ pub mod shim { pub header: Header, pub byzantine_validators: Vec, pub txs: Vec, - #[cfg(feature = "abcipp")] pub proposer_address: Vec, pub votes: Vec, } @@ -246,7 +245,6 @@ pub mod shim { }, byzantine_validators: req.byzantine_validators, txs: vec![], - #[cfg(feature = "abcipp")] proposer_address: req.proposer_address, votes: req .decided_last_commit @@ -288,6 +286,7 @@ pub mod shim { }, byzantine_validators: req.byzantine_validators, txs: vec![], + proposer_address: header.proposer_address, votes: req .last_commit_info .unwrap() diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 157ed7895c3..d049f470593 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -46,14 +46,14 @@ use parameters::PosParams; use rewards::PosRewardsCalculator; use rust_decimal::Decimal; use storage::{ - bonds_for_source_prefix, bonds_prefix, current_block_proposer_key, - get_validator_address_from_bond, into_tm_voting_power, is_bond_key, - is_unbond_key, is_validator_slashes_key, last_block_proposer_key, - mult_amount, mult_change_to_amount, num_consensus_validators_key, - params_key, slashes_prefix, unbonds_for_source_prefix, unbonds_prefix, - validator_address_raw_hash_key, validator_max_commission_rate_change_key, - BondDetails, BondsAndUnbondsDetail, BondsAndUnbondsDetails, - ReverseOrdTokenAmount, RewardsAccumulator, UnbondDetails, + bonds_for_source_prefix, bonds_prefix, get_validator_address_from_bond, + into_tm_voting_power, is_bond_key, is_unbond_key, is_validator_slashes_key, + last_block_proposer_key, mult_amount, mult_change_to_amount, + num_consensus_validators_key, params_key, slashes_prefix, + unbonds_for_source_prefix, unbonds_prefix, validator_address_raw_hash_key, + validator_max_commission_rate_change_key, BondDetails, + BondsAndUnbondsDetail, BondsAndUnbondsDetails, ReverseOrdTokenAmount, + RewardsAccumulator, UnbondDetails, }; use thiserror::Error; use types::{ @@ -510,29 +510,6 @@ where storage.write(&key, new_num) } -/// Read current block proposer address. -pub fn read_current_block_proposer_address( - storage: &S, -) -> storage_api::Result> -where - S: StorageRead, -{ - let key = current_block_proposer_key(); - storage.read(&key) -} - -/// Write current block proposer address. -pub fn write_current_block_proposer_address( - storage: &mut S, - address: Address, -) -> storage_api::Result<()> -where - S: StorageRead + StorageWrite, -{ - let key = current_block_proposer_key(); - storage.write(&key, address) -} - /// Read last block proposer address. pub fn read_last_block_proposer_address( storage: &S, diff --git a/proof_of_stake/src/storage.rs b/proof_of_stake/src/storage.rs index 3f7c0a04db9..87644772c99 100644 --- a/proof_of_stake/src/storage.rs +++ b/proof_of_stake/src/storage.rs @@ -32,7 +32,6 @@ const BELOW_CAPACITY_VALIDATOR_SET_STORAGE_KEY: &str = "below_capacity"; const TOTAL_DELTAS_STORAGE_KEY: &str = "total_deltas"; const VALIDATOR_SET_POSITIONS_KEY: &str = "validator_set_positions"; const LAST_BLOCK_PROPOSER_STORAGE_KEY: &str = "last_block_proposer"; -const CURRENT_BLOCK_PROPOSER_STORAGE_KEY: &str = "current_block_proposer"; const CONSENSUS_VALIDATOR_SET_ACCUMULATOR_STORAGE_KEY: &str = "validator_rewards_accumulator"; @@ -527,18 +526,6 @@ pub fn is_last_block_proposer_key(key: &Key) -> bool { matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] if addr == &ADDRESS && key == LAST_BLOCK_PROPOSER_STORAGE_KEY) } -/// Storage key for block proposer address of the current block. -pub fn current_block_proposer_key() -> Key { - Key::from(ADDRESS.to_db_key()) - .push(&CURRENT_BLOCK_PROPOSER_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - -/// Is storage key for block proposer address of the current block? -pub fn is_current_block_proposer_key(key: &Key) -> bool { - matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] if addr == &ADDRESS && key == CURRENT_BLOCK_PROPOSER_STORAGE_KEY) -} - /// Storage key for the consensus validator set rewards accumulator. pub fn consensus_validator_rewards_accumulator_key() -> Key { Key::from(ADDRESS.to_db_key()) From 0b2af59b995fa1429d8776a37658c1a5de2fb96c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 21 Feb 2023 00:20:04 +0000 Subject: [PATCH 17/48] [ci] wasm checksums update --- wasm/checksums.json | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/wasm/checksums.json b/wasm/checksums.json index 8bc0f653f3d..9a09d988b5b 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.d721788c7523275d9ecfbad72a133d3d0e7f437614dd0acaf6e256311e1fbac0.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.c2f8774276c84660c1dd0ce6a35538fdf33071bfa21b6cc96b8aa6e255899375.wasm", - "tx_ibc.wasm": "tx_ibc.7c85fcd12b81a7c49ccd265ca0606370ef5a349161010fea06852f6a7b537812.wasm", - "tx_init_account.wasm": "tx_init_account.7476d8b93ddfe8eeb7c74f7432f4a4f3346f120c58bce26eb03df817bb97cafa.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.f695ba92c0667b2575a3650f7ae403736691b0c7e340d7a4f11e0f8f1733368d.wasm", - "tx_init_validator.wasm": "tx_init_validator.991f79a941f3b04f7938a29ecdd0df11d0e94d8a3a43723f5455c58c36b9b781.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.0e0ae581558b1f08392eaecbb0f57a83646a0f88bbeca51e93a67a7a5fd9a693.wasm", - "tx_transfer.wasm": "tx_transfer.bd02d028d7d712ce8433da5327beb390a401fc14c425180542ae09858bc35967.wasm", - "tx_unbond.wasm": "tx_unbond.90d4ca02ed055405751305af028c8562d7cfdaa12be2a74b1c77bc183abd7ce6.wasm", - "tx_update_vp.wasm": "tx_update_vp.b9cb2b4b366de849385ff511b30bc5c9a49651007a7183ca46b8eaa43269e02c.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.f3ca9b0f6a4bee8c7f5f76fae84a2137861718b043d51ecfe33f0a004bb7debe.wasm", - "tx_withdraw.wasm": "tx_withdraw.d56bb112763e08ccc67b30005eeca14ec7f92f415c66232a7f46c5290c9a4909.wasm", - "vp_implicit.wasm": "vp_implicit.e4580c1644008b2721066ee57a32961f539c824f76b39df4246ffe0f112fc1f6.wasm", - "vp_masp.wasm": "vp_masp.a8d288a9b5d20026077103d4aec48ceb79cb964eb7170ba5e6becdda50bb4201.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.c6b4b606805ea213f9000a1872e55963710aafb1c24c17a685c26570dcafaa20.wasm", - "vp_token.wasm": "vp_token.ddc30091200e1dc28c1fd359017c87c822b733341cae268811694ce3d84e1186.wasm", - "vp_user.wasm": "vp_user.945dcf0c530ced6237c28ab9d0f4896e0d409ec7e6075dedc3b531cdd920e6fe.wasm", - "vp_validator.wasm": "vp_validator.fa04ed85352df83008afccadf058a3dc30956d0646f32221b458d6ea6d14b915.wasm" -} + "tx_bond.wasm": "tx_bond.025e4780cd7b63048d07832b940a3ee1c7f3a8be2e6135642bbf339f2fef9caa.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.c28d9ba24a8ad6726b750b71354c3a39174f52aa439ca382550f62558a21dabf.wasm", + "tx_ibc.wasm": "tx_ibc.dfd1d73d34b1dad76f3b37ca1caff4b817093bf88406f6391386f99a6b62a51f.wasm", + "tx_init_account.wasm": "tx_init_account.da0d005c0b44df80cd2fd286bfbeac9049a3bb2d9b153d0eae90f14129de396a.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.f75cf0d431d516a75d8a9868bcf11da6a97305f5d4eaa7d0a12b4711f9f96b21.wasm", + "tx_init_validator.wasm": "tx_init_validator.66b23482540998019986084204ffc1d3c22c731cdb8514ea05c89325df4de5aa.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.185c3215e683a8bad6a6e9625f073d96b30b625a27fb3bfa2dd0837478034b74.wasm", + "tx_transfer.wasm": "tx_transfer.8ac27a6fd4659c49fe6c0d8d0611cc894655380b278b1c0de2780d33b07ad95d.wasm", + "tx_unbond.wasm": "tx_unbond.b9d842aacd7965e28d7cb9429d7b2a62be025bd78ea062a0d3979927b5c2fe88.wasm", + "tx_update_vp.wasm": "tx_update_vp.8a23e2a22a11a7e59dd9fc4e64fc794a1ba457a3928cf2980e87b390d323ae2d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.fd72df547bd4c1f42ba47db2b3977341868bff661580cd04c9ebc48abfd6268d.wasm", + "tx_withdraw.wasm": "tx_withdraw.ae6637ffc5b62df578c2286f60e8d48227e5286bd4784ef608a314767cbc2728.wasm", + "vp_implicit.wasm": "vp_implicit.1287fa256849c683181c4597b1d30f979b95046ab2c5ae64847ed18d2b98a551.wasm", + "vp_masp.wasm": "vp_masp.631d43384e0d2754817c922ef17f6b30ff1fa33a09ec79c5a1826ea0544fc177.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.2a910bd3c81c7fc5b46b712e6f65b30e531b32447ef6ed1037d23685fc0b3da2.wasm", + "vp_token.wasm": "vp_token.1fd2173eea7d15990390b9d0dcfb41bd663fa5b0248176db87dc5c97e06d84e7.wasm", + "vp_user.wasm": "vp_user.22c7d630a3c1d6bbd3d9d9beb818795c9dbe08e82e6f5c05ba35c5b8d11cfabf.wasm", + "vp_validator.wasm": "vp_validator.353fa237cb5a36f6ea302bfc6c669f7b847d625fddbe35d2cecd51df9e74eb3a.wasm" +} \ No newline at end of file From e4275848087cc6eb762a60adb91e5d0c8e4240a1 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 9 Feb 2023 11:03:46 -0500 Subject: [PATCH 18/48] changelog: add #714 --- .changelog/unreleased/features/714-pos-inflation-rewards.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changelog/unreleased/features/714-pos-inflation-rewards.md diff --git a/.changelog/unreleased/features/714-pos-inflation-rewards.md b/.changelog/unreleased/features/714-pos-inflation-rewards.md new file mode 100644 index 00000000000..a1db1c147ea --- /dev/null +++ b/.changelog/unreleased/features/714-pos-inflation-rewards.md @@ -0,0 +1,6 @@ +- Introduce infrastructure for PoS inflation and rewards. Includes inflation + using the PD controller mechanism and rewards based on validator block voting + behavior. Rewards are tracked and effectively distributed using the F1 fee + mechanism. In this PR, rewards are calculated and stored, but they are not + yet applied to voting powers or considered when unbonding and withdrawing. + ([#714](https://github.com/anoma/namada/pull/714)) \ No newline at end of file From 40737029661c30cfbc1cdd8040105be9d918a9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 14 Mar 2023 09:49:31 +0000 Subject: [PATCH 19/48] apps/ledger: only specify num of validators in "dev" build --- apps/src/lib/node/ledger/mod.rs | 7 ++++++- apps/src/lib/node/ledger/shell/init_chain.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index 5e92c9879de..85875187e65 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -90,7 +90,12 @@ impl Shell { match req { Request::InitChain(init) => { tracing::debug!("Request InitChain"); - self.init_chain(init, 1).map(Response::InitChain) + self.init_chain( + init, + #[cfg(feature = "dev")] + 1, + ) + .map(Response::InitChain) } Request::Info(_) => Ok(Response::Info(self.last_state())), Request::Query(query) => Ok(Response::Query(self.query(query))), diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index f95a6d6bae8..562cb2fdcf9 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -33,7 +33,7 @@ where pub fn init_chain( &mut self, init: request::InitChain, - num_validators: u64, + #[cfg(feature = "dev")] num_validators: u64, ) -> Result { let mut response = response::InitChain::default(); let (current_chain_id, _) = self.wl_storage.storage.get_chain_id(); From dcca4dc0ed4f20accb6e2e78020ad6226258c5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 14 Mar 2023 09:50:22 +0000 Subject: [PATCH 20/48] test/finalize_block: extend no-DB commit test to ensure we hit inflation --- apps/src/lib/node/ledger/shell/finalize_block.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index ca77a8c0786..d2dd9d8be29 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -816,6 +816,7 @@ mod test_finalize_block { }; use namada::types::transaction::{EncryptionKey, Fee, WrapperTx, MIN_FEE}; use rust_decimal_macros::dec; + use test_log::test; use super::*; use crate::node::ledger::shell::test_utils::*; @@ -1247,8 +1248,20 @@ mod test_finalize_block { > = store_block_state(&shell); // Keep applying finalize block + let validator = shell.mode.get_validator_address().unwrap(); + let pos_params = + namada_proof_of_stake::read_pos_params(&shell.wl_storage).unwrap(); + let consensus_key = + namada_proof_of_stake::validator_consensus_key_handle(validator) + .get(&shell.wl_storage, Epoch::default(), &pos_params) + .unwrap() + .unwrap(); + let proposer_address = dbg!(consensus_key.tm_raw_hash()).into_bytes(); for _ in 0..20 { - let req = FinalizeBlock::default(); + let req = FinalizeBlock { + proposer_address: proposer_address.clone(), + ..Default::default() + }; let _events = shell.finalize_block(req).unwrap(); let new_state = store_block_state(&shell); // The new state must be unchanged From c744f898cafadec9e722e71b6b893c5ef45c1797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 14 Mar 2023 09:53:18 +0000 Subject: [PATCH 21/48] app/ledger/finalize_block: fix and refactor block proposer look-up --- .../lib/node/ledger/shell/finalize_block.rs | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index d2dd9d8be29..57eee333a50 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -415,12 +415,29 @@ where ) .unwrap(); } - let tm_raw_hash_string = - tm_raw_hash_to_string(req.proposer_address); + } + None => { + tracing::debug!("Can't find last block proposer"); + } + } + + // Look-up the native address of the proposer and persist it + match String::from_utf8(req.proposer_address.clone()) { + Err(err) => { + tracing::error!( + "Failed to decode proposer address from bytes. Expected \ + utf-8 string. Failed with {err}. Proposer raw bytes {:?}", + req.proposer_address + ) + } + Ok(tm_raw_hash_string) => { + // Get proposer address from storage based on the consensus + // key hash let native_proposer_address = find_validator_by_raw_hash( &self.wl_storage, tm_raw_hash_string, - )? + ) + .unwrap() .expect( "Unable to find native validator address of block \ proposer from tendermint raw hash", @@ -430,29 +447,6 @@ where native_proposer_address, )?; } - None => { - println!("CANT FIND LAST BLOCK PROPOSER"); - - if req.votes.is_empty() && !req.proposer_address.is_empty() { - // Get proposer address from storage based on the consensus - // key hash - let tm_raw_hash_string = - tm_raw_hash_to_string(req.proposer_address); - let native_proposer_address = find_validator_by_raw_hash( - &self.wl_storage, - tm_raw_hash_string, - ) - .unwrap() - .expect( - "Unable to find native validator address of block \ - proposer from tendermint raw hash", - ); - write_last_block_proposer_address( - &mut self.wl_storage, - native_proposer_address, - )?; - } - } } let _ = self From 0bcfed9747541d683bd34526790c1a599e806eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 14 Mar 2023 09:54:08 +0000 Subject: [PATCH 22/48] app/ledger: tidy up some logging --- apps/src/lib/node/ledger/shell/finalize_block.rs | 16 ++++++---------- apps/src/lib/node/ledger/shell/init_chain.rs | 4 ++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 57eee333a50..fb36e15b914 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -61,12 +61,6 @@ where &mut self, req: shim::request::FinalizeBlock, ) -> Result { - println!( - "\nFINALIZE BLOCK {} - NUM TXS = {}", - self.wl_storage.storage.block.height + 1, - req.txs.len() - ); - // Reset the gas meter before we start self.gas_meter.reset(); @@ -81,9 +75,9 @@ where self.wl_storage.storage.epoch_update_tracker.0 && self.wl_storage.storage.epoch_update_tracker.1 == 2; - println!( - "BLOCK HEIGHT {} AND EPOCH {}, NEW EPOCH = {}", - height, current_epoch, new_epoch + tracing::debug!( + "Block height: {height}, epoch: {current_epoch}, new epoch: \ + {new_epoch}." ); if new_epoch { @@ -399,7 +393,9 @@ where // (n-1 if we are in the process of finalizing n right now). match read_last_block_proposer_address(&self.wl_storage)? { Some(proposer_address) => { - println!("FOUND LAST BLOCK PROPOSER"); + tracing::debug!( + "Found last block proposer: {proposer_address}" + ); if new_epoch { self.apply_inflation( current_epoch, diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 562cb2fdcf9..086b8d07c1d 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -365,8 +365,8 @@ where ) .unwrap(); - println!("READ TOTAL NAM BALANCE = {}", total_nam); - println!("READ TOTAL STAKED NAM BALANCE = {}\n", total_staked_nam); + tracing::info!("Genesis total native tokens: {total_nam}."); + tracing::info!("Total staked tokens: {total_staked_nam}."); // Set the ratio of staked to total NAM tokens in the parameters storage self.wl_storage From 04f339254766cfb7a8c2e14b37d74b7daa49f0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 14 Mar 2023 09:54:58 +0000 Subject: [PATCH 23/48] app/ledger/finalize_block: write inflation + locked ratio via write-log --- .../lib/node/ledger/shell/finalize_block.rs | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index fb36e15b914..bce3699b9ee 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -11,7 +11,7 @@ use namada::ledger::pos::{ namada_proof_of_stake, staking_token_address, ADDRESS as POS_ADDRESS, }; use namada::ledger::protocol; -use namada::ledger::storage_api::StorageRead; +use namada::ledger::storage_api::{StorageRead, StorageWrite}; use namada::proof_of_stake::{ delegator_rewards_products_handle, find_validator_by_raw_hash, read_last_block_proposer_address, read_pos_params, read_total_stake, @@ -628,16 +628,18 @@ where ); // Run the rewards controllers - let new_pos_vals = RewardsController::run(&pos_controller); + let inflation::ValsToUpdate { + locked_ratio, + inflation, + } = RewardsController::run(&pos_controller); // let new_masp_vals = RewardsController::run(&_masp_controller); // Mint tokens to the PoS account for the last epoch's inflation - let pos_minted_tokens = new_pos_vals.inflation; inflation::mint_tokens( &mut self.wl_storage, &POS_ADDRESS, &staking_token_address(), - Amount::from(pos_minted_tokens), + Amount::from(inflation), )?; // For each consensus validator, update the rewards products @@ -667,7 +669,7 @@ where // to the accumulator storage earlier in apply_inflation // TODO: think about changing the reward to Decimal - let mut reward_tokens_remaining = pos_minted_tokens; + let mut reward_tokens_remaining = inflation; let mut new_rewards_products: HashMap = HashMap::new(); @@ -677,7 +679,7 @@ where // Get reward token amount for this validator let fractional_claim = value / Decimal::from(num_blocks_in_last_epoch); - let reward = decimal_mult_u64(fractional_claim, pos_minted_tokens); + let reward = decimal_mult_u64(fractional_claim, inflation); // Get validator data at the last epoch let stake = read_validator_stake( @@ -740,25 +742,11 @@ where // Write new rewards parameters that will be used for the inflation of // the current new epoch self.wl_storage - .storage - .write( - ¶ms_storage::get_pos_inflation_amount_key(), - new_pos_vals - .inflation - .try_to_vec() - .expect("encode new reward rate"), - ) - .expect("unable to encode new reward rate (Decimal)"); + .write(¶ms_storage::get_pos_inflation_amount_key(), inflation) + .expect("unable to write new reward rate"); self.wl_storage - .storage - .write( - ¶ms_storage::get_staked_ratio_key(), - new_pos_vals - .locked_ratio - .try_to_vec() - .expect("encode new locked ratio"), - ) - .expect("unable to encode new locked ratio (Decimal)"); + .write(¶ms_storage::get_staked_ratio_key(), locked_ratio) + .expect("unable to write new locked ratio"); // Delete the accumulators from storage // TODO: may want better way to implement this (for lazy PoS in general) From fc7fb50b32773d776bc46a455fbd1d695fd7b1c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 14 Mar 2023 09:55:48 +0000 Subject: [PATCH 24/48] pos/docs: fix docstring typo --- proof_of_stake/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index d049f470593..fed35606283 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -2419,7 +2419,7 @@ fn make_unbond_details( } } -/// Tally a running sum of the fracton of rewards owed to each validator in +/// Tally a running sum of the fraction of rewards owed to each validator in /// the consensus set. This is used to keep track of the rewards due to each /// consensus validator over the lifetime of an epoch. pub fn log_block_rewards( From bcd135fda60e6ab334f7f1c17214c5677eeb2386 Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 14 Mar 2023 12:17:58 -0400 Subject: [PATCH 25/48] fix block proposer look-up and unit tests --- .../lib/node/ledger/shell/finalize_block.rs | 63 +++++++++++-------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index bce3699b9ee..c146562567c 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -417,32 +417,22 @@ where } } - // Look-up the native address of the proposer and persist it - match String::from_utf8(req.proposer_address.clone()) { - Err(err) => { - tracing::error!( - "Failed to decode proposer address from bytes. Expected \ - utf-8 string. Failed with {err}. Proposer raw bytes {:?}", - req.proposer_address - ) - } - Ok(tm_raw_hash_string) => { - // Get proposer address from storage based on the consensus - // key hash - let native_proposer_address = find_validator_by_raw_hash( - &self.wl_storage, - tm_raw_hash_string, - ) - .unwrap() - .expect( - "Unable to find native validator address of block \ - proposer from tendermint raw hash", - ); - write_last_block_proposer_address( - &mut self.wl_storage, - native_proposer_address, - )?; - } + if !req.proposer_address.is_empty() { + let tm_raw_hash_string = + tm_raw_hash_to_string(req.proposer_address); + let native_proposer_address = find_validator_by_raw_hash( + &self.wl_storage, + tm_raw_hash_string, + ) + .unwrap() + .expect( + "Unable to find native validator address of block proposer \ + from tendermint raw hash", + ); + write_last_block_proposer_address( + &mut self.wl_storage, + native_proposer_address, + )?; } let _ = self @@ -1234,10 +1224,29 @@ mod test_finalize_block { .get(&shell.wl_storage, Epoch::default(), &pos_params) .unwrap() .unwrap(); - let proposer_address = dbg!(consensus_key.tm_raw_hash()).into_bytes(); + let proposer_address = HEXUPPER + .decode(consensus_key.tm_raw_hash().as_bytes()) + .unwrap(); + let val_stake = read_validator_stake( + &shell.wl_storage, + &pos_params, + validator, + Epoch::default(), + ) + .unwrap() + .unwrap(); + let votes = vec![VoteInfo { + validator_address: proposer_address.clone(), + validator_vp: u64::from(val_stake), + signed_last_block: true, + }]; + + // Need to supply a proposer address and votes to flow through the + // inflation code for _ in 0..20 { let req = FinalizeBlock { proposer_address: proposer_address.clone(), + votes: votes.clone(), ..Default::default() }; let _events = shell.finalize_block(req).unwrap(); From 869dbbe1af3055f83521c07bd6ccfa337b79b010 Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 14 Mar 2023 15:11:23 -0400 Subject: [PATCH 26/48] fix `value` types in functions that update parameters storage --- core/src/ledger/parameters/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/ledger/parameters/mod.rs b/core/src/ledger/parameters/mod.rs index fef582a33a8..e93f9d23e6a 100644 --- a/core/src/ledger/parameters/mod.rs +++ b/core/src/ledger/parameters/mod.rs @@ -322,7 +322,7 @@ where /// gas cost. pub fn update_epochs_per_year_parameter( storage: &mut Storage, - value: &EpochDuration, + value: &u64, ) -> std::result::Result where DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, @@ -336,7 +336,7 @@ where /// cost. pub fn update_pos_gain_p_parameter( storage: &mut Storage, - value: &EpochDuration, + value: &Decimal, ) -> std::result::Result where DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, @@ -350,7 +350,7 @@ where /// cost. pub fn update_pos_gain_d_parameter( storage: &mut Storage, - value: &EpochDuration, + value: &Decimal, ) -> std::result::Result where DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, @@ -364,7 +364,7 @@ where /// gas cost. pub fn update_staked_ratio_parameter( storage: &mut Storage, - value: &EpochDuration, + value: &Decimal, ) -> std::result::Result where DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, @@ -378,7 +378,7 @@ where /// and gas cost. pub fn update_pos_inflation_amount_parameter( storage: &mut Storage, - value: &EpochDuration, + value: &u64, ) -> std::result::Result where DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, From 3056d556c21abcfadeb600b747b9bb9fffb639ad Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 14 Mar 2023 15:11:45 -0400 Subject: [PATCH 27/48] clean redundant code and old comments --- .../lib/node/ledger/shell/finalize_block.rs | 19 ++++--------------- apps/src/lib/node/ledger/shell/init_chain.rs | 2 -- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index c146562567c..8af398342b8 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -555,7 +555,6 @@ where votes, )?; - // TODO: review if the appropriate epoch is being used (last vs now) let params = read_pos_params(&self.wl_storage)?; // Read from Parameters storage @@ -632,11 +631,6 @@ where Amount::from(inflation), )?; - // For each consensus validator, update the rewards products - // - // TODO: update implementation using lazy DS and be more - // memory-efficient - // Get the number of blocks in the last epoch let first_block_of_last_epoch = self .wl_storage @@ -646,23 +640,18 @@ where .first_block_heights[last_epoch.0 as usize] .0; let num_blocks_in_last_epoch = if first_block_of_last_epoch == 0 { - self.wl_storage.storage.block.height.0 - - first_block_of_last_epoch - - 1 + self.wl_storage.storage.block.height.0 - 1 } else { self.wl_storage.storage.block.height.0 - first_block_of_last_epoch }; - // Read the rewards accumulator, which was last updated when finalizing - // the previous block - // TODO: can/should this be optimized? Since we are reading and writing - // to the accumulator storage earlier in apply_inflation - + // Read the rewards accumulator and calculate the new rewards products + // for the previous epoch + // // TODO: think about changing the reward to Decimal let mut reward_tokens_remaining = inflation; let mut new_rewards_products: HashMap = HashMap::new(); - for acc in rewards_accumulator_handle().iter(&self.wl_storage)? { let (address, value) = acc?; diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 086b8d07c1d..930e17caaf2 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -231,8 +231,6 @@ where } // Initialize genesis implicit - // TODO: verify if we can get the total initial token supply from simply - // looping over the set of implicit accounts for genesis::ImplicitAccount { public_key } in genesis.implicit_accounts { let address: address::Address = (&public_key).into(); From 655e68934cf7cc79f18c76e5ae02c3bc2eff5337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 15 Mar 2023 06:58:03 +0000 Subject: [PATCH 28/48] app/finalize_block: refactor log_block_rewards/apply_inflation --- .../lib/node/ledger/shell/finalize_block.rs | 51 +++++++------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 8af398342b8..49be6321c80 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -4,9 +4,7 @@ use std::collections::HashMap; use namada::ledger::inflation::{self, RewardsController}; use namada::ledger::parameters::storage as params_storage; -use namada::ledger::pos::types::{ - decimal_mult_u64, into_tm_voting_power, VoteInfo, -}; +use namada::ledger::pos::types::{decimal_mult_u64, into_tm_voting_power}; use namada::ledger::pos::{ namada_proof_of_stake, staking_token_address, ADDRESS as POS_ADDRESS, }; @@ -396,27 +394,26 @@ where tracing::debug!( "Found last block proposer: {proposer_address}" ); - if new_epoch { - self.apply_inflation( - current_epoch, - &proposer_address, - &req.votes, - )?; - } else { - namada_proof_of_stake::log_block_rewards( - &mut self.wl_storage, - current_epoch, - &proposer_address, - &req.votes, - ) - .unwrap(); - } + namada_proof_of_stake::log_block_rewards( + &mut self.wl_storage, + if new_epoch { + current_epoch - 1 + } else { + current_epoch + }, + &proposer_address, + &req.votes, + )?; } None => { tracing::debug!("Can't find last block proposer"); } } + if new_epoch { + self.apply_inflation(current_epoch)?; + } + if !req.proposer_address.is_empty() { let tm_raw_hash_string = tm_raw_hash_to_string(req.proposer_address); @@ -533,28 +530,13 @@ where /// account, then update the reward products of the validators. This is /// executed while finalizing the first block of a new epoch and is applied /// with respect to the previous epoch. - fn apply_inflation( - &mut self, - current_epoch: Epoch, - proposer_address: &Address, - votes: &[VoteInfo], - ) -> Result<()> { + fn apply_inflation(&mut self, current_epoch: Epoch) -> Result<()> { let last_epoch = current_epoch - 1; // Get input values needed for the PD controller for PoS and MASP. // Run the PD controllers to calculate new rates. // // MASP is included below just for some completeness. - // Calculate the fractional block rewards for the previous block (final - // block of the previous epoch), which also gives the final - // accumulator value updates - namada_proof_of_stake::log_block_rewards( - &mut self.wl_storage, - last_epoch, - proposer_address, - votes, - )?; - let params = read_pos_params(&self.wl_storage)?; // Read from Parameters storage @@ -756,6 +738,7 @@ mod test_finalize_block { use data_encoding::HEXUPPER; use namada::ledger::parameters::EpochDuration; + use namada::ledger::pos::types::VoteInfo; use namada::ledger::storage_api; // use data_encoding::HEXUPPER; use namada::proof_of_stake::btree_set::BTreeSetShims; From 32b21e7b91d11b0a445470040494677acb7b8bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 15 Mar 2023 07:44:32 +0000 Subject: [PATCH 29/48] apps/finalize_block: log error when last proposer is missing --- apps/src/lib/node/ledger/shell/finalize_block.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 49be6321c80..071bb4b9fda 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -406,7 +406,15 @@ where )?; } None => { - tracing::debug!("Can't find last block proposer"); + if height > BlockHeight::default().next_height() { + tracing::error!( + "Can't find the last block proposer at height {height}" + ); + } else { + tracing::debug!( + "No last block proposer at height {height}" + ); + } } } From e9c53ab149b8fb4a95e9a1bc019d977455c9188a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 15 Mar 2023 09:41:58 +0000 Subject: [PATCH 30/48] core/storage: refactor epoch update tracker --- .../lib/node/ledger/shell/finalize_block.rs | 8 +- core/src/ledger/storage/mod.rs | 92 +++++++++---------- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 071bb4b9fda..67fc88eb5e8 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -9,6 +9,7 @@ use namada::ledger::pos::{ namada_proof_of_stake, staking_token_address, ADDRESS as POS_ADDRESS, }; use namada::ledger::protocol; +use namada::ledger::storage::EPOCH_SWITCH_BLOCKS_DELAY; use namada::ledger::storage_api::{StorageRead, StorageWrite}; use namada::proof_of_stake::{ delegator_rewards_products_handle, find_validator_by_raw_hash, @@ -69,9 +70,10 @@ where self.update_state(req.header, req.hash, req.byzantine_validators); let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); - let update_for_tendermint = - self.wl_storage.storage.epoch_update_tracker.0 - && self.wl_storage.storage.epoch_update_tracker.1 == 2; + let update_for_tendermint = matches!( + self.wl_storage.storage.update_epoch_blocks_delay, + Some(EPOCH_SWITCH_BLOCKS_DELAY) + ); tracing::debug!( "Block height: {height}, epoch: {current_epoch}, new epoch: \ diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index f50a2e4df55..d80d5fda91a 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -50,6 +50,10 @@ use crate::types::token; /// A result of a function that may fail pub type Result = std::result::Result; +/// We delay epoch change 2 blocks to keep it in sync with Tendermint, because +/// it has 2 blocks delay on validator set update. +pub const EPOCH_SWITCH_BLOCKS_DELAY: u32 = 2; + /// The storage data #[derive(Debug)] pub struct Storage @@ -82,8 +86,12 @@ where pub next_epoch_min_start_time: DateTimeUtc, /// The current established address generator pub address_gen: EstablishedAddressGen, - /// Epoch update info - pub epoch_update_tracker: (bool, u32), + /// We delay the switch to a new epoch by the number of blocks set in here. + /// This is `Some` when minimum number of blocks has been created and + /// minimum time has passed since the beginning of the last epoch. + /// Once the value is `Some(0)`, we're ready to switch to a new epoch and + /// this is reset back to `None`. + pub update_epoch_blocks_delay: Option, /// The shielded transaction index pub tx_index: TxIndex, /// The currently saved conversion state @@ -345,7 +353,7 @@ where address_gen: EstablishedAddressGen::new( "Privacy is a function of liberty.", ), - epoch_update_tracker: (false, 0), + update_epoch_blocks_delay: None, tx_index: TxIndex::default(), conversion_state: ConversionState::default(), #[cfg(feature = "ferveo-tpke")] @@ -723,29 +731,32 @@ where let (parameters, _gas) = parameters::read(self).expect("Couldn't read protocol parameters"); - // Check if the new epoch minimum start height and start time have been - // fulfilled. If so, queue the next epoch to start two blocks - // into the future so as to align validator set updates + etc with - // tendermint. This is because tendermint has a two block delay - // to validator changes. - let current_epoch_duration_satisfied = height - >= self.next_epoch_min_start_height - && time >= self.next_epoch_min_start_time; - - if current_epoch_duration_satisfied { - if !self.epoch_update_tracker.0 { - self.epoch_update_tracker = (true, 2); - } else { - self.epoch_update_tracker = - (true, self.epoch_update_tracker.1 - 1); + match self.update_epoch_blocks_delay.as_mut() { + None => { + // Check if the new epoch minimum start height and start time + // have been fulfilled. If so, queue the next + // epoch to start two blocks into the future so + // as to align validator set updates + etc with + // tendermint. This is because tendermint has a two block delay + // to validator changes. + let current_epoch_duration_satisfied = height + >= self.next_epoch_min_start_height + && time >= self.next_epoch_min_start_time; + if current_epoch_duration_satisfied { + self.update_epoch_blocks_delay = + Some(EPOCH_SWITCH_BLOCKS_DELAY); + } } - } else if self.epoch_update_tracker.0 { - self.epoch_update_tracker.0 = false - } - let new_epoch = - self.epoch_update_tracker.0 && self.epoch_update_tracker.1 == 0; + Some(blocks_until_switch) => { + *blocks_until_switch -= 1; + } + }; + let new_epoch = matches!(self.update_epoch_blocks_delay, Some(0)); if new_epoch { + // Reset the delay tracker + self.update_epoch_blocks_delay = None; + // Begin a new epoch self.block.epoch = self.block.epoch.next(); let EpochDuration { @@ -887,7 +898,7 @@ pub mod testing { address_gen: EstablishedAddressGen::new( "Test address generator seed", ), - epoch_update_tracker: (false, 0), + update_epoch_blocks_delay: None, tx_index: TxIndex::default(), conversion_state: ConversionState::default(), #[cfg(feature = "ferveo-tpke")] @@ -1020,22 +1031,19 @@ mod tests { { // Update will now be enqueued for 2 blocks in the future assert_eq!(storage.block.epoch, epoch_before); - assert!(storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,2); + assert_eq!(storage.update_epoch_blocks_delay, Some(2)); let block_height = block_height + 1; let block_time = block_time + Duration::seconds(1); storage.update_epoch(block_height, block_time).unwrap(); assert_eq!(storage.block.epoch, epoch_before); - assert!(storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,1); + assert_eq!(storage.update_epoch_blocks_delay, Some(1)); let block_height = block_height + 1; let block_time = block_time + Duration::seconds(1); storage.update_epoch(block_height, block_time).unwrap(); assert_eq!(storage.block.epoch, epoch_before.next()); - assert!(storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,0); + assert!(storage.update_epoch_blocks_delay.is_none()); assert_eq!(storage.next_epoch_min_start_height, block_height + epoch_duration.min_num_of_blocks); @@ -1048,8 +1056,7 @@ mod tests { storage.block.pred_epochs.get_epoch(block_height), Some(epoch_before.next())); } else { - assert!(!storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,0); + assert!(storage.update_epoch_blocks_delay.is_none()); assert_eq!(storage.block.epoch, epoch_before); assert_eq!( storage.block.pred_epochs.get_epoch(BlockHeight(block_height.0 - 1)), @@ -1084,37 +1091,31 @@ mod tests { // satisfied storage.update_epoch(height_before_update, time_before_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before); - assert!(!storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,0); + assert!(storage.update_epoch_blocks_delay.is_none()); storage.update_epoch(height_of_update, time_before_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before); - assert!(!storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,0); + assert!(storage.update_epoch_blocks_delay.is_none()); storage.update_epoch(height_before_update, time_of_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before); - assert!(!storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,0); + assert!(storage.update_epoch_blocks_delay.is_none()); // Update should be enqueued for 2 blocks in the future starting at or after this height and time storage.update_epoch(height_of_update, time_of_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before); - assert!(storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,2); + assert_eq!(storage.update_epoch_blocks_delay, Some(2)); // Increment the block height and time to simulate new blocks now let height_of_update = height_of_update + 1; let time_of_update = time_of_update + Duration::seconds(1); storage.update_epoch(height_of_update, time_of_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before); - assert!(storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,1); + assert_eq!(storage.update_epoch_blocks_delay, Some(1)); let height_of_update = height_of_update + 1; let time_of_update = time_of_update + Duration::seconds(1); storage.update_epoch(height_of_update, time_of_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before.next()); - assert!(storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,0); + assert!(storage.update_epoch_blocks_delay.is_none()); // The next epoch's minimum duration should change assert_eq!(storage.next_epoch_min_start_height, height_of_update + parameters.epoch_duration.min_num_of_blocks); @@ -1126,8 +1127,7 @@ mod tests { let time_of_update = time_of_update + Duration::seconds(1); storage.update_epoch(height_of_update, time_of_update).unwrap(); assert_eq!(storage.block.epoch, epoch_before.next()); - assert!(!storage.epoch_update_tracker.0); - assert_eq!(storage.epoch_update_tracker.1,0); + assert!(storage.update_epoch_blocks_delay.is_none()); } } } From 45f546a69da2c77ae19245dfbf4be0b462ed8dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 15 Mar 2023 10:40:20 +0000 Subject: [PATCH 31/48] app/node: tidy up some commented out code and logging --- .../lib/node/ledger/shell/finalize_block.rs | 8 +----- apps/src/lib/node/ledger/shims/abcipp_shim.rs | 26 +++++++------------ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 67fc88eb5e8..e9223996a9f 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -720,7 +720,7 @@ where .expect("unable to write new locked ratio"); // Delete the accumulators from storage - // TODO: may want better way to implement this (for lazy PoS in general) + // TODO: refactor with https://github.com/anoma/namada/issues/1225 let addresses_to_drop: HashSet
= rewards_accumulator_handle() .iter(&self.wl_storage)? .map(|a| a.unwrap().0) @@ -730,11 +730,6 @@ where .remove(&mut self.wl_storage, &address)?; } - // self.wl_storage - // .storage - // .delete(&consensus_validator_rewards_accumulator_key()) - // .unwrap(); - Ok(()) } } @@ -750,7 +745,6 @@ mod test_finalize_block { use namada::ledger::parameters::EpochDuration; use namada::ledger::pos::types::VoteInfo; use namada::ledger::storage_api; - // use data_encoding::HEXUPPER; use namada::proof_of_stake::btree_set::BTreeSetShims; use namada::proof_of_stake::types::WeightedValidator; use namada::proof_of_stake::{ diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim.rs b/apps/src/lib/node/ledger/shims/abcipp_shim.rs index 6d007aff722..74a56a4ddcc 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim.rs @@ -90,21 +90,18 @@ impl AbcippShim { pub fn run(mut self) { while let Ok((req, resp_sender)) = self.shell_recv.recv() { let resp = match req { - Req::ProcessProposal(proposal) => { - println!("\nRECEIVED REQUEST PROCESSPROPOSAL"); - self.service - .call(Request::ProcessProposal(proposal)) - .map_err(Error::from) - .and_then(|res| match res { - Response::ProcessProposal(resp) => { - Ok(Resp::ProcessProposal((&resp).into())) - } - _ => unreachable!(), - }) - } + Req::ProcessProposal(proposal) => self + .service + .call(Request::ProcessProposal(proposal)) + .map_err(Error::from) + .and_then(|res| match res { + Response::ProcessProposal(resp) => { + Ok(Resp::ProcessProposal((&resp).into())) + } + _ => unreachable!(), + }), #[cfg(feature = "abcipp")] Req::FinalizeBlock(block) => { - println!("RECEIVED REQUEST FINALIZEBLOCK"); let unprocessed_txs = block.txs.clone(); let processing_results = self.service.process_txs(&block.txs); @@ -129,20 +126,17 @@ impl AbcippShim { } #[cfg(not(feature = "abcipp"))] Req::BeginBlock(block) => { - println!("RECEIVED REQUEST BEGINBLOCK"); // we save this data to be forwarded to finalize later self.begin_block_request = Some(block); Ok(Resp::BeginBlock(Default::default())) } #[cfg(not(feature = "abcipp"))] Req::DeliverTx(tx) => { - println!("RECEIVED REQUEST DELIVERTX"); self.delivered_txs.push(tx.tx); Ok(Resp::DeliverTx(Default::default())) } #[cfg(not(feature = "abcipp"))] Req::EndBlock(_) => { - println!("RECEIVED REQUEST ENDBLOCK"); let processing_results = self.service.process_txs(&self.delivered_txs); let mut txs = Vec::with_capacity(self.delivered_txs.len()); From 5471a00666901aa17c6eb38d64c7b41299c54014 Mon Sep 17 00:00:00 2001 From: mariari Date: Fri, 13 Jan 2023 14:57:46 +0800 Subject: [PATCH 32/48] Move inflation to core from shared --- {shared => core}/src/ledger/inflation.rs | 0 core/src/ledger/mod.rs | 1 + shared/src/ledger/mod.rs | 3 +-- 3 files changed, 2 insertions(+), 2 deletions(-) rename {shared => core}/src/ledger/inflation.rs (100%) diff --git a/shared/src/ledger/inflation.rs b/core/src/ledger/inflation.rs similarity index 100% rename from shared/src/ledger/inflation.rs rename to core/src/ledger/inflation.rs diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index 31879e9b994..c0bd1e45a97 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -4,6 +4,7 @@ pub mod gas; pub mod governance; #[cfg(any(feature = "abciplus", feature = "abcipp"))] pub mod ibc; +pub mod inflation; pub mod parameters; pub mod slash_fund; pub mod storage; diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index 16cd09c841e..befd64ca64e 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -3,7 +3,6 @@ pub mod eth_bridge; pub mod events; pub mod ibc; -pub mod inflation; pub mod masp; pub mod native_vp; pub mod pos; @@ -14,5 +13,5 @@ pub mod storage; pub mod vp_host_fns; pub use namada_core::ledger::{ - gas, governance, parameters, storage_api, tx_env, vp_env, + gas, governance, inflation, parameters, storage_api, tx_env, vp_env, }; From 2c3c4d450e63b15d0876d0ee1978188e9e9c81ac Mon Sep 17 00:00:00 2001 From: mariari Date: Fri, 13 Jan 2023 18:51:43 +0800 Subject: [PATCH 33/48] Add Token Parameters module, and keys like inflation and lock ratio The token parameter module serves as a module that hosts channging parameters for each asset --- core/src/types/token.rs | 42 ++++++++++++++++++++----- core/src/types/token/parameters.rs | 49 ++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 core/src/types/token/parameters.rs diff --git a/core/src/types/token.rs b/core/src/types/token.rs index d77176eca99..6a7f4eea8ee 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -1,5 +1,5 @@ //! A basic fungible token - +pub mod parameters; use std::fmt::Display; use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; use std::str::FromStr; @@ -10,6 +10,7 @@ use rust_decimal::prelude::{Decimal, ToPrimitive}; use serde::{Deserialize, Serialize}; use thiserror::Error; +use self::parameters::key_of_token; use crate::types::address::{masp, Address, DecodeError as AddressError}; use crate::types::storage::{DbKeySeg, Key, KeySeg}; @@ -297,7 +298,12 @@ pub const TX_KEY_PREFIX: &str = "tx-"; pub const CONVERSION_KEY_PREFIX: &str = "conv"; /// Key segment prefix for pinned shielded transactions pub const PIN_KEY_PREFIX: &str = "pin-"; +/// Key for the total supply of tokens const TOTAL_SUPPLY_STORAGE_KEY: &str = "total_supply"; +/// Last calculated inflation value handed out +pub const LAST_INFLATION: &str = "last_inflation"; +/// The last locked ratio +pub const LAST_LOCKED_RATIO: &str = "last_locked_ratio"; /// Obtain a storage key for user's balance. pub fn balance_key(token_addr: &Address, owner: &Address) -> Key { @@ -310,9 +316,11 @@ pub fn balance_key(token_addr: &Address, owner: &Address) -> Key { /// Obtain a storage key prefix for all users' balances. pub fn balance_prefix(token_addr: &Address) -> Key { - Key::from(token_addr.to_db_key()) - .push(&BALANCE_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") + key_of_token( + token_addr, + BALANCE_STORAGE_KEY, + "cannot obtain a storage key", + ) } /// Obtain a storage key prefix for multitoken balances. @@ -373,9 +381,29 @@ pub fn is_masp_key(key: &Key) -> bool { /// Storage key for total supply of a token pub fn total_supply_key(token_address: &Address) -> Key { - Key::from(token_address.to_db_key()) - .push(&TOTAL_SUPPLY_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") + key_of_token( + token_address, + TOTAL_SUPPLY_STORAGE_KEY, + "cannot obtain a storage key", + ) +} + +/// The last locked ratio of a token +pub fn last_locked_ratio(token_address: &Address) -> Key { + key_of_token( + token_address, + LAST_LOCKED_RATIO, + "cannot obtain storage key for the last locked ratio", + ) +} + +/// The last inflation of a token +pub fn last_inflation(token_address: &Address) -> Key { + key_of_token( + token_address, + LAST_INFLATION, + "cannot obtain storage key for the last inflation rate", + ) } /// Is storage key for total supply of a specific token? diff --git a/core/src/types/token/parameters.rs b/core/src/types/token/parameters.rs new file mode 100644 index 00000000000..027f5784043 --- /dev/null +++ b/core/src/types/token/parameters.rs @@ -0,0 +1,49 @@ +//! Custom parameters for each token type. These are used for +//! determining the shielded pool incentives. + +use crate::types::address::Address; +use crate::types::storage::{Key, KeySeg}; + +/// The key for the nominal proportional gain of a shielded pool for a given +/// asset +pub const KP_SP_GAIN_KEY: &str = "proptional_gain"; + +/// The key for the nominal derivative gain of a shielded pool for a given asset +pub const KD_SP_GAIN_KEY: &str = "derivative_gain"; + +/// The key for the locked ratio target for a given asset +pub const LOCKED_RATIO_TARGET_KEY: &str = "locked_ratio_target"; + +/// The key for the max reward rate for a given asset +pub const MAX_REWARD_RATE: &str = "max_reward_rate"; + +/// Obtain the nominal proportional key for the given token +pub fn kp_sp_gain(token_addr: &Address) -> Key { + key_of_token(token_addr, KP_SP_GAIN_KEY, "nominal proproitonal gains") +} + +/// Obtain the nominal derivative key for the given token +pub fn kd_sp_gain(token_addr: &Address) -> Key { + key_of_token(token_addr, KD_SP_GAIN_KEY, "nominal proproitonal gains") +} + +/// Obtain the locked target ratio key for the given token +pub fn locked_token_ratio(token_addr: &Address) -> Key { + key_of_token( + token_addr, + LOCKED_RATIO_TARGET_KEY, + "nominal proproitonal gains", + ) +} + +/// Gets the key for the given token address, error with the given +/// message to expect if the key is not in the address +pub fn key_of_token( + token_addr: &Address, + specific_key: &str, + expect_message: &str, +) -> Key { + Key::from(token_addr.to_db_key()) + .push(&specific_key.to_owned()) + .expect(expect_message) +} From b0f315b7060fa027d528fa2e06621e625e3a90bc Mon Sep 17 00:00:00 2001 From: mariari Date: Fri, 13 Jan 2023 22:31:11 +0800 Subject: [PATCH 34/48] Reaplace by hand reward depositing, with a call to mint This abstracts out the manual logic of reading and writing from storage to be safe with the total_tokens logic, and any future changes that handle book keeping on minting extra tokens --- core/src/ledger/storage/masp_conversions.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index 0834d7bb53d..66e239c318a 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -39,6 +39,7 @@ where }; use rayon::prelude::ParallelSlice; + use crate::ledger::inflation::mint_tokens; use crate::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; use crate::types::storage::{self, KeySeg}; use crate::types::{address, token}; @@ -132,11 +133,8 @@ where // Update the MASP's transparent reward token balance to ensure that it // is sufficiently backed to redeem rewards - let reward_key = token::balance_key(&address::nam(), &masp_addr); - let addr_bal: token::Amount = - wl_storage.read(&reward_key)?.unwrap_or_default(); - let new_bal = addr_bal + total_reward; - wl_storage.write(&reward_key, new_bal)?; + mint_tokens(wl_storage, &masp_addr, &address::nam(), total_reward)?; + // Try to distribute Merkle tree construction as evenly as possible // across multiple cores // Merkle trees must have exactly 2^n leaves to be mergeable From 2d42286af48d2db09a8d994eff8583fb0f49206c Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 16 Jan 2023 16:25:33 +0800 Subject: [PATCH 35/48] Update the update_allowed_conversions to use the pd controller The work is split into the calculate_masp_rewards. This is fine for now, however in the long term this needs restructring, as this is nowhere near apply_inflation in src/lib/node/ledger/shell/finalize_block. Further two expects are had for most values read from storage to better isolate errors if they failed to be read rather than just returning a None making error tracing hard Finally the last check added is a 0 check so that divison does not fail if the masp address has no funds in it (for testing puproses it may not) --- core/src/ledger/storage/masp_conversions.rs | 115 +++++++++++++++++++- core/src/types/token/parameters.rs | 5 + 2 files changed, 115 insertions(+), 5 deletions(-) diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index 66e239c318a..a8af054cec6 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -8,8 +8,12 @@ use masp_primitives::convert::AllowedConversion; use masp_primitives::merkle_tree::FrozenCommitmentTree; use masp_primitives::sapling::Node; +use crate::ledger::inflation::{mint_tokens, RewardsController, ValsToUpdate}; +use crate::ledger::parameters; +use crate::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; use crate::types::address::Address; use crate::types::storage::Epoch; +use crate::types::{address, token}; /// A representation of the conversion state #[derive(Debug, Default, BorshSerialize, BorshDeserialize)] @@ -22,6 +26,108 @@ pub struct ConversionState { pub assets: BTreeMap, } +fn calculate_masp_rewards( + wl_storage: &mut super::WlStorage, + addr: &Address, +) -> crate::ledger::storage_api::Result<(u64, u64)> +where + D: super::DB + for<'iter> super::DBIter<'iter>, + H: super::StorageHasher, +{ + use rust_decimal::Decimal; + + let masp_addr = address::masp(); + // Query the storage for information + + //// information about the amount of tokens on the chain + let total_tokens: token::Amount = + wl_storage.read(&token::total_supply_key(addr))?.expect(""); + + // total staked amount in the Shielded pool + let total_token_in_masp: token::Amount = wl_storage + .read(&token::balance_key(addr, &masp_addr))? + .expect(""); + + let epochs_per_year: u64 = wl_storage + .read(¶meters::storage::get_epochs_per_year_key())? + .expect(""); + + //// Values from the last epoch + let last_inflation: u64 = wl_storage + .read(&token::last_inflation(addr)) + .expect("failure to read last inflation") + .expect(""); + + let last_locked_ratio: Decimal = wl_storage + .read(&token::last_locked_ratio(addr)) + .expect("failure to read last inflation") + .expect(""); + + //// Parameters for each token + let max_reward_rate: Decimal = wl_storage + .read(&token::parameters::max_reward_rate(addr)) + .expect("max reward should properly decode") + .expect(""); + + let kp_gain_nom: Decimal = wl_storage + .read(&token::parameters::kp_sp_gain(addr)) + .expect("kp_gain_nom reward should properly decode") + .expect(""); + + let kd_gain_nom: Decimal = wl_storage + .read(&token::parameters::kd_sp_gain(addr)) + .expect("kd_gain_nom reward should properly decode") + .expect(""); + + let locked_target_ratio: Decimal = wl_storage + .read(&token::parameters::locked_token_ratio(addr))? + .expect(""); + + // Creating the PD controller for handing out tokens + let controller = RewardsController::new( + total_token_in_masp, + total_tokens, + locked_target_ratio, + last_locked_ratio, + max_reward_rate, + token::Amount::from(last_inflation), + kp_gain_nom, + kd_gain_nom, + epochs_per_year, + ); + + let ValsToUpdate { + locked_ratio, + inflation, + } = RewardsController::run(&controller); + + // Is it fine to write the inflation rate, this is accurate, + // but we should make sure the return value's ratio matches + // this new inflation rate in 'update_allowed_conversions', + // otherwise we will have an inaccurate view of inflation + wl_storage + .write(&token::last_inflation(addr), inflation) + .expect("unable to encode new inflation rate (Decimal)"); + + wl_storage + .write(&token::last_locked_ratio(addr), locked_ratio) + .expect("unable to encode new locked ratio (Decimal)"); + + // to make it conform with the expected output, we need to + // move it to a ratio of x/100 to match the masp_rewards + // function This may be unneeded, as we could describe it as a + // ratio of x/1 + + // inflation-per-token = inflation / locked tokens = n/100 + // ∴ n = (inflation * 100) / locked tokens + let total_in = total_token_in_masp.change() as u64; + if 0u64 == total_in { + Ok((0u64, 100)) + } else { + Ok((inflation * 100 / total_token_in_masp.change() as u64, 100)) + } +} + // This is only enabled when "wasm-runtime" is on, because we're using rayon #[cfg(feature = "wasm-runtime")] /// Update the MASP's allowed conversions @@ -39,10 +145,7 @@ where }; use rayon::prelude::ParallelSlice; - use crate::ledger::inflation::mint_tokens; - use crate::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; use crate::types::storage::{self, KeySeg}; - use crate::types::{address, token}; // The derived conversions will be placed in MASP address space let masp_addr = address::masp(); @@ -64,7 +167,9 @@ where // Conversions from the previous to current asset for each address let mut current_convs = BTreeMap::::new(); // Reward all tokens according to above reward rates - for (addr, reward) in &masp_rewards { + for addr in masp_rewards.keys() { + let reward = calculate_masp_rewards(wl_storage, addr) + .expect("Calculating the masp rewards should not fail"); // Dispence a transparent reward in parallel to the shielded rewards let addr_bal: token::Amount = wl_storage .read(&token::balance_key(addr, &masp_addr))? @@ -73,7 +178,7 @@ where // reward.0 units of the reward token // Since floor(a) + floor(b) <= floor(a+b), there will always be // enough rewards to reimburse users - total_reward += (addr_bal * *reward).0; + total_reward += (addr_bal * reward).0; // Provide an allowed conversion from previous timestamp. The // negative sign allows each instance of the old asset to be // cancelled out/replaced with the new asset diff --git a/core/src/types/token/parameters.rs b/core/src/types/token/parameters.rs index 027f5784043..8aebc3752e7 100644 --- a/core/src/types/token/parameters.rs +++ b/core/src/types/token/parameters.rs @@ -27,6 +27,11 @@ pub fn kd_sp_gain(token_addr: &Address) -> Key { key_of_token(token_addr, KD_SP_GAIN_KEY, "nominal proproitonal gains") } +/// The max reward rate key for the given token +pub fn max_reward_rate(token_addr: &Address) -> Key { + key_of_token(token_addr, MAX_REWARD_RATE, "max reward rate") +} + /// Obtain the locked target ratio key for the given token pub fn locked_token_ratio(token_addr: &Address) -> Key { key_of_token( From 8ac033dc9215db4317cd2c9a71c8330ac356c435 Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 16 Jan 2023 18:44:23 +0800 Subject: [PATCH 36/48] Add Parameter data structure for holding an initalizing token params --- core/src/types/token/parameters.rs | 75 ++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/core/src/types/token/parameters.rs b/core/src/types/token/parameters.rs index 8aebc3752e7..2046e6923ef 100644 --- a/core/src/types/token/parameters.rs +++ b/core/src/types/token/parameters.rs @@ -1,9 +1,42 @@ //! Custom parameters for each token type. These are used for //! determining the shielded pool incentives. +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use rust_decimal::Decimal; +use rust_decimal_macros::dec; +use serde::{Deserialize, Serialize}; + +use crate::ledger::storage::{self as ledger_storage}; +use crate::ledger::storage_api::StorageWrite; use crate::types::address::Address; use crate::types::storage::{Key, KeySeg}; +/// Token parameters for each kind of asset held on chain +#[derive( + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Deserialize, + Serialize, +)] +pub struct Parameters { + /// Maximum reward rate + max_reward_rate: Decimal, + /// Shielded Pool nominal derivative gain + kd_gain_nom: Decimal, + /// Shielded Pool nominal proportional gain for the given token + kp_gain_nom: Decimal, + /// Locked ratio for the given token + locked_ratio_target_key: Decimal, +} + /// The key for the nominal proportional gain of a shielded pool for a given /// asset pub const KP_SP_GAIN_KEY: &str = "proptional_gain"; @@ -52,3 +85,45 @@ pub fn key_of_token( .push(&specific_key.to_owned()) .expect(expect_message) } + +impl Parameters { + /// Initialize parameters for the token in storage during the genesis block. + pub fn init_storage( + &self, + address: &Address, + wl_storage: &mut ledger_storage::WlStorage, + ) where + DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + H: ledger_storage::StorageHasher, + { + let Self { + max_reward_rate: max_rate, + kd_gain_nom, + kp_gain_nom, + locked_ratio_target_key: locked_target, + } = self; + wl_storage + .write(&max_reward_rate(address), max_rate) + .expect("max reward rate for the given asset must be initialized"); + wl_storage + .write(&locked_token_ratio(address), locked_target) + .expect("locked ratio must be initialized"); + wl_storage + .write(&kp_sp_gain(address), kp_gain_nom) + .expect("The nominal proportional gain must be initialized"); + wl_storage + .write(&kd_sp_gain(address), kd_gain_nom) + .expect("The nominal derivative gain must be initialized"); + } +} + +impl Default for Parameters { + fn default() -> Self { + Self { + max_reward_rate: dec!(0.1), + kp_gain_nom: dec!(0.1), + kd_gain_nom: dec!(0.1), + locked_ratio_target_key: dec!(0.6667), + } + } +} From 7fd7f3c013ed234e64fa9da036b11fd0b319e8a1 Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 17 Jan 2023 14:43:28 +0800 Subject: [PATCH 37/48] Add default values for each token parameter This updates the gensis config along with propagating the values to gensis itself. last_inflation and last_locked_ratio are not included in the config as they generated at every epoch being initalized at 0. --- apps/src/lib/config/genesis.rs | 16 ++++++++ apps/src/lib/node/ledger/shell/init_chain.rs | 20 ++++++++++ core/src/types/address.rs | 2 +- genesis/dev.toml | 39 +++++++++++++++++++- genesis/e2e-tests-single-node.toml | 37 ++++++++++++++++++- 5 files changed, 110 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 461728a97ed..5806b96a43d 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -212,6 +212,9 @@ pub mod genesis_config { // Initial balances held by accounts defined elsewhere. // XXX: u64 doesn't work with toml-rs! pub balances: Option>, + // Token parameters + // XXX: u64 doesn't work with toml-rs! + pub parameters: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -397,6 +400,9 @@ pub mod genesis_config { let token_vp_config = wasm.get(token_vp_name).unwrap(); TokenAccount { + last_locked_ratio: Decimal::ZERO, + last_inflation: 0, + parameters: config.parameters.as_ref().unwrap().to_owned(), address: Address::decode(config.address.as_ref().unwrap()).unwrap(), vp_code_path: token_vp_config.filename.to_owned(), vp_sha256: token_vp_config @@ -804,6 +810,13 @@ pub struct TokenAccount { /// Accounts' balances of this token #[derivative(PartialOrd = "ignore", Ord = "ignore")] pub balances: HashMap, + // please put the last inflation amount here. + /// Token parameters + pub parameters: token::parameters::Parameters, + /// Token inflation from the last epoch (read + write for every epoch) + pub last_inflation: u64, + /// Token shielded ratio from the last epoch (read + write for every epoch) + pub last_locked_ratio: Decimal, } #[derive( @@ -1026,6 +1039,9 @@ pub fn genesis(num_validators: u64) -> Genesis { vp_code_path: vp_token_path.into(), vp_sha256: Default::default(), balances: balances.clone(), + parameters: token::parameters::Parameters::default(), + last_inflation: 0, + last_locked_ratio: Decimal::ZERO, }) .collect(); Genesis { diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 930e17caaf2..6210e64b173 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -10,6 +10,7 @@ use namada::ledger::pos::{into_tm_voting_power, staking_token_address}; use namada::ledger::storage_api::token::{ credit_tokens, read_balance, read_total_supply, }; +use namada::ledger::storage::types; use namada::ledger::storage_api::StorageWrite; use namada::types::key::*; use rust_decimal::Decimal; @@ -244,8 +245,27 @@ where vp_code_path, vp_sha256, balances, + parameters, + last_inflation, + last_locked_ratio, } in genesis.token_accounts { + // Init token parameters and last inflation and caching rates + parameters.init_storage(&address, &mut self.wl_storage); + self.wl_storage + .write( + &token::last_inflation(&address), + last_inflation, + ) + .unwrap(); + self.wl_storage + .write( + &token::last_locked_ratio(&address), + last_locked_ratio, + ) + .unwrap(); + // self.storage.write(&token::last_inflation(&address), + // last_inflation).unwrap(); let vp_code = vp_code_cache.get_or_insert_with(vp_code_path.clone(), || { wasm_loader::read_wasm(&self.wasm_dir, &vp_code_path) diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 5104615cb7a..3543ef133d0 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -49,7 +49,7 @@ pub const POS_SLASH_POOL: Address = /// with `PREFIX_INTERNAL` and be `FIXED_LEN_STRING_BYTES` characters long. #[rustfmt::skip] mod internal { - pub const POS: &str = + pub const POS: &str = "ano::Proof of Stake "; pub const POS_SLASH_POOL: &str = "ano::Proof of Stake Slash Pool "; diff --git a/genesis/dev.toml b/genesis/dev.toml index 0aff92206f9..ccf3a5cd04f 100644 --- a/genesis/dev.toml +++ b/genesis/dev.toml @@ -36,10 +36,15 @@ atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 # 2. An alias of any account bertha = 1000000 -# 3. A public key of a validator or an established account from which the +# 3. A public key of a validator or an established account from which the # address of the implicit account is derived) "bertha.public_key" = 100 "validator.public_key" = 100 +[token.NAM.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.BTC] address = "atest1v4ehgw36xdzryve5gsc52veeg5cnsv2yx5eygvp38qcrvd29xy6rys6p8yc5xvp4xfpy2v694wgwcp" @@ -49,6 +54,12 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 +[token.BTC.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 + [token.ETH] address = "atest1v4ehgw36xqmr2d3nx3ryvd2xxgmrq33j8qcns33sxezrgv6zxdzrydjrxveygd2yxumrsdpsf9jc2p" @@ -58,6 +69,11 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 +[token.ETH.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.DOT] address = "atest1v4ehgw36gg6nvs2zgfpyxsfjgc65yv6pxy6nwwfsxgungdzrggeyzv35gveyxsjyxymyz335hur2jn" @@ -67,6 +83,11 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 +[token.Dot.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.schnitzel] address = "atest1v4ehgw36xue5xvf5xvuyzvpjx5un2v3k8qeyvd3cxdqns32p89rrxd6xx9zngvpegccnzs699rdnnt" @@ -76,6 +97,11 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 +[token.Schnitzel.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.apfel] address = "atest1v4ehgw36gfryydj9g3p5zv3kg9znyd358ycnzsfcggc5gvecgc6ygs2rxv6ry3zpg4zrwdfeumqcz9" @@ -85,6 +111,11 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 +[token.Apfel.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.kartoffel] address = "atest1v4ehgw36gep5ysecxq6nyv3jg3zygv3e89qn2vp48pryxsf4xpznvve5gvmy23fs89pryvf5a6ht90" @@ -95,7 +126,11 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 - +[token.Kartoffel.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 # Some established accounts present at genesis. [established.albert] diff --git a/genesis/e2e-tests-single-node.toml b/genesis/e2e-tests-single-node.toml index 73194ac9547..f7d78e3477c 100644 --- a/genesis/e2e-tests-single-node.toml +++ b/genesis/e2e-tests-single-node.toml @@ -19,7 +19,7 @@ commission_rate = 0.05 # Maximum change per epoch in the commission rate max_commission_rate_change = 0.01 # Public IP:port address. -# We set the port to be the default+1000, so that if a local node was running at +# We set the port to be the default+1000, so that if a local node was running at # the same time as the E2E tests, it wouldn't affect them. net_address = "127.0.0.1:27656" @@ -39,6 +39,11 @@ Daewon = 1000000 faucet = 9223372036 "faucet.public_key" = 100 "validator-0.public_key" = 100 +[token.NAM.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.BTC] address = "atest1v4ehgw36xdzryve5gsc52veeg5cnsv2yx5eygvp38qcrvd29xy6rys6p8yc5xvp4xfpy2v694wgwcp" @@ -49,6 +54,11 @@ Bertha = 1000000 Christel = 1000000 Daewon = 1000000 faucet = 9223372036854 +[token.BTC.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.ETH] address = "atest1v4ehgw36xqmr2d3nx3ryvd2xxgmrq33j8qcns33sxezrgv6zxdzrydjrxveygd2yxumrsdpsf9jc2p" @@ -59,6 +69,11 @@ Bertha = 1000000 Christel = 1000000 Daewon = 1000000 faucet = 9223372036854 +[token.ETH.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.DOT] address = "atest1v4ehgw36gg6nvs2zgfpyxsfjgc65yv6pxy6nwwfsxgungdzrggeyzv35gveyxsjyxymyz335hur2jn" @@ -69,6 +84,11 @@ Bertha = 1000000 Christel = 1000000 Daewon = 1000000 faucet = 9223372036854 +[token.Dot.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.Schnitzel] address = "atest1v4ehgw36xue5xvf5xvuyzvpjx5un2v3k8qeyvd3cxdqns32p89rrxd6xx9zngvpegccnzs699rdnnt" @@ -79,6 +99,11 @@ Bertha = 1000000 Christel = 1000000 Daewon = 1000000 faucet = 9223372036854 +[token.Schnitzel.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.Apfel] address = "atest1v4ehgw36gfryydj9g3p5zv3kg9znyd358ycnzsfcggc5gvecgc6ygs2rxv6ry3zpg4zrwdfeumqcz9" @@ -89,6 +114,11 @@ Bertha = 1000000 Christel = 1000000 Daewon = 1000000 faucet = 9223372036854 +[token.Apfel.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 [token.Kartoffel] address = "atest1v4ehgw36gep5ysecxq6nyv3jg3zygv3e89qn2vp48pryxsf4xpznvve5gvmy23fs89pryvf5a6ht90" @@ -100,6 +130,11 @@ Bertha = 1000000 Christel = 1000000 Daewon = 1000000 faucet = 9223372036854 +[token.Kartoffel.parameters] +max_reward_rate = 0.1 +kd_gain_nom = 0.1 +kp_gain_nom = 0.1 +locked_ratio_target_key = 0.6667 # Some established accounts present at genesis. [established.faucet] From db46ab5a0d542abc444f173809d380f8a8d8357e Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 17 Jan 2023 17:28:11 +0800 Subject: [PATCH 38/48] Add the total token balance to the address of each token account --- apps/src/lib/node/ledger/shell/init_chain.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 6210e64b173..434f8b45722 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -264,8 +264,7 @@ where last_locked_ratio, ) .unwrap(); - // self.storage.write(&token::last_inflation(&address), - // last_inflation).unwrap(); + let vp_code = vp_code_cache.get_or_insert_with(vp_code_path.clone(), || { wasm_loader::read_wasm(&self.wasm_dir, &vp_code_path) @@ -292,10 +291,19 @@ where .write_bytes(&Key::validity_predicate(&address), vp_code) .unwrap(); + let mut total_balance_for_token = token::Amount::default(); for (owner, amount) in balances { + total_balance_for_token += amount; credit_tokens(&mut self.wl_storage, &address, &owner, amount) .unwrap(); } + // Write the total amount of tokens for the ratio + self.wl_storage + .write( + &token::total_supply_key(&address), + total_balance_for_token, + ) + .unwrap(); } // Initialize genesis validator accounts From 8adf4edd7ed347be56a82500d8df576622a5208a Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 17 Jan 2023 19:07:39 +0800 Subject: [PATCH 39/48] Convert test storage to use the WlStorage and added token initalizer This adds the token intializer for the parameters needed for each token, along with refactoring the storage such that this can can be had --- core/src/ledger/storage/mod.rs | 161 ++++++++++++++++++++++----------- 1 file changed, 110 insertions(+), 51 deletions(-) diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index d80d5fda91a..71eade18b3a 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -859,10 +859,16 @@ impl From for Error { /// Helpers for testing components that depend on storage #[cfg(any(test, feature = "testing"))] pub mod testing { + use borsh::BorshSerialize; + use rust_decimal::Decimal; + use rust_decimal_macros::dec; + use super::mockdb::MockDB; use super::*; use crate::ledger::storage::traits::Sha256Hasher; + use crate::ledger::storage_api::StorageWrite; use crate::types::address; + use crate::types::token::parameters; /// `WlStorage` with a mock DB for testing pub type TestWlStorage = WlStorage; @@ -907,7 +913,6 @@ pub mod testing { } } } - #[allow(clippy::derivable_impls)] impl Default for TestWlStorage { fn default() -> Self { @@ -917,6 +922,49 @@ pub mod testing { } } } + + impl TestWlStorage { + /// Initializes all tokens in the test storage + pub fn initalize_tokens( + &mut self, + total_supply: token::Amount, + total_supply_in_masp: token::Amount, + ) { + let masp_rewards = address::masp_rewards(); + let masp_addr = address::masp(); + for addr in masp_rewards.keys() { + parameters::Parameters::init_storage( + ¶meters::Parameters::default(), + addr, + self, + ); + let initial_inflation: u64 = 1; + let initial_locked_ratio: Decimal = dec!(0.1); + + self.write(&token::last_inflation(addr), initial_inflation) + .expect("Should not fail to put a test inflation source"); + self.write( + &token::last_locked_ratio(addr), + initial_locked_ratio, + ) + .expect("Should not fail to put a test inflation source"); + + self.write( + &token::total_supply_key(addr), + total_supply.try_to_vec().unwrap(), + ) + .expect("Should not fail to put a test total supply"); + self.write( + &token::balance_key(addr, &masp_addr), + total_supply_in_masp.try_to_vec().unwrap(), + ) + .expect( + "Should not fail to put a test the total supply in the \ + MASP", + ); + } + } + } } #[cfg(test)] @@ -988,13 +1036,18 @@ mod tests { min_blocks_delta, min_duration_delta, max_time_per_block_delta) in arb_and_epoch_duration_start_and_block()) { - let mut storage = TestStorage { + let storage = TestStorage { next_epoch_min_start_height: start_height + epoch_duration.min_num_of_blocks, next_epoch_min_start_time: start_time + epoch_duration.min_duration, ..Default::default() }; + let mut wl_storage = TestWlStorage { + write_log: Default::default(), + storage, + }; + let mut parameters = Parameters { max_proposal_bytes: Default::default(), epoch_duration: epoch_duration.clone(), @@ -1012,13 +1065,14 @@ mod tests { #[cfg(not(feature = "mainnet"))] wrapper_tx_fees: None, }; - parameters.init_storage(&mut storage); + parameters.init_storage(&mut wl_storage.storage); - let epoch_before = storage.last_epoch; - assert_eq!(epoch_before, storage.block.epoch); + wl_storage.initalize_tokens(token::Amount::from(1000), token::Amount::from(500)); + let epoch_before = wl_storage.storage.last_epoch; + assert_eq!(epoch_before, wl_storage.storage.block.epoch); // Try to apply the epoch update - storage.update_epoch(block_height, block_time).unwrap(); + wl_storage.storage.update_epoch(block_height, block_time).unwrap(); // Test for 1. if block_height.0 - start_height.0 @@ -1030,43 +1084,43 @@ mod tests { ) { // Update will now be enqueued for 2 blocks in the future - assert_eq!(storage.block.epoch, epoch_before); - assert_eq!(storage.update_epoch_blocks_delay, Some(2)); + assert_eq!(wl_storage.storage.block.epoch, epoch_before); + assert_eq!(wl_storage.storage.update_epoch_blocks_delay, Some(2)); let block_height = block_height + 1; let block_time = block_time + Duration::seconds(1); - storage.update_epoch(block_height, block_time).unwrap(); - assert_eq!(storage.block.epoch, epoch_before); - assert_eq!(storage.update_epoch_blocks_delay, Some(1)); + wl_storage.storage.update_epoch(block_height, block_time).unwrap(); + assert_eq!(wl_storage.storage.block.epoch, epoch_before); + assert_eq!(wl_storage.storage.update_epoch_blocks_delay, Some(1)); let block_height = block_height + 1; let block_time = block_time + Duration::seconds(1); - storage.update_epoch(block_height, block_time).unwrap(); - assert_eq!(storage.block.epoch, epoch_before.next()); - assert!(storage.update_epoch_blocks_delay.is_none()); + wl_storage.storage.update_epoch(block_height, block_time).unwrap(); + assert_eq!(wl_storage.storage.block.epoch, epoch_before.next()); + assert!(wl_storage.storage.update_epoch_blocks_delay.is_none()); - assert_eq!(storage.next_epoch_min_start_height, + assert_eq!(wl_storage.storage.next_epoch_min_start_height, block_height + epoch_duration.min_num_of_blocks); - assert_eq!(storage.next_epoch_min_start_time, + assert_eq!(wl_storage.storage.next_epoch_min_start_time, block_time + epoch_duration.min_duration); assert_eq!( - storage.block.pred_epochs.get_epoch(BlockHeight(block_height.0 - 1)), + wl_storage.storage.block.pred_epochs.get_epoch(BlockHeight(block_height.0 - 1)), Some(epoch_before)); assert_eq!( - storage.block.pred_epochs.get_epoch(block_height), + wl_storage.storage.block.pred_epochs.get_epoch(block_height), Some(epoch_before.next())); } else { - assert!(storage.update_epoch_blocks_delay.is_none()); - assert_eq!(storage.block.epoch, epoch_before); + assert!(wl_storage.storage.update_epoch_blocks_delay.is_none()); + assert_eq!(wl_storage.storage.block.epoch, epoch_before); assert_eq!( - storage.block.pred_epochs.get_epoch(BlockHeight(block_height.0 - 1)), + wl_storage.storage.block.pred_epochs.get_epoch(BlockHeight(block_height.0 - 1)), Some(epoch_before)); assert_eq!( - storage.block.pred_epochs.get_epoch(block_height), + wl_storage.storage.block.pred_epochs.get_epoch(block_height), Some(epoch_before)); } // Last epoch should only change when the block is committed - assert_eq!(storage.last_epoch, epoch_before); + assert_eq!(wl_storage.storage.last_epoch, epoch_before); // Update the epoch duration parameters parameters.epoch_duration.min_num_of_blocks = @@ -1076,58 +1130,63 @@ mod tests { Duration::seconds(min_duration + min_duration_delta).into(); parameters.max_expected_time_per_block = Duration::seconds(max_expected_time_per_block + max_time_per_block_delta).into(); - parameters::update_max_expected_time_per_block_parameter(&mut storage, ¶meters.max_expected_time_per_block).unwrap(); - parameters::update_epoch_parameter(&mut storage, ¶meters.epoch_duration).unwrap(); + parameters::update_max_expected_time_per_block_parameter( + &mut wl_storage.storage, + ¶meters.max_expected_time_per_block + ).unwrap(); + parameters::update_epoch_parameter( + &mut wl_storage.storage, + ¶meters.epoch_duration + ).unwrap(); // Test for 2. - let epoch_before = storage.block.epoch; - let height_of_update = storage.next_epoch_min_start_height.0 ; - let time_of_update = storage.next_epoch_min_start_time; + let epoch_before = wl_storage.storage.block.epoch; + let height_of_update = wl_storage.storage.next_epoch_min_start_height.0 ; + let time_of_update = wl_storage.storage.next_epoch_min_start_time; let height_before_update = BlockHeight(height_of_update - 1); let height_of_update = BlockHeight(height_of_update); let time_before_update = time_of_update - Duration::seconds(1); // No update should happen before both epoch duration conditions are // satisfied - storage.update_epoch(height_before_update, time_before_update).unwrap(); - assert_eq!(storage.block.epoch, epoch_before); - assert!(storage.update_epoch_blocks_delay.is_none()); - storage.update_epoch(height_of_update, time_before_update).unwrap(); - assert_eq!(storage.block.epoch, epoch_before); - assert!(storage.update_epoch_blocks_delay.is_none()); - storage.update_epoch(height_before_update, time_of_update).unwrap(); - assert_eq!(storage.block.epoch, epoch_before); - assert!(storage.update_epoch_blocks_delay.is_none()); + wl_storage.storage.update_epoch(height_before_update, time_before_update).unwrap(); + assert_eq!(wl_storage.storage.block.epoch, epoch_before); + assert!(wl_storage.storage.update_epoch_blocks_delay.is_none()); + wl_storage.storage.update_epoch(height_of_update, time_before_update).unwrap(); + assert_eq!(wl_storage.storage.block.epoch, epoch_before); + assert!(wl_storage.storage.update_epoch_blocks_delay.is_none()); + wl_storage.storage.update_epoch(height_before_update, time_of_update).unwrap(); + assert_eq!(wl_storage.storage.block.epoch, epoch_before); + assert!(wl_storage.storage.update_epoch_blocks_delay.is_none()); // Update should be enqueued for 2 blocks in the future starting at or after this height and time - storage.update_epoch(height_of_update, time_of_update).unwrap(); - assert_eq!(storage.block.epoch, epoch_before); - assert_eq!(storage.update_epoch_blocks_delay, Some(2)); + wl_storage.storage.update_epoch(height_of_update, time_of_update).unwrap(); + assert_eq!(wl_storage.storage.block.epoch, epoch_before); + assert_eq!(wl_storage.storage.update_epoch_blocks_delay, Some(2)); // Increment the block height and time to simulate new blocks now let height_of_update = height_of_update + 1; let time_of_update = time_of_update + Duration::seconds(1); - storage.update_epoch(height_of_update, time_of_update).unwrap(); - assert_eq!(storage.block.epoch, epoch_before); - assert_eq!(storage.update_epoch_blocks_delay, Some(1)); + wl_storage.storage.update_epoch(height_of_update, time_of_update).unwrap(); + assert_eq!(wl_storage.storage.block.epoch, epoch_before); + assert_eq!(wl_storage.storage.update_epoch_blocks_delay, Some(1)); let height_of_update = height_of_update + 1; let time_of_update = time_of_update + Duration::seconds(1); - storage.update_epoch(height_of_update, time_of_update).unwrap(); - assert_eq!(storage.block.epoch, epoch_before.next()); - assert!(storage.update_epoch_blocks_delay.is_none()); + wl_storage.storage.update_epoch(height_of_update, time_of_update).unwrap(); + assert_eq!(wl_storage.storage.block.epoch, epoch_before.next()); + assert!(wl_storage.storage.update_epoch_blocks_delay.is_none()); // The next epoch's minimum duration should change - assert_eq!(storage.next_epoch_min_start_height, + assert_eq!(wl_storage.storage.next_epoch_min_start_height, height_of_update + parameters.epoch_duration.min_num_of_blocks); - assert_eq!(storage.next_epoch_min_start_time, + assert_eq!(wl_storage.storage.next_epoch_min_start_time, time_of_update + parameters.epoch_duration.min_duration); // Increment the block height and time once more to make sure things reset let height_of_update = height_of_update + 1; let time_of_update = time_of_update + Duration::seconds(1); - storage.update_epoch(height_of_update, time_of_update).unwrap(); - assert_eq!(storage.block.epoch, epoch_before.next()); - assert!(storage.update_epoch_blocks_delay.is_none()); + wl_storage.storage.update_epoch(height_of_update, time_of_update).unwrap(); + assert_eq!(wl_storage.storage.block.epoch, epoch_before.next()); } } } From 77c3e42b203ef125c0d7501dde751037c0bc7a41 Mon Sep 17 00:00:00 2001 From: mariari Date: Wed, 18 Jan 2023 13:21:19 +0800 Subject: [PATCH 40/48] Fix dev.toml having inconsistent token values and inconsistent dot This addresses #1048 that creates two different dot toekns, along with fixing dev's parameters to be consistent with dev's toml file and not the e2e toml file --- genesis/dev.toml | 15 ++++++++++----- genesis/e2e-tests-single-node.toml | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/genesis/dev.toml b/genesis/dev.toml index ccf3a5cd04f..755b39163e9 100644 --- a/genesis/dev.toml +++ b/genesis/dev.toml @@ -83,7 +83,7 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 -[token.Dot.parameters] +[token.DOT.parameters] max_reward_rate = 0.1 kd_gain_nom = 0.1 kp_gain_nom = 0.1 @@ -97,7 +97,7 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 -[token.Schnitzel.parameters] +[token.schnitzel.parameters] max_reward_rate = 0.1 kd_gain_nom = 0.1 kp_gain_nom = 0.1 @@ -111,7 +111,7 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 -[token.Apfel.parameters] +[token.apfel.parameters] max_reward_rate = 0.1 kd_gain_nom = 0.1 kp_gain_nom = 0.1 @@ -126,7 +126,7 @@ atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw = 1000000 a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6mtz = 1000000 -[token.Kartoffel.parameters] +[token.kartoffel.parameters] max_reward_rate = 0.1 kd_gain_nom = 0.1 kp_gain_nom = 0.1 @@ -183,7 +183,12 @@ max_expected_time_per_block = 30 epochs_per_year = 525_600 # Max payload size, in bytes, for a tx batch proposal. max_proposal_bytes = 22020096 - +implicit_vp = "vp_implicit" +# Expected number of epochs per year (also sets the min duration of an epoch in seconds) +# The P gain factor in the Proof of Stake rewards controller +pos_gain_p = 0.1 +# The D gain factor in the Proof of Stake rewards controller +pos_gain_d = 0.1 # Proof of stake parameters. [pos_params] # Maximum number of consensus validators. diff --git a/genesis/e2e-tests-single-node.toml b/genesis/e2e-tests-single-node.toml index f7d78e3477c..0abc4e32fc7 100644 --- a/genesis/e2e-tests-single-node.toml +++ b/genesis/e2e-tests-single-node.toml @@ -78,13 +78,13 @@ locked_ratio_target_key = 0.6667 [token.DOT] address = "atest1v4ehgw36gg6nvs2zgfpyxsfjgc65yv6pxy6nwwfsxgungdzrggeyzv35gveyxsjyxymyz335hur2jn" vp = "vp_token" -[token.Dot.balances] +[token.DOT.balances] Albert = 1000000 Bertha = 1000000 Christel = 1000000 Daewon = 1000000 faucet = 9223372036854 -[token.Dot.parameters] +[token.DOT.parameters] max_reward_rate = 0.1 kd_gain_nom = 0.1 kp_gain_nom = 0.1 From 7739fe474c881e7d972be58c566d6780a00a8026 Mon Sep 17 00:00:00 2001 From: mariari Date: Wed, 22 Feb 2023 00:02:14 +0800 Subject: [PATCH 41/48] Change the last inflation amount to reflect the actual amount minted --- core/src/ledger/storage/masp_conversions.rs | 23 +++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index a8af054cec6..abf19774983 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -101,12 +101,24 @@ where inflation, } = RewardsController::run(&controller); + // inflation-per-token = inflation / locked tokens = n/100 + // ∴ n = (inflation * 100) / locked tokens + // Since we must put the notes in a compatible format with the + // note format, we must make the inflation amount discrete. + let total_in = total_token_in_masp.change() as u64; + let noterized_inflation = + if 0u64 == total_in { + 0u64 + } else { + inflation * 100 / total_token_in_masp.change() as u64 + }; + // Is it fine to write the inflation rate, this is accurate, // but we should make sure the return value's ratio matches // this new inflation rate in 'update_allowed_conversions', // otherwise we will have an inaccurate view of inflation wl_storage - .write(&token::last_inflation(addr), inflation) + .write(&token::last_inflation(addr), noterized_inflation) .expect("unable to encode new inflation rate (Decimal)"); wl_storage @@ -118,14 +130,7 @@ where // function This may be unneeded, as we could describe it as a // ratio of x/1 - // inflation-per-token = inflation / locked tokens = n/100 - // ∴ n = (inflation * 100) / locked tokens - let total_in = total_token_in_masp.change() as u64; - if 0u64 == total_in { - Ok((0u64, 100)) - } else { - Ok((inflation * 100 / total_token_in_masp.change() as u64, 100)) - } + Ok((noterized_inflation, 100)) } // This is only enabled when "wasm-runtime" is on, because we're using rayon From 878e88e276ab8f25e574a6cfa44ea620de1615f9 Mon Sep 17 00:00:00 2001 From: mariari Date: Wed, 22 Feb 2023 17:25:12 +0800 Subject: [PATCH 42/48] Update e2e tests --- tests/src/e2e/ledger_tests.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index f35be5898d4..104d10126a7 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -26,6 +26,7 @@ use namada_apps::client::tx::ShieldedContext; use namada_apps::config::genesis::genesis_config::{ GenesisConfig, ParametersConfig, PosParamsConfig, }; +use namada_apps::config::genesis::genesis_config::TokenAccountConfig; use serde_json::json; use setup::constants::*; @@ -943,8 +944,31 @@ fn masp_incentives() -> Result<()> { min_num_of_blocks: 1, ..genesis.parameters }; + let token = genesis + .token + .into_iter() + .map(|(token, account_config)| + (token, + TokenAccountConfig { + balances: + Some(account_config + .balances + .into_iter() + .flat_map(|m| m.into_keys()) + .map(|validator| { + if validator == ALBERT || validator == BERTHA { + (validator, 100u64) + } else { + (validator, 0u64) + } + }).collect()), + ..account_config + }, + )) + .collect(); GenesisConfig { parameters, + token, ..genesis } }, @@ -1047,7 +1071,10 @@ fn masp_incentives() -> Result<()> { let amt20 = token::Amount::from_str("20").unwrap(); let amt30 = token::Amount::from_str("30").unwrap(); - + print!("---------------------------------------------------------"); + let str = client.exp_string(&format!("NAM:")); + print!("{:?}", str); + print!("---------------------------------------------------------"); // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_1-epoch_0) let mut client = run!( test, @@ -1063,6 +1090,8 @@ fn masp_incentives() -> Result<()> { ], Some(300) )?; + // 200 BTC in total, 20 in the shielded pool + // 10% locked client.exp_string(&format!( "NAM: {}", (amt20 * masp_rewards[&btc()]).0 * (ep1.0 - ep0.0) From 3db724a8a62ca3478fad6d6cd4540b474fc2f687 Mon Sep 17 00:00:00 2001 From: mariari Date: Fri, 24 Feb 2023 23:19:23 +0800 Subject: [PATCH 43/48] Make the formula for the conversion rates reliable, also dump logs for e2e --- apps/src/lib/node/ledger/shell/init_chain.rs | 11 +- core/src/ledger/storage/masp_conversions.rs | 41 +- core/src/types/token/parameters.rs | 8 +- tests/src/e2e/ledger_tests.rs | 1136 +++++++++--------- wasm/checksums.json | 36 +- 5 files changed, 638 insertions(+), 594 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 434f8b45722..b40e777cd67 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -10,7 +10,6 @@ use namada::ledger::pos::{into_tm_voting_power, staking_token_address}; use namada::ledger::storage_api::token::{ credit_tokens, read_balance, read_total_supply, }; -use namada::ledger::storage::types; use namada::ledger::storage_api::StorageWrite; use namada::types::key::*; use rust_decimal::Decimal; @@ -253,16 +252,10 @@ where // Init token parameters and last inflation and caching rates parameters.init_storage(&address, &mut self.wl_storage); self.wl_storage - .write( - &token::last_inflation(&address), - last_inflation, - ) + .write(&token::last_inflation(&address), last_inflation) .unwrap(); self.wl_storage - .write( - &token::last_locked_ratio(&address), - last_locked_ratio, - ) + .write(&token::last_locked_ratio(&address), last_locked_ratio) .unwrap(); let vp_code = diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index abf19774983..a87c12f3cfd 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -46,7 +46,7 @@ where // total staked amount in the Shielded pool let total_token_in_masp: token::Amount = wl_storage .read(&token::balance_key(addr, &masp_addr))? - .expect(""); + .unwrap_or_default(); let epochs_per_year: u64 = wl_storage .read(¶meters::storage::get_epochs_per_year_key())? @@ -106,19 +106,44 @@ where // Since we must put the notes in a compatible format with the // note format, we must make the inflation amount discrete. let total_in = total_token_in_masp.change() as u64; - let noterized_inflation = - if 0u64 == total_in { - 0u64 - } else { - inflation * 100 / total_token_in_masp.change() as u64 - }; + let noterized_inflation = if 0u64 == total_in { + 0u64 + } else { + ((100 * inflation as u128) / (total_token_in_masp.change() as u128)) + as u64 + }; + + tracing::debug!( + "Controller, call: total_in_masp {:?}, total_tokens {:?}, \ + locked_target_ratio {:?}, last_locked_ratio {:?}, max_reward_rate \ + {:?}, last_inflation {:?}, kp_gain_nom {:?}, kd_gain_nom {:?}, \ + epochs_per_year {:?}", + total_token_in_masp, + total_tokens, + locked_target_ratio, + last_locked_ratio, + max_reward_rate, + token::Amount::from(last_inflation), + kp_gain_nom, + kd_gain_nom, + epochs_per_year, + ); + tracing::debug!("Please give me: {:?}", addr); + tracing::debug!("Ratio {:?}", locked_ratio); + tracing::debug!("inflation from the pd controller {:?}", inflation); + tracing::debug!("total in the masp {:?}", total_in); + tracing::debug!("Please give me inflation: {:?}", noterized_inflation); // Is it fine to write the inflation rate, this is accurate, // but we should make sure the return value's ratio matches // this new inflation rate in 'update_allowed_conversions', // otherwise we will have an inaccurate view of inflation wl_storage - .write(&token::last_inflation(addr), noterized_inflation) + .write( + &token::last_inflation(addr), + (noterized_inflation / 100u64) + * total_token_in_masp.change() as u64, + ) .expect("unable to encode new inflation rate (Decimal)"); wl_storage diff --git a/core/src/types/token/parameters.rs b/core/src/types/token/parameters.rs index 2046e6923ef..42dc7f62426 100644 --- a/core/src/types/token/parameters.rs +++ b/core/src/types/token/parameters.rs @@ -28,13 +28,13 @@ use crate::types::storage::{Key, KeySeg}; )] pub struct Parameters { /// Maximum reward rate - max_reward_rate: Decimal, + pub max_reward_rate: Decimal, /// Shielded Pool nominal derivative gain - kd_gain_nom: Decimal, + pub kd_gain_nom: Decimal, /// Shielded Pool nominal proportional gain for the given token - kp_gain_nom: Decimal, + pub kp_gain_nom: Decimal, /// Locked ratio for the given token - locked_ratio_target_key: Decimal, + pub locked_ratio_target_key: Decimal, } /// The key for the nominal proportional gain of a shielded pool for a given diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 104d10126a7..6d5bc059a0c 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -22,11 +22,12 @@ use data_encoding::HEXLOWER; use namada::types::address::{btc, eth, masp_rewards, Address}; use namada::types::storage::Epoch; use namada::types::token; +use namada::types::token::parameters::Parameters; use namada_apps::client::tx::ShieldedContext; use namada_apps::config::genesis::genesis_config::{ - GenesisConfig, ParametersConfig, PosParamsConfig, + GenesisConfig, ParametersConfig, PosParamsConfig, TokenAccountConfig, }; -use namada_apps::config::genesis::genesis_config::TokenAccountConfig; +use rust_decimal_macros::dec; use serde_json::json; use setup::constants::*; @@ -947,25 +948,52 @@ fn masp_incentives() -> Result<()> { let token = genesis .token .into_iter() - .map(|(token, account_config)| - (token, - TokenAccountConfig { - balances: - Some(account_config - .balances - .into_iter() - .flat_map(|m| m.into_keys()) - .map(|validator| { - if validator == ALBERT || validator == BERTHA { - (validator, 100u64) - } else { - (validator, 0u64) - } - }).collect()), - ..account_config - }, - )) + .map(|(token, account_config)| { + (token.clone(), { + let parameters = + account_config.parameters.map(|parameters| { + if token == String::from(NAM) { + Parameters { + max_reward_rate: dec!(100000.5), + // these need to be set to 0 + kd_gain_nom: dec!(0.0), + kp_gain_nom: dec!(0.0), + ..parameters + } + } else { + Parameters { + max_reward_rate: dec!(100000.5), + kd_gain_nom: dec!(0.5), + kp_gain_nom: dec!(0.5), + ..parameters + } + } + }); + + TokenAccountConfig { + balances: Some( + account_config + .balances + .into_iter() + .flat_map(|m| m.into_keys()) + .map(|validator| { + if validator == ALBERT + || validator == BERTHA + { + (validator, 1000000u64) + } else { + (validator, 0u64) + } + }) + .collect(), + ), + parameters, + ..account_config + } + }) + }) .collect(); + GenesisConfig { parameters, token, @@ -986,9 +1014,9 @@ fn masp_incentives() -> Result<()> { let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); // Wait till epoch boundary - let ep0 = epoch_sleep(&test, &validator_one_rpc, 720)?; + let ep0 = epoch_sleep(&test, &validator_one_rpc, 7200000)?; - // Send 20 BTC from Albert to PA(A) + // Send 200000 BTC from Albert to PA(A) let mut client = run!( test, Bin::Client, @@ -1001,7 +1029,7 @@ fn masp_incentives() -> Result<()> { "--token", BTC, "--amount", - "20", + "200000", "--ledger-address", &validator_one_rpc ], @@ -1010,7 +1038,7 @@ fn masp_incentives() -> Result<()> { client.exp_string("Transaction is valid")?; client.assert_success(); - // Assert BTC balance at VK(A) is 20 + // Assert BTC balance at VK(A) is 200000 let mut client = run!( test, Bin::Client, @@ -1025,7 +1053,7 @@ fn masp_incentives() -> Result<()> { ], Some(300) )?; - client.exp_string("BTC: 20")?; + client.exp_string("BTC: 200000")?; client.assert_success(); // Assert NAM balance at VK(A) is 0 @@ -1043,15 +1071,16 @@ fn masp_incentives() -> Result<()> { ], Some(300) )?; + client.exp_string("No shielded NAM balance found")?; client.assert_success(); let masp_rewards = masp_rewards(); // Wait till epoch boundary - let ep1 = epoch_sleep(&test, &validator_one_rpc, 720)?; + let ep1 = epoch_sleep(&test, &validator_one_rpc, 7200000)?; - // Assert BTC balance at VK(A) is 20 + // Assert BTC balance at VK(A) is 200000 let mut client = run!( test, Bin::Client, @@ -1066,63 +1095,14 @@ fn masp_incentives() -> Result<()> { ], Some(300) )?; - client.exp_string("BTC: 20")?; + client.exp_string("BTC: 200000")?; client.assert_success(); - let amt20 = token::Amount::from_str("20").unwrap(); + let amt200000 = token::Amount::from_str("200000").unwrap(); let amt30 = token::Amount::from_str("30").unwrap(); - print!("---------------------------------------------------------"); - let str = client.exp_string(&format!("NAM:")); - print!("{:?}", str); - print!("---------------------------------------------------------"); - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_1-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - // 200 BTC in total, 20 in the shielded pool - // 10% locked - client.exp_string(&format!( - "NAM: {}", - (amt20 * masp_rewards[&btc()]).0 * (ep1.0 - ep0.0) - ))?; - client.assert_success(); - // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_1-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - (amt20 * masp_rewards[&btc()]).0 * (ep1.0 - ep0.0) - ))?; - client.assert_success(); - - // Wait till epoch boundary - let ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; + // Assert NAM balance at VK(A) is 200000*BTC_reward*(epoch_1-epoch_0) - // Assert BTC balance at VK(A) is 20 let mut client = run!( test, Bin::Client, @@ -1131,162 +1111,25 @@ fn masp_incentives() -> Result<()> { "--owner", AA_VIEWING_KEY, "--token", - BTC, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("BTC: 20")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_2-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - (amt20 * masp_rewards[&btc()]).0 * (ep2.0 - ep0.0) - ))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_2-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - (amt20 * masp_rewards[&btc()]).0 * (ep2.0 - ep0.0) - ))?; - client.assert_success(); - - // Wait till epoch boundary - let ep3 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 30 ETH from Albert to PA(B) - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AB_PAYMENT_ADDRESS, - "--token", - ETH, - "--amount", - "30", - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert ETH balance at VK(B) is 30 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("ETH: 30")?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", NAM, "--ledger-address", &validator_one_rpc ], Some(300) )?; - client.exp_string("No shielded NAM balance found")?; - client.assert_success(); - // Wait till epoch boundary - let ep4 = epoch_sleep(&test, &validator_one_rpc, 720)?; + // 2000000 BTC in total, 200000 in the shielded pool + // 10% locked, 0% in the last. + // R_SPA = 0.1 + // KP_SPA = KD_SPA = 7.61e11 + // The expected ratio is 0667, so we end up with + // 0.557 * 7.61e11 - -0.1 * 7.61e11 = 5.07e11. This gets rounded + // down due to masp conversion notes. - // Assert ETH balance at VK(B) is 30 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("ETH: 30")?; + client.exp_string("NAM: 506000")?; client.assert_success(); - // Assert NAM balance at VK(B) is 30*ETH_reward*(epoch_4-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - (amt30 * masp_rewards[ð()]).0 * (ep4.0 - ep3.0) - ))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_4-epoch_0)+30*ETH_reward*(epoch_4-epoch_3) + // Assert NAM balance at MASP pool is 200000*BTC_reward*(epoch_1-epoch_0) let mut client = run!( test, Bin::Client, @@ -1301,133 +1144,13 @@ fn masp_incentives() -> Result<()> { ], Some(300) )?; - client.exp_string(&format!( - "NAM: {}", - ((amt20 * masp_rewards[&btc()]).0 * (ep4.0 - ep0.0)) - + ((amt30 * masp_rewards[ð()]).0 * (ep4.0 - ep3.0)) - ))?; + client.exp_string("NAM: 506000")?; client.assert_success(); // Wait till epoch boundary - let ep5 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 30 ETH from SK(B) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - B_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - ETH, - "--amount", - "30", - "--signer", - BERTHA, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert ETH balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("No shielded ETH balance found")?; - client.assert_success(); - - let mut ep = get_epoch(&test, &validator_one_rpc)?; - - // Assert NAM balance at VK(B) is 30*ETH_reward*(ep-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - (amt30 * masp_rewards[ð()]).0 * (ep.0 - ep3.0) - ))?; - client.assert_success(); - - ep = get_epoch(&test, &validator_one_rpc)?; - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_5-epoch_0)+30*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - ((amt20 * masp_rewards[&btc()]).0 * (ep.0 - ep0.0)) - + ((amt30 * masp_rewards[ð()]).0 * (ep.0 - ep3.0)) - ))?; - client.assert_success(); - - // Wait till epoch boundary - let ep6 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20 BTC from SK(A) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - BTC, - "--amount", - "20", - "--signer", - ALBERT, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction is valid")?; - client.assert_success(); + let ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; - // Assert BTC balance at VK(A) is 0 + // Assert BTC balance at VK(A) is 20 let mut client = run!( test, Bin::Client, @@ -1442,177 +1165,10 @@ fn masp_incentives() -> Result<()> { ], Some(300) )?; - client.exp_string("No shielded BTC balance found")?; + client.exp_string("BTC: 200000")?; client.assert_success(); - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - (amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0) - ))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_6-epoch_0)+20*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - ((amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) - + ((amt30 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)) - ))?; - client.assert_success(); - - // Wait till epoch boundary - let _ep7 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - (amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0) - ))?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 30*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - (amt30 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0) - ))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_6-epoch_0)+30*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string(&format!( - "NAM: {}", - ((amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) - + ((amt30 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)) - ))?; - client.assert_success(); - - // Wait till epoch boundary to prevent conversion expiry during transaction - // construction - let _ep8 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 30*ETH_reward*(epoch_5-epoch_3) NAM from SK(B) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - B_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - NAM, - "--amount", - &((amt30 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)).to_string(), - "--signer", - BERTHA, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Wait till epoch boundary - let _ep9 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20*BTC_reward*(epoch_6-epoch_0) NAM from SK(A) to Bertha - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BERTHA, - "--token", - NAM, - "--amount", - &((amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)).to_string(), - "--signer", - ALBERT, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 0 + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_2-epoch_0) let mut client = run!( test, Bin::Client, @@ -1627,44 +1183,514 @@ fn masp_incentives() -> Result<()> { ], Some(300) )?; - client.exp_string("No shielded NAM balance found")?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("No shielded NAM balance found")?; - client.assert_success(); - - // Assert NAM balance at MASP pool is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--ledger-address", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("NAM: 0")?; - client.assert_success(); + client.exp_string("NAM: 253690200000")?; + client.assert_success(); + + // // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_2-epoch_0) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // MASP, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // (amt200000 * masp_rewards[&btc()]).0 * (ep2.0 - ep0.0) + // ))?; + // client.assert_success(); + + // // Wait till epoch boundary + // let ep3 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // Send 30 ETH from Albert to PA(B) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "transfer", + // "--source", + // ALBERT, + // "--target", + // AB_PAYMENT_ADDRESS, + // "--token", + // ETH, + // "--amount", + // "30", + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("Transaction is valid")?; + // client.assert_success(); + + // // Assert ETH balance at VK(B) is 30 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // ETH, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("ETH: 30")?; + // client.assert_success(); + + // // Assert NAM balance at VK(B) is 0 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("No shielded NAM balance found")?; + // client.assert_success(); + + // // Wait till epoch boundary + // let ep4 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // Assert ETH balance at VK(B) is 30 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // ETH, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("ETH: 30")?; + // client.assert_success(); + + // // Assert NAM balance at VK(B) is 30*ETH_reward*(epoch_4-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // (amt30 * masp_rewards[ð()]).0 * (ep4.0 - ep3.0) + // ))?; + // client.assert_success(); + + // // Assert NAM balance at MASP pool is + // // 20*BTC_reward*(epoch_4-epoch_0)+30*ETH_reward*(epoch_4-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // MASP, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // ((amt200000 * masp_rewards[&btc()]).0 * (ep4.0 - ep0.0)) + // + ((amt30 * masp_rewards[ð()]).0 * (ep4.0 - ep3.0)) + // ))?; + // client.assert_success(); + + // // Wait till epoch boundary + // let ep5 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // Send 30 ETH from SK(B) to Christel + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "transfer", + // "--source", + // B_SPENDING_KEY, + // "--target", + // CHRISTEL, + // "--token", + // ETH, + // "--amount", + // "30", + // "--signer", + // BERTHA, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("Transaction is valid")?; + // client.assert_success(); + + // // Assert ETH balance at VK(B) is 0 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // ETH, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("No shielded ETH balance found")?; + // client.assert_success(); + + // let mut ep = get_epoch(&test, &validator_one_rpc)?; + + // // Assert NAM balance at VK(B) is 30*ETH_reward*(ep-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // (amt30 * masp_rewards[ð()]).0 * (ep.0 - ep3.0) + // ))?; + // client.assert_success(); + + // ep = get_epoch(&test, &validator_one_rpc)?; + // // Assert NAM balance at MASP pool is + // // 20*BTC_reward*(epoch_5-epoch_0)+30*ETH_reward*(epoch_5-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // MASP, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // ((amt200000 * masp_rewards[&btc()]).0 * (ep.0 - ep0.0)) + // + ((amt30 * masp_rewards[ð()]).0 * (ep.0 - ep3.0)) + // ))?; + // client.assert_success(); + + // // Wait till epoch boundary + // let ep6 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // Send 20 BTC from SK(A) to Christel + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "transfer", + // "--source", + // A_SPENDING_KEY, + // "--target", + // CHRISTEL, + // "--token", + // BTC, + // "--amount", + // "20", + // "--signer", + // ALBERT, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("Transaction is valid")?; + // client.assert_success(); + + // // Assert BTC balance at VK(A) is 0 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AA_VIEWING_KEY, + // "--token", + // BTC, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("No shielded BTC balance found")?; + // client.assert_success(); + + // // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AA_VIEWING_KEY, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // (amt200000 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0) + // ))?; + // client.assert_success(); + + // // Assert NAM balance at MASP pool is + // // 20*BTC_reward*(epoch_6-epoch_0)+20*ETH_reward*(epoch_5-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // MASP, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // ((amt200000 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) + // + ((amt30 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)) + // ))?; + // client.assert_success(); + + // // Wait till epoch boundary + // let _ep7 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AA_VIEWING_KEY, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // (amt200000 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0) + // ))?; + // client.assert_success(); + + // // Assert NAM balance at VK(B) is 30*ETH_reward*(epoch_5-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // (amt30 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0) + // ))?; + // client.assert_success(); + + // // Assert NAM balance at MASP pool is + // // 20*BTC_reward*(epoch_6-epoch_0)+30*ETH_reward*(epoch_5-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // MASP, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string(&format!( + // "NAM: {}", + // ((amt200000 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) + // + ((amt30 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)) + // ))?; + // client.assert_success(); + + // // Wait till epoch boundary to prevent conversion expiry during + // transaction // construction + // let _ep8 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // Send 30*ETH_reward*(epoch_5-epoch_3) NAM from SK(B) to Christel + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "transfer", + // "--source", + // B_SPENDING_KEY, + // "--target", + // CHRISTEL, + // "--token", + // NAM, + // "--amount", + // &((amt30 * masp_rewards[ð()]).0 * (ep5.0 - + // ep3.0)).to_string(), "--signer", + // BERTHA, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("Transaction is valid")?; + // client.assert_success(); + + // // Wait till epoch boundary + // let _ep9 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // Send 20*BTC_reward*(epoch_6-epoch_0) NAM from SK(A) to Bertha + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "transfer", + // "--source", + // A_SPENDING_KEY, + // "--target", + // BERTHA, + // "--token", + // NAM, + // "--amount", + // &((amt200000 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) + // .to_string(), + // "--signer", + // ALBERT, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("Transaction is valid")?; + // client.assert_success(); + + // // Assert NAM balance at VK(A) is 0 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AA_VIEWING_KEY, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("No shielded NAM balance found")?; + // client.assert_success(); + + // // Assert NAM balance at VK(B) is 0 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("No shielded NAM balance found")?; + // client.assert_success(); + + // // Assert NAM balance at MASP pool is 0 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // MASP, + // "--token", + // NAM, + // "--ledger-address", + // &validator_one_rpc + // ], + // Some(300) + // )?; + // client.exp_string("NAM: 0")?; + // client.assert_success(); Ok(()) } diff --git a/wasm/checksums.json b/wasm/checksums.json index 9a09d988b5b..61ed29b2c38 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.025e4780cd7b63048d07832b940a3ee1c7f3a8be2e6135642bbf339f2fef9caa.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.c28d9ba24a8ad6726b750b71354c3a39174f52aa439ca382550f62558a21dabf.wasm", - "tx_ibc.wasm": "tx_ibc.dfd1d73d34b1dad76f3b37ca1caff4b817093bf88406f6391386f99a6b62a51f.wasm", - "tx_init_account.wasm": "tx_init_account.da0d005c0b44df80cd2fd286bfbeac9049a3bb2d9b153d0eae90f14129de396a.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.f75cf0d431d516a75d8a9868bcf11da6a97305f5d4eaa7d0a12b4711f9f96b21.wasm", - "tx_init_validator.wasm": "tx_init_validator.66b23482540998019986084204ffc1d3c22c731cdb8514ea05c89325df4de5aa.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.185c3215e683a8bad6a6e9625f073d96b30b625a27fb3bfa2dd0837478034b74.wasm", - "tx_transfer.wasm": "tx_transfer.8ac27a6fd4659c49fe6c0d8d0611cc894655380b278b1c0de2780d33b07ad95d.wasm", - "tx_unbond.wasm": "tx_unbond.b9d842aacd7965e28d7cb9429d7b2a62be025bd78ea062a0d3979927b5c2fe88.wasm", - "tx_update_vp.wasm": "tx_update_vp.8a23e2a22a11a7e59dd9fc4e64fc794a1ba457a3928cf2980e87b390d323ae2d.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.fd72df547bd4c1f42ba47db2b3977341868bff661580cd04c9ebc48abfd6268d.wasm", - "tx_withdraw.wasm": "tx_withdraw.ae6637ffc5b62df578c2286f60e8d48227e5286bd4784ef608a314767cbc2728.wasm", - "vp_implicit.wasm": "vp_implicit.1287fa256849c683181c4597b1d30f979b95046ab2c5ae64847ed18d2b98a551.wasm", - "vp_masp.wasm": "vp_masp.631d43384e0d2754817c922ef17f6b30ff1fa33a09ec79c5a1826ea0544fc177.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.2a910bd3c81c7fc5b46b712e6f65b30e531b32447ef6ed1037d23685fc0b3da2.wasm", - "vp_token.wasm": "vp_token.1fd2173eea7d15990390b9d0dcfb41bd663fa5b0248176db87dc5c97e06d84e7.wasm", - "vp_user.wasm": "vp_user.22c7d630a3c1d6bbd3d9d9beb818795c9dbe08e82e6f5c05ba35c5b8d11cfabf.wasm", - "vp_validator.wasm": "vp_validator.353fa237cb5a36f6ea302bfc6c669f7b847d625fddbe35d2cecd51df9e74eb3a.wasm" + "tx_bond.wasm": "tx_bond.f56c2b5087323936640f0ed4834cbc552350c71d23a330b984029e52469fd1c8.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.14684b6ca477a1faf1f22e153d323847b41f78835341c1d646e873c7ac634650.wasm", + "tx_ibc.wasm": "tx_ibc.c00455e52414d9347f0386791a323f705cbade50093fa8c00a918d67131b08e2.wasm", + "tx_init_account.wasm": "tx_init_account.a69f28b21cec7f13c5fbe9f96ece7dd5da29619fbeb6fc6e12bebbd4d736305f.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.b4a887f16884502756feb3b913555993c58d8305e518e0b93fda16097dcae544.wasm", + "tx_init_validator.wasm": "tx_init_validator.e367230c964672ef6ac61851d44065d2ddcae077e10fb9711d49e42e0b052ef3.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.00a374f624cef526f2d8427e93f7bb42c8d6f1bb20dd9bd699f49e235e0f2384.wasm", + "tx_transfer.wasm": "tx_transfer.36a4fcbf691562a251389373803e3f41cd28b3c892d56c7c9863d8285a113d0b.wasm", + "tx_unbond.wasm": "tx_unbond.d8d20942cb23a0084b1f20d8b3462cd11de5aad9bfce2f31efc4aeafa7296018.wasm", + "tx_update_vp.wasm": "tx_update_vp.8a35ef29f4157062de6630096b53a4f1bc91ca1a710c974e14307a32701c9563.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.979d783d14bf36c32a38b219ef1da2d774a8b1cd19e111e130ab545fb9b8a6b7.wasm", + "tx_withdraw.wasm": "tx_withdraw.7833537d46f9f5c85abc0912d6f290e6490a85d61dbcfd98a610bc17e5dd0f58.wasm", + "vp_implicit.wasm": "vp_implicit.efab4bb66d60d4084bcaed5541d16edf46877f059169d601ba42bef44e9b81c9.wasm", + "vp_masp.wasm": "vp_masp.bf50fbe641cca59a64a925a8557c81653b2b06067152050d968584f0d7e0cd54.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.f441be573f13c6bbcabf880de7f06b1990398683b46801e183b659333310851f.wasm", + "vp_token.wasm": "vp_token.3993c0ce822837714e2cea5ed659b4613663f5f645b451c477bf6b07d55846a6.wasm", + "vp_user.wasm": "vp_user.ec2719fec8208c541788dfd2c73385be786067d3d087983f0b8c34e432b76e18.wasm", + "vp_validator.wasm": "vp_validator.db53e41ebdb1e4dd25233680a1167e586827ca372e98f0fc3e48abfac373e4d4.wasm" } \ No newline at end of file From efef60c31ea12c66cf329f672ed5ffa8d724103c Mon Sep 17 00:00:00 2001 From: mariari Date: Thu, 16 Mar 2023 12:52:00 +0800 Subject: [PATCH 44/48] Now handle native inflation rewards for native tokens. --- core/src/ledger/storage/masp_conversions.rs | 74 ++++++++++++++++----- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index a87c12f3cfd..8f379aa52e9 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -18,8 +18,8 @@ use crate::types::{address, token}; /// A representation of the conversion state #[derive(Debug, Default, BorshSerialize, BorshDeserialize)] pub struct ConversionState { - /// The merkle root from the previous epoch - pub prev_root: Node, + /// The last amount of the native token distributed + pub normed_inflation: Option, /// The tree currently containing all the conversions pub tree: FrozenCommitmentTree, /// Map assets to their latest conversion and position in Merkle tree @@ -168,6 +168,8 @@ where D: super::DB + for<'iter> super::DBIter<'iter>, H: super::StorageHasher, { + use std::cmp::Ordering; + use masp_primitives::ff::PrimeField; use masp_primitives::transaction::components::Amount as MaspAmount; use rayon::iter::{ @@ -182,6 +184,18 @@ where let key_prefix: storage::Key = masp_addr.to_db_key().into(); let masp_rewards = address::masp_rewards(); + let mut masp_reward_keys: Vec<_> = masp_rewards.keys().collect(); + // Put the native rewards first because other inflation computations depend + // on it + masp_reward_keys.sort_unstable_by(|x, y| { + if (**x == address::nam()) == (**y == address::nam()) { + Ordering::Equal + } else if **x == address::nam() { + Ordering::Less + } else { + Ordering::Greater + } + }); // The total transparent value of the rewards being distributed let mut total_reward = token::Amount::from(0); @@ -197,7 +211,8 @@ where // Conversions from the previous to current asset for each address let mut current_convs = BTreeMap::::new(); // Reward all tokens according to above reward rates - for addr in masp_rewards.keys() { + + for addr in masp_reward_keys { let reward = calculate_masp_rewards(wl_storage, addr) .expect("Calculating the masp rewards should not fail"); // Dispence a transparent reward in parallel to the shielded rewards @@ -216,13 +231,47 @@ where encode_asset_type(addr.clone(), wl_storage.storage.last_epoch); let new_asset = encode_asset_type(addr.clone(), wl_storage.storage.block.epoch); - current_convs.insert( - addr.clone(), - (MaspAmount::from_pair(old_asset, -(reward.1 as i64)).unwrap() - + MaspAmount::from_pair(new_asset, reward.1).unwrap() - + MaspAmount::from_pair(reward_asset, reward.0).unwrap()) - .into(), - ); + // Native token inflation values are always with respect to this + let ref_inflation = masp_rewards[&address::nam()].1; + // Get the last rewarded amount of the native token + let normed_inflation = wl_storage + .storage + .conversion_state + .normed_inflation + .get_or_insert(ref_inflation); + if *addr == address::nam() { + // The amount that will be given of the new native token for every + // amount of the native token given in the previous epoch + let new_normed_inflation = + *normed_inflation + (*normed_inflation * reward.0) / reward.1; + // The conversion is computed such that if consecutive conversions + // are added together, the intermediate native tokens cancel/ + // telescope out + current_convs.insert( + addr.clone(), + (MaspAmount::from_pair(old_asset, -(*normed_inflation as i64)) + .unwrap() + + MaspAmount::from_pair(new_asset, new_normed_inflation) + .unwrap()) + .into(), + ); + // Save the new normed inflation + *normed_inflation = new_normed_inflation; + } else { + // Express the inflation reward in real terms, that is, with respect + // to the native asset in the zeroth epoch + let real_reward = (reward.0 * ref_inflation) / *normed_inflation; + // The conversion is computed such that if consecutive conversions + // are added together, the intermediate tokens cancel/ telescope out + current_convs.insert( + addr.clone(), + (MaspAmount::from_pair(old_asset, -(reward.1 as i64)).unwrap() + + MaspAmount::from_pair(new_asset, reward.1).unwrap() + + MaspAmount::from_pair(reward_asset, real_reward) + .unwrap()) + .into(), + ); + } // Add a conversion from the previous asset type wl_storage.storage.conversion_state.assets.insert( old_asset, @@ -283,11 +332,6 @@ where .map(FrozenCommitmentTree::new) .collect(); - // Keep the merkle root from the old tree for transactions constructed - // close to the epoch boundary - wl_storage.storage.conversion_state.prev_root = - wl_storage.storage.conversion_state.tree.root(); - // Convert conversion vector into tree so that Merkle paths can be // obtained wl_storage.storage.conversion_state.tree = From 95357f3bf02a4ddc6a5a7b266fac518a53293946 Mon Sep 17 00:00:00 2001 From: mariari Date: Thu, 16 Mar 2023 12:53:45 +0800 Subject: [PATCH 45/48] Corrected the compounding of the MASP transparent balance. --- core/src/ledger/storage/masp_conversions.rs | 14 +++++--- wasm/checksums.json | 38 ++++++++++---------- wasm_for_tests/tx_no_op.wasm | Bin 25554 -> 25554 bytes 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index 8f379aa52e9..accd472e3fa 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -219,11 +219,6 @@ where let addr_bal: token::Amount = wl_storage .read(&token::balance_key(addr, &masp_addr))? .unwrap_or_default(); - // The reward for each reward.1 units of the current asset is - // reward.0 units of the reward token - // Since floor(a) + floor(b) <= floor(a+b), there will always be - // enough rewards to reimburse users - total_reward += (addr_bal * reward).0; // Provide an allowed conversion from previous timestamp. The // negative sign allows each instance of the old asset to be // cancelled out/replaced with the new asset @@ -255,6 +250,10 @@ where .unwrap()) .into(), ); + // The reward for each reward.1 units of the current asset is + // reward.0 units of the reward token + total_reward += + (addr_bal * (new_normed_inflation, *normed_inflation)).0 - addr_bal; // Save the new normed inflation *normed_inflation = new_normed_inflation; } else { @@ -271,6 +270,11 @@ where .unwrap()) .into(), ); + // The reward for each reward.1 units of the current asset is + // reward.0 units of the reward token + total_reward += + ((addr_bal * (real_reward, reward.1)).0 * + (*normed_inflation, ref_inflation)).0; } // Add a conversion from the previous asset type wl_storage.storage.conversion_state.assets.insert( diff --git a/wasm/checksums.json b/wasm/checksums.json index 61ed29b2c38..312bcd5307b 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.f56c2b5087323936640f0ed4834cbc552350c71d23a330b984029e52469fd1c8.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.14684b6ca477a1faf1f22e153d323847b41f78835341c1d646e873c7ac634650.wasm", - "tx_ibc.wasm": "tx_ibc.c00455e52414d9347f0386791a323f705cbade50093fa8c00a918d67131b08e2.wasm", - "tx_init_account.wasm": "tx_init_account.a69f28b21cec7f13c5fbe9f96ece7dd5da29619fbeb6fc6e12bebbd4d736305f.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.b4a887f16884502756feb3b913555993c58d8305e518e0b93fda16097dcae544.wasm", - "tx_init_validator.wasm": "tx_init_validator.e367230c964672ef6ac61851d44065d2ddcae077e10fb9711d49e42e0b052ef3.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.00a374f624cef526f2d8427e93f7bb42c8d6f1bb20dd9bd699f49e235e0f2384.wasm", - "tx_transfer.wasm": "tx_transfer.36a4fcbf691562a251389373803e3f41cd28b3c892d56c7c9863d8285a113d0b.wasm", - "tx_unbond.wasm": "tx_unbond.d8d20942cb23a0084b1f20d8b3462cd11de5aad9bfce2f31efc4aeafa7296018.wasm", - "tx_update_vp.wasm": "tx_update_vp.8a35ef29f4157062de6630096b53a4f1bc91ca1a710c974e14307a32701c9563.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.979d783d14bf36c32a38b219ef1da2d774a8b1cd19e111e130ab545fb9b8a6b7.wasm", - "tx_withdraw.wasm": "tx_withdraw.7833537d46f9f5c85abc0912d6f290e6490a85d61dbcfd98a610bc17e5dd0f58.wasm", - "vp_implicit.wasm": "vp_implicit.efab4bb66d60d4084bcaed5541d16edf46877f059169d601ba42bef44e9b81c9.wasm", - "vp_masp.wasm": "vp_masp.bf50fbe641cca59a64a925a8557c81653b2b06067152050d968584f0d7e0cd54.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.f441be573f13c6bbcabf880de7f06b1990398683b46801e183b659333310851f.wasm", - "vp_token.wasm": "vp_token.3993c0ce822837714e2cea5ed659b4613663f5f645b451c477bf6b07d55846a6.wasm", - "vp_user.wasm": "vp_user.ec2719fec8208c541788dfd2c73385be786067d3d087983f0b8c34e432b76e18.wasm", - "vp_validator.wasm": "vp_validator.db53e41ebdb1e4dd25233680a1167e586827ca372e98f0fc3e48abfac373e4d4.wasm" -} \ No newline at end of file + "tx_bond.wasm": "tx_bond.8d7d124ac92b9f014522ddb21b651186d4530f55462d8be4ebeda10f4ccaeaed.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.20d7f3ad9431a2d22e0b955622ac66b99a94a84fb8ad0828ad16fcb426335472.wasm", + "tx_ibc.wasm": "tx_ibc.00179813a013f997f0c192938eb231dcfff1ffc79304e36af1ff470ab540e649.wasm", + "tx_init_account.wasm": "tx_init_account.9fade81282d4e299460d777888d0259cfefa558731285fd54f780a914c84c0e5.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.db5211f020325da4a2209fdca803b87989e23600490861f59b37e74e84a80109.wasm", + "tx_init_validator.wasm": "tx_init_validator.1539bac0ecb1aeebcbcdd299d5f887de9283d56a05eec2f26826822616c651f5.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.fa96359e712c2eb98ba7b1498d4df5fa79c38a780fe06e3628f0d749bb1423e3.wasm", + "tx_transfer.wasm": "tx_transfer.0839693d13d7a7d24aec254c532b50752425eae704a2da826f544f27821ea3f2.wasm", + "tx_unbond.wasm": "tx_unbond.a489091c72da12a76dd3665260ab8c69569e45632cb86c0b31d00000d4afccb7.wasm", + "tx_update_vp.wasm": "tx_update_vp.caf7814b442e86f5ae1b4dae1edf13cba6fcf8c997f4735df97649a08bd4d223.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.8fdf692589ee2e7cb37f76ffd8bb25e869498f3158a52c33ea4dc305e9dfea15.wasm", + "tx_withdraw.wasm": "tx_withdraw.0265190e0d29ea96820c76e5aa5e0adc7dac6e087f3f72ebc4451047afb0a65e.wasm", + "vp_implicit.wasm": "vp_implicit.cabb4658d17a95c011e9782ac921360a801442681589bc8a20ecc88680476691.wasm", + "vp_masp.wasm": "vp_masp.5576090f52935f7763d8ca48fb559312cf65cc928c68598e299062adb29140ec.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.f7b5d93ff4fa09332390ffc8fae6894706fc9becefbaa4d3fb311326848f507e.wasm", + "vp_token.wasm": "vp_token.3a9a7d3b6f590a8b15cb0d1e93d99d37ced5604de8f6a52527bed680661f9d3d.wasm", + "vp_user.wasm": "vp_user.1d0a07ed40765cff97c85a2a9fb467dfe7af56bc51a4b95ad12ed77aea82e8a9.wasm", + "vp_validator.wasm": "vp_validator.de713b8aa04874c4af9620db6db0009a83a1795759302066753608abb98d5745.wasm" +} diff --git a/wasm_for_tests/tx_no_op.wasm b/wasm_for_tests/tx_no_op.wasm index 105a68cd1b7b12afd3382e087fea275772fba9a7..160795df998e80776d5a8be0ede94659ace00623 100755 GIT binary patch delta 83 zcmca~obl3e#tr(6LXI5`j0!A{HHAtnKnlq5-R#IXiB;6GK|q1Ykts`wQGqcBL;wYR dC%<4b5OQ1q)B%(#QDRkK1#x^gYq7700RYL771#g( delta 83 zcmca~obl3e#tr(6LJBO7HHAtnjvWn*Kqip%-t5RYiB(jA$&o2biBW+uM~Ts~K|ldS ccu#)8W*`KTDN$l|TmaMoCcQUnv9E{$0J|X-(f|Me From 38bb6bb51da8ddde2ac53e8406a849dceffdc753 Mon Sep 17 00:00:00 2001 From: mariari Date: Thu, 16 Mar 2023 15:58:40 +0800 Subject: [PATCH 46/48] Update the test with the correct inflation values --- tests/src/e2e/ledger_tests.rs | 8 ++++---- wasm/checksums.json | 38 +++++++++++++++++------------------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 6d5bc059a0c..db57e6fba87 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -954,10 +954,10 @@ fn masp_incentives() -> Result<()> { account_config.parameters.map(|parameters| { if token == String::from(NAM) { Parameters { - max_reward_rate: dec!(100000.5), + max_reward_rate: dec!(1000.5), // these need to be set to 0 - kd_gain_nom: dec!(0.0), - kp_gain_nom: dec!(0.0), + kd_gain_nom: dec!(0.005), + kp_gain_nom: dec!(0.005), ..parameters } } else { @@ -1183,7 +1183,7 @@ fn masp_incentives() -> Result<()> { ], Some(300) )?; - client.exp_string("NAM: 253690200000")?; + client.exp_string("NAM: 26296240")?; client.assert_success(); // // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_2-epoch_0) diff --git a/wasm/checksums.json b/wasm/checksums.json index 312bcd5307b..3400d1a135a 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.8d7d124ac92b9f014522ddb21b651186d4530f55462d8be4ebeda10f4ccaeaed.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.20d7f3ad9431a2d22e0b955622ac66b99a94a84fb8ad0828ad16fcb426335472.wasm", - "tx_ibc.wasm": "tx_ibc.00179813a013f997f0c192938eb231dcfff1ffc79304e36af1ff470ab540e649.wasm", - "tx_init_account.wasm": "tx_init_account.9fade81282d4e299460d777888d0259cfefa558731285fd54f780a914c84c0e5.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.db5211f020325da4a2209fdca803b87989e23600490861f59b37e74e84a80109.wasm", - "tx_init_validator.wasm": "tx_init_validator.1539bac0ecb1aeebcbcdd299d5f887de9283d56a05eec2f26826822616c651f5.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.fa96359e712c2eb98ba7b1498d4df5fa79c38a780fe06e3628f0d749bb1423e3.wasm", - "tx_transfer.wasm": "tx_transfer.0839693d13d7a7d24aec254c532b50752425eae704a2da826f544f27821ea3f2.wasm", - "tx_unbond.wasm": "tx_unbond.a489091c72da12a76dd3665260ab8c69569e45632cb86c0b31d00000d4afccb7.wasm", - "tx_update_vp.wasm": "tx_update_vp.caf7814b442e86f5ae1b4dae1edf13cba6fcf8c997f4735df97649a08bd4d223.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.8fdf692589ee2e7cb37f76ffd8bb25e869498f3158a52c33ea4dc305e9dfea15.wasm", - "tx_withdraw.wasm": "tx_withdraw.0265190e0d29ea96820c76e5aa5e0adc7dac6e087f3f72ebc4451047afb0a65e.wasm", - "vp_implicit.wasm": "vp_implicit.cabb4658d17a95c011e9782ac921360a801442681589bc8a20ecc88680476691.wasm", - "vp_masp.wasm": "vp_masp.5576090f52935f7763d8ca48fb559312cf65cc928c68598e299062adb29140ec.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.f7b5d93ff4fa09332390ffc8fae6894706fc9becefbaa4d3fb311326848f507e.wasm", - "vp_token.wasm": "vp_token.3a9a7d3b6f590a8b15cb0d1e93d99d37ced5604de8f6a52527bed680661f9d3d.wasm", - "vp_user.wasm": "vp_user.1d0a07ed40765cff97c85a2a9fb467dfe7af56bc51a4b95ad12ed77aea82e8a9.wasm", - "vp_validator.wasm": "vp_validator.de713b8aa04874c4af9620db6db0009a83a1795759302066753608abb98d5745.wasm" -} + "tx_bond.wasm": "tx_bond.2910631adf11b0368d2f09ce30ecba7ca7a18a38e17c00d920765075b4064a14.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.d4f15084da1355bf2749f2bcb380a89d3a4b90cf00288daf0fc975208c9d93e7.wasm", + "tx_ibc.wasm": "tx_ibc.80df46ff21d54755af23a6a7395c72fc320fbc61e9f5487a438ec4a306ba1484.wasm", + "tx_init_account.wasm": "tx_init_account.ff822951455cc89960d3ca1221488f1271012b38629c5a9662bb930595bb1997.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.0a842c6e0bfa10f3d2261048c28e84bd8e775f323d7db740ec656339298cfbd8.wasm", + "tx_init_validator.wasm": "tx_init_validator.9f1e01de1dc5f09b649ef34226e6064ddf39035fec3259edcc46858997df1f15.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.bd9674f90665eafb89f6c96d33760b22e5f33244cecbd29184bf1fe4168dc1ce.wasm", + "tx_transfer.wasm": "tx_transfer.f1f313acc5d1da6eed60deeb6b95a4bf57d5011e64da4c1baf8107ae2986e44c.wasm", + "tx_unbond.wasm": "tx_unbond.ba355d2459b55234f27f793132735d80b5daf5f70196ffcbe4bbe8bd64951cf3.wasm", + "tx_update_vp.wasm": "tx_update_vp.56c511136750585d0b5ee2ddadc98932569993bfb382b179868e653036564a26.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.eff1f05103c2ae1a2f33a8dee67d7fc01067b1951d99c1d147b74a6ca119d470.wasm", + "tx_withdraw.wasm": "tx_withdraw.c61702c41e8fa51c9ed77d67e934dd9eaa8fea54ff1e12cac841d14f6611c98d.wasm", + "vp_implicit.wasm": "vp_implicit.7d6bb83dce87d30c5cf209a5301679bbcf91cd15e031ef22f15429498f5f9eb5.wasm", + "vp_masp.wasm": "vp_masp.fc8e582dc105e2dd9d08c58459df2f98753c550c9b43cd54d07570ff24c92d9e.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.08c662d6dab741cbef58a6c95f22646ab08df4769fcee4059c47bcb9185bf3a9.wasm", + "vp_token.wasm": "vp_token.2824f68745a628faa79f7fa2bdf6582809c969f9fdca68913b4e84e5e4a83852.wasm", + "vp_user.wasm": "vp_user.af8d1c7035ced526ea22108a1226a61e3eeff4db919cb73df8d6ec42ec2018ad.wasm", + "vp_validator.wasm": "vp_validator.ec97ca9f091c6f2cbe558f98b39bc2a699bf9aa780c47b405b4bd1dd8ec68dad.wasm" +} \ No newline at end of file From 67d9ce5278a4ab08f4e84fffda1399c32d086857 Mon Sep 17 00:00:00 2001 From: mariari Date: Wed, 14 Jun 2023 12:26:23 +0800 Subject: [PATCH 47/48] Fixup clippy issues --- tests/src/e2e/ledger_tests.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 1bf5ad4514a..1d8afac7dcc 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -19,7 +19,7 @@ use std::time::{Duration, Instant}; use borsh::BorshSerialize; use color_eyre::eyre::Result; use data_encoding::HEXLOWER; -use namada::types::address::{btc, eth, masp_rewards, Address}; +use namada::types::address::{masp_rewards, Address}; use namada::types::governance::ProposalType; use namada::types::storage::Epoch; use namada::types::token; @@ -1033,7 +1033,7 @@ fn masp_incentives() -> Result<()> { (token.clone(), { let parameters = account_config.parameters.map(|parameters| { - if token == String::from(NAM) { + if token == *NAM { Parameters { max_reward_rate: dec!(1000.5), // these need to be set to 0 @@ -1096,7 +1096,7 @@ fn masp_incentives() -> Result<()> { let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); // Wait till epoch boundary - let ep0 = epoch_sleep(&test, &validator_one_rpc, 7200000)?; + let _ep0 = epoch_sleep(&test, &validator_one_rpc, 7200000)?; // Send 200000 BTC from Albert to PA(A) let mut client = run!( @@ -1156,10 +1156,10 @@ fn masp_incentives() -> Result<()> { client.exp_string("No shielded nam balance found")?; client.assert_success(); - let masp_rewards = masp_rewards(); + let _masp_rewards = masp_rewards(); // Wait till epoch boundary - let ep1 = epoch_sleep(&test, &validator_one_rpc, 7200000)?; + let _ep1 = epoch_sleep(&test, &validator_one_rpc, 7200000)?; // Assert BTC balance at VK(A) is 200000 let mut client = run!( @@ -1179,8 +1179,8 @@ fn masp_incentives() -> Result<()> { client.exp_string("btc: 200000")?; client.assert_success(); - let amt200000 = token::Amount::from_str("200000").unwrap(); - let amt30 = token::Amount::from_str("30").unwrap(); + let _amt200000 = token::Amount::from_str("200000").unwrap(); + let _amt30 = token::Amount::from_str("30").unwrap(); // Assert NAM balance at VK(A) is 200000*BTC_reward*(epoch_1-epoch_0) @@ -1229,7 +1229,7 @@ fn masp_incentives() -> Result<()> { client.assert_success(); // Wait till epoch boundary - let ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; + let _ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; // Assert BTC balance at VK(A) is 20 let mut client = run!( From 9cab267bfe6cc9f8cb9a952dddca31e6d8b6f21d Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 3 Jul 2023 21:12:41 +0800 Subject: [PATCH 48/48] masp: implement remaining inflation logic Inflation logic that works with 256-bit Amounts was deferred until now. We correct the implementation to adjust rewards for words above the lowest. (Rewards are only awarded in the lowest denomination of NAM, which is the only one used; if a reward exceeds i64::MAX, this is certainly an error.) For nonstandard (non-10^6) denominations, we must also perform an adjustment in decimal places. This will apply to ETH in reality, and our test ETH now. --- apps/src/lib/config/genesis.rs | 20 +- core/src/ledger/inflation.rs | 13 +- core/src/ledger/storage/masp_conversions.rs | 215 +++++++++++------ core/src/ledger/storage/mod.rs | 11 +- core/src/types/address.rs | 22 +- core/src/types/token.rs | 14 ++ core/src/types/token/parameters.rs | 21 +- genesis/dev.toml | 4 +- proof_of_stake/src/lib.rs | 9 +- tests/src/e2e/ledger_tests.rs | 248 +++++++++++++++++++- tests/src/e2e/setup.rs | 1 + wasm/checksums.json | 38 +-- 12 files changed, 462 insertions(+), 154 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 312c8e4a858..419f4aa3707 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -15,6 +15,7 @@ use namada::types::key::dkg_session_keys::DkgPublicKey; use namada::types::key::*; use namada::types::time::{DateTimeUtc, DurationSecs}; use namada::types::token::Denomination; +use namada::types::uint::I256; use namada::types::{storage, token}; /// Genesis configuration file format @@ -38,6 +39,7 @@ pub mod genesis_config { use namada::types::key::*; use namada::types::time::Rfc3339String; use namada::types::token::Denomination; + use namada::types::uint::I256; use namada::types::{storage, token}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -395,8 +397,8 @@ pub mod genesis_config { let token_vp_config = wasm.get(token_vp_name).unwrap(); TokenAccount { - last_locked_ratio: Decimal::ZERO, - last_inflation: 0, + last_locked_ratio: Dec::zero(), + last_inflation: I256::zero(), parameters: config.parameters.as_ref().unwrap().to_owned(), address: Address::decode(config.address.as_ref().unwrap()).unwrap(), denom: config.denom, @@ -816,9 +818,9 @@ pub struct TokenAccount { /// Token parameters pub parameters: token::parameters::Parameters, /// Token inflation from the last epoch (read + write for every epoch) - pub last_inflation: u64, + pub last_inflation: I256, /// Token shielded ratio from the last epoch (read + write for every epoch) - pub last_locked_ratio: Decimal, + pub last_locked_ratio: Dec, } #[derive( @@ -1038,17 +1040,17 @@ pub fn genesis(num_validators: u64) -> Genesis { balances.insert((&validator.account_key).into(), default_key_tokens); } - let token_accounts = address::masp_rewards() - .into_keys() - .map(|address| TokenAccount { + let token_accounts = address::tokens() + .into_iter() + .map(|(address, (_, denom))| TokenAccount { address, denom, vp_code_path: vp_token_path.into(), vp_sha256: Default::default(), balances: balances.clone(), parameters: token::parameters::Parameters::default(), - last_inflation: 0, - last_locked_ratio: Decimal::ZERO, + last_inflation: I256::zero(), + last_locked_ratio: Dec::zero(), }) .collect(); Genesis { diff --git a/core/src/ledger/inflation.rs b/core/src/ledger/inflation.rs index e3bdaf88b8f..041d023735f 100644 --- a/core/src/ledger/inflation.rs +++ b/core/src/ledger/inflation.rs @@ -2,10 +2,9 @@ //! proof-of-stake, providing liquity to shielded asset pools, and public goods //! funding. -use namada_core::types::dec::Dec; - use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; use crate::types::address::Address; +use crate::types::dec::Dec; use crate::types::token; /// The domains of inflation @@ -54,12 +53,12 @@ impl RewardsController { pub fn new( locked_tokens: token::Amount, total_tokens: token::Amount, - locked_ratio_target: Decimal, - locked_ratio_last: Decimal, - max_reward_rate: Decimal, + locked_ratio_target: Dec, + locked_ratio_last: Dec, + max_reward_rate: Dec, last_inflation_amount: token::Amount, - p_gain_nom: Decimal, - d_gain_nom: Decimal, + p_gain_nom: Dec, + d_gain_nom: Dec, epochs_per_year: u64, ) -> Self { Self { diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index 61bf9bab22f..024d09d0d8d 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -10,17 +10,20 @@ use masp_primitives::sapling::Node; use crate::ledger::inflation::{mint_tokens, RewardsController, ValsToUpdate}; use crate::ledger::parameters; +use crate::ledger::storage_api::token::read_denom; use crate::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; use crate::types::address::Address; -use crate::types::{address, token}; +use crate::types::dec::Dec; use crate::types::storage::{Epoch, Key}; use crate::types::token::MaspDenom; +use crate::types::uint::{Uint, I256}; +use crate::types::{address, token}; /// A representation of the conversion state #[derive(Debug, Default, BorshSerialize, BorshDeserialize)] pub struct ConversionState { /// The last amount of the native token distributed - pub normed_inflation: Option, + pub normed_inflation: Option, /// The tree currently containing all the conversions pub tree: FrozenCommitmentTree, /// Map assets to their latest conversion and position in Merkle tree @@ -36,16 +39,16 @@ pub struct ConversionState { >, } +#[cfg(feature = "wasm-runtime")] fn calculate_masp_rewards( wl_storage: &mut super::WlStorage, addr: &Address, -) -> crate::ledger::storage_api::Result<(u64, u64)> + sub_prefix: Option, +) -> crate::ledger::storage_api::Result<(I256, I256)> where D: 'static + super::DB + for<'iter> super::DBIter<'iter>, H: 'static + super::StorageHasher, { - use rust_decimal::Decimal; - let masp_addr = address::masp(); // Query the storage for information @@ -59,38 +62,53 @@ where .read(&token::balance_key(addr, &masp_addr))? .unwrap_or_default(); + let denomination = read_denom(wl_storage, addr, sub_prefix.as_ref()) + .unwrap() + .unwrap(); + + let denomination_base = + read_denom(wl_storage, &wl_storage.get_native_token().unwrap(), None) + .unwrap() + .unwrap(); + + let denomination_offset = + 10u64.pow((denomination.0 - denomination_base.0) as u32); + let conversion = |amt| amt / denomination_offset; + let total_tokens = conversion(total_tokens); + let total_token_in_masp = conversion(total_token_in_masp); + let epochs_per_year: u64 = wl_storage .read(¶meters::storage::get_epochs_per_year_key())? .expect(""); //// Values from the last epoch - let last_inflation: u64 = wl_storage + let last_inflation: I256 = wl_storage .read(&token::last_inflation(addr)) .expect("failure to read last inflation") .expect(""); - let last_locked_ratio: Decimal = wl_storage + let last_locked_ratio: Dec = wl_storage .read(&token::last_locked_ratio(addr)) .expect("failure to read last inflation") .expect(""); //// Parameters for each token - let max_reward_rate: Decimal = wl_storage + let max_reward_rate: Dec = wl_storage .read(&token::parameters::max_reward_rate(addr)) .expect("max reward should properly decode") .expect(""); - let kp_gain_nom: Decimal = wl_storage + let kp_gain_nom: Dec = wl_storage .read(&token::parameters::kp_sp_gain(addr)) .expect("kp_gain_nom reward should properly decode") .expect(""); - let kd_gain_nom: Decimal = wl_storage + let kd_gain_nom: Dec = wl_storage .read(&token::parameters::kd_sp_gain(addr)) .expect("kd_gain_nom reward should properly decode") .expect(""); - let locked_target_ratio: Decimal = wl_storage + let locked_target_ratio: Dec = wl_storage .read(&token::parameters::locked_token_ratio(addr))? .expect(""); @@ -116,13 +134,14 @@ where // ∴ n = (inflation * 100) / locked tokens // Since we must put the notes in a compatible format with the // note format, we must make the inflation amount discrete. - let total_in = total_token_in_masp.change() as u64; - let noterized_inflation = if 0u64 == total_in { - 0u64 + let total_in = total_token_in_masp.change(); + let noterized_inflation = if total_in.is_zero() { + I256::zero() } else { - ((100 * inflation as u128) / (total_token_in_masp.change() as u128)) - as u64 + I256::from(100 * inflation) / (total_token_in_masp.change()) }; + let clamped_inflation = + I256::max(noterized_inflation, I256::from(i64::MAX)); tracing::debug!( "Controller, call: total_in_masp {:?}, total_tokens {:?}, \ @@ -139,11 +158,6 @@ where kd_gain_nom, epochs_per_year, ); - tracing::debug!("Please give me: {:?}", addr); - tracing::debug!("Ratio {:?}", locked_ratio); - tracing::debug!("inflation from the pd controller {:?}", inflation); - tracing::debug!("total in the masp {:?}", total_in); - tracing::debug!("Please give me inflation: {:?}", noterized_inflation); // Is it fine to write the inflation rate, this is accurate, // but we should make sure the return value's ratio matches @@ -152,8 +166,8 @@ where wl_storage .write( &token::last_inflation(addr), - (noterized_inflation / 100u64) - * total_token_in_masp.change() as u64, + (clamped_inflation / I256::from(100)) + * total_token_in_masp.change() as I256, ) .expect("unable to encode new inflation rate (Decimal)"); @@ -166,7 +180,7 @@ where // function This may be unneeded, as we could describe it as a // ratio of x/1 - Ok((noterized_inflation, 100)) + Ok((clamped_inflation, I256::from(100 * denomination_offset))) } // This is only enabled when "wasm-runtime" is on, because we're using rayon @@ -194,14 +208,15 @@ where let masp_addr = address::masp(); let key_prefix: storage::Key = masp_addr.to_db_key().into(); + let native_token = wl_storage.get_native_token().unwrap(); let masp_rewards = address::masp_rewards(); let mut masp_reward_keys: Vec<_> = masp_rewards.keys().collect(); // Put the native rewards first because other inflation computations depend // on it - masp_reward_keys.sort_unstable_by(|x, y| { - if (**x == address::nam()) == (**y == address::nam()) { + masp_reward_keys.sort_unstable_by(|(x, _key), (y, _)| { + if (*x == native_token) == (*y == native_token) { Ordering::Equal - } else if **x == address::nam() { + } else if *x == native_token { Ordering::Less } else { Ordering::Greater @@ -215,25 +230,26 @@ where // have to use. This trick works under the assumption that reward tokens // from different epochs are exactly equivalent. let reward_asset = - encode_asset_type(address::nam(), &None, MaspDenom::Zero, Epoch(0)); + encode_asset_type(native_token, &None, MaspDenom::Zero, Epoch(0)); // Conversions from the previous to current asset for each address let mut current_convs = BTreeMap::<(Address, Option, MaspDenom), AllowedConversion>::new(); // Reward all tokens according to above reward rates - for ((addr, sub_prefix), reward) in &masp_rewards { + for (addr, sub_prefix) in masp_rewards.keys() { + // TODO please intergate this into the logic + let reward = + calculate_masp_rewards(wl_storage, addr, sub_prefix.clone())?; + // TODO Fix for multiple inflation // Native token inflation values are always with respect to this - let ref_inflation = masp_rewards[&address::nam()].1; + let ref_inflation = I256::from(1); // Get the last rewarded amount of the native token - let normed_inflation = wl_storage + let normed_inflation = *wl_storage .storage .conversion_state .normed_inflation .get_or_insert(ref_inflation); - - - // Dispense a transparent reward in parallel to the shielded rewards let addr_bal: token::Amount = match sub_prefix { None => wl_storage @@ -246,13 +262,58 @@ where ))? .unwrap_or_default(), }; - // The reward for each reward.1 units of the current asset is - // reward.0 units of the reward token - // Since floor(a) + floor(b) <= floor(a+b), there will always be - // enough rewards to reimburse users - total_reward += (addr_bal * *reward).0; + + let mut new_normed_inflation = I256::zero(); + let mut real_reward = I256::zero(); + + // TODO properly fix + if *addr == address::nam() { + // The amount that will be given of the new native token for + // every amount of the native token given in the + // previous epoch + new_normed_inflation = + normed_inflation + (normed_inflation * reward.0) / reward.1; + + println!("=============================================="); + println!( + "reward before nam total_reward: {}", + total_reward.to_string_native() + ); + println!("=============================================="); + // The reward for each reward.1 units of the current asset is + // reward.0 units of the reward token + total_reward += + (addr_bal * (new_normed_inflation, normed_inflation)).0 + - addr_bal; + // Save the new normed inflation + _ = wl_storage + .storage + .conversion_state + .normed_inflation + .insert(new_normed_inflation); + } else { + // Express the inflation reward in real terms, that is, with + // respect to the native asset in the zeroth + // epoch + real_reward = (reward.0 * ref_inflation) / normed_inflation; + + println!("=============================================="); + println!( + "reward before non nam total_reward: {}", + total_reward.to_string_native() + ); + println!("=============================================="); + // The reward for each reward.1 units of the current asset is + // reward.0 units of the reward token + total_reward += ((addr_bal * (real_reward, reward.1)).0 + * (normed_inflation, ref_inflation)) + .0; + } for denom in token::MaspDenom::iter() { + let total_reward_multiplier = + Uint::pow(2.into(), (denom as u64 * 64).into()); + let total_reward = total_reward * total_reward_multiplier; // Provide an allowed conversion from previous timestamp. The // negative sign allows each instance of the old asset to be // cancelled out/replaced with the new asset @@ -268,51 +329,54 @@ where denom, wl_storage.storage.block.epoch, ); - // TODO properly fix + + println!("=============================================="); + println!( + "final total_reward for denom {:?}: {:?}", + denom, total_reward + ); + println!("=============================================="); + if *addr == address::nam() { - // The amount that will be given of the new native token for every - // amount of the native token given in the previous epoch let new_normed_inflation = - *normed_inflation + (*normed_inflation * reward.0) / reward.1; - // The conversion is computed such that if consecutive conversions - // are added together, the intermediate native tokens cancel/ + new_normed_inflation % I256::from(u64::MAX); + // The conversion is computed such that if consecutive + // conversions are added together, the + // intermediate native tokens cancel/ // telescope out current_convs.insert( - addr.clone(), - (MaspAmount::from_pair(old_asset, -(*normed_inflation as i64)) - .unwrap() - + MaspAmount::from_pair(new_asset, new_normed_inflation) - .unwrap()) - .into(), + (addr.clone(), sub_prefix.clone(), denom), + (MaspAmount::from_pair(old_asset, -(normed_inflation)) + .unwrap() + + MaspAmount::from_pair( + new_asset, + new_normed_inflation, + ) + .unwrap()) + .into(), ); - // The reward for each reward.1 units of the current asset is - // reward.0 units of the reward token - total_reward += - (addr_bal * (new_normed_inflation, *normed_inflation)).0 - - addr_bal; - // Save the new normed inflation - *normed_inflation = new_normed_inflation; } else { - // Express the inflation reward in real terms, that is, with respect - // to the native asset in the zeroth epoch - let real_reward = (reward.0 * ref_inflation) / *normed_inflation; - // The conversion is computed such that if consecutive conversions - // are added together, the intermediate tokens cancel/ telescope out + let real_reward = real_reward % I256::from(u64::MAX); + // The conversion is computed such that if consecutive + // conversions are added together, the + // intermediate tokens cancel/ telescope out current_convs.insert( - addr.clone(), - (MaspAmount::from_pair(old_asset, -(reward.1 as i64)).unwrap() - + MaspAmount::from_pair(new_asset, reward.1).unwrap() - + MaspAmount::from_pair(reward_asset, real_reward) - .unwrap()) - .into(), + (addr.clone(), sub_prefix.clone(), denom), + (MaspAmount::from_pair(old_asset, -(reward.1)).unwrap() + + MaspAmount::from_pair(new_asset, reward.1).unwrap() + + MaspAmount::from_pair(reward_asset, real_reward) + .unwrap()) + .into(), ); - // The reward for each reward.1 units of the current asset is - // reward.0 units of the reward token - total_reward += ((addr_bal * (real_reward, reward.1)).0 - * (*normed_inflation, ref_inflation)) - .0; } + // Add a conversion from the previous asset type + println!("=============================================="); + println!("inserting conversions now"); + println!("old_asset: {}", old_asset); + println!("denom: {:?}", denom); + println!("addr, sub_prefix: {:?}", (addr, sub_prefix)); + println!("=============================================="); wl_storage.storage.conversion_state.assets.insert( old_asset, ( @@ -358,6 +422,9 @@ where // Update the MASP's transparent reward token balance to ensure that it // is sufficiently backed to redeem rewards + println!("=============================================="); + println!("current total_reward: {}", total_reward.to_string_native()); + println!("=============================================="); mint_tokens(wl_storage, &masp_addr, &address::nam(), total_reward)?; // Try to distribute Merkle tree construction as evenly as possible diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 417a4864e2d..7d99fd7426d 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -990,15 +990,16 @@ impl From for Error { /// Helpers for testing components that depend on storage #[cfg(any(test, feature = "testing"))] pub mod testing { + use std::str::FromStr; + use borsh::BorshSerialize; - use rust_decimal::Decimal; - use rust_decimal_macros::dec; use super::mockdb::MockDB; use super::*; use crate::ledger::storage::traits::Sha256Hasher; use crate::ledger::storage_api::StorageWrite; use crate::types::address; + use crate::types::dec::Dec; use crate::types::token::parameters; /// `WlStorage` with a mock DB for testing @@ -1064,14 +1065,14 @@ pub mod testing { ) { let masp_rewards = address::masp_rewards(); let masp_addr = address::masp(); - for addr in masp_rewards.keys() { + for (addr, _key) in masp_rewards.keys() { parameters::Parameters::init_storage( ¶meters::Parameters::default(), addr, self, ); - let initial_inflation: u64 = 1; - let initial_locked_ratio: Decimal = dec!(0.1); + let initial_inflation: u128 = 1; + let initial_locked_ratio: Dec = Dec::from_str("0.1").unwrap(); self.write(&token::last_inflation(addr), initial_inflation) .expect("Should not fail to put a test inflation source"); diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 2372c66bb74..a9457321299 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -9,7 +9,6 @@ use std::str::FromStr; use bech32::{self, FromBase32, ToBase32, Variant}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::HEXUPPER; -use rust_decimal_macros::dec; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; @@ -18,10 +17,12 @@ use crate::ibc::signer::Signer; use crate::ledger::parameters; use crate::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; use crate::ledger::storage_api::StorageWrite; +use crate::types::dec::Dec; use crate::types::key::PublicKeyHash; -use crate::types::{key, token}; use crate::types::storage::Key; use crate::types::token::Denomination; +use crate::types::uint::I256; +use crate::types::{key, token}; /// The length of an established [`Address`] encoded with Borsh. pub const ESTABLISHED_ADDRESS_BYTES_LEN: usize = 21; @@ -686,22 +687,25 @@ pub fn init_token_storage( epochs_per_year, ) .unwrap(); - for address in masp_reward_keys { + for (address, _key) in masp_reward_keys { wl_storage .write( &token::total_supply_key(address), - token::Amount::whole(5), // arbitrary amount + token::Amount::native_whole(5), // arbitrary amount ) .unwrap(); - let default_gain = dec!(0.1); + let default_gain = Dec::from_str("0.1").unwrap(); wl_storage - .write(&token::last_inflation(address), 0u64) + .write(&token::last_inflation(address), I256::zero()) .expect("inflation ought to be written"); wl_storage - .write(&token::last_locked_ratio(address), dec!(0)) + .write(&token::last_locked_ratio(address), Dec::zero()) .expect("last locked set default"); wl_storage - .write(&token::parameters::max_reward_rate(address), dec!(0.1)) + .write( + &token::parameters::max_reward_rate(address), + Dec::from_str("0.1").unwrap(), + ) .expect("max reward rate write"); wl_storage .write(&token::parameters::kp_sp_gain(address), default_gain) @@ -710,7 +714,7 @@ pub fn init_token_storage( .write(&token::parameters::kd_sp_gain(address), default_gain) .expect("kd sp gain write"); wl_storage - .write(&token::parameters::locked_token_ratio(address), dec!(0.0)) + .write(&token::parameters::locked_token_ratio(address), Dec::zero()) .expect("Write locked ratio"); } } diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 61d861ab0f3..b00a41cd0f4 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -549,6 +549,20 @@ impl Mul<(u64, u64)> for Amount { } } +/// A combination of Euclidean division and fractions: +/// x*(a,b) = (a*(x//b), x%b). +impl Mul<(I256, I256)> for Amount { + type Output = (Amount, Amount); + + fn mul(mut self, rhs: (I256, I256)) -> Self::Output { + let amt = Amount { + raw: (self.raw / rhs.1.0) * rhs.0.0, + }; + self.raw %= rhs.1.0; + (amt, self) + } +} + impl Div for Amount { type Output = Self; diff --git a/core/src/types/token/parameters.rs b/core/src/types/token/parameters.rs index 42dc7f62426..5e16c25b0f3 100644 --- a/core/src/types/token/parameters.rs +++ b/core/src/types/token/parameters.rs @@ -1,14 +1,15 @@ //! Custom parameters for each token type. These are used for //! determining the shielded pool incentives. +use std::str::FromStr; + use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use rust_decimal::Decimal; -use rust_decimal_macros::dec; use serde::{Deserialize, Serialize}; use crate::ledger::storage::{self as ledger_storage}; use crate::ledger::storage_api::StorageWrite; use crate::types::address::Address; +use crate::types::dec::Dec; use crate::types::storage::{Key, KeySeg}; /// Token parameters for each kind of asset held on chain @@ -28,13 +29,13 @@ use crate::types::storage::{Key, KeySeg}; )] pub struct Parameters { /// Maximum reward rate - pub max_reward_rate: Decimal, + pub max_reward_rate: Dec, /// Shielded Pool nominal derivative gain - pub kd_gain_nom: Decimal, + pub kd_gain_nom: Dec, /// Shielded Pool nominal proportional gain for the given token - pub kp_gain_nom: Decimal, + pub kp_gain_nom: Dec, /// Locked ratio for the given token - pub locked_ratio_target_key: Decimal, + pub locked_ratio_target_key: Dec, } /// The key for the nominal proportional gain of a shielded pool for a given @@ -120,10 +121,10 @@ impl Parameters { impl Default for Parameters { fn default() -> Self { Self { - max_reward_rate: dec!(0.1), - kp_gain_nom: dec!(0.1), - kd_gain_nom: dec!(0.1), - locked_ratio_target_key: dec!(0.6667), + max_reward_rate: Dec::from_str("0.1").unwrap(), + kp_gain_nom: Dec::from_str("0.1").unwrap(), + kd_gain_nom: Dec::from_str("0.1").unwrap(), + locked_ratio_target_key: Dec::from_str("0.6667").unwrap(), } } } diff --git a/genesis/dev.toml b/genesis/dev.toml index 22d8de250e4..de9bf95d5b3 100644 --- a/genesis/dev.toml +++ b/genesis/dev.toml @@ -218,10 +218,10 @@ max_inflation_rate = "0.1" target_staked_ratio = "0.6667" # Portion of a validator's stake that should be slashed on a duplicate # vote. -duplicate_vote_min_slash_rate = "0.001" +duplicate_vote_min_slash_rate = "0.1" # Portion of a validator's stake that should be slashed on a light # client attack. -light_client_attack_min_slash_rate = "0.001" +light_client_attack_min_slash_rate = "0.1" # Number of epochs above and below (separately) the current epoch to # consider when doing cubic slashing cubic_slashing_window_length = 1 diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index d8430fc8874..1f4be911bd4 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -2285,21 +2285,16 @@ where }, address, ) = validator.unwrap(); - let cur_stake = token::Amount::from(cur_stake); - - tracing::debug!( - "Below-capacity validator address {address}, stake {}", - cur_stake.to_string_native() - ); let prev_validator_stake = validator_deltas_handle(&address) .get_sum(storage, current_epoch, params) .unwrap() .map(token::Amount::from_change) .unwrap_or_default(); + let prev_native = prev_validator_stake.to_string_native(); tracing::debug!( "Below-capacity validator address {address}, stake \ - {prev_validator_stake}" + {prev_native}" ); let prev_tm_voting_power = into_tm_voting_power( diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 5038df839a7..8fd41098509 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -30,9 +30,8 @@ use namada_apps::config::genesis::genesis_config::{ }; use namada_apps::config::utils::convert_tm_addr_to_socket_addr; use namada_apps::facade::tendermint_config::net::Address as TendermintAddress; -use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; +use namada_core::types::dec::Dec; use namada_test_utils::TestWasms; -use rust_decimal_macros::dec; use serde_json::json; use setup::constants::*; @@ -1025,8 +1024,216 @@ fn masp_pinned_txs() -> Result<()> { /// In this test we verify that users of the MASP receive the correct rewards /// for leaving their assets in the pool for varying periods of time. +/// For now, it's marked ignore as these tests need to be redesigned. #[test] +#[ignore] +fn masp_overflow() -> Result<()> { + let _test = setup::network( + |genesis| { + let parameters = ParametersConfig { + epochs_per_year: epochs_per_year_from_min_duration( + if is_debug_mode() { 240 } else { 60 }, + ), + min_num_of_blocks: 1, + ..genesis.parameters + }; + GenesisConfig { + parameters, + ..genesis + } + }, + None, + )?; + let test = setup::network( + |genesis| { + let parameters = ParametersConfig { + epochs_per_year: epochs_per_year_from_min_duration( + if is_debug_mode() { 240 } else { 60 }, + ), + min_num_of_blocks: 1, + ..genesis.parameters + }; + let token = genesis + .token + .into_iter() + .map(|(token, account_config)| { + (token, { + let parameters = account_config.parameters; + TokenAccountConfig { + balances: Some( + account_config + .balances + .into_iter() + .flatten() + .map(|(validator, value)| { + if validator == FAUCET { + ( + validator, + token::Amount::from(0u64), + ) + } else { + (validator, value) + } + }) + .collect(), + ), + parameters, + ..account_config + } + }) + }) + .collect(); + + GenesisConfig { + parameters, + token, + ..genesis + } + }, + None, + )?; + + // 1. Run the ledger node + let mut ledger = + run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; + + wait_for_wasm_pre_compile(&mut ledger)?; + + let _bg_ledger = ledger.background(); + + let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); + + // Wait till epoch boundary + let _ep0 = get_epoch(&test, &validator_one_rpc)?; + // Send 10 ETH from Albert to PA(B) + let mut client = run!( + test, + Bin::Client, + vec![ + "transfer", + "--source", + ALBERT, + "--target", + AB_PAYMENT_ADDRESS, + "--token", + ETH, + "--amount", + "1", + "--node", + &validator_one_rpc + ], + Some(300) + )?; + client.exp_string("Transaction accepted")?; + client.exp_string("Transaction applied")?; + client.exp_string("Transaction is valid")?; + client.assert_success(); + + // Assert ETH balance at VK(B) is 10 + let mut client = run!( + test, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + ETH, + "--node", + &validator_one_rpc + ], + Some(60) + )?; + client.exp_string("eth: 1")?; + client.assert_success(); + + // Assert NAM balance at VK(B) is 0 + let mut client = run!( + test, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + &validator_one_rpc + ], + Some(60) + )?; + client.exp_string("No shielded nam balance found")?; + client.assert_success(); + + // Wait till epoch boundary + let _ep4 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // Assert ETH balance at VK(B) is 10 + let mut client = run!( + test, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + ETH, + "--node", + &validator_one_rpc + ], + Some(60) + )?; + client.exp_string("eth: 1")?; + client.assert_success(); + + // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_4-epoch_3) + let mut client = run!( + test, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + &validator_one_rpc + ], + Some(60) + )?; + client.assert_success(); + + // Assert NAM balance at MASP pool is 200000*BTC_reward*(epoch_1-epoch_0) + let mut client = run!( + test, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + &validator_one_rpc + ], + Some(60) + )?; + client.assert_success(); + + // let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep4.0 - ep3.0); + // let denominated = DenominatedAmount { + // amount: amt, + // denom: NATIVE_MAX_DECIMAL_PLACES.into(), + // }; + // client.exp_string(&format!("nam: {}", denominated,))?; + // client.assert_success(); + Ok(()) +} + +/// Marked ignore as these tests need to be redesigned. +#[test] +#[ignore] fn masp_incentives() -> Result<()> { // The number of decimal places used by BTC amounts. const BTC_DENOMINATION: u8 = 8; @@ -1055,17 +1262,27 @@ fn masp_incentives() -> Result<()> { account_config.parameters.map(|parameters| { if token == *NAM { Parameters { - max_reward_rate: dec!(1000.5), + max_reward_rate: Dec::from_str( + "1000.5", + ) + .unwrap(), // these need to be set to 0 - kd_gain_nom: dec!(0.005), - kp_gain_nom: dec!(0.005), + kd_gain_nom: Dec::from_str("0.005") + .unwrap(), + kp_gain_nom: Dec::from_str("0.005") + .unwrap(), ..parameters } } else { Parameters { - max_reward_rate: dec!(100000.5), - kd_gain_nom: dec!(0.5), - kp_gain_nom: dec!(0.5), + max_reward_rate: Dec::from_str( + "100000.5", + ) + .unwrap(), + kd_gain_nom: Dec::from_str("0.5") + .unwrap(), + kp_gain_nom: Dec::from_str("0.5") + .unwrap(), ..parameters } } @@ -1081,9 +1298,15 @@ fn masp_incentives() -> Result<()> { if validator == ALBERT || validator == BERTHA { - (validator, 1000000u64) + ( + validator, + token::Amount::from(1000000u64), + ) } else { - (validator, 0u64) + ( + validator, + token::Amount::from(0u64), + ) } }) .collect(), @@ -1115,7 +1338,7 @@ fn masp_incentives() -> Result<()> { let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); // Wait till epoch boundary - let ep0 = get_epoch(&test, &validator_one_rpc)?; + let _ep0 = get_epoch(&test, &validator_one_rpc)?; // Send 200000 BTC from Albert to PA(A) let mut client = run!( @@ -1201,7 +1424,8 @@ fn masp_incentives() -> Result<()> { client.assert_success(); // Assert NAM balance at VK(A) is 200000*BTC_reward*(epoch_1-epoch_0) - let _amt200000 = token::Amount::from_uint(200000, BTC_DENOMINATION).unwrap(); + let _amt200000 = + token::Amount::from_uint(200000, BTC_DENOMINATION).unwrap(); let _amt30 = token::Amount::from_uint(30, ETH_DENOMINATION).unwrap(); let mut client = run!( diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 930b4974803..b70b7b4a323 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -791,6 +791,7 @@ pub mod constants { // User addresses aliases pub const ALBERT: &str = "Albert"; pub const ALBERT_KEY: &str = "Albert-key"; + pub const FAUCET: &str = "faucet"; pub const BERTHA: &str = "Bertha"; pub const BERTHA_KEY: &str = "Bertha-key"; pub const CHRISTEL: &str = "Christel"; diff --git a/wasm/checksums.json b/wasm/checksums.json index ba4de65e01f..097984b7eb5 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.fcdaed302f8734412a830b4993bff4619a4c559e014b71deaa5fed77fdef3680.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.26e4a245a07a07a9bdb90e87c09a19678489df09a1663b0a7d5672ff1bfa661b.wasm", - "tx_ibc.wasm": "tx_ibc.2327a35b2cf355e485e8f03e2c475b3c388167edc9ad15fbee630941320920b6.wasm", - "tx_init_account.wasm": "tx_init_account.9153e300b7198ce515693852c138c14b23838b6f85fa0a6db71f3294ca4b25ac.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.1630673aeef70942c7c786fef6944b96d47fef7e50df994b390b6500349d136e.wasm", - "tx_init_validator.wasm": "tx_init_validator.a2398d56e4002ac996069f0df93cbd7c61f4326438ed6e3cadac5a0460b547e9.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.d7268e61b97c3e6db9f0139b7db4b42b133b5e6ce42fe17ff4adc0988da520eb.wasm", - "tx_transfer.wasm": "tx_transfer.b2a7576aaa21bdca0ad0e810788b9c7cf3e58d7d0442a75a3290563d03e0412f.wasm", - "tx_unbond.wasm": "tx_unbond.6b9651b1ed2922d0c4982748ad20679298eb77929eaeefff6b2c792f9004c657.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.b2fab3af06dc591ef39ade0372bc6a1e850fb87d00dcdba3ab9452cebd278bea.wasm", - "tx_update_vp.wasm": "tx_update_vp.8e9a1a4827f6c899c2828493b213d21bdf32232eaf53fccb7a6d6535baa39f99.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.c16e405aedc46b59bb57d012d3e11e1ecbd28654c25557f376cabfb7bea483d9.wasm", - "tx_withdraw.wasm": "tx_withdraw.1751e4c9304349354f0b0afc3fe4214327c30f35bcffee2f805d4ff68d61b907.wasm", - "vp_implicit.wasm": "vp_implicit.d82b7b4525391f8777bcc4699bd973b0b6c3cdf82838791ca78ebd74392aa18e.wasm", - "vp_masp.wasm": "vp_masp.8b16fb8926a8fcda25796dd50c6e3ce70041b54692481102d8b5ba6f778853b2.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.a55fb86c460abfb44abf926a32db18c813722f97a87fc9dc3323c86dc4447f1c.wasm", - "vp_token.wasm": "vp_token.eb78c39b03703447b3f35926d2e67e11637f5c6eaa83fb6f9cfbec94f8732bb9.wasm", - "vp_user.wasm": "vp_user.6e831fc2fce1ae9b2b13549e3b4397c54ced19222efb697722c96c6fede0f143.wasm", - "vp_validator.wasm": "vp_validator.a3c3d2e361a530419601abcfad7880adfa8748655304c22014a48208a4d8ac92.wasm" + "tx_bond.wasm": "tx_bond.df7f526bf3c601e2ba0ab1496d1812ef50b91863670f60fa52a9161ae6e169b9.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.95a73d96ef7a478ecf5f62966ad7a06313b030f6d87fe9c9e158ff09c2753abf.wasm", + "tx_ibc.wasm": "tx_ibc.46f8fad45a9ef7af43fab7a065dd4d0965667fd89aad0d45efde634554c9e3ae.wasm", + "tx_init_account.wasm": "tx_init_account.89e1e08351ee4ea3ba255ffb8581202f8af831b553a9047b8232e267169bfde9.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.0529dc70850530c3410b865616a6dcfba02aa003b60a9a8913a667cef48ce9cf.wasm", + "tx_init_validator.wasm": "tx_init_validator.e4c3f9eecbdbd169a257ff0661ca9f7ab43dbc76bdb0d7cf22e267a2822a8274.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.9686b20c20f4944b708cf0f8a7def1ee66aa69055b2c3daedc7c1f03c5529c86.wasm", + "tx_transfer.wasm": "tx_transfer.098a0cc708d0e3d9a34cd38cdbd1bf3b933ca0f7992790de03b6cd5303970045.wasm", + "tx_unbond.wasm": "tx_unbond.e002a11cfc8b7ebd18536d5170ce26a9b9e5cb2f25727bac9620d9a37825df1a.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.094242d81846257c0fbf52be4841ccb49cc5eef3f175e0cec7b1b6530dfd491c.wasm", + "tx_update_vp.wasm": "tx_update_vp.cb2a5aa80be8dab99a871d631bfb4f12222de6c569e03a83d1914c9a17e766f6.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.6056a39f38a84e255045dc4fe2afc8dc86cdde915d571f0ca37b4e88fc251bfa.wasm", + "tx_withdraw.wasm": "tx_withdraw.f7663aa6c9057c7437e05531378c1db81abf25c2f6fd0c9cb43bdb66f268fce3.wasm", + "vp_implicit.wasm": "vp_implicit.4a74f2e837ec02c17c2e5afb8c247df3d6af8f91d7a18e5c6bd5068061952ef7.wasm", + "vp_masp.wasm": "vp_masp.afdac45eaf1554bd7eda74f5d402ea466f6d1999e013d7d8836dd0fa95720c4b.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.1626d01c441ebe9f01070187afebcbaade0cc9564b5c6f0a1226b770811b162f.wasm", + "vp_token.wasm": "vp_token.525d4aacd7253257bcde9737253b6e80015616e1e8fe6854685f0a9851dc2fca.wasm", + "vp_user.wasm": "vp_user.41ef5757a764fca972f5257e8daff62cf20967b528e3a87cd78d667b3a632921.wasm", + "vp_validator.wasm": "vp_validator.63f734c714108d70b95df45d6d6a81ad181b872615c3d78e04c75d557478c6ae.wasm" } \ No newline at end of file