Tokenomics rewards (#802)
* Initial analysis * Implement core rewards distribution * Tests and refactoring for better testability * Feature gate ts-rs in mixnet-contract * No more floats * Fix performance calculation * Add migration * Bandwidth fix, reduce inflation pool size * Update tokenomics * Refactor rewarding and replace num crate * Address review comments * Cleanup, better test values * Simplify formula * Cleanup, add rewarding formulas to README * Address review comments * Cleanup rebase * [ci skip] Generate TS types * fmt Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>
This commit is contained in:
@@ -29,3 +29,8 @@ validator-api/v4.json
|
||||
validator-api/v6.json
|
||||
**/node_modules
|
||||
validator-api/keypair
|
||||
contracts/mixnet/code_id
|
||||
contracts/mixnet/Justfile
|
||||
contracts/mixnet/Makefile
|
||||
validator-config
|
||||
*.patch
|
||||
Generated
+35
@@ -228,6 +228,12 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d6dff4a1892b54d70af377bf7a17064192e822865791d812957f21e3108c325"
|
||||
|
||||
[[package]]
|
||||
name = "base-x"
|
||||
version = "0.2.8"
|
||||
@@ -411,6 +417,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
@@ -1642,6 +1654,18 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d333a26ec13a023c6dff4b7584de4d323cfee2e508f5dd2bbee6669e4f7efdf"
|
||||
dependencies = [
|
||||
"az",
|
||||
"bytemuck",
|
||||
"half",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.22"
|
||||
@@ -2271,6 +2295,12 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "3.5.5"
|
||||
@@ -2986,10 +3016,15 @@ dependencies = [
|
||||
name = "mixnet-contract"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"cosmwasm-std",
|
||||
"fixed",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
|
||||
+1
-1
@@ -62,4 +62,4 @@ default-members = [
|
||||
"validator-api",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts"]
|
||||
exclude = ["explorer", "contracts", "tokenomics-py"]
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
## The Nym Privacy Platform
|
||||
|
||||
This repository contains the Nym mixnet.
|
||||
|
||||
The platform is composed of multiple Rust crates. Top-level executable binary crates include:
|
||||
|
||||
* nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers.
|
||||
@@ -33,6 +31,45 @@ There's a `.env.sample-dev` file provided which you can rename to `.env` if you
|
||||
|
||||
You can chat to us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes places in the **#dev** channel. Node operators should be in the **#node-operators** channel.
|
||||
|
||||
### Rewards
|
||||
|
||||
Node, node operator and delegator rewards are determined according to the principles laid out in the section 6 of [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf). Below is a TLDR of the variables and formulas involved in calculating the epoch rewards. Initial reward pool is set to 250 million Nym, making the circulating supply 750 million Nym.
|
||||
|
||||
|Symbol|Definition|
|
||||
|---|---|
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=R">|global share of rewards available, starts at 2% of the reward pool.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=R_{i}">|node reward for mixnode `i`.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=\sigma_{i}">|ratio of total node stake (node bond + all delegations) to the token circulating supply.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}">|ratio of stake operator has plaged to their node to the token circulating supply.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}">|fraction of total effort undertaken by node `i`, set to `1/k` in testnet Milhon.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=k">|number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `active set` size, and set to 5000 in testnet Milhon.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=\alpha">|Sybil attack resistance parameter - the higher this parameter is set the stronger the reduction in competitivness gets for a Sybil attacker.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}">|declared profit margin of operator `i`, defaults to 10% in testnet Milhon.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=PF_{i}">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}">|cost of operating node `i` for the duration of the rewarding eopoch, set to 40 Nym for testnet Milhon.
|
||||
|
||||
Node reward for node `i` is determined as:
|
||||
|
||||
<img src="https://render.githubusercontent.com/render/math?math=R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)">
|
||||
|
||||
where:
|
||||
|
||||
<img src="https://render.githubusercontent.com/render/math?math=\sigma^'_{i} = min\{\sigma_{i}, 1/k\}">
|
||||
|
||||
and
|
||||
|
||||
<img src="https://render.githubusercontent.com/render/math?math=\lambda^'_{i} = min\{\lambda_{i}, 1/k\}">
|
||||
|
||||
Operator of node `i` is credited with the following amount:
|
||||
|
||||
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}">
|
||||
|
||||
Delegate with stake `s` recieves:
|
||||
|
||||
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}">
|
||||
|
||||
where `s'` is stake `s` scaled over total token circulating supply.
|
||||
|
||||
### Licensing and copyright information
|
||||
|
||||
This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files.
|
||||
|
||||
@@ -523,8 +523,11 @@ impl GatewayClient {
|
||||
return Err(GatewayClientError::NotAuthenticated);
|
||||
}
|
||||
#[cfg(feature = "coconut")]
|
||||
if self.estimate_required_bandwidth(&packets) < self.bandwidth_remaining {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth);
|
||||
if self.bandwidth_remaining <= self.estimate_required_bandwidth(&packets) {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth((
|
||||
self.estimate_required_bandwidth(&packets),
|
||||
self.bandwidth_remaining,
|
||||
)));
|
||||
}
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
@@ -592,7 +595,10 @@ impl GatewayClient {
|
||||
}
|
||||
#[cfg(feature = "coconut")]
|
||||
if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth);
|
||||
return Err(GatewayClientError::NotEnoughBandwidth((
|
||||
(mix_packet.sphinx_packet().len() as i64),
|
||||
self.bandwidth_remaining,
|
||||
)));
|
||||
}
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
@@ -631,7 +637,10 @@ impl GatewayClient {
|
||||
}
|
||||
#[cfg(feature = "coconut")]
|
||||
if self.bandwidth_remaining <= 0 {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth);
|
||||
return Err(GatewayClientError::NotEnoughBandwidth((
|
||||
0,
|
||||
self.bandwidth_remaining,
|
||||
)));
|
||||
}
|
||||
if self.connection.is_partially_delegated() {
|
||||
return Ok(());
|
||||
|
||||
@@ -23,7 +23,7 @@ pub enum GatewayClientError {
|
||||
MalformedResponse,
|
||||
SerializeCredential,
|
||||
NotAuthenticated,
|
||||
NotEnoughBandwidth,
|
||||
NotEnoughBandwidth((i64, i64)),
|
||||
UnexpectedResponse,
|
||||
ConnectionInInvalidState,
|
||||
RegistrationFailure(HandshakeError),
|
||||
@@ -102,8 +102,12 @@ impl fmt::Display for GatewayClientError {
|
||||
write!(f, "gateway returned an error response - {}", err)
|
||||
}
|
||||
GatewayClientError::UnexpectedResponse => write!(f, "received an unexpected response"),
|
||||
GatewayClientError::NotEnoughBandwidth => {
|
||||
write!(f, "client does not have enough bandwidth")
|
||||
GatewayClientError::NotEnoughBandwidth((estimated, remaining)) => {
|
||||
write!(
|
||||
f,
|
||||
"Client does not have enough bandwidth: estimated {}, remaining: {}",
|
||||
estimated, remaining
|
||||
)
|
||||
}
|
||||
GatewayClientError::SerializeCredential => {
|
||||
write!(f, "credential could not be serialized")
|
||||
|
||||
@@ -119,7 +119,7 @@ impl PartiallyDelegated {
|
||||
}
|
||||
.is_err()
|
||||
{
|
||||
panic!("failed to send back `mixnet_receiver_future` result on the oneshot channel")
|
||||
warn!("failed to send back `mixnet_receiver_future` result on the oneshot channel")
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -172,6 +172,34 @@ impl<C> Client<C> {
|
||||
Ok(self.nymd.get_state_params().await?)
|
||||
}
|
||||
|
||||
pub async fn get_reward_pool(&self) -> Result<u128, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_reward_pool().await?.u128())
|
||||
}
|
||||
|
||||
pub async fn get_circulating_supply(&self) -> Result<u128, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
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
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::nymd::fee_helpers::Operation;
|
||||
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
||||
use cosmrs::rpc::endpoint::broadcast;
|
||||
use cosmrs::rpc::{Error as TendermintRpcError, HttpClientUrl};
|
||||
use cosmwasm_std::Coin;
|
||||
use cosmwasm_std::{Coin, Uint128};
|
||||
use mixnet_contract::{
|
||||
Addr, Delegation, ExecuteMsg, Gateway, GatewayOwnershipResponse, IdentityKey,
|
||||
LayerDistribution, MixNode, MixOwnershipResponse, PagedAllDelegationsResponse,
|
||||
@@ -211,6 +211,46 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_reward_pool(&self) -> Result<Uint128, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetRewardPool {};
|
||||
self.client
|
||||
.query_contract_smart(self.contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_circulating_supply(&self) -> Result<Uint128, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetCirculatingSupply {};
|
||||
self.client
|
||||
.query_contract_smart(self.contract_address()?, &request)
|
||||
.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
|
||||
|
||||
@@ -8,10 +8,18 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
# this branch is identical to 0.14.1 with addition of updated k256 dependency required to help poor cargo choose correct version
|
||||
cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch="0.14.1-updatedk256" }
|
||||
cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch = "0.14.1-updatedk256" }
|
||||
#cosmwasm-std = { version = "0.14.1" }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_repr = "0.1"
|
||||
schemars = "0.8"
|
||||
ts-rs = "3.0"
|
||||
ts-rs = { version = "3.0", optional = true }
|
||||
thiserror = "1.0"
|
||||
network-defaults = { path = "../network-defaults" }
|
||||
fixed = "1.1"
|
||||
az = "1.1"
|
||||
log = "0.4.14"
|
||||
|
||||
[features]
|
||||
default = ["ts-rs"]
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum MixnetContractError {
|
||||
#[error("Overflow Error")]
|
||||
OverflowError(#[from] cosmwasm_std::OverflowError),
|
||||
#[error("reward_blockstamp field not set, set_reward_blockstamp must be called before attempting to issue rewards")]
|
||||
BlockstampNotSet,
|
||||
}
|
||||
@@ -7,9 +7,9 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Display;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema, TS)]
|
||||
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
|
||||
pub struct Gateway {
|
||||
pub host: String,
|
||||
pub mix_port: u16,
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod delegation;
|
||||
pub mod error;
|
||||
mod gateway;
|
||||
mod mixnode;
|
||||
pub mod mixnode;
|
||||
mod msg;
|
||||
mod types;
|
||||
|
||||
|
||||
@@ -2,15 +2,20 @@
|
||||
#![allow(clippy::field_reassign_with_default)]
|
||||
|
||||
use crate::{IdentityKey, SphinxKey};
|
||||
use cosmwasm_std::{coin, Addr, Coin};
|
||||
use az::CheckedCast;
|
||||
use cosmwasm_std::{coin, Addr, Coin, Uint128};
|
||||
use log::error;
|
||||
use network_defaults::{DEFAULT_OPERATOR_EPOCH_COST, DEFAULT_PROFIT_MARGIN};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Display;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema, TS)]
|
||||
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)]
|
||||
pub struct MixNode {
|
||||
pub host: String,
|
||||
pub mix_port: u16,
|
||||
@@ -43,6 +48,97 @@ pub enum Layer {
|
||||
Three = 3,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
|
||||
pub struct NodeRewardParams {
|
||||
period_reward_pool: Uint128,
|
||||
k: Uint128,
|
||||
reward_blockstamp: u64,
|
||||
circulating_supply: Uint128,
|
||||
uptime: Uint128,
|
||||
sybil_resistance_percent: u8,
|
||||
}
|
||||
|
||||
impl NodeRewardParams {
|
||||
pub fn new(
|
||||
period_reward_pool: u128,
|
||||
k: u128,
|
||||
reward_blockstamp: u64,
|
||||
circulating_supply: u128,
|
||||
uptime: u128,
|
||||
sybil_resistance_percent: u8,
|
||||
) -> NodeRewardParams {
|
||||
NodeRewardParams {
|
||||
period_reward_pool: Uint128(period_reward_pool),
|
||||
k: Uint128(k),
|
||||
reward_blockstamp,
|
||||
circulating_supply: Uint128(circulating_supply),
|
||||
uptime: Uint128(uptime),
|
||||
sybil_resistance_percent,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn performance(&self) -> U128 {
|
||||
U128::from_num(self.uptime.u128()) / U128::from_num(100)
|
||||
}
|
||||
|
||||
pub fn operator_cost(&self) -> U128 {
|
||||
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_EPOCH_COST as u128)
|
||||
}
|
||||
|
||||
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
|
||||
self.reward_blockstamp = blockstamp;
|
||||
}
|
||||
|
||||
pub fn period_reward_pool(&self) -> u128 {
|
||||
self.period_reward_pool.u128()
|
||||
}
|
||||
|
||||
pub fn k(&self) -> u128 {
|
||||
self.k.u128()
|
||||
}
|
||||
|
||||
pub fn circulating_supply(&self) -> u128 {
|
||||
self.circulating_supply.u128()
|
||||
}
|
||||
|
||||
pub fn reward_blockstamp(&self) -> u64 {
|
||||
self.reward_blockstamp
|
||||
}
|
||||
|
||||
pub fn uptime(&self) -> u128 {
|
||||
self.uptime.u128()
|
||||
}
|
||||
|
||||
pub fn one_over_k(&self) -> U128 {
|
||||
U128::from_num(1) / U128::from_num(self.k.u128())
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> U128 {
|
||||
U128::from_num(self.sybil_resistance_percent) / U128::from_num(100)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeRewardResult {
|
||||
reward: U128,
|
||||
lambda: U128,
|
||||
sigma: U128,
|
||||
}
|
||||
|
||||
impl NodeRewardResult {
|
||||
pub fn reward(&self) -> U128 {
|
||||
self.reward
|
||||
}
|
||||
|
||||
pub fn lambda(&self) -> U128 {
|
||||
self.lambda
|
||||
}
|
||||
|
||||
pub fn sigma(&self) -> U128 {
|
||||
self.sigma
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeBond {
|
||||
pub bond_amount: Coin,
|
||||
@@ -51,6 +147,7 @@ pub struct MixNodeBond {
|
||||
pub layer: Layer,
|
||||
pub block_height: u64,
|
||||
pub mix_node: MixNode,
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
}
|
||||
|
||||
impl MixNodeBond {
|
||||
@@ -60,6 +157,7 @@ impl MixNodeBond {
|
||||
layer: Layer,
|
||||
block_height: u64,
|
||||
mix_node: MixNode,
|
||||
profit_margin_percent: Option<u8>,
|
||||
) -> Self {
|
||||
MixNodeBond {
|
||||
total_delegation: coin(0, &bond_amount.denom),
|
||||
@@ -68,9 +166,15 @@ impl MixNodeBond {
|
||||
layer,
|
||||
block_height,
|
||||
mix_node,
|
||||
profit_margin_percent,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn profit_margin(&self) -> U128 {
|
||||
U128::from_num(self.profit_margin_percent.unwrap_or(DEFAULT_PROFIT_MARGIN))
|
||||
/ U128::from_num(100)
|
||||
}
|
||||
|
||||
pub fn identity(&self) -> &String {
|
||||
&self.mix_node.identity_key
|
||||
}
|
||||
@@ -86,6 +190,116 @@ impl MixNodeBond {
|
||||
pub fn mix_node(&self) -> &MixNode {
|
||||
&self.mix_node
|
||||
}
|
||||
|
||||
pub fn total_delegation(&self) -> Coin {
|
||||
self.total_delegation.clone()
|
||||
}
|
||||
|
||||
pub fn bond_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
|
||||
U128::from_num(self.bond_amount().amount.u128()) / U128::from_num(circulating_supply)
|
||||
}
|
||||
|
||||
pub fn total_stake_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
|
||||
U128::from_num(self.bond_amount().amount.u128() + self.total_delegation().amount.u128())
|
||||
/ U128::from_num(circulating_supply)
|
||||
}
|
||||
|
||||
pub fn lambda(&self, params: &NodeRewardParams) -> U128 {
|
||||
// Ratio of a bond to the token circulating supply
|
||||
let bond_to_circulating_supply_ratio =
|
||||
self.bond_to_circulating_supply(params.circulating_supply());
|
||||
bond_to_circulating_supply_ratio.min(params.one_over_k())
|
||||
}
|
||||
|
||||
pub fn sigma(&self, params: &NodeRewardParams) -> U128 {
|
||||
// Ratio of a delegation to the the token circulating supply
|
||||
let total_stake_to_circulating_supply_ratio =
|
||||
self.total_stake_to_circulating_supply(params.circulating_supply());
|
||||
total_stake_to_circulating_supply_ratio.min(params.one_over_k())
|
||||
}
|
||||
|
||||
pub fn reward(&self, params: &NodeRewardParams) -> NodeRewardResult {
|
||||
// Assuming uniform work distribution across the network this is one_over_k * k
|
||||
let omega_k = U128::from_num(1u128);
|
||||
let lambda = self.lambda(params);
|
||||
let sigma = self.sigma(params);
|
||||
|
||||
let reward = params.performance()
|
||||
* params.period_reward_pool()
|
||||
* (sigma * omega_k + params.alpha() * lambda * sigma * params.k())
|
||||
/ (U128::from_num(1) + params.alpha());
|
||||
|
||||
NodeRewardResult {
|
||||
reward,
|
||||
lambda,
|
||||
sigma,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_profit(&self, params: &NodeRewardParams) -> U128 {
|
||||
if self.reward(params).reward() < params.operator_cost() {
|
||||
U128::from_num(0)
|
||||
} else {
|
||||
self.reward(params).reward() - params.operator_cost()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operator_reward(&self, params: &NodeRewardParams) -> u128 {
|
||||
let reward = self.reward(params);
|
||||
let profit = if reward.reward < params.operator_cost() {
|
||||
U128::from_num(0)
|
||||
} else {
|
||||
reward.reward - params.operator_cost()
|
||||
};
|
||||
let operator_base_reward = reward.reward.min(params.operator_cost());
|
||||
let operator_reward = (self.profit_margin()
|
||||
+ (U128::from_num(1) - self.profit_margin()) * reward.lambda / reward.sigma)
|
||||
* profit;
|
||||
|
||||
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0));
|
||||
|
||||
if let Some(int_reward) = reward.checked_cast() {
|
||||
int_reward
|
||||
} else {
|
||||
error!(
|
||||
"Could not cast reward ({}) to u128, returning 0 - mixnode {}",
|
||||
reward,
|
||||
self.identity()
|
||||
);
|
||||
0u128
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sigma_ratio(&self, params: &NodeRewardParams) -> U128 {
|
||||
if self.total_stake_to_circulating_supply(params.circulating_supply()) < params.one_over_k()
|
||||
{
|
||||
self.total_stake_to_circulating_supply(params.circulating_supply())
|
||||
} else {
|
||||
params.one_over_k()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reward_delegation(&self, delegation_amount: Uint128, params: &NodeRewardParams) -> u128 {
|
||||
let scaled_delegation_amount =
|
||||
U128::from_num(delegation_amount.u128()) / U128::from_num(params.circulating_supply());
|
||||
|
||||
let delegator_reward = (U128::from_num(1) - self.profit_margin())
|
||||
* scaled_delegation_amount
|
||||
/ self.sigma(params)
|
||||
* self.node_profit(params);
|
||||
|
||||
let reward = delegator_reward.max(U128::from_num(0));
|
||||
if let Some(int_reward) = reward.checked_cast() {
|
||||
int_reward
|
||||
} else {
|
||||
error!(
|
||||
"Could not cast delegator reward ({}) to u128, returning 0 - mixnode {}",
|
||||
reward,
|
||||
self.identity()
|
||||
);
|
||||
0u128
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for MixNodeBond {
|
||||
@@ -217,6 +431,7 @@ mod tests {
|
||||
layer: Layer::One,
|
||||
block_height: 100,
|
||||
mix_node: mixnode_fixture(),
|
||||
profit_margin_percent: Some(10),
|
||||
};
|
||||
|
||||
let mix2 = MixNodeBond {
|
||||
@@ -226,6 +441,7 @@ mod tests {
|
||||
layer: Layer::One,
|
||||
block_height: 120,
|
||||
mix_node: mixnode_fixture(),
|
||||
profit_margin_percent: Some(10),
|
||||
};
|
||||
|
||||
let mix3 = MixNodeBond {
|
||||
@@ -235,6 +451,7 @@ mod tests {
|
||||
layer: Layer::One,
|
||||
block_height: 120,
|
||||
mix_node: mixnode_fixture(),
|
||||
profit_margin_percent: Some(10),
|
||||
};
|
||||
|
||||
let mix4 = MixNodeBond {
|
||||
@@ -244,6 +461,7 @@ mod tests {
|
||||
layer: Layer::One,
|
||||
block_height: 120,
|
||||
mix_node: mixnode_fixture(),
|
||||
profit_margin_percent: Some(10),
|
||||
};
|
||||
|
||||
let mix5 = MixNodeBond {
|
||||
@@ -253,6 +471,7 @@ mod tests {
|
||||
layer: Layer::One,
|
||||
block_height: 120,
|
||||
mix_node: mixnode_fixture(),
|
||||
profit_margin_percent: Some(10),
|
||||
};
|
||||
|
||||
// summary:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::mixnode::NodeRewardParams;
|
||||
use crate::StateParams;
|
||||
use crate::{Gateway, IdentityKey, MixNode};
|
||||
use cosmwasm_std::Addr;
|
||||
@@ -36,6 +37,12 @@ pub enum ExecuteMsg {
|
||||
// percentage value in range 0-100
|
||||
uptime: u32,
|
||||
},
|
||||
|
||||
RewardMixnodeV2 {
|
||||
identity: IdentityKey,
|
||||
// percentage value in range 0-100
|
||||
params: NodeRewardParams,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
@@ -75,6 +82,10 @@ pub enum QueryMsg {
|
||||
address: Addr,
|
||||
},
|
||||
LayerDistribution {},
|
||||
GetRewardPool {},
|
||||
GetCirculatingSupply {},
|
||||
GetEpochRewardPercent {},
|
||||
GetSybilResistancePercent {},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
|
||||
@@ -93,4 +93,11 @@ pub const VALIDATOR_API_VERSION: &str = "v1";
|
||||
|
||||
// REWARDING
|
||||
pub const DEFAULT_FIRST_EPOCH_START: OffsetDateTime = time::macros::datetime!(2021-08-23 12:00 UTC);
|
||||
pub const DEFAULT_EPOCH_LENGTH: Duration = Duration::from_secs(24 * 60 * 60); // 24h
|
||||
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
|
||||
pub const TOTAL_SUPPLY: u128 = 1_000_000_000_000_000;
|
||||
|
||||
pub const DEFAULT_PROFIT_MARGIN: u8 = 10;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.envrc
|
||||
Generated
+213
-93
@@ -23,9 +23,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ast_node"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93f52ce8fac3d0e6720a92b0576d737c01b1b5db4dd786e962e5925f00bf755"
|
||||
checksum = "e96d5444b02f3080edac8a144f6baf29b2fb6ff589ad4311559731a7c7529381"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"pmutil",
|
||||
@@ -41,12 +41,24 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d6dff4a1892b54d70af377bf7a17064192e822865791d812957f21e3108c325"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
@@ -79,9 +91,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.7.1"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
|
||||
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
@@ -89,6 +101,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
@@ -121,9 +139,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.6.0"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c32f031ea41b4291d695026c023b95d59db2d8a2c7640800ed56bc8f510f22"
|
||||
checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b"
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-crypto"
|
||||
@@ -147,9 +165,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-schema"
|
||||
version = "0.14.0"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6a9a6a4ca3b4d7b56f943312a65857cdfc84424f9c2773889f4cd17f36fba61"
|
||||
checksum = "04159eec9b583671db7923ff2b979736dfb8f0152347cab9fd02373c22e1a870"
|
||||
dependencies = [
|
||||
"schemars",
|
||||
"serde_json",
|
||||
@@ -181,9 +199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.1.4"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8"
|
||||
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -196,9 +214,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.2.2"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b32a398eb1ccfbe7e4f452bc749c44d38dd732e9a253f19da224c416f00ee7f4"
|
||||
checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"rand_core 0.6.3",
|
||||
@@ -218,9 +236,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.1.0"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "639891fde0dbea823fc3d798a0fdf9d2f9440a42d64a78ab3488b0ca025117b3"
|
||||
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest 0.9.0",
|
||||
@@ -266,9 +284,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.4.0"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f215f706081a44cb702c71c39a52c05da637822e9c1645a50b7202689e982d"
|
||||
checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
]
|
||||
@@ -338,9 +356,9 @@ checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf"
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.12.3"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "713c32426287891008edb98f8b5c6abb2130aa043c93a818728fcda78606f274"
|
||||
checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372"
|
||||
dependencies = [
|
||||
"der",
|
||||
"elliptic-curve",
|
||||
@@ -370,9 +388,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.10.4"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83e5c176479da93a0983f0a6fdc3c1b8e7d5be0d7fe3fe05a99f15b96582b9a8"
|
||||
checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b"
|
||||
dependencies = [
|
||||
"crypto-bigint",
|
||||
"ff",
|
||||
@@ -404,14 +422,26 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63eec06c61e487eecf0f7e6e6372e596a81922c28d33e645d6983ca6493a1af0"
|
||||
checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f"
|
||||
dependencies = [
|
||||
"rand_core 0.6.3",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d333a26ec13a023c6dff4b7584de4d323cfee2e508f5dd2bbee6669e4f7efdf"
|
||||
dependencies = [
|
||||
"az",
|
||||
"bytemuck",
|
||||
"half",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@@ -501,6 +531,12 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "3.5.5"
|
||||
@@ -564,6 +600,15 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-macro"
|
||||
version = "0.1.9"
|
||||
@@ -579,15 +624,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "k256"
|
||||
version = "0.9.5"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "008b0281ca8032567c9711cd48631781c15228301860a39b32deb28d63125e46"
|
||||
checksum = "903ae2481bcdfdb7b68e0a9baa4b7c9aff600b9ae2e8e5bb5833b8c91ab851ea"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"ecdsa",
|
||||
@@ -603,9 +648,18 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.100"
|
||||
version = "0.2.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
|
||||
checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -624,9 +678,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
@@ -638,10 +692,15 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
name = "mixnet-contract"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"cosmwasm-std",
|
||||
"fixed",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
@@ -653,6 +712,7 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"fixed",
|
||||
"mixnet-contract",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -732,6 +792,31 @@ dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@@ -802,9 +887,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbee84ed13e44dd82689fa18348a49934fa79cc774a344c42fc9b301c71b140a"
|
||||
checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
@@ -823,9 +908,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
@@ -835,9 +920,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -850,9 +935,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.8"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -917,6 +1002,15 @@ dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
@@ -942,9 +1036,9 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.3"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6ab463ae35acccb5cba66c0084c985257b797d288b6050cc2f6ac1b266cb78"
|
||||
checksum = "d7a48d098c2a7fdf5740b19deb1181b4fb8a9e68e03ae517c14cde04b5725409"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"schemars_derive",
|
||||
@@ -954,9 +1048,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.3"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "902fdfbcf871ae8f653bddf4b2c05905ddaabc08f69d32a915787e3be0d31356"
|
||||
checksum = "4a9ea2a613fe4cd7118b2bb101a25d8ae6192e1975179b67b2f17afd11e70ac8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -971,10 +1065,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.122"
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "974ef1bd2ad8a507599b336595454081ff68a9599b4890af7643c0c0ed73a62c"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -990,9 +1090,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.122"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dee1f300f838c8ac340ecb0112b3ac472464fa67e87292bdb3dfc9c49128e17"
|
||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1012,9 +1112,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.61"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
|
||||
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -1046,9 +1146,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.5"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
|
||||
checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -1059,9 +1159,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.3.1"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19772be3c4dd2ceaacf03cb41d5885f2a02c4d8804884918e3a258480803335"
|
||||
checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"rand_core 0.6.3",
|
||||
@@ -1075,15 +1175,15 @@ checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.6.1"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "987637c5ae6b3121aba9d513f869bd2bff11c4cc086c22473befd6649c0bd521"
|
||||
checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32"
|
||||
dependencies = [
|
||||
"der",
|
||||
]
|
||||
@@ -1102,12 +1202,13 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a"
|
||||
checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"new_debug_unreachable",
|
||||
"parking_lot",
|
||||
"phf_shared",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
@@ -1146,15 +1247,15 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "swc_atoms"
|
||||
version = "0.2.7"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "837a3ef86c2817228e733b6f173c821fd76f9eb21a0bc9001a826be48b00b4e7"
|
||||
checksum = "9f5229fe227ff0060e13baa386d6e368797700eab909523f730008d191ee53ae"
|
||||
dependencies = [
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
@@ -1254,9 +1355,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_macros_common"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08ed2e930f5a1a4071fe62c90fd3a296f6030e5d94bfe13993244423caf59a78"
|
||||
checksum = "bf7c68e78ffbcba3d38abe6d0b76a0e1a37888b5c9301db3426537207090ada3"
|
||||
dependencies = [
|
||||
"pmutil",
|
||||
"proc-macro2",
|
||||
@@ -1266,9 +1367,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_visit"
|
||||
version = "0.2.6"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a423caa0b4585118164dbad8f1ad52b592a9a9370b25decc4d84c6b4309132c0"
|
||||
checksum = "f8511a4788ab29daf00bee23e425aac92c9be4eec74c98fec4a45d0e710be695"
|
||||
dependencies = [
|
||||
"either",
|
||||
"swc_visit_macros",
|
||||
@@ -1290,9 +1391,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.65"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
|
||||
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1301,18 +1402,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.23"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.23"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1321,9 +1422,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.1"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a776787d9c5d455bec3db044586ccdd8a9c74d5da5dc319fb80f3db08808fe6"
|
||||
checksum = "99beeb0daeac2bd1e86ac2c21caddecb244b39a093594da1a661ec2060c7aedd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"time-macros",
|
||||
@@ -1331,15 +1432,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.1"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04a153416002296880a3b51329a0e3df31c779c53ec827993e865ce427982843"
|
||||
checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6"
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.3.1"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338"
|
||||
checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
@@ -1383,9 +1484,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.13.0"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
@@ -1395,9 +1496,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "uint"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e11fe9a9348741cf134085ad57c249508345fe16411b3d7fb4ff2da2f1d6382e"
|
||||
checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crunchy",
|
||||
@@ -1407,12 +1508,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.5"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
|
||||
dependencies = [
|
||||
"matches",
|
||||
]
|
||||
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -1431,9 +1529,9 @@ checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
@@ -1466,7 +1564,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.3.0"
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf68b08513768deaa790264a7fac27a58cbf2705cfcdc9448362229217d7e970"
|
||||
|
||||
@@ -49,3 +49,4 @@ thiserror = { version = "1.0.23" }
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { version = "0.14.0" }
|
||||
fixed = "1.1"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::u128;
|
||||
|
||||
use crate::helpers::calculate_epoch_reward_rate;
|
||||
use crate::state::State;
|
||||
use crate::storage::{config, layer_distribution};
|
||||
@@ -26,6 +28,13 @@ pub const INITIAL_MIXNODE_DELEGATION_REWARD_RATE: u64 = 110;
|
||||
|
||||
pub const INITIAL_MIXNODE_ACTIVE_SET_SIZE: u32 = 100;
|
||||
|
||||
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;
|
||||
|
||||
fn default_initial_state(owner: Addr) -> State {
|
||||
let mixnode_bond_reward_rate = Decimal::percent(INITIAL_MIXNODE_BOND_REWARD_RATE);
|
||||
let mixnode_delegation_reward_rate = Decimal::percent(INITIAL_MIXNODE_DELEGATION_REWARD_RATE);
|
||||
@@ -94,6 +103,9 @@ pub fn execute(
|
||||
ExecuteMsg::RewardMixnode { identity, uptime } => {
|
||||
transactions::try_reward_mixnode(deps, env, info, identity, uptime)
|
||||
}
|
||||
ExecuteMsg::RewardMixnodeV2 { identity, params } => {
|
||||
transactions::try_reward_mixnode_v2(deps, env, info, identity, params)
|
||||
}
|
||||
ExecuteMsg::DelegateToMixnode { mix_identity } => {
|
||||
transactions::try_delegate_to_mixnode(deps, env, info, mix_identity)
|
||||
}
|
||||
@@ -151,11 +163,14 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
|
||||
mix_identity,
|
||||
address,
|
||||
)?),
|
||||
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?)
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
Ok(Default::default())
|
||||
|
||||
@@ -77,4 +77,12 @@ pub enum ContractError {
|
||||
identity: IdentityKey,
|
||||
address: Addr,
|
||||
},
|
||||
#[error("Overflow error!")]
|
||||
Overflow(#[from] cosmwasm_std::OverflowError),
|
||||
|
||||
#[error("We tried to remove more funds then are available in the Reward pool. Wanted to remove {to_remove}, but have only {reward_pool}")]
|
||||
OutOfFunds { to_remove: u128, reward_pool: u128 },
|
||||
|
||||
#[error("Invalid ratio")]
|
||||
Ratio(#[from] mixnet_contract::error::MixnetContractError),
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ const DECIMAL_FRACTIONAL: Uint128 = Uint128(1_000_000_000_000_000_000u128);
|
||||
// cosmwasm bucket internal value
|
||||
const NAMESPACE_LENGTH: usize = 2;
|
||||
|
||||
fn decimal_to_uint128(value: Decimal) -> Uint128 {
|
||||
pub fn decimal_to_uint128(value: Decimal) -> Uint128 {
|
||||
value * DECIMAL_FRACTIONAL
|
||||
}
|
||||
|
||||
fn uint128_to_decimal(value: Uint128) -> Decimal {
|
||||
pub fn uint128_to_decimal(value: Uint128) -> Decimal {
|
||||
Decimal::from_ratio(value, DECIMAL_FRACTIONAL)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
use crate::error::ContractError;
|
||||
use crate::helpers::get_all_delegations_paged;
|
||||
use crate::storage::{
|
||||
all_mix_delegations_read, gateways_owners_read, gateways_read, mix_delegations_read,
|
||||
mixnodes_owners_read, mixnodes_read, read_layer_distribution, read_state_params,
|
||||
reverse_mix_delegations_read,
|
||||
all_mix_delegations_read, circulating_supply, gateways_owners_read, gateways_read,
|
||||
mix_delegations_read, mixnodes_owners_read, mixnodes_read, read_layer_distribution,
|
||||
read_state_params, reverse_mix_delegations_read, reward_pool_value,
|
||||
};
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{coin, Addr, Deps, Order, StdResult};
|
||||
use cosmwasm_std::{coin, Addr, Deps, Order, StdResult, Uint128};
|
||||
use mixnet_contract::{
|
||||
Delegation, GatewayBond, GatewayOwnershipResponse, IdentityKey, LayerDistribution, MixNodeBond,
|
||||
MixOwnershipResponse, PagedAllDelegationsResponse, PagedGatewayResponse,
|
||||
@@ -91,6 +91,14 @@ pub(crate) fn query_layer_distribution(deps: Deps) -> LayerDistribution {
|
||||
read_layer_distribution(deps.storage)
|
||||
}
|
||||
|
||||
pub(crate) fn query_reward_pool(deps: Deps) -> Uint128 {
|
||||
reward_pool_value(deps.storage)
|
||||
}
|
||||
|
||||
pub(crate) fn query_circulating_supply(deps: Deps) -> Uint128 {
|
||||
circulating_supply(deps.storage)
|
||||
}
|
||||
|
||||
/// Adds a 0 byte to terminate the `start_after` value given. This allows CosmWasm
|
||||
/// to get the succeeding key as the start of the next page.
|
||||
// S works for both `String` and `Addr` and that's what we wanted
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::queries;
|
||||
use crate::contract::INITIAL_REWARD_POOL;
|
||||
use crate::state::State;
|
||||
use crate::transactions::MINIMUM_BLOCK_AGE_FOR_REWARDING;
|
||||
use crate::{error::ContractError, queries};
|
||||
use config::defaults::TOTAL_SUPPLY;
|
||||
use cosmwasm_std::{Decimal, Order, StdResult, Storage, Uint128};
|
||||
use cosmwasm_storage::{
|
||||
bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton,
|
||||
Singleton,
|
||||
};
|
||||
use mixnet_contract::mixnode::NodeRewardParams;
|
||||
use mixnet_contract::{
|
||||
Addr, GatewayBond, IdentityKey, IdentityKeyRef, Layer, LayerDistribution, MixNodeBond,
|
||||
RawDelegationData, StateParams,
|
||||
@@ -24,9 +26,10 @@ use serde::Serialize;
|
||||
// singletons
|
||||
const CONFIG_KEY: &[u8] = b"config";
|
||||
const LAYER_DISTRIBUTION_KEY: &[u8] = b"layers";
|
||||
const REWARD_POOL_PREFIX: &[u8] = b"pool";
|
||||
|
||||
// buckets
|
||||
const PREFIX_MIXNODES: &[u8] = b"mn";
|
||||
pub const PREFIX_MIXNODES: &[u8] = b"mn";
|
||||
const PREFIX_MIXNODES_OWNERS: &[u8] = b"mo";
|
||||
const PREFIX_GATEWAYS: &[u8] = b"gt";
|
||||
const PREFIX_GATEWAYS_OWNERS: &[u8] = b"go";
|
||||
@@ -46,6 +49,53 @@ pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton<State> {
|
||||
singleton_read(storage, CONFIG_KEY)
|
||||
}
|
||||
|
||||
fn reward_pool(storage: &dyn Storage) -> ReadonlySingleton<Uint128> {
|
||||
singleton_read(storage, REWARD_POOL_PREFIX)
|
||||
}
|
||||
|
||||
pub fn mut_reward_pool(storage: &mut dyn Storage) -> Singleton<Uint128> {
|
||||
singleton(storage, REWARD_POOL_PREFIX)
|
||||
}
|
||||
|
||||
pub fn reward_pool_value(storage: &dyn Storage) -> Uint128 {
|
||||
match reward_pool(storage).load() {
|
||||
Ok(value) => value,
|
||||
Err(_e) => Uint128(INITIAL_REWARD_POOL),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn incr_reward_pool(
|
||||
amount: Uint128,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<Uint128, ContractError> {
|
||||
let stake = reward_pool_value(storage).saturating_add(amount);
|
||||
mut_reward_pool(storage).save(&stake)?;
|
||||
Ok(stake)
|
||||
}
|
||||
|
||||
pub fn decr_reward_pool(
|
||||
amount: Uint128,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<Uint128, ContractError> {
|
||||
let stake = match reward_pool_value(storage).checked_sub(amount) {
|
||||
Ok(stake) => stake,
|
||||
Err(_e) => {
|
||||
return Err(ContractError::OutOfFunds {
|
||||
to_remove: amount.u128(),
|
||||
reward_pool: reward_pool_value(storage).u128(),
|
||||
})
|
||||
}
|
||||
};
|
||||
mut_reward_pool(storage).save(&stake)?;
|
||||
Ok(stake)
|
||||
}
|
||||
|
||||
pub fn circulating_supply(storage: &dyn Storage) -> Uint128 {
|
||||
let reward_pool = reward_pool_value(storage).u128();
|
||||
Uint128(TOTAL_SUPPLY - reward_pool)
|
||||
}
|
||||
|
||||
pub(crate) fn read_state_params(storage: &dyn Storage) -> StateParams {
|
||||
// note: In any other case, I wouldn't have attempted to unwrap this result, but in here
|
||||
// if we fail to load the stored state we would already be in the undefined behaviour land,
|
||||
@@ -196,6 +246,55 @@ pub(crate) fn increase_mix_delegated_stakes(
|
||||
Ok(total_rewarded)
|
||||
}
|
||||
|
||||
pub(crate) fn increase_mix_delegated_stakes_v2(
|
||||
storage: &mut dyn Storage,
|
||||
bond: &MixNodeBond,
|
||||
params: &NodeRewardParams,
|
||||
) -> Result<Uint128, ContractError> {
|
||||
let chunk_size = queries::DELEGATION_PAGE_MAX_LIMIT as usize;
|
||||
|
||||
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, bond.identity())
|
||||
.range(chunk_start.as_deref(), None, Order::Ascending)
|
||||
.take(chunk_size)
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
if delegations_chunk.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
// append 0 byte to the last value to start with whatever is the next succeeding key
|
||||
chunk_start = Some(
|
||||
delegations_chunk
|
||||
.last()
|
||||
.unwrap()
|
||||
.0
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(std::iter::once(0u8))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
// 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
|
||||
<= params.reward_blockstamp()
|
||||
{
|
||||
let reward = bond.reward_delegation(delegation.amount, params);
|
||||
delegation.amount += Uint128(reward);
|
||||
total_rewarded += Uint128(reward);
|
||||
mix_delegations(storage, bond.identity()).save(&delegator_address, &delegation)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(total_rewarded)
|
||||
}
|
||||
// currently not used outside tests
|
||||
#[cfg(test)]
|
||||
pub(crate) fn read_mixnode_bond(
|
||||
@@ -345,6 +444,7 @@ mod tests {
|
||||
identity_key: node_identity.clone(),
|
||||
..mix_node_fixture()
|
||||
},
|
||||
profit_margin_percent: Some(10),
|
||||
};
|
||||
|
||||
mixnodes(&mut storage)
|
||||
|
||||
@@ -133,6 +133,7 @@ pub mod helpers {
|
||||
Layer::One,
|
||||
12_345,
|
||||
mix_node,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::ContractError;
|
||||
use crate::helpers::{calculate_epoch_reward_rate, scale_reward_by_uptime, Delegations};
|
||||
use crate::queries;
|
||||
@@ -10,6 +9,7 @@ use cosmwasm_std::{
|
||||
attr, coins, BankMsg, Coin, Decimal, DepsMut, Env, MessageInfo, Response, StdResult, Uint128,
|
||||
};
|
||||
use cosmwasm_storage::ReadonlyBucket;
|
||||
use mixnet_contract::mixnode::NodeRewardParams;
|
||||
use mixnet_contract::{
|
||||
Gateway, GatewayBond, IdentityKey, Layer, MixNode, MixNodeBond, RawDelegationData, StateParams,
|
||||
};
|
||||
@@ -99,6 +99,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
|
||||
@@ -416,6 +417,65 @@ pub(crate) fn try_reward_mixnode(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn try_reward_mixnode_v2(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
mix_identity: IdentityKey,
|
||||
params: NodeRewardParams,
|
||||
) -> Result<Response, ContractError> {
|
||||
let state = config_read(deps.storage).load().unwrap();
|
||||
|
||||
// check if this is executed by the monitor, if not reject the transaction
|
||||
if info.sender != state.network_monitor_address {
|
||||
return Err(ContractError::Unauthorized);
|
||||
}
|
||||
|
||||
// check if the bond even exists
|
||||
let mut current_bond = match mixnodes_read(deps.storage).load(mix_identity.as_bytes()) {
|
||||
Ok(bond) => bond,
|
||||
Err(_) => {
|
||||
return Ok(Response {
|
||||
attributes: vec![attr("result", "bond not found")],
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let mut reward_params = params;
|
||||
|
||||
reward_params.set_reward_blockstamp(env.block.height);
|
||||
|
||||
let reward_result = current_bond.reward(&reward_params);
|
||||
|
||||
// Omitting the price per packet function now, it follows that base operator reward is the node_reward
|
||||
let operator_reward = current_bond.operator_reward(&reward_params);
|
||||
|
||||
let total_delegation_reward =
|
||||
increase_mix_delegated_stakes_v2(deps.storage, ¤t_bond, &reward_params)?;
|
||||
|
||||
// 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
|
||||
<= reward_params.reward_blockstamp()
|
||||
{
|
||||
current_bond.bond_amount.amount += Uint128(operator_reward);
|
||||
current_bond.total_delegation.amount += total_delegation_reward;
|
||||
mixnodes(deps.storage).save(mix_identity.as_bytes(), ¤t_bond)?;
|
||||
decr_reward_pool(Uint128(operator_reward), deps.storage)?;
|
||||
decr_reward_pool(total_delegation_reward, deps.storage)?;
|
||||
}
|
||||
|
||||
Ok(Response {
|
||||
submessages: vec![],
|
||||
messages: vec![],
|
||||
attributes: vec![
|
||||
attr("bond increase", reward_result.reward()),
|
||||
attr("total delegation increase", total_delegation_reward),
|
||||
],
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
fn validate_delegation_stake(delegation: &[Coin]) -> Result<(), ContractError> {
|
||||
// check if anything was put as delegation
|
||||
if delegation.is_empty() {
|
||||
@@ -459,8 +519,10 @@ pub(crate) fn try_delegate_to_mixnode(
|
||||
}
|
||||
};
|
||||
|
||||
let amount = info.funds[0].amount;
|
||||
|
||||
// update total_delegation of this node
|
||||
current_bond.total_delegation.amount += info.funds[0].amount;
|
||||
current_bond.total_delegation.amount += amount;
|
||||
mixnodes_bucket.save(mix_identity.as_bytes(), ¤t_bond)?;
|
||||
|
||||
let mut delegation_bucket = mix_delegations(deps.storage, &mix_identity);
|
||||
@@ -468,8 +530,8 @@ pub(crate) fn try_delegate_to_mixnode(
|
||||
|
||||
// write the delegation
|
||||
let new_amount = match delegation_bucket.may_load(sender_bytes)? {
|
||||
Some(existing_delegation) => existing_delegation.amount + info.funds[0].amount,
|
||||
None => info.funds[0].amount,
|
||||
Some(existing_delegation) => existing_delegation.amount + amount,
|
||||
None => amount,
|
||||
};
|
||||
// the block height is reset, if it existed
|
||||
let new_delegation = RawDelegationData::new(new_amount, env.block.height);
|
||||
@@ -532,8 +594,9 @@ pub(crate) fn try_remove_delegation_from_mixnode(
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::contract::{
|
||||
execute, query, INITIAL_DEFAULT_EPOCH_LENGTH, INITIAL_GATEWAY_BOND, INITIAL_MIXNODE_BOND,
|
||||
INITIAL_MIXNODE_BOND_REWARD_RATE, INITIAL_MIXNODE_DELEGATION_REWARD_RATE,
|
||||
execute, query, DEFAULT_SYBIL_RESISTANCE_PERCENT, INITIAL_DEFAULT_EPOCH_LENGTH,
|
||||
INITIAL_GATEWAY_BOND, INITIAL_MIXNODE_BOND, INITIAL_MIXNODE_BOND_REWARD_RATE,
|
||||
INITIAL_MIXNODE_DELEGATION_REWARD_RATE,
|
||||
};
|
||||
use crate::helpers::calculate_epoch_reward_rate;
|
||||
use crate::queries::DELEGATION_PAGE_DEFAULT_LIMIT;
|
||||
@@ -1506,6 +1569,7 @@ pub mod tests {
|
||||
identity_key: node_identity.clone(),
|
||||
..mix_node_fixture()
|
||||
},
|
||||
profit_margin_percent: Some(10),
|
||||
};
|
||||
|
||||
mixnodes(deps.as_mut().storage)
|
||||
@@ -1605,6 +1669,7 @@ pub mod tests {
|
||||
identity_key: node_identity.clone(),
|
||||
..mix_node_fixture()
|
||||
},
|
||||
profit_margin_percent: Some(10),
|
||||
};
|
||||
|
||||
mixnodes(deps.as_mut().storage)
|
||||
@@ -2654,4 +2719,128 @@ pub mod tests {
|
||||
assert_eq!(bob_node.mix_node.identity_key, "bob");
|
||||
assert_eq!(bob_node.layer, Layer::Two);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tokenomics_rewarding() {
|
||||
use crate::contract::{EPOCH_REWARD_PERCENT, INITIAL_REWARD_POOL};
|
||||
|
||||
type U128 = fixed::types::U75F53;
|
||||
|
||||
let mut deps = helpers::init_contract();
|
||||
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 = (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();
|
||||
assert_eq!(circulating_supply, 750_000_000_000_000u128);
|
||||
// mut_reward_pool(deps.as_mut().storage)
|
||||
// .save(&Uint128(period_reward_pool))
|
||||
// .unwrap();
|
||||
|
||||
try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
mock_info(
|
||||
"alice",
|
||||
&vec![Coin {
|
||||
denom: DENOM.to_string(),
|
||||
amount: Uint128(10000_000_000),
|
||||
}],
|
||||
),
|
||||
MixNode {
|
||||
identity_key: "alice".to_string(),
|
||||
..helpers::mix_node_fixture()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
try_delegate_to_mixnode(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
mock_info("d1", &vec![coin(8000_000000, DENOM)]),
|
||||
"alice".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
try_delegate_to_mixnode(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
mock_info("d2", &vec![coin(2000_000000, DENOM)]),
|
||||
"alice".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let info = mock_info(network_monitor_address.as_ref(), &[]);
|
||||
|
||||
env.block.height += 2 * MINIMUM_BLOCK_AGE_FOR_REWARDING;
|
||||
|
||||
let mix_1 = mixnodes_read(&deps.storage).load(b"alice").unwrap();
|
||||
let mix_1_uptime = 100;
|
||||
|
||||
let mut params = NodeRewardParams::new(
|
||||
period_reward_pool,
|
||||
k,
|
||||
0,
|
||||
circulating_supply,
|
||||
mix_1_uptime,
|
||||
DEFAULT_SYBIL_RESISTANCE_PERCENT,
|
||||
);
|
||||
|
||||
params.set_reward_blockstamp(env.block.height);
|
||||
|
||||
assert_eq!(params.performance(), 1);
|
||||
|
||||
let mix_1_reward_result = mix_1.reward(¶ms);
|
||||
|
||||
assert_eq!(
|
||||
mix_1_reward_result.sigma(),
|
||||
U128::from_num(0.0000266666666666)
|
||||
);
|
||||
assert_eq!(
|
||||
mix_1_reward_result.lambda(),
|
||||
U128::from_num(0.0000133333333333)
|
||||
);
|
||||
assert_eq!(mix_1_reward_result.reward().int(), 102646153);
|
||||
|
||||
let mix1_operator_profit = mix_1.operator_reward(¶ms);
|
||||
|
||||
let mix1_delegator1_reward = mix_1.reward_delegation(Uint128(8000_000000), ¶ms);
|
||||
|
||||
let mix1_delegator2_reward = mix_1.reward_delegation(Uint128(2000_000000), ¶ms);
|
||||
|
||||
assert_eq!(mix1_operator_profit, U128::from_num(74455384));
|
||||
assert_eq!(mix1_delegator1_reward, U128::from_num(22552615));
|
||||
assert_eq!(mix1_delegator2_reward, U128::from_num(5638153));
|
||||
|
||||
let pre_reward_bond = read_mixnode_bond(&deps.storage, b"alice").unwrap().u128();
|
||||
assert_eq!(pre_reward_bond, 10000_000_000);
|
||||
|
||||
let pre_reward_delegation = read_mixnode_delegation(&deps.storage, b"alice")
|
||||
.unwrap()
|
||||
.u128();
|
||||
assert_eq!(pre_reward_delegation, 10000_000_000);
|
||||
|
||||
try_reward_mixnode_v2(deps.as_mut(), env, info, "alice".to_string(), params).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
read_mixnode_bond(&deps.storage, b"alice").unwrap().u128(),
|
||||
U128::from_num(pre_reward_bond) + U128::from_num(mix1_operator_profit)
|
||||
);
|
||||
assert_eq!(
|
||||
read_mixnode_delegation(&deps.storage, b"alice")
|
||||
.unwrap()
|
||||
.u128(),
|
||||
pre_reward_delegation + mix1_delegator1_reward + mix1_delegator2_reward
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
reward_pool_value(&deps.storage).u128(),
|
||||
U128::from_num(INITIAL_REWARD_POOL)
|
||||
- (U128::from_num(mix1_operator_profit)
|
||||
+ U128::from_num(mix1_delegator1_reward)
|
||||
+ U128::from_num(mix1_delegator2_reward))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+35
@@ -130,6 +130,12 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d6dff4a1892b54d70af377bf7a17064192e822865791d812957f21e3108c325"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
@@ -288,6 +294,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
@@ -1197,6 +1209,18 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d333a26ec13a023c6dff4b7584de4d323cfee2e508f5dd2bbee6669e4f7efdf"
|
||||
dependencies = [
|
||||
"az",
|
||||
"bytemuck",
|
||||
"half",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.21"
|
||||
@@ -1741,6 +1765,12 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "3.5.5"
|
||||
@@ -2329,10 +2359,15 @@ dependencies = [
|
||||
name = "mixnet-contract"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"cosmwasm-std",
|
||||
"fixed",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ getset = "0.1.1"
|
||||
rocket_sync_db_pools = {version = "0.1.0-rc.1", default-features = false}
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]}
|
||||
|
||||
|
||||
## internal
|
||||
config = { path = "../common/config" }
|
||||
crypto = { path="../common/crypto" }
|
||||
@@ -54,6 +55,8 @@ credentials = { path = "../common/credentials", optional = true }
|
||||
|
||||
[features]
|
||||
coconut = ["coconut-interface", "credentials", "gateway-client/coconut"]
|
||||
default = ["tokenomics"]
|
||||
tokenomics = []
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { version = "1.4", features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
Vendored
+2
-2
@@ -75,7 +75,7 @@ impl<C> ValidatorCacheRefresher<C> {
|
||||
{
|
||||
let (mixnodes, gateways) = tokio::try_join!(
|
||||
self.nymd_client.get_mixnodes(),
|
||||
self.nymd_client.get_gateways()
|
||||
self.nymd_client.get_gateways(),
|
||||
)?;
|
||||
|
||||
let state_params = self.nymd_client.get_state_params().await?;
|
||||
@@ -83,7 +83,7 @@ impl<C> ValidatorCacheRefresher<C> {
|
||||
info!(
|
||||
"Updating validator cache. There are {} mixnodes and {} gateways",
|
||||
mixnodes.len(),
|
||||
gateways.len()
|
||||
gateways.len(),
|
||||
);
|
||||
|
||||
self.cache
|
||||
|
||||
@@ -93,6 +93,34 @@ impl<C> Client<C> {
|
||||
Ok(time)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_reward_pool(&self) -> Result<u128, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.0.read().await.get_reward_pool().await?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_circulating_supply(&self) -> Result<u128, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
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,
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::storage::models::{
|
||||
};
|
||||
use crate::storage::ValidatorApiStorage;
|
||||
use log::{error, info};
|
||||
use mixnet_contract::mixnode::NodeRewardParams;
|
||||
use mixnet_contract::{ExecuteMsg, IdentityKey};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
@@ -48,6 +49,19 @@ pub(crate) struct MixnodeToReward {
|
||||
|
||||
/// Total number of individual addresses that have delegated to this particular node
|
||||
pub(crate) total_delegations: usize,
|
||||
/// Node absolute uptime over total active set uptime
|
||||
params: Option<NodeRewardParams>,
|
||||
}
|
||||
|
||||
impl MixnodeToReward {
|
||||
/// Somewhat clumsy way of feature gatting tokenomics payments. In a tokenomics scenario this will never be None at reward time. We levarage that to Into a different ExecuteMsg variant
|
||||
fn params(&self) -> Option<NodeRewardParams> {
|
||||
if cfg!(feature = "tokenomics") {
|
||||
self.params
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct FailedMixnodeRewardChunkDetails {
|
||||
@@ -62,9 +76,16 @@ pub(crate) struct FailureData {
|
||||
|
||||
impl<'a> From<&'a MixnodeToReward> for ExecuteMsg {
|
||||
fn from(node: &MixnodeToReward) -> Self {
|
||||
ExecuteMsg::RewardMixnode {
|
||||
identity: node.identity.clone(),
|
||||
uptime: node.uptime.u8() as u32,
|
||||
if let Some(params) = node.params() {
|
||||
ExecuteMsg::RewardMixnodeV2 {
|
||||
identity: node.identity.clone(),
|
||||
params,
|
||||
}
|
||||
} else {
|
||||
ExecuteMsg::RewardMixnode {
|
||||
identity: node.identity.clone(),
|
||||
uptime: node.uptime.u8() as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,12 +206,13 @@ 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 state = self.nymd_client.get_state_params().await?;
|
||||
|
||||
// 1. go through all active mixnodes
|
||||
// 2. filter out nodes that are currently not in the active set (as `mixnode_delegators` was obtained by
|
||||
// querying the validator)
|
||||
// 3. determine uptime and attach delegators count
|
||||
let eligible_nodes = active_mixnodes
|
||||
let mut eligible_nodes: Vec<MixnodeToReward> = active_mixnodes
|
||||
.iter()
|
||||
.filter_map(|mix| {
|
||||
mixnode_delegators
|
||||
@@ -199,11 +221,39 @@ impl Rewarder {
|
||||
identity: mix.identity.clone(),
|
||||
uptime: mix.last_day,
|
||||
total_delegations,
|
||||
params: None,
|
||||
})
|
||||
})
|
||||
.filter(|node| node.uptime.u8() > 0)
|
||||
.collect();
|
||||
|
||||
if cfg!(feature = "tokenomics") {
|
||||
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,
|
||||
k.into(),
|
||||
0,
|
||||
circulating_supply,
|
||||
mix.uptime.u8().into(),
|
||||
sybil_resistance_percent,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
info!("Tokenomics feature is OFF");
|
||||
}
|
||||
|
||||
Ok(eligible_nodes)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user