From ef8e452f3050dec2d63adbd7ac00356fca43df10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 10 Nov 2023 09:54:03 +0000 Subject: [PATCH 1/3] using performance^20 when calculating active set selection weight --- .../contracts-common/src/types.rs | 10 ++++++++++ .../src/epoch_operations/rewarded_set_assignment.rs | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs index 4dca6b173b..46a6b174c6 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs @@ -71,6 +71,16 @@ impl Percent { // we know the cast from u128 to u8 is a safe one since the internal value must be within 0 - 1 range truncate_decimal(hundred * self.0).u128() as u8 } + + pub fn pow(&self, exp: u32) -> Self { + match self.0.checked_pow(exp) { + Ok(res) => Percent(res), + Err(overflow) => { + // since the percent is meant to always be strictly less than 1, this should NEVER hapen + panic!("the percent invariant has been broken. the exponentiation result has overflown: {overflow}"); + } + } + } } impl Display for Percent { diff --git a/nym-api/src/epoch_operations/rewarded_set_assignment.rs b/nym-api/src/epoch_operations/rewarded_set_assignment.rs index 12bc33eaae..aa382f5414 100644 --- a/nym-api/src/epoch_operations/rewarded_set_assignment.rs +++ b/nym-api/src/epoch_operations/rewarded_set_assignment.rs @@ -24,7 +24,7 @@ struct MixnodeWithStakeAndPerformance { impl MixnodeWithStakeAndPerformance { fn to_selection_weight(&self) -> f64 { - let scaled_stake = self.total_stake * self.performance; + let scaled_stake = self.total_stake * self.performance.pow(20); stake_to_f64(scaled_stake) } } From 4c8fa74dfef875aad8f102240ea216aac160d72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 24 Nov 2023 09:31:46 +0000 Subject: [PATCH 2/3] remove the panic --- .../contracts-common/src/types.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs index 46a6b174c6..95ba1fa6f3 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs @@ -75,9 +75,14 @@ impl Percent { pub fn pow(&self, exp: u32) -> Self { match self.0.checked_pow(exp) { Ok(res) => Percent(res), - Err(overflow) => { - // since the percent is meant to always be strictly less than 1, this should NEVER hapen - panic!("the percent invariant has been broken. the exponentiation result has overflown: {overflow}"); + Err(_overflow) => { + // since the percent is meant to always be less than 1, this should NEVER hapen, however, + // when the inevitable happens because of some misuse, just saturate the result + if self.0 < Decimal::one() { + Percent::zero() + } else { + Percent(Decimal::MAX) + } } } } From d92c8c41498ed181de2829404b693e59399cb7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 24 Nov 2023 09:40:17 +0000 Subject: [PATCH 3/3] deal with the pow error during stake adjustment --- .../contracts-common/src/types.rs | 16 +++------------- .../epoch_operations/rewarded_set_assignment.rs | 10 +++++++++- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs index 95ba1fa6f3..218bd6ca50 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs @@ -3,6 +3,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::Decimal; +use cosmwasm_std::OverflowError; use cosmwasm_std::Uint128; use serde::de::Error; use serde::{Deserialize, Deserializer}; @@ -72,19 +73,8 @@ impl Percent { truncate_decimal(hundred * self.0).u128() as u8 } - pub fn pow(&self, exp: u32) -> Self { - match self.0.checked_pow(exp) { - Ok(res) => Percent(res), - Err(_overflow) => { - // since the percent is meant to always be less than 1, this should NEVER hapen, however, - // when the inevitable happens because of some misuse, just saturate the result - if self.0 < Decimal::one() { - Percent::zero() - } else { - Percent(Decimal::MAX) - } - } - } + pub fn checked_pow(&self, exp: u32) -> Result { + self.0.checked_pow(exp).map(Percent) } } diff --git a/nym-api/src/epoch_operations/rewarded_set_assignment.rs b/nym-api/src/epoch_operations/rewarded_set_assignment.rs index aa382f5414..f4bded8c09 100644 --- a/nym-api/src/epoch_operations/rewarded_set_assignment.rs +++ b/nym-api/src/epoch_operations/rewarded_set_assignment.rs @@ -24,7 +24,15 @@ struct MixnodeWithStakeAndPerformance { impl MixnodeWithStakeAndPerformance { fn to_selection_weight(&self) -> f64 { - let scaled_stake = self.total_stake * self.performance.pow(20); + let scaled_performance = match self.performance.checked_pow(20) { + Ok(perf) => perf, + Err(overflow) => { + warn!("the node's performance ({}) has overflow while scaling it by the factor of 20: {overflow}. Setting it to 0 instead.", self.performance); + return 0.; + } + }; + + let scaled_stake = self.total_stake * scaled_performance; stake_to_f64(scaled_stake) } }