Finalize implementation
Fix reciprocal Fix, fix reciprocal cargo fmt
This commit is contained in:
@@ -30,11 +30,10 @@ pub const INITIAL_MIXNODE_ACTIVE_SET_SIZE: u32 = 100;
|
||||
pub const INITIAL_GATEWAY_ACTIVE_SET_SIZE: u32 = 20;
|
||||
|
||||
// This is totally made up, lets set the pool to billion nyms, so million billion micro nyms
|
||||
pub const INITIAL_INFLATION_POOL: u64 = 1_000_000_000_000_000;
|
||||
pub const INITIAL_INFLATION_POOL: u128 = 1_000_000_000_000_000;
|
||||
// Sybil attack resistance parameter
|
||||
pub const ALPHA: f64 = 0.3;
|
||||
// We'll be assuming a few more things, profit margin and cost function. Since we don't have relialable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate epoch costs to Nyms. We'll also assume a cost of 40$ per epoch(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
|
||||
pub const DEFAULT_PROFIT_MARGIN: f64 = 0.1; // Assume operators want a 10 % profit
|
||||
pub const DEFAULT_COST_PER_EPOCH: u32 = 40_000_000;
|
||||
|
||||
fn default_initial_state(owner: Addr) -> State {
|
||||
@@ -118,9 +117,11 @@ pub fn execute(
|
||||
ExecuteMsg::RewardMixnode { identity, uptime } => {
|
||||
transactions::try_reward_mixnode(deps, env, info, identity, uptime)
|
||||
}
|
||||
ExecuteMsg::RewardMixnodeV2 { identity, uptime, performance } => {
|
||||
transactions::try_reward_mixnode_v2(deps, env, info, identity, uptime, performance)
|
||||
}
|
||||
ExecuteMsg::RewardMixnodeV2 {
|
||||
identity,
|
||||
uptime,
|
||||
performance,
|
||||
} => transactions::try_reward_mixnode_v2(deps, env, info, identity, uptime, performance),
|
||||
ExecuteMsg::RewardGateway { identity, uptime } => {
|
||||
transactions::try_reward_gateway(deps, env, info, identity, uptime)
|
||||
}
|
||||
@@ -226,7 +227,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
|
||||
}
|
||||
#[entry_point]
|
||||
pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
todo!("Calculate initial total mix and gateway stake after initial deployment");
|
||||
todo!("Calculate initial total mix and gateway stake after initial deployment. Update mixnode model with profit sharing");
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
Ok(Default::default())
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::contract::{DEFAULT_COST_PER_EPOCH, INITIAL_INFLATION_POOL};
|
||||
use crate::contract::{
|
||||
DEFAULT_COST_PER_EPOCH, INITIAL_INFLATION_POOL, INITIAL_MIXNODE_ACTIVE_SET_SIZE,
|
||||
};
|
||||
use crate::state::State;
|
||||
use crate::transactions::MINIMUM_BLOCK_AGE_FOR_REWARDING;
|
||||
use crate::{error::ContractError, queries};
|
||||
@@ -14,6 +16,8 @@ use mixnet_contract::{
|
||||
Addr, GatewayBond, IdentityKey, IdentityKeyRef, Layer, LayerDistribution, MixNodeBond,
|
||||
RawDelegationData, StateParams,
|
||||
};
|
||||
use num::rational::Ratio;
|
||||
use num::ToPrimitive;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -61,11 +65,11 @@ pub fn mut_total_mix_stake(storage: &mut dyn Storage) -> Singleton<Uint128> {
|
||||
singleton(storage, TOTAL_MIX_STAKE_KEY)
|
||||
}
|
||||
|
||||
fn inflation_pool(storage: &dyn Storage) -> ReadonlySingleton<u64> {
|
||||
fn inflation_pool(storage: &dyn Storage) -> ReadonlySingleton<u128> {
|
||||
singleton_read(storage, INFLATION_POOL_PREFIX)
|
||||
}
|
||||
|
||||
pub fn mut_inflation_pool(storage: &mut dyn Storage) -> Singleton<u64> {
|
||||
pub fn mut_inflation_pool(storage: &mut dyn Storage) -> Singleton<u128> {
|
||||
singleton(storage, INFLATION_POOL_PREFIX)
|
||||
}
|
||||
|
||||
@@ -91,7 +95,7 @@ pub fn total_gateway_stake_value(storage: &dyn Storage) -> Uint128 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inflation_pool_value(storage: &dyn Storage) -> u64 {
|
||||
pub fn inflation_pool_value(storage: &dyn Storage) -> u128 {
|
||||
match inflation_pool(storage).load() {
|
||||
Ok(value) => value,
|
||||
Err(_e) => INITIAL_INFLATION_POOL,
|
||||
@@ -134,13 +138,14 @@ pub fn decr_total_gateway_stake(
|
||||
Ok(stake)
|
||||
}
|
||||
|
||||
pub fn incr_inflation_pool(amount: u64, storage: &mut dyn Storage) -> Result<u64, ContractError> {
|
||||
#[allow(dead_code)]
|
||||
pub fn incr_inflation_pool(amount: u128, storage: &mut dyn Storage) -> Result<u128, ContractError> {
|
||||
let stake = inflation_pool_value(storage) + amount;
|
||||
mut_inflation_pool(storage).save(&stake)?;
|
||||
Ok(stake)
|
||||
}
|
||||
|
||||
pub fn decr_inflation_pool(amount: u64, storage: &mut dyn Storage) -> Result<u64, ContractError> {
|
||||
pub fn decr_inflation_pool(amount: u128, storage: &mut dyn Storage) -> Result<u128, ContractError> {
|
||||
// TODO: This could got to < 0
|
||||
let stake = inflation_pool_value(storage) - amount;
|
||||
mut_inflation_pool(storage).save(&stake)?;
|
||||
@@ -319,20 +324,20 @@ pub(crate) fn price_for_uptime(uptime: u32) -> f64 {
|
||||
|
||||
pub(crate) fn increase_mix_delegated_stakes_v2(
|
||||
storage: &mut dyn Storage,
|
||||
mix_identity: IdentityKeyRef,
|
||||
sigma: f64,
|
||||
bond: &MixNodeBond,
|
||||
node_reward: f64,
|
||||
uptime: u32,
|
||||
profit_margin: f64,
|
||||
reward_blockstamp: u64,
|
||||
) -> StdResult<Uint128> {
|
||||
) -> Result<Uint128, ContractError> {
|
||||
let chunk_size = queries::DELEGATION_PAGE_MAX_LIMIT as usize;
|
||||
let total_mix_stake = total_gateway_stake_value(storage);
|
||||
let one_over_k = 1. / INITIAL_MIXNODE_ACTIVE_SET_SIZE as f64;
|
||||
|
||||
let mut total_rewarded = Uint128::zero();
|
||||
let mut chunk_start: Option<Vec<_>> = None;
|
||||
loop {
|
||||
// get `chunk_size` of delegations
|
||||
let delegations_chunk = mix_delegations_read(storage, mix_identity)
|
||||
let delegations_chunk = mix_delegations_read(storage, bond.identity())
|
||||
.range(chunk_start.as_deref(), None, Order::Ascending)
|
||||
.take(chunk_size)
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
@@ -352,20 +357,40 @@ pub(crate) fn increase_mix_delegated_stakes_v2(
|
||||
.chain(std::iter::once(0u8))
|
||||
.collect(),
|
||||
);
|
||||
let sigma_ratio = if bond.stake_to_total_stake_f64(total_mix_stake)? < one_over_k {
|
||||
bond.stake_to_total_stake_ratio(total_mix_stake)
|
||||
} else {
|
||||
Ratio::new(1, INITIAL_MIXNODE_ACTIVE_SET_SIZE as u128)
|
||||
};
|
||||
|
||||
// and for each of them increase the stake proportionally to the reward
|
||||
// if at least `MINIMUM_BLOCK_AGE_FOR_REWARDING` blocks have been created
|
||||
// since they delegated
|
||||
for (delegator_address, mut delegation) in delegations_chunk.into_iter() {
|
||||
if delegation.block_height + MINIMUM_BLOCK_AGE_FOR_REWARDING <= reward_blockstamp {
|
||||
let reward = ((1. - profit_margin)
|
||||
// Getting amount / over sigma by reciprocal fraction multiplication'
|
||||
// Delegation rewards almost certanly need to be scaled by total_stake
|
||||
let delegation_over_sigma_ratio = Ratio::new(
|
||||
delegation.amount.u128() * sigma_ratio.denom(),
|
||||
total_mix_stake.u128() * sigma_ratio.numer(),
|
||||
);
|
||||
// If we can't resolve the ration we move on, and the delegation does not get rewarded
|
||||
let delegation_over_sigma_f64 =
|
||||
if let Some(f) = delegation_over_sigma_ratio.to_f64() {
|
||||
f
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let reward = Uint128(
|
||||
((1. - bond.profit_margin())
|
||||
// Need to convert this to ratio multiplication
|
||||
* (delegation.amount / sigma)
|
||||
* delegation_over_sigma_f64
|
||||
* (node_reward * price_for_uptime(uptime)))
|
||||
.max(0.);
|
||||
.max(0.) as u128,
|
||||
);
|
||||
delegation.amount += reward;
|
||||
total_rewarded += reward;
|
||||
mix_delegations(storage, mix_identity).save(&delegator_address, &delegation)?;
|
||||
mix_delegations(storage, bond.identity()).save(&delegator_address, &delegation)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -630,6 +655,7 @@ mod tests {
|
||||
identity_key: node_identity.clone(),
|
||||
..mix_node_fixture()
|
||||
},
|
||||
profit_margin: None,
|
||||
};
|
||||
|
||||
mixnodes(&mut storage)
|
||||
|
||||
@@ -133,6 +133,7 @@ pub mod helpers {
|
||||
Layer::One,
|
||||
12_345,
|
||||
mix_node,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::contract::{
|
||||
ALPHA, DEFAULT_PROFIT_MARGIN, INITIAL_MIXNODE_ACTIVE_SET_SIZE,
|
||||
};
|
||||
use crate::contract::{ALPHA, INITIAL_MIXNODE_ACTIVE_SET_SIZE};
|
||||
use crate::error::ContractError;
|
||||
use crate::helpers::{calculate_epoch_reward_rate, scale_reward_by_uptime, Delegations};
|
||||
use crate::queries;
|
||||
@@ -16,8 +14,6 @@ use cosmwasm_storage::ReadonlyBucket;
|
||||
use mixnet_contract::{
|
||||
Gateway, GatewayBond, IdentityKey, Layer, MixNode, MixNodeBond, RawDelegationData, StateParams,
|
||||
};
|
||||
use num::rational::Ratio;
|
||||
use num::ToPrimitive;
|
||||
|
||||
pub(crate) const OLD_DELEGATIONS_CHUNK_SIZE: usize = 500;
|
||||
pub(crate) const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 17280;
|
||||
@@ -104,6 +100,7 @@ pub(crate) fn try_add_mixnode(
|
||||
layer,
|
||||
env.block.height,
|
||||
mix_node,
|
||||
None,
|
||||
);
|
||||
|
||||
// this might potentially require more gas if a significant number of delegations was there
|
||||
@@ -459,7 +456,6 @@ pub(crate) fn try_reward_mixnode(
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub(crate) fn try_reward_mixnode_v2(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
@@ -503,32 +499,32 @@ pub(crate) fn try_reward_mixnode_v2(
|
||||
/ (1. + ALPHA);
|
||||
|
||||
// Omitting the price per packet function now, it follows that base operator reward is the node_reward
|
||||
let operator_profit = ((DEFAULT_PROFIT_MARGIN
|
||||
+ (1. - DEFAULT_PROFIT_MARGIN) * (lambda / sigma))
|
||||
let operator_profit = ((current_bond.profit_margin()
|
||||
+ (1. - current_bond.profit_margin()) * (lambda / sigma))
|
||||
* (node_reward - price_for_uptime(uptime)))
|
||||
.max(0.);
|
||||
let operator_base_reward = node_reward.min(price_for_uptime(uptime));
|
||||
let total_operator_reward = (operator_base_reward + operator_profit) as u128;
|
||||
let total_operator_reward = Uint128((operator_base_reward + operator_profit) as u128);
|
||||
|
||||
let total_delegation_reward = increase_mix_delegated_stakes_v2(
|
||||
deps.storage,
|
||||
&mix_identity,
|
||||
sigma,
|
||||
¤t_bond,
|
||||
node_reward,
|
||||
uptime,
|
||||
DEFAULT_PROFIT_MARGIN,
|
||||
env.block.height
|
||||
env.block.height,
|
||||
)?;
|
||||
|
||||
// update current bond with the reward given to the node and the delegators
|
||||
// if it has been bonded for long enough
|
||||
if current_bond.block_height + MINIMUM_BLOCK_AGE_FOR_REWARDING <= env.block.height {
|
||||
node_reward = current_bond.bond_amount.amount * bond_scaled_reward_rate;
|
||||
current_bond.bond_amount.amount += node_reward;
|
||||
current_bond.bond_amount.amount += total_operator_reward;
|
||||
current_bond.total_delegation.amount += total_delegation_reward;
|
||||
mixnodes(deps.storage).save(mix_identity.as_bytes(), ¤t_bond)?;
|
||||
}
|
||||
|
||||
decr_inflation_pool(total_operator_reward.u128(), deps.storage)?;
|
||||
decr_inflation_pool(total_delegation_reward.u128(), deps.storage)?;
|
||||
|
||||
Ok(Response {
|
||||
submessages: vec![],
|
||||
messages: vec![],
|
||||
@@ -1918,6 +1914,7 @@ pub mod tests {
|
||||
identity_key: node_identity.clone(),
|
||||
..mix_node_fixture()
|
||||
},
|
||||
profit_margin: None,
|
||||
};
|
||||
|
||||
mixnodes(deps.as_mut().storage)
|
||||
@@ -2017,6 +2014,7 @@ pub mod tests {
|
||||
identity_key: node_identity.clone(),
|
||||
..mix_node_fixture()
|
||||
},
|
||||
profit_margin: None,
|
||||
};
|
||||
|
||||
mixnodes(deps.as_mut().storage)
|
||||
|
||||
Reference in New Issue
Block a user