Address review comments

This commit is contained in:
Drazen Urch
2021-11-03 11:20:48 +01:00
parent 0f66be4c83
commit 6aed23960c
19 changed files with 97 additions and 153 deletions
+3 -1
View File
@@ -30,4 +30,6 @@ validator-api/v4.json
validator-api/v6.json
**/node_modules
validator-api/keypair
contracts/mixnet/code_id
contracts/mixnet/code_id
contracts/mixnet/Justfile
contracts/mixnet/Makefile
Generated
+1 -1
View File
@@ -3054,9 +3054,9 @@ name = "mixnet-contract"
version = "0.1.0"
dependencies = [
"az",
"config",
"cosmwasm-std",
"fixed",
"network-defaults",
"schemars",
"serde",
"serde_repr",
@@ -196,6 +196,20 @@ impl<C> Client<C> {
Ok(self.nymd.get_circulating_supply().await?.u128())
}
pub async fn get_sybil_resistance_percent(&self) -> Result<u8, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_sybil_resistance_percent().await?)
}
pub async fn get_epoch_reward_percent(&self) -> Result<u8, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_epoch_reward_percent().await?)
}
// basically handles paging for us
pub async fn get_all_nymd_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
where
@@ -232,6 +232,26 @@ impl<C> NymdClient<C> {
.await
}
pub async fn get_sybil_resistance_percent(&self) -> Result<u8, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetSybilResistancePercent {};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
pub async fn get_epoch_reward_percent(&self) -> Result<u8, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetEpochRewardPercent {};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Checks whether there is a bonded mixnode associated with the provided client's address
pub async fn owns_mixnode(&self, address: &AccountId) -> Result<bool, NymdError>
where
+1 -1
View File
@@ -16,7 +16,7 @@ serde_repr = "0.1"
schemars = "0.8"
ts-rs = { version = "3.0", optional = true }
thiserror = "1.0"
config = { path = "../config" }
network-defaults = { path = "../network-defaults" }
fixed = "1.1"
az = "1.1"
+6 -10
View File
@@ -3,8 +3,8 @@
use crate::{IdentityKey, SphinxKey};
use az::CheckedCast;
use config::defaults::DEFAULT_OPERATOR_EPOCH_COST;
use cosmwasm_std::{coin, Addr, Coin, Decimal, Uint128};
use network_defaults::{DEFAULT_OPERATOR_EPOCH_COST, DEFAULT_PROFIT_MARGIN};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -13,14 +13,7 @@ use std::fmt::Display;
use crate::current_block_height;
type U128 = fixed::types::U75F53;
const DEFAULT_PROFIT_MARGIN: u8 = 10;
fn default_alpha() -> U128 {
// Sybil resistance parameter
U128::from_num(0.3)
}
type U128 = fixed::types::U75F53; // u128 with 18 significant digits
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
@@ -56,6 +49,7 @@ pub struct NodeRewardParams {
reward_blockstamp: u64,
circulating_supply: Uint128,
uptime: Uint128,
sybil_resistance_percent: u8
}
impl NodeRewardParams {
@@ -66,6 +60,7 @@ impl NodeRewardParams {
reward_blockstamp: u64,
circulating_supply: u128,
uptime: u128,
sybil_resistance_percent: u8
) -> NodeRewardParams {
NodeRewardParams {
period_reward_pool: Uint128(period_reward_pool),
@@ -75,6 +70,7 @@ impl NodeRewardParams {
reward_blockstamp,
circulating_supply: Uint128(circulating_supply),
uptime: Uint128(uptime),
sybil_resistance_percent
}
}
@@ -119,7 +115,7 @@ impl NodeRewardParams {
}
pub fn alpha(&self) -> U128 {
default_alpha()
U128::from_num(self.sybil_resistance_percent) / U128::from_num(100)
}
}
+2
View File
@@ -116,6 +116,8 @@ pub enum QueryMsg {
LayerDistribution {},
GetRewardPool {},
GetCirculatingSupply {},
GetEpochRewardPercent {},
GetSybilResistancePercent {},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
+4 -1
View File
@@ -96,5 +96,8 @@ pub const DEFAULT_FIRST_EPOCH_START: OffsetDateTime = time::macros::datetime!(20
pub const DEFAULT_EPOCH_LENGTH: Duration = Duration::from_secs(24 * 60 * 60 * 30); // 30 days
/// 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_OPERATOR_EPOCH_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$
// TODO: is there a way to get this from the chain
// TODO: is there a way to get this from the chain
pub const TOTAL_SUPPLY: u128 = 1_000_000_000_000_000;
pub const DEFAULT_PROFIT_MARGIN: u8 = 10;
+1 -1
View File
@@ -693,9 +693,9 @@ name = "mixnet-contract"
version = "0.1.0"
dependencies = [
"az",
"config",
"cosmwasm-std",
"fixed",
"network-defaults",
"schemars",
"serde",
"serde_repr",
-21
View File
@@ -1,21 +0,0 @@
# Like makefile but in Rust, and possibly simpler / more powerful
# https://github.com/casey/just#quick-start
upload: code_id
wasm:
RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown
code_id: wasm
validator-client-scripts --nymd-url ${QA_NYMD_URL} --mnemonic "${QA_MNEMONIC}" --contract ${QA_CONTRACT} upload --wasm-path target/wasm32-unknown-unknown/release/mixnet_contracts.wasm > code_id
migrate: upload
cat code_id | xargs -I {} validator-client-scripts --nymd-url ${QA_NYMD_URL} --mnemonic "${QA_MNEMONIC}" --contract ${QA_CONTRACT} migrate --code-id {}
init: upload
cat code_id | xargs -I {} validator-client-scripts --nymd-url ${QA_NYMD_URL} --mnemonic "${QA_MNEMONIC}" --contract ${QA_CONTRACT} init --code-id {}
clean:
cargo clean
rm -f code_id
-18
View File
@@ -1,18 +0,0 @@
upload: code_id
wasm:
RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown
code_id: wasm
validator-client-scripts --nymd-url ${QA_NYMD_URL} --mnemonic "${QA_MNEMONIC}" --contract ${QA_CONTRACT} upload --wasm-path target/wasm32-unknown-unknown/release/mixnet_contracts.wasm > code_id
migrate: upload
cat code_id | xargs -I {} validator-client-scripts --nymd-url ${QA_NYMD_URL} --mnemonic "${QA_MNEMONIC}" --contract ${QA_CONTRACT} migrate --code-id {}
init: upload
cat code_id | xargs -I {} validator-client-scripts --nymd-url ${QA_NYMD_URL} --mnemonic "${QA_MNEMONIC}" --contract ${QA_CONTRACT} init --code-id {}
clean:
cargo clean
rm -f code_id
+4 -3
View File
@@ -31,9 +31,9 @@ pub const INITIAL_GATEWAY_DELEGATION_REWARD_RATE: u64 = 110;
pub const INITIAL_MIXNODE_ACTIVE_SET_SIZE: u32 = 100;
pub const INITIAL_GATEWAY_ACTIVE_SET_SIZE: u32 = 20;
// TODO: Make sure future migrations don't use this one, but the one pulled from storage
pub const REWARD_POOL: u128 = 250_000_000_000_000;
pub const INITIAL_REWARD_POOL: u128 = 250_000_000_000_000;
pub const EPOCH_REWARD_PERCENT: u8 = 2; // Used to calculate epoch reward pool
pub const DEFAULT_SYBIL_RESISTANCE_PERCENT: u8 = 30;
// 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_COST_PER_EPOCH: u32 = 40_000_000;
@@ -44,7 +44,6 @@ fn default_initial_state(owner: Addr) -> State {
let mixnode_delegation_reward_rate = Decimal::percent(INITIAL_MIXNODE_DELEGATION_REWARD_RATE);
let gateway_delegation_reward_rate = Decimal::percent(INITIAL_GATEWAY_DELEGATION_REWARD_RATE);
// TODO: Wire this through to state and rewarding
let epoch_reward_percent = EPOCH_REWARD_PERCENT;
State {
owner,
@@ -223,6 +222,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
)?),
QueryMsg::GetRewardPool {} => to_binary(&queries::query_reward_pool(deps)),
QueryMsg::GetCirculatingSupply {} => to_binary(&queries::query_circulating_supply(deps)),
QueryMsg::GetEpochRewardPercent {} => to_binary(&EPOCH_REWARD_PERCENT),
QueryMsg::GetSybilResistancePercent {} => to_binary(&DEFAULT_SYBIL_RESISTANCE_PERCENT)
};
Ok(query_res?)
+3 -3
View File
@@ -1,6 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::contract::REWARD_POOL;
use crate::contract::INITIAL_REWARD_POOL;
use crate::state::State;
use crate::transactions::MINIMUM_BLOCK_AGE_FOR_REWARDING;
use crate::{error::ContractError, queries};
@@ -31,7 +31,7 @@ const REWARD_POOL_PREFIX: &[u8] = b"pool";
// buckets
pub const PREFIX_MIXNODES: &[u8] = b"mn";
const PREFIX_MIXNODES_OWNERS: &[u8] = b"mo";
pub const PREFIX_GATEWAYS: &[u8] = b"gt";
const PREFIX_GATEWAYS: &[u8] = b"gt";
const PREFIX_GATEWAYS_OWNERS: &[u8] = b"go";
const PREFIX_MIX_DELEGATION: &[u8] = b"md";
@@ -62,7 +62,7 @@ pub fn mut_reward_pool(storage: &mut dyn Storage) -> Singleton<Uint128> {
pub fn reward_pool_value(storage: &dyn Storage) -> Uint128 {
match reward_pool(storage).load() {
Ok(value) => value,
Err(_e) => Uint128(REWARD_POOL),
Err(_e) => Uint128(INITIAL_REWARD_POOL),
}
}
+6 -11
View File
@@ -488,7 +488,6 @@ pub(crate) fn try_reward_mixnode_v2(
let total_delegation_reward =
increase_mix_delegated_stakes_v2(deps.storage, &current_bond, &reward_params)?;
// assert_eq!(total_delegation_reward.u128(), 1);
// update current bond with the reward given to the node and the delegators
// if it has been bonded for long enough
@@ -798,12 +797,7 @@ pub(crate) fn try_remove_delegation_from_gateway(
#[cfg(test)]
pub mod tests {
use super::*;
use crate::contract::{
execute, query, INITIAL_DEFAULT_EPOCH_LENGTH, INITIAL_GATEWAY_BOND,
INITIAL_GATEWAY_BOND_REWARD_RATE, INITIAL_GATEWAY_DELEGATION_REWARD_RATE,
INITIAL_MIXNODE_BOND, INITIAL_MIXNODE_BOND_REWARD_RATE,
INITIAL_MIXNODE_DELEGATION_REWARD_RATE,
};
use crate::contract::{DEFAULT_SYBIL_RESISTANCE_PERCENT, INITIAL_DEFAULT_EPOCH_LENGTH, INITIAL_GATEWAY_BOND, INITIAL_GATEWAY_BOND_REWARD_RATE, INITIAL_GATEWAY_DELEGATION_REWARD_RATE, INITIAL_MIXNODE_BOND, INITIAL_MIXNODE_BOND_REWARD_RATE, INITIAL_MIXNODE_DELEGATION_REWARD_RATE, execute, query};
use crate::helpers::calculate_epoch_reward_rate;
use crate::queries::DELEGATION_PAGE_DEFAULT_LIMIT;
use crate::storage::{
@@ -4078,7 +4072,7 @@ pub mod tests {
#[test]
fn test_tokenomics_rewarding() {
use crate::contract::{EPOCH_REWARD_PERCENT, REWARD_POOL};
use crate::contract::{EPOCH_REWARD_PERCENT, INITIAL_REWARD_POOL};
type U128 = fixed::types::U75F53;
@@ -4086,7 +4080,7 @@ pub mod tests {
let mut env = mock_env();
let current_state = config(deps.as_mut().storage).load().unwrap();
let network_monitor_address = current_state.network_monitor_address;
let period_reward_pool = (REWARD_POOL / 100) * EPOCH_REWARD_PERCENT as u128;
let period_reward_pool = (INITIAL_REWARD_POOL / 100) * EPOCH_REWARD_PERCENT as u128;
assert_eq!(period_reward_pool, 5_000_000_000_000);
let k = 200; // Imagining our active set size is 200
let circulating_supply = circulating_supply(&deps.storage).u128();
@@ -4139,6 +4133,7 @@ pub mod tests {
0,
circulating_supply,
mix_1_uptime,
DEFAULT_SYBIL_RESISTANCE_PERCENT
);
params.set_reward_blockstamp(env.block.height);
@@ -4158,7 +4153,7 @@ pub mod tests {
mix_1_reward_result.lambda(),
U128::from_num(0.0000001333333333)
);
// assert_eq!(mix_1_reward_result.reward(), U128::from_num(333.3359996146436116));
assert_eq!(mix_1_reward_result.reward().int(), 333);
let mix1_operator_profit = mix_1.operator_reward(&params);
@@ -4194,7 +4189,7 @@ pub mod tests {
assert_eq!(
reward_pool_value(&deps.storage).u128(),
U128::from_num(REWARD_POOL)
U128::from_num(INITIAL_REWARD_POOL)
- (U128::from_num(mix1_operator_profit)
+ U128::from_num(mix1_delegator1_reward)
+ U128::from_num(mix1_delegator2_reward))
+1 -1
View File
@@ -2402,9 +2402,9 @@ name = "mixnet-contract"
version = "0.1.0"
dependencies = [
"az",
"config",
"cosmwasm-std",
"fixed",
"network-defaults",
"schemars",
"serde",
"serde_repr",
-20
View File
@@ -1,20 +0,0 @@
[package]
name = "tokenomics-py"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "tokenomics_py"
crate-type = ["cdylib"]
[dependencies]
mixnet-contract = {path = "../common/mixnet-contract"}
cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch="0.14.1-updatedk256" }
[dependencies.pyo3]
version = "0.14.5"
features = ["extension-module"]
-48
View File
@@ -1,48 +0,0 @@
use cosmwasm_std::Uint128;
use mixnet_contract::MixNodeBond;
use pyo3::prelude::*;
const ACTIVE_MIX_SET_SIZE: u32 = 1000;
#[pymodule]
fn tokenomics_py(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(compute_mix_rewards_py, m)?)?;
Ok(())
}
// assign_rewards_to_nodes, Econ_results:344
#[pyfunction]
fn compute_mix_rewards_py(
pledged: f64,
delegated: f64,
total_stake: f64, // total stake delegated across the network, ideally persisted in the blockchain
performance: f64, // uptime ?
income_global_mix: f64, // inflation pool
omega_k: f64, // work_share * number of noes, where workshare is considered uniform 1/k, so it follows this is k in the uniform case
alpha: f64, // sybil resistance param - externally defined constant
k: f64, // number of desired mixnodes - externally defined constant
) -> f64 {
// set_lambda_sigma_mixnet, Network_econ:308
let lambda = (pledged / total_stake).min(1. / k);
let sigma = ((pledged + delegated) / total_stake).min(1. / k);
performance * income_global_mix * (sigma * omega_k + alpha * lambda) / (1. + alpha)
}
fn compute_mix_rewards(mix: &MixNodeBond) {
let k = ACTIVE_MIX_SET_SIZE as f64;
let one_over_k = 1. / k;
let one_over_k_uint = (one_over_k * 1_000_000.) as u128;
// Assume uniform node work distribution for simplicity
let work_share = one_over_k;
let omega_k = work_share * k;
// TODO: Use Coin struct from the Tauri wallet, this must be in the Minor denom it will be much easier then
}
// TODO:
// Compute total stake across the entire network, open question is how to handle orphaned delegations
// Compute performance as uptime, uptime can be obtained from the validator API, might be useful to introduce some sugar functions. Validator API is also not available from the contract most likely
// Compute workshare, as either 1/k or node uptime / total uptime, probably best to assume 1/k for now
// k is the active set size
// Where should the inflation pool size be set
+14
View File
@@ -108,6 +108,20 @@ impl<C> Client<C> {
Ok(self.0.read().await.get_circulating_supply().await?)
}
pub(crate) async fn get_sybil_resistance_percent(&self) -> Result<u8, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.0.read().await.get_sybil_resistance_percent().await?)
}
pub(crate) async fn get_epoch_reward_percent(&self) -> Result<u8, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.0.read().await.get_epoch_reward_percent().await?)
}
pub(crate) async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
+17 -13
View File
@@ -291,16 +291,7 @@ impl Rewarder {
// by people hesitating to delegate to nodes without them and thus those nodes disappearing
// from the active set (once introduced)
let mixnode_delegators = self.produce_active_mixnode_delegators_map().await?;
let reward_pool = self.nymd_client.get_reward_pool().await?;
let circulating_supply = self.nymd_client.get_circulating_supply().await?;
let state = self.nymd_client.get_state_params().await?;
let k = state.mixnode_active_set_size;
let period_reward_pool = (reward_pool / 100) * 2_u128;
info!("Rewarding pool stats");
info!("-- Reward pool: {} unym", reward_pool);
info!("---- Epoch reward pool: {} unym", period_reward_pool);
info!("-- Circulating supply: {} unym", circulating_supply);
// 1. go through all active mixnodes
// 2. filter out nodes that are currently not in the active set (as `mixnode_delegators` was obtained by
@@ -322,11 +313,23 @@ impl Rewarder {
.filter(|node| node.uptime.u8() > 0)
.collect();
let total_epoch_uptime = eligible_nodes
.iter()
.fold(0, |acc, mix| acc + mix.uptime.u8() as u128);
if cfg!(feature = "tokenomics") {
let total_epoch_uptime = eligible_nodes
.iter()
.fold(0, |acc, mix| acc + mix.uptime.u8() as u128);
let reward_pool = self.nymd_client.get_reward_pool().await?;
let circulating_supply = self.nymd_client.get_circulating_supply().await?;
let sybil_resistance_percent = self.nymd_client.get_sybil_resistance_percent().await?;
let epoch_reward_percent = self.nymd_client.get_epoch_reward_percent().await?;
let k = state.mixnode_active_set_size;
let period_reward_pool = (reward_pool / 100) * epoch_reward_percent as u128;
info!("Rewarding pool stats");
info!("-- Reward pool: {} unym", reward_pool);
info!("---- Epoch reward pool: {} unym", period_reward_pool);
info!("-- Circulating supply: {} unym", circulating_supply);
for mix in eligible_nodes.iter_mut() {
mix.params = Some(NodeRewardParams::new(
period_reward_pool,
@@ -335,6 +338,7 @@ impl Rewarder {
0,
circulating_supply,
mix.uptime.u8().into(),
sybil_resistance_percent
));
}
} else {