From eff732aa2cdbe148c3006ed854955130b5f1e774 Mon Sep 17 00:00:00 2001 From: Drazen Urch Date: Fri, 10 Jun 2022 12:36:35 +0200 Subject: [PATCH] Introduce staking supply, contract and wallet (#1324) * Introduce staking supply, contract and wallet * Migration staking supply (#1327) * Staking supply to params migration * Include into migration entrypoint * StateParams merge * Add changelog * Make everyone happy --- CHANGELOG.md | 4 ++ .../mixnet-contract/src/mixnode.rs | 26 +++++----- .../mixnet-contract/src/reward_params.rs | 21 ++++---- .../mixnet-contract/src/types.rs | 1 + contracts/mixnet/src/contract.rs | 52 ++++++++++++++++++- .../src/mixnet_contract_settings/queries.rs | 3 +- .../mixnet_contract_settings/transactions.rs | 3 +- contracts/mixnet/src/rewards/storage.rs | 19 +++++-- contracts/mixnet/src/rewards/transactions.rs | 2 +- .../nym-wallet/src/types/rust/AppEnv.ts | 2 + .../nym-wallet/src/types/rust/ValidatorUrl.ts | 2 + .../src/types/rust/ValidatorUrls.ts | 2 + nym-wallet/nym-wallet-types/src/admin.rs | 3 ++ nym-wallet/src/pages/Admin/index.tsx | 26 ++++++++++ nym-wallet/src/types/rust/StateParams.ts | 2 +- validator-api/src/contract_cache/mod.rs | 2 +- validator-api/src/node_status_api/routes.rs | 2 +- validator-api/src/nymd_client.rs | 2 +- 18 files changed, 137 insertions(+), 37 deletions(-) create mode 100644 nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/AppEnv.ts create mode 100644 nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrl.ts create mode 100644 nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrls.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e187adc7c..c8d055e0c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - gateway: Added gateway coconut verifications and validator-api communication for double spending protection ([#1261]) - mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292]) - mixnet-contract: Replace all naked `-` with `saturating_sub`. +- mixnet-contrat: Added staking_supply field to ContractStateParams. +- rewarding: replace circulating supply with staking supply in reward calculations ([#1324]) - validator-api: add `estimated_node_profit` and `estimated_operator_cost` to `reward-estimate` endpoint ([#1284]) - validator-api: add detailed mixnode bond endpoints, and explorer-api makes use of that data to append stake saturation. - validator-api: add Swagger to document the REST API ([#1249]). @@ -24,6 +26,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - wallet: new delegation and rewards UI - wallet: show version in nav bar - wallet: contract admin route put back +- waller: staking_supply field to StateParams - network-statistics: a new mixnet service that aggregates and exposes anonymized data about mixnet services ([#1328]) ### Fixed @@ -61,6 +64,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// [#1302]: https://github.com/nymtech/nym/pull/1302 [#1318]: https://github.com/nymtech/nym/pull/1318 [#1322]: https://github.com/nymtech/nym/pull/1322 +[#1324]: https://github.com/nymtech/nym/pull/1324 [#1328]: https://github.com/nymtech/nym/pull/1328 [#1329]: https://github.com/nymtech/nym/pull/1329 diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs index 32b8611eff..54724d0b5a 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs @@ -220,9 +220,9 @@ impl DelegatorRewardParams { // change all values into their fixed representations let delegation_amount = U128::from_num(delegation_amount.u128()); - let circulating_supply = U128::from_num(self.reward_params.circulating_supply()); + let staking_supply = U128::from_num(self.reward_params.staking_supply()); - let scaled_delegation_amount = delegation_amount / circulating_supply; + let scaled_delegation_amount = delegation_amount / staking_supply; // Div by zero checked above let delegator_reward = @@ -392,22 +392,21 @@ impl MixNodeBond { self.total_delegation.clone() } - pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> U128 { - self.total_bond_to_circulating_supply(circulating_supply) - * U128::from_num(rewarded_set_size) + pub fn stake_saturation(&self, staking_supply: u128, rewarded_set_size: u32) -> U128 { + self.total_bond_to_staking_supply(staking_supply) * U128::from_num(rewarded_set_size) } // TODO: There is an effect here when adding accumulted rewards to the total bond, ie accumulated rewards will not // affect lambda, but will affect sigma, in turn over time, if left unclaimed operator rewards will not compound, but // behave similarly to delegations. // The question is should this be taken into account when calculating operator rewards? - pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> U128 { - U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply) + pub fn pledge_to_staking_supply(&self, staking_supply: u128) -> U128 { + U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(staking_supply) } - pub fn total_bond_to_circulating_supply(&self, circulating_supply: u128) -> U128 { + pub fn total_bond_to_staking_supply(&self, staking_supply: u128) -> U128 { U128::from_num(self.pledge_amount().amount.u128() + self.total_delegation().amount.u128()) - / U128::from_num(circulating_supply) + / U128::from_num(staking_supply) } pub fn lambda_ticked(&self, params: &RewardParams) -> U128 { @@ -417,7 +416,7 @@ impl MixNodeBond { pub fn lambda(&self, params: &RewardParams) -> U128 { // Ratio of a bond to the token circulating supply - self.pledge_to_circulating_supply(params.circulating_supply()) + self.pledge_to_staking_supply(params.staking_supply()) } pub fn sigma_ticked(&self, params: &RewardParams) -> U128 { @@ -427,7 +426,7 @@ impl MixNodeBond { pub fn sigma(&self, params: &RewardParams) -> U128 { // Ratio of a delegation to the the token circulating supply - self.total_bond_to_circulating_supply(params.circulating_supply()) + self.total_bond_to_staking_supply(params.staking_supply()) } pub fn estimate_reward( @@ -515,9 +514,8 @@ impl MixNodeBond { } pub fn sigma_ratio(&self, params: &RewardParams) -> U128 { - if self.total_bond_to_circulating_supply(params.circulating_supply()) < params.one_over_k() - { - self.total_bond_to_circulating_supply(params.circulating_supply()) + if self.total_bond_to_staking_supply(params.staking_supply()) < params.one_over_k() { + self.total_bond_to_staking_supply(params.staking_supply()) } else { params.one_over_k() } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs index b033e06c60..a00eb0db16 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs @@ -78,9 +78,9 @@ impl NodeEpochRewards { ) -> Result { // change all values into their fixed representations let delegation_amount = U128::from_num(delegation_amount.u128()); - let circulating_supply = U128::from_num(epoch_reward_params.circulating_supply()); + let staking_supply = U128::from_num(epoch_reward_params.staking_supply()); - let scaled_delegation_amount = delegation_amount / circulating_supply; + let scaled_delegation_amount = delegation_amount / staking_supply; let check_div_by_zero = if let Some(value) = scaled_delegation_amount.checked_div(self.sigma()) { @@ -105,7 +105,8 @@ pub struct EpochRewardParams { epoch_reward_pool: Uint128, rewarded_set_size: Uint128, active_set_size: Uint128, - circulating_supply: Uint128, + #[serde(alias = "circulating_supply")] + staking_supply: Uint128, sybil_resistance_percent: u8, active_set_work_factor: u8, } @@ -115,7 +116,7 @@ impl EpochRewardParams { epoch_reward_pool: u128, rewarded_set_size: u128, active_set_size: u128, - circulating_supply: u128, + staking_supply: u128, sybil_resistance_percent: u8, active_set_work_factor: u8, ) -> EpochRewardParams { @@ -123,7 +124,7 @@ impl EpochRewardParams { epoch_reward_pool: Uint128::new(epoch_reward_pool), rewarded_set_size: Uint128::new(rewarded_set_size), active_set_size: Uint128::new(active_set_size), - circulating_supply: Uint128::new(circulating_supply), + staking_supply: Uint128::new(staking_supply), sybil_resistance_percent, active_set_work_factor, } @@ -136,7 +137,7 @@ impl EpochRewardParams { pub fn new_empty() -> Self { EpochRewardParams { epoch_reward_pool: Uint128::new(0), - circulating_supply: Uint128::new(0), + staking_supply: Uint128::new(0), sybil_resistance_percent: 0, rewarded_set_size: Uint128::new(0), active_set_size: Uint128::new(0), @@ -152,8 +153,8 @@ impl EpochRewardParams { self.active_set_size.u128() } - pub fn circulating_supply(&self) -> u128 { - self.circulating_supply.u128() + pub fn staking_supply(&self) -> u128 { + self.staking_supply.u128() } pub fn epoch_reward_pool(&self) -> u128 { @@ -252,8 +253,8 @@ impl RewardParams { self.epoch.rewarded_set_size.u128() } - pub fn circulating_supply(&self) -> u128 { - self.epoch.circulating_supply.u128() + pub fn staking_supply(&self) -> u128 { + self.epoch.staking_supply.u128() } pub fn reward_blockstamp(&self) -> u64 { diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs index a67f8d0343..94c91bfe58 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs @@ -43,6 +43,7 @@ pub struct ContractStateParams { // subset of rewarded mixnodes that are actively receiving mix traffic // used to handle shorter-term (e.g. hourly) fluctuations of demand pub mixnode_active_set_size: u32, + pub staking_supply: Uint128, } impl Display for ContractStateParams { diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index 240055062c..3b16b955dc 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -57,6 +57,8 @@ pub const INITIAL_ACTIVE_SET_WORK_FACTOR: u8 = 10; pub const DEFAULT_FIRST_INTERVAL_START: OffsetDateTime = time::macros::datetime!(2022-01-01 12:00 UTC); +pub const INITIAL_STAKING_SUPPLY: Uint128 = Uint128::new(100_000_000_000_000); + pub fn debug_with_visibility>(api: &dyn Api, msg: S) { api.debug(&*format!("\n\n\n=========================================\n{}\n=========================================\n\n\n", msg.into())); } @@ -70,6 +72,7 @@ fn default_initial_state(owner: Addr, rewarding_validator_address: Addr) -> Cont minimum_gateway_pledge: INITIAL_GATEWAY_PLEDGE, mixnode_rewarded_set_size: INITIAL_MIXNODE_REWARDED_SET_SIZE, mixnode_active_set_size: INITIAL_MIXNODE_ACTIVE_SET_SIZE, + staking_supply: INITIAL_STAKING_SUPPLY, }, } } @@ -432,7 +435,52 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result) -> Result<(), ContractError> { +fn migrate_contract_state_params(deps: DepsMut<'_>) -> Result<(), ContractError> { + use crate::mixnet_contract_settings::storage::CONTRACT_STATE; + use cw_storage_plus::Item; + use serde::{Deserialize, Serialize}; + + #[derive(Serialize, Deserialize)] + struct OldContractState { + pub owner: Addr, + pub rewarding_validator_address: Addr, + pub params: OldContractStateParams, + } + + #[derive(Serialize, Deserialize)] + struct OldContractStateParams { + pub minimum_mixnode_pledge: Uint128, + pub minimum_gateway_pledge: Uint128, + pub mixnode_rewarded_set_size: u32, + pub mixnode_active_set_size: u32, + } + + const OLD_CONTRACT_STATE: Item<'_, OldContractState> = Item::new("config"); + + let old_contract_state = OLD_CONTRACT_STATE.load(deps.storage)?; + + let old_params = old_contract_state.params; + + let new_params = ContractStateParams { + minimum_mixnode_pledge: old_params.minimum_mixnode_pledge, + minimum_gateway_pledge: old_params.minimum_gateway_pledge, + mixnode_rewarded_set_size: old_params.mixnode_rewarded_set_size, + mixnode_active_set_size: old_params.mixnode_active_set_size, + staking_supply: INITIAL_STAKING_SUPPLY, + }; + + let new_contract_state = ContractState { + owner: old_contract_state.owner, + rewarding_validator_address: old_contract_state.rewarding_validator_address, + params: new_params, + }; + + CONTRACT_STATE.save(deps.storage, &new_contract_state)?; + + Ok(()) +} + +fn _deal_with_zero_delegations(deps: DepsMut<'_>) -> Result<(), ContractError> { // if there exists any delegation of 0 value, remove it let zero_delegations = delegations() .range(deps.storage, None, None, cosmwasm_std::Order::Ascending) @@ -466,7 +514,7 @@ fn deal_with_zero_delegations(deps: DepsMut<'_>) -> Result<(), ContractError> { #[entry_point] pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { - deal_with_zero_delegations(deps)?; + migrate_contract_state_params(deps)?; Ok(Default::default()) } diff --git a/contracts/mixnet/src/mixnet_contract_settings/queries.rs b/contracts/mixnet/src/mixnet_contract_settings/queries.rs index 40c5274e37..9ed2ac8a2e 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/queries.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/queries.rs @@ -5,7 +5,7 @@ use super::storage; use cosmwasm_std::{Deps, StdResult}; use mixnet_contract_common::{ContractStateParams, MixnetContractVersion}; -pub(crate) fn query_contract_settings_params(deps: Deps<'_>) -> StdResult { +pub fn query_contract_settings_params(deps: Deps<'_>) -> StdResult { storage::CONTRACT_STATE .load(deps.storage) .map(|settings| settings.params) @@ -51,6 +51,7 @@ pub(crate) mod tests { minimum_gateway_pledge: 456u128.into(), mixnode_rewarded_set_size: 1000, mixnode_active_set_size: 500, + staking_supply: 1000000u128.into(), }, }; diff --git a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs index bf4e7613e6..2a4cef7af7 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs @@ -64,7 +64,7 @@ pub(crate) fn try_update_contract_settings( #[cfg(test)] pub mod tests { use super::*; - use crate::contract::{INITIAL_GATEWAY_PLEDGE, INITIAL_MIXNODE_PLEDGE}; + use crate::contract::{INITIAL_GATEWAY_PLEDGE, INITIAL_MIXNODE_PLEDGE, INITIAL_STAKING_SUPPLY}; use crate::error::ContractError; use crate::mixnet_contract_settings::queries::query_rewarding_validator_address; use crate::mixnet_contract_settings::transactions::try_update_contract_settings; @@ -114,6 +114,7 @@ pub mod tests { minimum_gateway_pledge: INITIAL_GATEWAY_PLEDGE, mixnode_rewarded_set_size: 100, mixnode_active_set_size: 50, + staking_supply: INITIAL_STAKING_SUPPLY, }; let initial_params = storage::CONTRACT_STATE diff --git a/contracts/mixnet/src/rewards/storage.rs b/contracts/mixnet/src/rewards/storage.rs index 074a025703..28f4c2ac4e 100644 --- a/contracts/mixnet/src/rewards/storage.rs +++ b/contracts/mixnet/src/rewards/storage.rs @@ -42,10 +42,21 @@ pub fn incr_reward_pool( }) } -pub fn decr_reward_pool( - storage: &mut dyn Storage, - amount: Uint128, -) -> Result { +pub fn reward_accounting(storage: &mut dyn Storage, amount: Uint128) -> Result<(), ContractError> { + decr_reward_pool(storage, amount)?; + incr_staking_supply(storage, amount)?; + Ok(()) +} + +fn incr_staking_supply(storage: &mut dyn Storage, amount: Uint128) -> Result<(), ContractError> { + let mut contract_state = + crate::mixnet_contract_settings::storage::CONTRACT_STATE.load(storage)?; + contract_state.params.staking_supply += amount; + crate::mixnet_contract_settings::storage::CONTRACT_STATE.save(storage, &contract_state)?; + Ok(()) +} + +fn decr_reward_pool(storage: &mut dyn Storage, amount: Uint128) -> Result { REWARD_POOL.update(storage, |current_pool| { let stake = current_pool .checked_sub(amount) diff --git a/contracts/mixnet/src/rewards/transactions.rs b/contracts/mixnet/src/rewards/transactions.rs index 239b992fc9..28e1a93494 100644 --- a/contracts/mixnet/src/rewards/transactions.rs +++ b/contracts/mixnet/src/rewards/transactions.rs @@ -678,7 +678,7 @@ pub(crate) fn try_reward_mixnode( )?; // Take rewards out of the rewarding pool - storage::decr_reward_pool(deps.storage, stored_node_result.reward())?; + storage::reward_accounting(deps.storage, stored_node_result.reward())?; let rewarding_result = RewardingResult { node_reward: stored_node_result.reward(), diff --git a/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/AppEnv.ts b/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/AppEnv.ts new file mode 100644 index 0000000000..111e2dfd13 --- /dev/null +++ b/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/AppEnv.ts @@ -0,0 +1,2 @@ + +export interface AppEnv { ADMIN_ADDRESS: string | null, SHOW_TERMINAL: string | null, } \ No newline at end of file diff --git a/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrl.ts b/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrl.ts new file mode 100644 index 0000000000..9c74679bac --- /dev/null +++ b/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrl.ts @@ -0,0 +1,2 @@ + +export interface ValidatorUrl { url: string, name: string | null, } \ No newline at end of file diff --git a/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrls.ts b/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrls.ts new file mode 100644 index 0000000000..2ac6693e0b --- /dev/null +++ b/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrls.ts @@ -0,0 +1,2 @@ + +export interface Validator { nymd_url: string, nymd_name: string | null, api_url: string | null, } \ No newline at end of file diff --git a/nym-wallet/nym-wallet-types/src/admin.rs b/nym-wallet/nym-wallet-types/src/admin.rs index 0afaa6fb13..9e2c30aefd 100644 --- a/nym-wallet/nym-wallet-types/src/admin.rs +++ b/nym-wallet/nym-wallet-types/src/admin.rs @@ -17,6 +17,7 @@ pub struct TauriContractStateParams { minimum_gateway_pledge: String, mixnode_rewarded_set_size: u32, mixnode_active_set_size: u32, + staking_supply: String, } impl From for TauriContractStateParams { @@ -26,6 +27,7 @@ impl From for TauriContractStateParams { minimum_gateway_pledge: p.minimum_gateway_pledge.to_string(), mixnode_rewarded_set_size: p.mixnode_rewarded_set_size, mixnode_active_set_size: p.mixnode_active_set_size, + staking_supply: p.staking_supply.to_string(), } } } @@ -39,6 +41,7 @@ impl TryFrom for ContractStateParams { minimum_gateway_pledge: Uint128::try_from(p.minimum_gateway_pledge.as_str())?, mixnode_rewarded_set_size: p.mixnode_rewarded_set_size, mixnode_active_set_size: p.mixnode_active_set_size, + staking_supply: Uint128::try_from(p.staking_supply.as_str())?, }) } } diff --git a/nym-wallet/src/pages/Admin/index.tsx b/nym-wallet/src/pages/Admin/index.tsx index 1bb394c6d2..eff8d8dc35 100644 --- a/nym-wallet/src/pages/Admin/index.tsx +++ b/nym-wallet/src/pages/Admin/index.tsx @@ -63,6 +63,32 @@ const AdminForm: React.FC<{ helperText={errors?.mixnode_active_set_size?.message} /> + + + + + + ValidatorCacheRefresher { .map(|mixnode_bond| { let stake_saturation = mixnode_bond .stake_saturation( - interval_reward_params.circulating_supply(), + interval_reward_params.staking_supply(), interval_reward_params.rewarded_set_size() as u32, ) .to_num(); diff --git a/validator-api/src/node_status_api/routes.rs b/validator-api/src/node_status_api/routes.rs index f82206e32b..1cf9a85e5b 100644 --- a/validator-api/src/node_status_api/routes.rs +++ b/validator-api/src/node_status_api/routes.rs @@ -188,7 +188,7 @@ pub(crate) async fn get_mixnode_stake_saturation( let interval_reward_params = interval_reward_params.into_inner(); let saturation = bond.mixnode_bond.stake_saturation( - interval_reward_params.circulating_supply(), + interval_reward_params.staking_supply(), interval_reward_params.rewarded_set_size() as u32, ); diff --git a/validator-api/src/nymd_client.rs b/validator-api/src/nymd_client.rs index b2b43b179d..fa511d6c4f 100644 --- a/validator-api/src/nymd_client.rs +++ b/validator-api/src/nymd_client.rs @@ -193,7 +193,7 @@ impl Client { * interval_reward_percent as u128, state.mixnode_rewarded_set_size as u128, state.mixnode_active_set_size as u128, - this.get_circulating_supply().await?, + state.staking_supply.u128(), this.get_sybil_resistance_percent().await?, this.get_active_set_work_factor().await?, );