diff --git a/pallets/networks/Cargo.toml b/pallets/networks/Cargo.toml index 243b529..377100c 100644 --- a/pallets/networks/Cargo.toml +++ b/pallets/networks/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-networks" -version = "0.1.15" +version = "0.1.16" license.workspace = true authors.workspace = true edition.workspace = true diff --git a/pallets/networks/src/lib.rs b/pallets/networks/src/lib.rs index 3d42c02..c6db8e6 100644 --- a/pallets/networks/src/lib.rs +++ b/pallets/networks/src/lib.rs @@ -83,7 +83,9 @@ where + num_traits::ops::overflowing::OverflowingAdd + sp_std::ops::AddAssign + sp_std::ops::Not - + sp_std::ops::BitAnd, + + sp_std::ops::Shl + + sp_std::ops::Shr + + sp_std::ops::BitAnd, RewardCurve: Get<&'static PiecewiseLinear<'static>>, T: Config, { diff --git a/pallets/networks/src/math.rs b/pallets/networks/src/math.rs index 9051d27..97c2bca 100644 --- a/pallets/networks/src/math.rs +++ b/pallets/networks/src/math.rs @@ -9,24 +9,41 @@ where + num_traits::ops::overflowing::OverflowingAdd + sp_std::ops::AddAssign + sp_std::ops::Not - + sp_std::ops::BitAnd, + + sp_std::ops::Shl + + sp_std::ops::Shr + + sp_std::ops::BitAnd, { + + fn zero(&self) -> Balance { + 0u32.into() + } + + fn one(&self) -> Balance { + 1u32.into() + } + + fn bit_shift(&self) -> Balance { + let u32_shift: u32 = core::mem::size_of::() + .saturating_mul(4) + .try_into() + .unwrap_or_default(); + u32_shift.into() + } + fn least_significant_bits(&self, a: Balance) -> Balance { - a & ((1 << 64) - 1) + a & ((self.one() << self.bit_shift()) - self.one()) } fn most_significant_bits(&self, a: Balance) -> Balance { - a >> 64 + a >> self.bit_shift() } fn two_complement(&self, a: Balance) -> Balance { - let one: Balance = 1u32.into(); - (!a).wrapping_add(&one) + (!a).wrapping_add(&self.one()) } fn adjusted_ratio(&self, a: Balance) -> Balance { - let one: Balance = 1u32.into(); - (self.two_complement(a) / a).wrapping_add(&one) + (self.two_complement(a) / a).wrapping_add(&self.one()) } fn modulo(&self, a: Balance) -> Balance { @@ -61,13 +78,13 @@ where let (r0, r1) = self.overflow_resistant_addition( r0, r1, - self.least_significant_bits(x) << 64, + self.least_significant_bits(x) << self.bit_shift(), self.most_significant_bits(x), ); let (r0, r1) = self.overflow_resistant_addition( r0, r1, - self.least_significant_bits(y) << 64, + self.least_significant_bits(y) << self.bit_shift(), self.most_significant_bits(y), ); @@ -80,7 +97,7 @@ where mut a1: Balance, b: Balance, ) -> (Balance, Balance) { - if b == 1u32.into() { + if b == self.one() { return (a0, a1); } @@ -109,12 +126,10 @@ where } pub fn calculate(a: Balance, b: Balance, c: Balance) -> Balance { - let zero: Balance = 0u32.into(); - if a == zero || b == zero || c == zero { - return zero; - } - let inner = MulDiv(core::marker::PhantomData); + if c == inner.zero() { + return c; + } inner.mul_div(a, b, c) } } diff --git a/pallets/networks/src/tests.rs b/pallets/networks/src/tests.rs index 0ada03d..b7a980c 100644 --- a/pallets/networks/src/tests.rs +++ b/pallets/networks/src/tests.rs @@ -1593,18 +1593,18 @@ fn trigger_nullification_works_as_expected() { } #[test] -fn check_substrate_guarantees_not_to_overflow() { +fn check_substrate_guarantees_not_to_overflow_u128() { ExtBuilder::build().execute_with(|| { let reward_curve = RewardCurve::get(); let mut n: u128 = 69; let mut d: u128 = 100; loop { - n = match n.checked_mul(1_000_000) { + n = match n.checked_mul(1_000) { Some(value) => value, None => break, }; - d = match d.checked_mul(1_000_000) { + d = match d.checked_mul(1_000) { Some(value) => value, None => break, }; @@ -1617,7 +1617,55 @@ fn check_substrate_guarantees_not_to_overflow() { } #[test] -fn check_muldiv_guarantees_not_to_overflow() { +fn check_substrate_guarantees_not_to_overflow_u64() { + ExtBuilder::build().execute_with(|| { + let reward_curve = RewardCurve::get(); + let mut n: u64 = 69; + let mut d: u64 = 100; + + loop { + n = match n.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + d = match d.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + assert_eq!( + reward_curve.calculate_for_fraction_times_denominator(n, d), + d + ); + } + }); +} + +#[test] +fn check_substrate_guarantees_not_to_overflow_u32() { + ExtBuilder::build().execute_with(|| { + let reward_curve = RewardCurve::get(); + let mut n: u32 = 69; + let mut d: u32 = 100; + + loop { + n = match n.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + d = match d.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + assert_eq!( + reward_curve.calculate_for_fraction_times_denominator(n, d), + d + ); + } + }); +} + +#[test] +fn check_muldiv_guarantees_not_to_overflow_for_u128() { ExtBuilder::build().execute_with(|| { let mut a: u128 = 2; let mut b: u128 = 3; @@ -1625,19 +1673,19 @@ fn check_muldiv_guarantees_not_to_overflow() { let mut result: u128 = 1; loop { - a = match a.checked_mul(1_000_000) { + a = match a.checked_mul(1_000) { Some(value) => value, None => break, }; - b = match b.checked_mul(1_000_000) { + b = match b.checked_mul(1_000) { Some(value) => value, None => break, }; - c = match c.checked_mul(1_000_000) { + c = match c.checked_mul(1_000) { Some(value) => value, None => break, }; - result = match result.checked_mul(1_000_000) { + result = match result.checked_mul(1_000) { Some(value) => value, None => break, }; @@ -1655,6 +1703,84 @@ fn check_muldiv_guarantees_not_to_overflow() { }); } +#[test] +fn check_muldiv_guarantees_not_to_overflow_for_u64() { + ExtBuilder::build().execute_with(|| { + let mut a: u64 = 2; + let mut b: u64 = 3; + let mut c: u64 = 6; + let mut result: u64 = 1; + + loop { + a = match a.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + b = match b.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + c = match c.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + result = match result.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + + assert_eq!(MulDiv::::calculate(a, b, c), result); + } + + assert_eq!( + MulDiv::::calculate(u64::MAX, u64::MAX, u64::MAX), + u64::MAX + ); + assert_eq!(MulDiv::::calculate(u64::MAX, 0, 0), 0); + assert_eq!(MulDiv::::calculate(0, u64::MAX, 0), 0); + assert_eq!(MulDiv::::calculate(0, 0, u64::MAX), 0); + }); +} + +#[test] +fn check_muldiv_guarantees_not_to_overflow_for_u32() { + ExtBuilder::build().execute_with(|| { + let mut a: u32 = 2; + let mut b: u32 = 3; + let mut c: u32 = 6; + let mut result: u32 = 1; + + loop { + a = match a.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + b = match b.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + c = match c.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + result = match result.checked_mul(1_000) { + Some(value) => value, + None => break, + }; + + assert_eq!(MulDiv::::calculate(a, b, c), result); + } + + assert_eq!( + MulDiv::::calculate(u32::MAX, u32::MAX, u32::MAX), + u32::MAX + ); + assert_eq!(MulDiv::::calculate(u32::MAX, 0, 0), 0); + assert_eq!(MulDiv::::calculate(0, u32::MAX, 0), 0); + assert_eq!(MulDiv::::calculate(0, 0, u32::MAX), 0); + }); +} + #[test] fn check_bridged_inflation_curve_for_overflow() { ExtBuilder::build().execute_with(|| { @@ -1675,15 +1801,15 @@ fn check_bridged_inflation_curve_for_overflow() { assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); loop { - total_staked_ideal = match total_staked_ideal.checked_mul(1_000_000) { + total_staked_ideal = match total_staked_ideal.checked_mul(1_000) { Some(value) => value, None => break, }; - total_staked_not_ideal = match total_staked_not_ideal.checked_mul(1_000_000) { + total_staked_not_ideal = match total_staked_not_ideal.checked_mul(1_000) { Some(value) => value, None => break, }; - total_issuance = match total_issuance.checked_mul(1_000_000) { + total_issuance = match total_issuance.checked_mul(1_000) { Some(value) => value, None => break, }; @@ -1726,10 +1852,12 @@ fn check_bridged_inflation_curve_for_big_commissions() { let mut amount_full: u128 = 1337; let total_staked_ideal: u128 = 69_000_000; + let total_staked_gt_ideal: u128 = 100_000_000; + let total_staked_lt_ideal: u128 = 3_000_000; let total_issuance: u128 = 100_000_000; loop { - amount_full = match amount_full.checked_mul(1_000_000) { + amount_full = match amount_full.checked_mul(1_000) { Some(value) => value, None => break, }; @@ -1750,6 +1878,22 @@ fn check_bridged_inflation_curve_for_big_commissions() { ), (commission, 0) ); + + let (payout, rest) = BridgedInflationCurve::::era_payout( + total_staked_gt_ideal, + total_issuance + amount, + 0 + ); + assert!(payout < commission); + assert!(rest < commission); + + let (payout, rest) = BridgedInflationCurve::::era_payout( + total_staked_lt_ideal, + total_issuance + amount, + 0 + ); + assert!(payout < commission); + assert!(rest < commission); } }); }