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 diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index a77a34ab42c..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; @@ -210,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)] @@ -392,6 +397,9 @@ pub mod genesis_config { let token_vp_config = wasm.get(token_vp_name).unwrap(); TokenAccount { + 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, vp_code_path: token_vp_config.filename.to_owned(), @@ -806,6 +814,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: I256, + /// Token shielded ratio from the last epoch (read + write for every epoch) + pub last_locked_ratio: Dec, } #[derive( @@ -881,9 +896,7 @@ pub fn genesis( } #[cfg(any(test, feature = "dev"))] pub fn genesis(num_validators: u64) -> Genesis { - use namada::types::address::{ - self, apfel, btc, dot, eth, kartoffel, nam, schnitzel, - }; + use namada::types::address::{self}; use crate::wallet; @@ -1027,21 +1040,7 @@ pub fn genesis(num_validators: u64) -> Genesis { balances.insert((&validator.account_key).into(), default_key_tokens); } - /// Deprecated function, soon to be deleted. Generates default tokens - fn tokens() -> HashMap { - vec![ - (nam(), ("NAM", 6.into())), - (btc(), ("BTC", 8.into())), - (eth(), ("ETH", 18.into())), - (dot(), ("DOT", 10.into())), - (schnitzel(), ("Schnitzel", 6.into())), - (apfel(), ("Apfel", 6.into())), - (kartoffel(), ("Kartoffel", 6.into())), - ] - .into_iter() - .collect() - } - let token_accounts = tokens() + let token_accounts = address::tokens() .into_iter() .map(|(address, (_, denom))| TokenAccount { address, @@ -1049,6 +1048,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: I256::zero(), + last_locked_ratio: Dec::zero(), }) .collect(); Genesis { diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 76b12cf1848..7875074c680 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -945,7 +945,6 @@ mod test_finalize_block { use crate::node::ledger::shims::abcipp_shim_types::shim::request::{ FinalizeBlock, ProcessedTx, }; - /// Check that if a wrapper tx was rejected by [`process_proposal`], /// check that the correct event is returned. Check that it does /// not appear in the queue of txs to be decrypted diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index cf7eda38845..2a4b7182e52 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -277,8 +277,20 @@ 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(); + // associate a token with its denomination. write_denom( &mut self.wl_storage, @@ -313,10 +325,19 @@ where .write_bytes(&Key::validity_predicate(&address), vp_code_hash) .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 diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 6a2b297a867..28df6769976 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -1002,6 +1002,7 @@ mod test_utils { use namada::ledger::storage::mockdb::MockDB; use namada::ledger::storage::{update_allowed_conversions, Sha256Hasher}; use namada::proto::{Code, Data}; + use namada::types::address::init_token_storage; use namada::types::chain::ChainId; use namada::types::hash::Hash; use namada::types::key::*; @@ -1235,6 +1236,7 @@ mod test_utils { .storage .begin_block(BlockHash::default(), BlockHeight(1)) .expect("begin_block failed"); + init_token_storage(&mut shell.wl_storage, 60); let keypair = gen_keypair(); // enqueue a wrapper tx let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 5b42c0dc778..e6d060e5337 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -58,6 +58,7 @@ mod tests { types, update_allowed_conversions, WlStorage, }; use namada::ledger::storage_api::{self, StorageWrite}; + use namada::types::address::init_token_storage; use namada::types::chain::ChainId; use namada::types::hash::Hash; use namada::types::storage::{BlockHash, BlockHeight, Key}; @@ -143,6 +144,7 @@ mod tests { storage.block.pred_epochs.new_epoch(BlockHeight(100), 1000); // make wl_storage to update conversion for a new epoch let mut wl_storage = WlStorage::new(WriteLog::default(), storage); + init_token_storage(&mut wl_storage, 60); update_allowed_conversions(&mut wl_storage) .expect("update conversions failed"); wl_storage.commit_block().expect("commit failed"); diff --git a/shared/src/ledger/inflation.rs b/core/src/ledger/inflation.rs similarity index 64% rename from shared/src/ledger/inflation.rs rename to core/src/ledger/inflation.rs index 1c4850d2b10..041d023735f 100644 --- a/shared/src/ledger/inflation.rs +++ b/core/src/ledger/inflation.rs @@ -2,8 +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 @@ -47,6 +48,32 @@ pub struct RewardsController { } 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: Dec, + locked_ratio_last: Dec, + max_reward_rate: Dec, + last_inflation_amount: token::Amount, + p_gain_nom: Dec, + d_gain_nom: Dec, + 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) -> ValsToUpdate { let Self { @@ -95,3 +122,31 @@ impl RewardsController { } } } + +/// 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/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index f472c5e321a..43ed966699c 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 replay_protection; pub mod slash_fund; diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index 4dec02d4f50..6462f032591 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -8,15 +8,22 @@ 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::ledger::storage_api::token::read_denom; use crate::types::address::Address; +use crate::types::dec::Dec; use crate::types::storage::{Epoch, Key}; use crate::types::token::MaspDenom; +use crate::types::uint::{I256, Uint}; +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 @@ -32,6 +39,170 @@ pub struct ConversionState { >, } +#[cfg(feature = "wasm-runtime")] +fn calculate_masp_rewards( + wl_storage: &mut super::WlStorage, + addr: &Address, + sub_prefix: Option +) -> crate::ledger::storage_api::Result<(I256, I256)> +where + D: 'static + super::DB + for<'iter> super::DBIter<'iter>, + H: 'static + super::StorageHasher, +{ + 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("the total supply key should be here"); + + // total staked amount in the Shielded pool + let total_token_in_masp: token::Amount = wl_storage + .read(&token::balance_key(addr, &masp_addr))? + .unwrap_or_default(); + + println!("----------------------------"); + println!("TIME TO LIVE"); + println!("total_tokens: {}", total_tokens.to_string_native()); + println!("total_tokens_in_masp: {}", total_token_in_masp.to_string_native()); + println!("TIME TO DIE"); + println!("----------------------------"); + + 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); + + println!("----------------------------"); + println!("TIME TO LIVE ADJUSTE"); + println!("ADJUSTED total_tokens: {}", total_tokens.to_string_native()); + println!("ADJUSTED total_tokens_in_masp: {}", total_token_in_masp.to_string_native()); + println!("TIME TO DIE ADJUSTED"); + println!("----------------------------"); + + let epochs_per_year: u64 = wl_storage + .read(¶meters::storage::get_epochs_per_year_key())? + .expect(""); + + //// Values from the last epoch + let last_inflation: I256 = wl_storage + .read(&token::last_inflation(addr)) + .expect("failure to read last inflation") + .expect(""); + + 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: Dec = wl_storage + .read(&token::parameters::max_reward_rate(addr)) + .expect("max reward should properly decode") + .expect(""); + + 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: Dec = wl_storage + .read(&token::parameters::kd_sp_gain(addr)) + .expect("kd_gain_nom reward should properly decode") + .expect(""); + + let locked_target_ratio: Dec = 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); + + // 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(); + let noterized_inflation = if total_in.is_zero() { + I256::zero() + } else { + I256::from(100 * inflation) / (total_token_in_masp.change()) + }; + + 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, + ); + println!("Please give me: {:?}", addr); + println!("Ratio {:?}", locked_ratio); + println!("inflation from the pd controller {:?}", inflation); + println!("total in the masp {:?}", total_in); + println!("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 / I256::from(100)) + * total_token_in_masp.change() as I256, + ) + .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 + + println!("----------------------------"); + println!("TIME TO DEBUG"); + println!("noterized_inflation: {:?}", noterized_inflation); + println!("denomination_offset: {}", denomination_offset); + println!("TIME TO DEBUG"); + println!("----------------------------"); + + Ok((noterized_inflation, I256::from(100 * denomination_offset))) +} + // This is only enabled when "wasm-runtime" is on, because we're using rayon #[cfg(feature = "wasm-runtime")] /// Update the MASP's allowed conversions @@ -42,6 +213,8 @@ where D: 'static + super::DB + for<'iter> super::DBIter<'iter>, H: 'static + super::StorageHasher, { + use std::cmp::Ordering; + use masp_primitives::ff::PrimeField; use masp_primitives::transaction::components::Amount as MaspAmount; use rayon::iter::{ @@ -49,15 +222,26 @@ where }; use rayon::prelude::ParallelSlice; - 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(); 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, _key), (y, _)| { + if (*x == native_token) == (*y == native_token) { + Ordering::Equal + } else if *x == native_token { + Ordering::Less + } else { + Ordering::Greater + } + }); // The total transparent value of the rewards being distributed let mut total_reward = token::Amount::native_whole(0); @@ -66,12 +250,25 @@ 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), _reward) in &masp_rewards { + // 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 = I256::from(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); + // Dispense a transparent reward in parallel to the shielded rewards let addr_bal: token::Amount = match sub_prefix { None => wl_storage @@ -84,12 +281,51 @@ 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 @@ -105,14 +341,50 @@ where denom, wl_storage.storage.block.epoch, ); - current_convs.insert( - (addr.clone(), sub_prefix.clone(), denom), - (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(), - ); + + println!("=============================================="); + println!("final total_reward for denom {:?}: {:?}", denom, total_reward); + println!("=============================================="); + + if *addr == address::nam() { + let new_normed_inflation = 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(), sub_prefix.clone(), denom), + (MaspAmount::from_pair(old_asset, -(normed_inflation)) + .unwrap() + + MaspAmount::from_pair( + new_asset, + new_normed_inflation, + ) + .unwrap()) + .into(), + ); + } else { + 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(), 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(), + ); + } + // 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, ( @@ -158,11 +430,11 @@ 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)?; + 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 // across multiple cores // Merkle trees must have exactly 2^n leaves to be mergeable @@ -176,11 +448,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 = diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index b60ad4ff8c2..7d99fd7426d 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -990,10 +990,17 @@ 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 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 pub type TestWlStorage = WlStorage; @@ -1039,7 +1046,6 @@ pub mod testing { } } } - #[allow(clippy::derivable_impls)] impl Default for TestWlStorage { fn default() -> Self { @@ -1049,6 +1055,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, _key) in masp_rewards.keys() { + parameters::Parameters::init_storage( + ¶meters::Parameters::default(), + addr, + self, + ); + 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"); + 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)] @@ -1131,6 +1180,7 @@ mod tests { }, ..Default::default() }; + let mut parameters = Parameters { max_proposal_bytes: Default::default(), epoch_duration: epoch_duration.clone(), @@ -1150,6 +1200,7 @@ mod tests { }; parameters.init_storage(&mut wl_storage).unwrap(); + 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); diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 32a5d1c6cdd..a9457321299 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -14,10 +14,15 @@ use sha2::{Digest, Sha256}; use thiserror::Error; use crate::ibc::signer::Signer; -use crate::types::key; +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::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; @@ -71,7 +76,7 @@ pub const GOV: Address = Address::Internal(InternalAddress::Governance); /// 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 "; @@ -662,6 +667,58 @@ pub fn masp_rewards() -> HashMap<(Address, Option), (u64, u64)> { .collect() } +/// init_token_storage is useful when the initialization of the network is not +/// properly made. This properly sets up the storage such that inflation +/// calculations can be ran on the token addresses. We assume a total supply +/// that may not be real +pub fn init_token_storage( + wl_storage: &mut WlStorage, + epochs_per_year: u64, +) where + D: 'static + DB + for<'iter> DBIter<'iter>, + H: 'static + StorageHasher, +{ + let masp_rewards = masp_rewards(); + let masp_reward_keys: Vec<_> = masp_rewards.keys().collect(); + + wl_storage + .write( + ¶meters::storage::get_epochs_per_year_key(), + epochs_per_year, + ) + .unwrap(); + for (address, _key) in masp_reward_keys { + wl_storage + .write( + &token::total_supply_key(address), + token::Amount::native_whole(5), // arbitrary amount + ) + .unwrap(); + let default_gain = Dec::from_str("0.1").unwrap(); + wl_storage + .write(&token::last_inflation(address), I256::zero()) + .expect("inflation ought to be written"); + wl_storage + .write(&token::last_locked_ratio(address), Dec::zero()) + .expect("last locked set default"); + wl_storage + .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) + .expect("kp sp gain write"); + wl_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::zero()) + .expect("Write locked ratio"); + } +} + #[cfg(test)] pub mod tests { use proptest::prelude::*; diff --git a/core/src/types/token.rs b/core/src/types/token.rs index c30319dedd9..b00a41cd0f4 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, Formatter}; use std::iter::Sum; use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}; @@ -10,6 +10,7 @@ use data_encoding::BASE32HEX_NOPAD; use serde::{Deserialize, Serialize}; use thiserror::Error; +use self::parameters::key_of_token; use super::dec::POS_DECIMAL_PRECISION; use crate::ibc::applications::transfer::Amount as IbcAmount; use crate::ledger::storage_api::token::read_denom; @@ -548,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; @@ -743,7 +758,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"; /// A fully qualified (multi-) token address. #[derive( @@ -804,9 +824,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. @@ -890,9 +912,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..5e16c25b0f3 --- /dev/null +++ b/core/src/types/token/parameters.rs @@ -0,0 +1,130 @@ +//! 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 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 +#[derive( + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Deserialize, + Serialize, +)] +pub struct Parameters { + /// Maximum reward rate + pub max_reward_rate: Dec, + /// Shielded Pool nominal derivative gain + pub kd_gain_nom: Dec, + /// Shielded Pool nominal proportional gain for the given token + pub kp_gain_nom: Dec, + /// Locked ratio for the given token + pub locked_ratio_target_key: Dec, +} + +/// 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") +} + +/// 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( + 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) +} + +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::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 b6eb070b424..de9bf95d5b3 100644 --- a/genesis/dev.toml +++ b/genesis/dev.toml @@ -37,10 +37,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" @@ -51,6 +56,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" @@ -61,6 +72,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" @@ -71,6 +87,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" @@ -81,6 +102,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" @@ -91,6 +117,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" @@ -102,7 +133,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] @@ -155,7 +190,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. @@ -178,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/genesis/e2e-tests-single-node.toml b/genesis/e2e-tests-single-node.toml index 86b0072223f..9fbf12ccdff 100644 --- a/genesis/e2e-tests-single-node.toml +++ b/genesis/e2e-tests-single-node.toml @@ -40,6 +40,11 @@ Daewon = "1000000" faucet = "9223372036854" "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" @@ -51,6 +56,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" @@ -62,6 +72,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" @@ -73,6 +88,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" @@ -84,6 +104,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" @@ -95,6 +120,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" @@ -107,6 +137,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] diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index a18ddb1a5e8..1f4be911bd4 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -2280,23 +2280,23 @@ where .filter_map(|validator| { let ( NestedSubKey::Data { - key: cur_stake, + key: _, nested_sub_key: _, }, 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_native}" + ); + let prev_tm_voting_power = into_tm_voting_power( params.tm_votes_per_token, prev_validator_stake, diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index 399f75f800b..1ccb983f7be 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -4,7 +4,6 @@ pub mod args; pub mod eth_bridge; pub mod events; pub mod ibc; -pub mod inflation; pub mod masp; pub mod native_vp; pub mod pos; @@ -20,5 +19,6 @@ pub mod vp_host_fns; pub mod wallet; pub use namada_core::ledger::{ - gas, governance, parameters, replay_protection, storage_api, tx_env, vp_env, + gas, governance, inflation, parameters, replay_protection, storage_api, + tx_env, vp_env, }; diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 5273aa08427..178b1063197 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -19,16 +19,18 @@ 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; +use namada::types::token::parameters::Parameters; use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::config::genesis::genesis_config::{ - GenesisConfig, ParametersConfig, PosParamsConfig, + GenesisConfig, ParametersConfig, PosParamsConfig, TokenAccountConfig, }; 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::dec::Dec; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada_test_utils::TestWasms; use serde_json::json; @@ -1025,16 +1027,7 @@ fn masp_pinned_txs() -> Result<()> { /// for leaving their assets in the pool for varying periods of time. #[test] -fn masp_incentives() -> Result<()> { - // The number of decimal places used by BTC amounts. - const BTC_DENOMINATION: u8 = 8; - // The number of decimal places used by ETH amounts. - const ETH_DENOMINATION: u8 = 18; - // Download the shielded pool parameters before starting node - let _ = CLIShieldedUtils::new(PathBuf::new()); - // Lengthen epoch to ensure that a transaction can be constructed and - // submitted within the same block. Necessary to ensure that conversion is - // not invalidated. +fn masp_overflow() -> Result<()> { let test = setup::network( |genesis| { let parameters = ParametersConfig { @@ -1051,6 +1044,57 @@ fn masp_incentives() -> Result<()> { }, 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.clone(), { + let parameters = + account_config.parameters; + TokenAccountConfig { + balances: Some( + account_config + .balances + .into_iter() + .flat_map(|m| m) + .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 = @@ -1064,209 +1108,6 @@ fn masp_incentives() -> Result<()> { // Wait till epoch boundary let ep0 = get_epoch(&test, &validator_one_rpc)?; - - // Send 20 BTC from Albert to PA(A) - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AA_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "20", - "--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 BTC balance at VK(A) is 20 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("btc: 20")?; - 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, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - 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)?; - - // Assert BTC balance at VK(A) is 20 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("btc: 20")?; - client.assert_success(); - - let amt20 = token::Amount::from_uint(20, BTC_DENOMINATION).unwrap(); - let amt10 = token::Amount::from_uint(10, ETH_DENOMINATION).unwrap(); - - // 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, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - 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, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary - let ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert BTC balance at VK(A) is 20 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - 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, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - 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, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary - let ep3 = epoch_sleep(&test, &validator_one_rpc, 720)?; - // Send 10 ETH from Albert to PA(B) let mut client = run!( test, @@ -1280,7 +1121,7 @@ fn masp_incentives() -> Result<()> { "--token", ETH, "--amount", - "10", + "1", "--node", &validator_one_rpc ], @@ -1306,7 +1147,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - client.exp_string("eth: 10")?; + client.exp_string("eth: 1")?; client.assert_success(); // Assert NAM balance at VK(B) is 0 @@ -1345,7 +1186,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - client.exp_string("eth: 10")?; + client.exp_string("eth: 1")?; client.assert_success(); // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_4-epoch_3) @@ -1363,16 +1204,8 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - 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(); - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_4-epoch_0)+10*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, @@ -1387,132 +1220,139 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep4.0 - ep0.0)) - + ((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(); - // Wait till epoch boundary - let ep5 = epoch_sleep(&test, &validator_one_rpc, 720)?; + // 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(()) +} - // Send 10 ETH from SK(B) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - B_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - ETH, - "--amount", - "10", - "--signer", - BERTHA, - "--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(); +#[test] +fn masp_incentives() -> Result<()> { + // The number of decimal places used by BTC amounts. + const BTC_DENOMINATION: u8 = 8; + // The number of decimal places used by ETH amounts. + const ETH_DENOMINATION: u8 = 18; + // Download the shielded pool parameters before starting node + let _ = CLIShieldedUtils::new(PathBuf::new()); + // Lengthen epoch to ensure that a transaction can be constructed and + // submitted within the same block. Necessary to ensure that conversion is + // not invalidated. + 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.clone(), { + let parameters = + account_config.parameters.map(|parameters| { + if token == *NAM { + Parameters { + max_reward_rate: Dec::from_str( + "1000.5", + ) + .unwrap(), + // these need to be set to 0 + 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::from_str( + "100000.5", + ) + .unwrap(), + kd_gain_nom: Dec::from_str("0.5") + .unwrap(), + kp_gain_nom: Dec::from_str("0.5") + .unwrap(), + ..parameters + } + } + }); + + TokenAccountConfig { + balances: Some( + account_config + .balances + .into_iter() + .flat_map(|m| m.into_keys()) + .map(|validator| { + if validator == ALBERT + || validator == BERTHA + { + ( + validator, + token::Amount::from(1000000u64), + ) + } else { + ( + validator, + token::Amount::from(0u64), + ) + } + }) + .collect(), + ), + parameters, + ..account_config + } + }) + }) + .collect(); - // Assert ETH balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--node", - &validator_one_rpc - ], - Some(60) + GenesisConfig { + parameters, + token, + ..genesis + } + }, + None, )?; - client.exp_string("No shielded eth balance found")?; - client.assert_success(); - let mut ep = get_epoch(&test, &validator_one_rpc)?; + // 1. Run the ledger node + let mut ledger = + run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - // Assert NAM balance at VK(B) is 10*ETH_reward*(ep-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep.0 - ep3.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); + wait_for_wasm_pre_compile(&mut ledger)?; - ep = get_epoch(&test, &validator_one_rpc)?; - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_5-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep.0 - ep3.0)); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated))?; - client.assert_success(); + let _bg_ledger = ledger.background(); + + let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); // Wait till epoch boundary - let ep6 = epoch_sleep(&test, &validator_one_rpc, 720)?; + let ep0 = get_epoch(&test, &validator_one_rpc)?; - // Send 20 BTC from SK(A) to Christel + // Send 200000 BTC from Albert to PA(A) let mut client = run!( test, Bin::Client, vec![ "transfer", "--source", - A_SPENDING_KEY, + ALBERT, "--target", - CHRISTEL, + AA_PAYMENT_ADDRESS, "--token", BTC, "--amount", - "20", - "--signer", - ALBERT, + "200000", "--node", &validator_one_rpc ], @@ -1523,7 +1363,7 @@ fn masp_incentives() -> Result<()> { client.exp_string("Transaction is valid")?; client.assert_success(); - // Assert BTC balance at VK(A) is 0 + // Assert BTC balance at VK(A) is 200000 let mut client = run!( test, Bin::Client, @@ -1538,10 +1378,10 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - 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) + // Assert NAM balance at VK(A) is 0 let mut client = run!( test, Bin::Client, @@ -1556,43 +1396,15 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; + client.exp_string("No shielded nam balance found")?; 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, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); + let _masp_rewards = masp_rewards(); // Wait till epoch boundary - let _ep7 = epoch_sleep(&test, &validator_one_rpc, 720)?; + let _ep1 = epoch_sleep(&test, &validator_one_rpc, 7200000)?; - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) + // Assert BTC balance at VK(A) is 200000 let mut client = run!( test, Bin::Client, @@ -1601,52 +1413,27 @@ fn masp_incentives() -> Result<()> { "--owner", AA_VIEWING_KEY, "--token", - NAM, + BTC, "--node", &validator_one_rpc ], Some(60) )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated))?; + client.exp_string("btc: 200000")?; client.assert_success(); - // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - 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 _amt30 = token::Amount::from_uint(30, ETH_DENOMINATION).unwrap(); - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_6-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) let mut client = run!( test, Bin::Client, vec![ "balance", "--owner", - MASP, + AA_VIEWING_KEY, "--token", NAM, "--node", @@ -1654,84 +1441,28 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - // Wait till epoch boundary to prevent conversion expiry during transaction - // construction - let _ep8 = epoch_sleep(&test, &validator_one_rpc, 720)?; - // Send 10*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", - &((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)) - .to_string_native(), - "--signer", - BERTHA, - "--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(); - // Wait till epoch boundary - let _ep9 = 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. - // 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(), None)]).0 * (ep6.0 - ep0.0)) - .to_string_native(), - "--signer", - ALBERT, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; + client.exp_string("nam: 506000")?; client.assert_success(); - // Assert NAM balance at VK(A) is 0 + // Assert NAM balance at MASP pool is 200000*BTC_reward*(epoch_1-epoch_0) let mut client = run!( test, Bin::Client, vec![ "balance", "--owner", - AA_VIEWING_KEY, + MASP, "--token", NAM, "--node", @@ -1739,35 +1470,38 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - client.exp_string("No shielded nam balance found")?; + client.exp_string("nam: 506000")?; client.assert_success(); - // Assert NAM balance at VK(B) is 0 + // Wait till epoch boundary + let _ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // Assert BTC balance at VK(A) is 20 let mut client = run!( test, Bin::Client, vec![ "balance", "--owner", - AB_VIEWING_KEY, + AA_VIEWING_KEY, "--token", - NAM, + BTC, "--node", &validator_one_rpc ], Some(60) )?; - client.exp_string("No shielded nam balance found")?; + client.exp_string("btc: 200000")?; client.assert_success(); - // Assert NAM balance at MASP pool is 0 + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_2-epoch_0) let mut client = run!( test, Bin::Client, vec![ "balance", "--owner", - MASP, + AA_VIEWING_KEY, "--token", NAM, "--node", @@ -1775,9 +1509,546 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - client.exp_string("nam: 0")?; + client.exp_string("nam: 26296240")?; 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, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); + // let denominated = DenominatedAmount { + // amount: amt, + // denom: NATIVE_MAX_DECIMAL_PLACES.into(), + // }; + // client.exp_string(&format!("nam: {}", denominated,))?; + // client.assert_success(); + + // // Wait till epoch boundary + // let ep3 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // 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", + // "10", + // "--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: 10")?; + // 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: 10")?; + // 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) + // )?; + // 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(); + + // // Assert NAM balance at MASP pool is + // // 20*BTC_reward*(epoch_4-epoch_0)+10*ETH_reward*(epoch_4-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // MASP, + // "--token", + // NAM, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep4.0 - ep0.0)) + // + ((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(); + + // // Wait till epoch boundary + // let ep5 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // Send 10 ETH from SK(B) to Christel + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "transfer", + // "--source", + // B_SPENDING_KEY, + // "--target", + // CHRISTEL, + // "--token", + // ETH, + // "--amount", + // "10", + // "--signer", + // BERTHA, + // "--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 0 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // ETH, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // 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 10*ETH_reward*(ep-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // NAM, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep.0 - ep3.0); + // let denominated = DenominatedAmount { + // amount: amt, + // denom: NATIVE_MAX_DECIMAL_PLACES.into(), + // }; + // client.exp_string(&format!("nam: {}", denominated,))?; + // client.assert_success(); + + // ep = get_epoch(&test, &validator_one_rpc)?; + // // Assert NAM balance at MASP pool is + // // 20*BTC_reward*(epoch_5-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // MASP, + // "--token", + // NAM, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep.0 - ep0.0)) + // + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep.0 - ep3.0)); + // let denominated = DenominatedAmount { + // amount: amt, + // denom: NATIVE_MAX_DECIMAL_PLACES.into(), + // }; + // client.exp_string(&format!("nam: {}", denominated))?; + // 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, + // "--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 BTC balance at VK(A) is 0 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AA_VIEWING_KEY, + // "--token", + // BTC, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // 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, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); + // let denominated = DenominatedAmount { + // amount: amt, + // denom: NATIVE_MAX_DECIMAL_PLACES.into(), + // }; + // client.exp_string(&format!("nam: {}", denominated,))?; + // 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, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) + // + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); + // let denominated = DenominatedAmount { + // amount: amt, + // denom: NATIVE_MAX_DECIMAL_PLACES.into(), + // }; + // client.exp_string(&format!("nam: {}", denominated,))?; + // 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, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); + // let denominated = DenominatedAmount { + // amount: amt, + // denom: NATIVE_MAX_DECIMAL_PLACES.into(), + // }; + // client.exp_string(&format!("nam: {}", denominated))?; + // client.assert_success(); + + // // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_5-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AB_VIEWING_KEY, + // "--token", + // NAM, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); + // let denominated = DenominatedAmount { + // amount: amt, + // denom: NATIVE_MAX_DECIMAL_PLACES.into(), + // }; + // client.exp_string(&format!("nam: {}", denominated,))?; + // client.assert_success(); + + // // Assert NAM balance at MASP pool is + // // 20*BTC_reward*(epoch_6-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // MASP, + // "--token", + // NAM, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) + // + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); + // let denominated = DenominatedAmount { + // amount: amt, + // denom: NATIVE_MAX_DECIMAL_PLACES.into(), + // }; + // client.exp_string(&format!("nam: {}", denominated,))?; + // client.assert_success(); + + // // Wait till epoch boundary to prevent conversion expiry during + // transaction // construction + // let _ep8 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + // // Send 10*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", + // &((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)) + // .to_string_native(), + // "--signer", + // BERTHA, + // "--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(); + + // // 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(), None)]).0 * (ep6.0 - ep0.0)) + // .to_string_native(), + // "--signer", + // ALBERT, + // "--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 NAM balance at VK(A) is 0 + // let mut client = run!( + // test, + // Bin::Client, + // vec![ + // "balance", + // "--owner", + // AA_VIEWING_KEY, + // "--token", + // NAM, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // 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, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // 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, + // "--node", + // &validator_one_rpc + // ], + // Some(60) + // )?; + // client.exp_string("nam: 0")?; + // client.assert_success(); + Ok(()) } @@ -2315,7 +2586,6 @@ fn test_bond_queries() -> Result<()> { ]; let mut client = run!(test, Bin::Client, tx_args, Some(40))?; client.exp_string("Transaction is valid.")?; - client.assert_success(); // 3. Wait for epoch 4 let start = Instant::now(); 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