Fix unbonding for new nodes

This commit is contained in:
durch
2022-04-30 23:09:02 +02:00
parent 4fcf0da5c0
commit 41319fe7ad
7 changed files with 116 additions and 37 deletions
@@ -18,4 +18,6 @@ pub enum MixnetContractError {
#[from]
source: cosmwasm_std::StdError,
},
#[error("Division by zero at {}", line!())]
DivisionByZero,
}
@@ -222,11 +222,17 @@ impl DelegatorRewardParams {
}
pub fn determine_delegation_reward(&self, delegation_amount: Uint128) -> u128 {
if self.sigma == 0 {
return 0;
}
// change all values into their fixed representations
let delegation_amount = U128::from_num(delegation_amount.u128());
let circulating_supply = U128::from_num(self.reward_params.circulating_supply());
let scaled_delegation_amount = delegation_amount / circulating_supply;
// Div by zero checked above
let delegator_reward =
(ONE - self.profit_margin) * (scaled_delegation_amount / self.sigma) * self.node_profit;
@@ -465,12 +471,16 @@ impl MixNodeBond {
pub fn operator_reward(&self, params: &RewardParams) -> u128 {
let reward = self.reward(params);
if reward.sigma == 0 {
return 0;
}
let profit = if reward.reward < params.node.operator_cost() {
U128::from_num(0u128)
} else {
reward.reward - params.node.operator_cost()
};
let operator_base_reward = reward.reward.min(params.node.operator_cost());
// Div by zero checked above
let operator_reward = (self.profit_margin()
+ (ONE - self.profit_margin()) * reward.lambda / reward.sigma)
* profit;
@@ -57,8 +57,12 @@ impl NodeEpochRewards {
pub fn operator_reward(&self, profit_margin: U128) -> Result<Uint128, MixnetContractError> {
let reward = self.node_profit();
let operator_base_reward = reward.min(self.operator_cost());
let operator_reward =
(profit_margin + (ONE - profit_margin) * self.lambda() / self.sigma()) * reward;
let div_by_zero_check = if let Some(value) = self.lambda().checked_div(self.sigma()) {
value
} else {
return Err(MixnetContractError::DivisionByZero);
};
let operator_reward = (profit_margin + (ONE - profit_margin) * div_by_zero_check) * reward;
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0u128));
@@ -80,8 +84,15 @@ impl NodeEpochRewards {
let circulating_supply = U128::from_num(epoch_reward_params.circulating_supply());
let scaled_delegation_amount = delegation_amount / circulating_supply;
let delegator_reward =
(ONE - profit_margin) * scaled_delegation_amount / self.sigma() * self.node_profit();
let check_div_by_zero =
if let Some(value) = scaled_delegation_amount.checked_div(self.sigma()) {
value
} else {
return Err(MixnetContractError::DivisionByZero);
};
let delegator_reward = (ONE - profit_margin) * check_div_by_zero * self.node_profit();
let reward = delegator_reward.max(U128::ZERO);
if let Some(int_reward) = reward.checked_cast() {
@@ -198,6 +209,11 @@ impl RewardParams {
let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
- (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
if denom == 0 {
return U128::ZERO;
}
// Div by zero checked above
if self.in_active_set() {
// work_active = factor / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
self.active_set_work_factor() / denom * self.rewarded_set_size()
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use super::storage::{self, PENDING_DELEGATION_EVENTS};
// use crate::contract::debug_with_visibility;
// use crate::contract::debug_with_visibility;
use crate::error::ContractError;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnodes_storage;
@@ -44,6 +45,8 @@ pub(crate) fn _try_reconcile_all_delegation_events(
let mut response = Response::new();
// debug_with_visibility(api, "Reconciling delegation events");
for (key, delegation_event) in pending_delegation_events {
match delegation_event {
DelegationEvent::Delegate(delegation) => {
@@ -253,7 +256,7 @@ pub struct ReconcileUndelegateResponse {
pub(crate) fn try_reconcile_undelegation(
storage: &mut dyn Storage,
_api: &dyn Api,
api: &dyn Api,
pending_undelegate: &PendingUndelegate,
) -> Result<ReconcileUndelegateResponse, ContractError> {
let delegation_map = storage::delegations();
@@ -283,6 +286,7 @@ pub(crate) fn try_reconcile_undelegation(
let reward = crate::rewards::transactions::calculate_delegator_reward(
storage,
api,
pending_undelegate.proxy_storage_key(),
&pending_undelegate.mix_identity(),
)?;
@@ -191,6 +191,7 @@ pub(crate) fn _try_remove_mixnode(
crate::rewards::transactions::_try_compound_operator_reward(
deps.storage,
deps.api,
env.block.height,
&owner,
None,
+2 -2
View File
@@ -39,7 +39,7 @@ pub fn query_operator_reward(deps: Deps, owner: String) -> Result<Uint128, Contr
}
};
super::transactions::calculate_operator_reward(deps.storage, &owner_address, &bond)
super::transactions::calculate_operator_reward(deps.storage, deps.api, &owner_address, &bond)
}
pub fn query_delegator_reward(
@@ -58,7 +58,7 @@ pub fn query_delegator_reward(
&deps.api.addr_validate(&owner)?,
proxy.as_ref(),
);
super::transactions::calculate_delegator_reward(deps.storage, key, &mix_identity)
super::transactions::calculate_delegator_reward(deps.storage, deps.api, key, &mix_identity)
}
#[cfg(test)]
+76 -30
View File
@@ -6,6 +6,7 @@ use super::storage::{
OPERATOR_REWARD_CLAIMED_HEIGHT,
};
use crate::constants;
use crate::contract::debug_with_visibility;
use crate::delegations::storage as delegations_storage;
use crate::delegations::transactions::_try_delegate_to_mixnode;
use crate::error::ContractError;
@@ -14,7 +15,7 @@ use crate::mixnodes::storage::{self as mixnodes_storage, StoredMixnodeBond};
use crate::rewards::helpers;
use crate::support::helpers::is_authorized;
use config::defaults::DENOM;
use cosmwasm_std::{Addr, Coin, DepsMut, Env, MessageInfo, Order, Response, Storage, Uint128};
use cosmwasm_std::{Addr, Api, Coin, DepsMut, Env, MessageInfo, Order, Response, Storage, Uint128};
use mixnet_contract_common::events::{
new_compound_delegator_reward_event, new_compound_operator_reward_event,
new_mix_operator_rewarding_event, new_not_found_mix_operator_rewarding_event,
@@ -35,8 +36,13 @@ pub fn try_compound_operator_reward_on_behalf(
let proxy = deps.api.addr_validate(info.sender.as_str())?;
let owner = deps.api.addr_validate(&owner)?;
let reward =
_try_compound_operator_reward(deps.storage, env.block.height, &owner, Some(proxy))?;
let reward = _try_compound_operator_reward(
deps.storage,
deps.api,
env.block.height,
&owner,
Some(proxy),
)?;
Ok(Response::new().add_event(new_compound_operator_reward_event(&owner, reward)))
}
@@ -47,13 +53,15 @@ pub fn try_compound_operator_reward(
info: MessageInfo,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(info.sender.as_str())?;
let reward = _try_compound_operator_reward(deps.storage, env.block.height, &owner, None)?;
let reward =
_try_compound_operator_reward(deps.storage, deps.api, env.block.height, &owner, None)?;
Ok(Response::new().add_event(new_compound_operator_reward_event(&owner, reward)))
}
pub fn _try_compound_operator_reward(
storage: &mut dyn Storage,
api: &dyn Api,
block_height: u64,
owner: &Addr,
proxy: Option<Addr>,
@@ -72,7 +80,7 @@ pub fn _try_compound_operator_reward(
}
let mut updated_bond = bond.clone();
let reward = calculate_operator_reward(storage, owner, &bond)?;
let reward = calculate_operator_reward(storage, api, owner, &bond)?;
updated_bond.accumulated_rewards = Some(updated_bond.accumulated_rewards() - reward);
updated_bond.pledge_amount.amount += reward;
mixnodes().replace(
@@ -94,6 +102,7 @@ pub fn _try_compound_operator_reward(
pub fn calculate_operator_reward(
storage: &dyn Storage,
api: &dyn Api,
owner: &Addr,
bond: &StoredMixnodeBond,
) -> Result<Uint128, ContractError> {
@@ -111,19 +120,22 @@ pub fn calculate_operator_reward(
Ok(Uint128::zero()),
|acc, height| -> Result<Uint128, ContractError> {
let accumulated_reward = acc?;
if let Some(bond) =
mixnodes().may_load_at_height(storage, bond.identity().as_str(), height)?
if let Some(bond) = mixnodes()
.may_load_at_height(storage, bond.identity().as_str(), height)
.ok()
.flatten()
{
if let Some(ref epoch_rewards) = bond.epoch_rewards {
let epoch_reward_params =
epoch_reward_params_for_id(storage, epoch_rewards.epoch_id())?;
// Compound rewards from previous heights
let reward_at_height = epoch_rewards.delegation_reward(
bond.pledge_amount().amount + accumulated_reward,
bond.profit_margin(),
epoch_reward_params,
)?;
return Ok(accumulated_reward + reward_at_height);
match epoch_rewards.operator_reward(bond.profit_margin()) {
Ok(reward) => return Ok(accumulated_reward + reward),
Err(err) => {
debug_with_visibility(
api,
format!("Failed to calculate operator reward: {:?}", err),
);
}
};
}
};
Ok(accumulated_reward)
@@ -204,7 +216,7 @@ pub fn _try_compound_delegator_reward(
&deps.api.addr_validate(owner_address)?,
proxy.as_ref(),
);
let reward = calculate_delegator_reward(deps.storage, key.clone(), mix_identity)?;
let reward = calculate_delegator_reward(deps.storage, deps.api, key.clone(), mix_identity)?;
let mut compounded_delegation = reward;
// Might want to introduce paging here
@@ -261,6 +273,7 @@ pub fn _try_compound_delegator_reward(
// + last_reward_claimed height is correctly used
pub fn calculate_delegator_reward(
storage: &dyn Storage,
api: &dyn Api,
key: Vec<u8>,
mix_identity: &str,
) -> Result<Uint128, ContractError> {
@@ -296,7 +309,15 @@ pub fn calculate_delegator_reward(
.fold(Uint128::zero(), |total, delegation| {
total + delegation.amount.amount
});
// debug_with_visibility(
// api,
// format!("delegation at height {} - {}", height, delegation_at_height),
// );
if delegation_at_height != Uint128::zero() {
// debug_with_visibility(
// api,
// format!("Loading bond {} at height {}", mix_identity, height),
// );
if let Some(bond) = mixnodes()
.may_load_at_height(storage, mix_identity, height)
.ok()
@@ -304,17 +325,40 @@ pub fn calculate_delegator_reward(
{
if let Some(ref epoch_rewards) = bond.epoch_rewards {
// Compound rewards from previous heights
let epoch_reward_params =
epoch_reward_params_for_id(storage, epoch_rewards.epoch_id())?;
let reward_at_height = match epoch_rewards.delegation_reward(
delegation_at_height + accumulated_reward,
bond.profit_margin(),
epoch_reward_params,
) {
Ok(reward) => reward,
Err(_err) => Uint128::zero(),
};
return Ok(accumulated_reward + reward_at_height);
match epoch_reward_params_for_id(storage, epoch_rewards.epoch_id()) {
Ok(params) => {
let reward_at_height = match epoch_rewards.delegation_reward(
delegation_at_height + accumulated_reward,
bond.profit_margin(),
params,
) {
Ok(reward) => {
// debug_with_visibility(
// api,
// format!("Reward at height {} - {}", height, reward),
// );
reward
}
Err(err) => {
debug_with_visibility(
api,
format!(
"Error calculating reward at {} - {}",
height, err
),
);
Uint128::zero()
}
};
return Ok(accumulated_reward + reward_at_height);
}
Err(_err) => {
debug_with_visibility(
api,
format!("No epoch reward params for epoch {}", height),
);
}
}
}
}
};
@@ -1181,7 +1225,8 @@ pub mod tests {
}
let alice_reward =
calculate_delegator_reward(&deps.storage, key.clone(), &node_identity_1).unwrap();
calculate_delegator_reward(&deps.storage, &deps.api, key.clone(), &node_identity_1)
.unwrap();
assert_eq!(alice_reward, Uint128::new(304552));
let mix_0 = mixnodes.load(&deps.storage, &node_identity_1).unwrap();
@@ -1231,8 +1276,9 @@ pub mod tests {
);
let operator_reward =
calculate_operator_reward(&deps.storage, &Addr::unchecked("alice"), &mix_1).unwrap();
assert_eq!(operator_reward, Uint128::new(190345));
calculate_operator_reward(&deps.storage, &deps.api, &Addr::unchecked("alice"), &mix_1)
.unwrap();
assert_eq!(operator_reward, Uint128::new(352532));
assert_eq!(
mix_1_reward_result.sigma(),