From 7ceabdafdff76871d4c406464be96bcf53cf9564 Mon Sep 17 00:00:00 2001 From: Luiz Carvalho Date: Wed, 12 Feb 2025 22:48:10 -0300 Subject: [PATCH] chore: drop substrate-fixed types --- Cargo.lock | 24 -- Cargo.toml | 1 - pallets/emission0/Cargo.toml | 2 - pallets/emission0/src/distribute.rs | 69 ++--- pallets/emission0/src/distribute/math.rs | 315 +++++++++-------------- pallets/emission0/tests/distribution.rs | 27 +- pallets/governance/Cargo.toml | 2 - pallets/governance/src/proposal.rs | 58 ++--- pallets/governance/tests/voting.rs | 2 +- pallets/torus0/Cargo.toml | 2 - pallets/torus0/src/burn.rs | 39 +-- 11 files changed, 222 insertions(+), 319 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10e9a97..a647b4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8317,7 +8317,6 @@ dependencies = [ "parity-scale-codec", "polkadot-sdk", "scale-info", - "substrate-fixed", "test-utils", ] @@ -8460,7 +8459,6 @@ dependencies = [ "parity-scale-codec", "polkadot-sdk", "scale-info", - "substrate-fixed", "test-utils", ] @@ -9360,7 +9358,6 @@ dependencies = [ "parity-scale-codec", "polkadot-sdk", "scale-info", - "substrate-fixed", "test-utils", ] @@ -16451,17 +16448,6 @@ name = "substrate-build-script-utils" version = "11.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=stable2409#981d6c0fa87a00b72bb3b6211d1e71deed21f0cc" -[[package]] -name = "substrate-fixed" -version = "0.5.9" -source = "git+https://github.com/encointer/substrate-fixed.git?branch=master#ddaa922892d1565f02c9c5702f0aacd17da53ce2" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "substrate-typenum", -] - [[package]] name = "substrate-frame-rpc-support" version = "40.0.0" @@ -16539,16 +16525,6 @@ dependencies = [ "trie-db", ] -[[package]] -name = "substrate-typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f0091e93c2c75b233ae39424c52cb8a662c0811fb68add149e20e5d7e8a788" -dependencies = [ - "parity-scale-codec", - "scale-info", -] - [[package]] name = "substrate-wasm-builder" version = "24.0.1" diff --git a/Cargo.toml b/Cargo.toml index bfde1ee..3df988d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ codec = { package = "parity-scale-codec", version = "3.6.12", default-features = scale-info = { version = "2.11.1", default-features = false } polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "stable2409", default-features = false } substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "stable2409", default-features = false } -substrate-fixed = { git = "https://github.com/encointer/substrate-fixed.git", branch = "master", default-features = false } # Utils num-traits = { version = "0.2.19", default-features = false, features = [ diff --git a/pallets/emission0/Cargo.toml b/pallets/emission0/Cargo.toml index 10749aa..db36db6 100644 --- a/pallets/emission0/Cargo.toml +++ b/pallets/emission0/Cargo.toml @@ -12,7 +12,6 @@ std = [ "codec/std", "polkadot-sdk/std", "scale-info/std", - "substrate-fixed/std", "pallet-torus0-api/std", "num-traits/std", ] @@ -27,7 +26,6 @@ try-runtime = ["polkadot-sdk/try-runtime"] codec = { workspace = true, features = ["derive"] } scale-info = { workspace = true, features = ["derive"] } polkadot-sdk = { workspace = true, features = ["experimental", "runtime"] } -substrate-fixed.workspace = true num-traits.workspace = true diff --git a/pallets/emission0/src/distribute.rs b/pallets/emission0/src/distribute.rs index 67f4963..b91a959 100644 --- a/pallets/emission0/src/distribute.rs +++ b/pallets/emission0/src/distribute.rs @@ -9,7 +9,9 @@ use polkadot_sdk::{ }, polkadot_sdk_frame::prelude::BlockNumberFor, sp_core::Get, - sp_runtime::{traits::Saturating, ArithmeticError, DispatchError, Percent, Perquintill}, + sp_runtime::{ + traits::Saturating, ArithmeticError, DispatchError, FixedU128, Percent, Perquintill, + }, sp_std::{ borrow::Cow, collections::{btree_map::BTreeMap, btree_set::BTreeSet}, @@ -18,7 +20,6 @@ use polkadot_sdk::{ }, sp_tracing::{error, info}, }; -use substrate_fixed::{traits::ToFixed, types::I96F32}; use crate::{BalanceOf, Config, ConsensusMember, IncentivesRatio, Weights}; @@ -84,10 +85,10 @@ pub fn get_total_emission_per_block() -> BalanceOf { pub struct ConsensusMemberInput { pub agent_id: T::AccountId, pub validator_permit: bool, - pub weights: Vec<(T::AccountId, I96F32)>, + pub weights: Vec<(T::AccountId, FixedU128)>, pub stakes: Vec<(T::AccountId, u128)>, pub total_stake: u128, - pub normalized_stake: I96F32, + pub normalized_stake: FixedU128, pub delegating_to: Option, pub registered: bool, } @@ -149,8 +150,8 @@ impl ConsensusMemberInput { (agent_id, input) })); - let total_network_stake: I96F32 = - I96F32::from_num::(inputs.iter().map(|(_, member)| member.total_stake).sum()); + let total_network_stake = + FixedU128::from_inner(inputs.iter().map(|(_, member)| member.total_stake).sum()); inputs.sort_unstable_by(|(_, a), (_, b)| { b.validator_permit @@ -165,9 +166,9 @@ impl ConsensusMemberInput { input.weights.clear(); } - if total_network_stake != I96F32::from_num(0) { + if total_network_stake != 0.into() { input.normalized_stake = - I96F32::from_num(input.total_stake).saturating_div(total_network_stake) + FixedU128::from_inner(input.total_stake) / total_network_stake; } } @@ -219,8 +220,8 @@ impl ConsensusMemberInput { pub fn prepare_weights( weights: Weights, agent_id: &T::AccountId, - ) -> Vec<(T::AccountId, I96F32)> { - let mut weights_sum = I96F32::default(); + ) -> Vec<(T::AccountId, FixedU128)> { + let mut weights_sum = FixedU128::default(); let mut weights: Vec<_> = weights .into_iter() .filter(|(id, _)| { @@ -229,15 +230,15 @@ impl ConsensusMemberInput { || ::is_agent_registered(id)) }) .map(|(id, weight)| { - let weight = I96F32::from_num(weight); + let weight = FixedU128::from_u32(weight as u32); weights_sum = weights_sum.saturating_add(weight); (id, weight) }) .collect(); - if weights_sum > I96F32::from_num(0) { + if weights_sum > 0.into() { for (_, weight) in weights.iter_mut() { - *weight = weight.saturating_div(weights_sum); + *weight = *weight / weights_sum; } } @@ -274,8 +275,8 @@ fn linear_rewards( .map(|(idx, id)| (id, idx)) .collect(); - let mut weights: Vec> = vec![vec![]; inputs.len()]; - let mut stakes = vec![I96F32::from_num(0); inputs.len()]; + let mut weights: Vec> = vec![vec![]; inputs.len()]; + let mut stakes = vec![FixedU128::from_inner(0); inputs.len()]; for ((input, weights), stake) in inputs.values().zip(&mut weights).zip(&mut stakes) { *stake = input.normalized_stake; @@ -335,15 +336,14 @@ fn linear_rewards( let upscaled_incentives: Vec<_> = incentives .iter() - .map(|i| I96F32::from_num(i.peek())) + .map(|i| FixedU128::from_inner(i.peek())) .collect(); let upscaled_incentives = math::vec_max_upscale_to_u16(&upscaled_incentives); let upscaled_dividends: Vec<_> = dividends .iter() - .map(|i| I96F32::from_num(i.peek())) + .map(|i| FixedU128::from_inner(i.peek())) .collect(); - let upscaled_dividends = math::vec_max_upscale_to_u16(&upscaled_dividends); for ((((input, incentive), mut dividend), upscaled_incentives), upscaled_dividends) in inputs @@ -405,19 +405,21 @@ struct Emissions { fn compute_emissions( emission: &mut >::NegativeImbalance, - stake: &[I96F32], - incentives: Vec, - dividends: Vec, + stake: &[FixedU128], + incentives: Vec, + dividends: Vec, ) -> Emissions { let combined_emission: Vec<_> = incentives .iter() .zip(dividends.iter()) .map(|(incentive, dividend)| incentive.saturating_add(*dividend)) .collect(); - let emission_sum: I96F32 = combined_emission.iter().sum(); + let emission_sum = combined_emission + .iter() + .fold(FixedU128::default(), |acc, &e| acc + e); let normalized_incentives = math::normalize_with_sum(incentives, emission_sum); - let normalized_dividends = if emission_sum == I96F32::from_num(0) { + let normalized_dividends = if emission_sum == 0.into() { // When incentives and dividends are zero, the protocol still needs to issue tokens, // so it is distributed for all stake-holder agents. Cow::Borrowed(stake) @@ -426,18 +428,18 @@ fn compute_emissions( Cow::Owned(dividends_emission) }; - let to_be_emitted = I96F32::from_num(emission.peek()); + let to_be_emitted = FixedU128::from_inner(emission.peek()); - let mut calculate_emissions = |v: &[I96F32], to_be_emitted: I96F32| { + let mut calculate_emissions = |v: &[FixedU128], to_be_emitted: FixedU128| { v.iter() - .map(|&se| se.checked_mul(to_be_emitted).unwrap_or_default()) - .map(|amount| emission.extract(amount.to_num())) + .map(|&se| se.const_checked_mul(to_be_emitted).unwrap_or_default()) + .map(|amount| emission.extract(amount.into_inner())) .collect::>() }; let incentives_ratio = IncentivesRatio::::get().deconstruct(); - let to_be_emitted = to_be_emitted.to_num::(); + let to_be_emitted = to_be_emitted.into_inner(); let incentives_to_be_emitted; let dividends_to_be_emitted; @@ -455,9 +457,14 @@ fn compute_emissions( unreachable!() } - let incentives = - calculate_emissions(&normalized_incentives, incentives_to_be_emitted.to_fixed()); - let dividends = calculate_emissions(&normalized_dividends, dividends_to_be_emitted.to_fixed()); + let incentives = calculate_emissions( + &normalized_incentives, + FixedU128::from_inner(incentives_to_be_emitted), + ); + let dividends = calculate_emissions( + &normalized_dividends, + FixedU128::from_inner(dividends_to_be_emitted), + ); Emissions { dividends, diff --git a/pallets/emission0/src/distribute/math.rs b/pallets/emission0/src/distribute/math.rs index 5ab2aa2..2ce8357 100644 --- a/pallets/emission0/src/distribute/math.rs +++ b/pallets/emission0/src/distribute/math.rs @@ -3,48 +3,47 @@ #[cfg(not(feature = "std"))] use num_traits::float::FloatCore; use polkadot_sdk::{ - sp_runtime::{traits::Saturating, FixedI128, FixedPointNumber}, + sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128}, sp_std::vec, sp_std::vec::Vec, }; -use substrate_fixed::types::I96F32; -pub fn normalize(x: Vec) -> Vec { - let sum: I96F32 = x.iter().sum(); +pub fn normalize(x: Vec) -> Vec { + let sum: FixedU128 = x.iter().fold(FixedU128::default(), |acc, &e| acc + e); normalize_with_sum(x, sum) } #[inline] -pub fn normalize_with_sum(mut x: Vec, sum: I96F32) -> Vec { - if sum == I96F32::from_num(0.) { +pub fn normalize_with_sum(mut x: Vec, sum: FixedU128) -> Vec { + if sum == FixedU128::from_inner(0) { return x; } for ele in &mut x { - *ele = ele.saturating_div(sum); + *ele = *ele / sum; } x } #[allow(dead_code)] -pub fn inplace_row_normalize_64(x: &mut [Vec]) { +pub fn inplace_row_normalize_64(x: &mut [Vec]) { for row in x { - let row_sum: I96F32 = row.iter().sum(); - if row_sum > I96F32::from_num(0.0_f64) { - row.iter_mut().for_each(|x_ij: &mut I96F32| { - *x_ij = x_ij.checked_div(row_sum).unwrap_or_default(); + let row_sum: FixedU128 = row.iter().fold(FixedU128::default(), |acc, &e| acc + e); + if row_sum > FixedU128::from_inner(0) { + row.iter_mut().for_each(|x_ij: &mut FixedU128| { + *x_ij = x_ij.const_checked_div(row_sum).unwrap_or_default(); }); } } } pub fn matmul_sparse( - sparse_matrix: &[Vec<(usize, I96F32)>], - vector: &[I96F32], + sparse_matrix: &[Vec<(usize, FixedU128)>], + vector: &[FixedU128], columns: usize, -) -> Vec { - let mut result: Vec = vec![I96F32::from_num(0.0); columns]; +) -> Vec { + let mut result: Vec = vec![FixedU128::from_inner(0); columns]; for (i, sparse_row) in sparse_matrix.iter().enumerate() { for (j, value) in sparse_row.iter() { let Some(target) = result.get_mut(*j) else { @@ -60,10 +59,10 @@ pub fn matmul_sparse( } pub fn col_normalize_sparse( - mut sparse_matrix: Vec>, + mut sparse_matrix: Vec>, columns: usize, -) -> Vec> { - let mut col_sum: Vec = vec![I96F32::from_num(0); columns]; // assume square matrix, rows=cols +) -> Vec> { + let mut col_sum: Vec = vec![FixedU128::from_inner(0); columns]; // assume square matrix, rows=cols for sparse_row in &sparse_matrix { for (j, value) in sparse_row { @@ -79,10 +78,9 @@ pub fn col_normalize_sparse( let Some(col_sum_j) = col_sum.get(*j) else { continue; }; - if *col_sum_j == I96F32::from_num(0.) { - continue; + if *col_sum_j != 0.into() { + *value = *value / *col_sum_j; } - *value = value.saturating_div(*col_sum_j); } } @@ -90,10 +88,10 @@ pub fn col_normalize_sparse( } pub fn row_hadamard_sparse( - sparse_matrix: &[Vec<(usize, I96F32)>], - vector: &[I96F32], -) -> Vec> { - let mut result: Vec> = sparse_matrix.to_vec(); + sparse_matrix: &[Vec<(usize, FixedU128)>], + vector: &[FixedU128], +) -> Vec> { + let mut result: Vec> = sparse_matrix.to_vec(); for (row_idx, sparse_row) in result.iter_mut().enumerate() { for (_, value) in sparse_row { @@ -108,10 +106,10 @@ pub fn row_hadamard_sparse( } pub fn matmul_transpose_sparse( - sparse_matrix: &[Vec<(usize, I96F32)>], - vector: &[I96F32], -) -> Vec { - let mut result: Vec = vec![I96F32::from_num(0); sparse_matrix.len()]; + sparse_matrix: &[Vec<(usize, FixedU128)>], + vector: &[FixedU128], +) -> Vec { + let mut result: Vec = vec![FixedU128::from_inner(0); sparse_matrix.len()]; for (i, sparse_row) in sparse_matrix.iter().enumerate() { for (j, value) in sparse_row { // Compute dividends: d_j = SUM(i) b_ji * inc_i @@ -131,49 +129,30 @@ pub fn matmul_transpose_sparse( /// Max-upscale vector and convert to u16 so max_value = u16::MAX. Assumes /// non-negative normalized input. -pub fn vec_max_upscale_to_u16(vec: &[I96F32]) -> Vec { - let u16_max = I96F32::from_num(u16::MAX); - let threshold = I96F32::from_num(32768); - let Some(max_val) = vec.iter().max() else { +pub fn vec_max_upscale_to_u16(vec: &[FixedU128]) -> Vec { + const MAX: FixedU128 = FixedU128::from_u32(u16::MAX as u32); + + let Some(max_ele) = vec.iter().max().filter(|x| **x != 0.into()) else { return vec![0; vec.len()]; }; - if *max_val == I96F32::from_num(0) { - vec.iter() - .map(|e| e.saturating_mul(u16_max).to_num()) - .collect() - } else if *max_val > threshold { - let ratio = - FixedI128::from_u32(u16::MAX as u32).div(FixedI128::from_inner(max_val.to_num())); + let ratio = MAX / *max_ele; - vec.iter() - .map(|e| FixedI128::from_inner(e.to_num())) - .map(|e| { - e.saturating_mul(ratio) - .round() - .trunc() - .into_inner() - .checked_div(FixedI128::DIV) - .unwrap_or_default() as u16 - }) - .collect() - } else { - vec.iter() - .map(|e| { - e.saturating_mul(u16_max) - .saturating_div(*max_val) - .round() - .to_num() - }) - .collect() - } + vec.iter() + .map(|e| { + e.saturating_mul(ratio) + .round() + .trunc() + .into_inner() + .checked_div(FixedU128::DIV) + .unwrap_or_default() as u16 + }) + .collect() } #[cfg(test)] #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] mod tests { - use substrate_fixed::types::{I96F32, U64F64}; - use super::*; macro_rules! fixed_vec { @@ -181,40 +160,40 @@ mod tests { vec![] }; ($($x:expr),+ $(,)?) => { - vec![$(I96F32::from_num($x)),+] + vec![$(FixedU128::from_float($x)),+] }; } /// Reshape vector to sparse matrix with specified number of input rows, - /// cast f32 to I96F32. + /// cast f32 to FixedU128. fn vec_to_sparse_mat_fixed( - vector: &[f32], + vector: &[f64], rows: usize, transpose: bool, - ) -> Vec> { + ) -> Vec> { assert!( vector.len() % rows == 0, "Vector of len {:?} cannot reshape to {rows} rows.", vector.len() ); let cols: usize = vector.len() / rows; - let mut mat: Vec> = vec![]; + let mut mat: Vec> = vec![]; if transpose { for col in 0..cols { - let mut row_vec: Vec<(u16, I96F32)> = vec![]; + let mut row_vec: Vec<(u16, FixedU128)> = vec![]; for row in 0..rows { if vector[row * cols + col] > 0. { - row_vec.push((row as u16, I96F32::from_num(vector[row * cols + col]))); + row_vec.push((row as u16, FixedU128::from_float(vector[row * cols + col]))); } } mat.push(row_vec); } } else { for row in 0..rows { - let mut row_vec: Vec<(u16, I96F32)> = vec![]; + let mut row_vec: Vec<(u16, FixedU128)> = vec![]; for col in 0..cols { if vector[row * cols + col] > 0. { - row_vec.push((col as u16, I96F32::from_num(vector[row * cols + col]))); + row_vec.push((col as u16, FixedU128::from_float(vector[row * cols + col]))); } } mat.push(row_vec); @@ -223,11 +202,15 @@ mod tests { mat } - fn assert_float_compare(a: I96F32, b: I96F32, epsilon: I96F32) { - assert!(I96F32::abs(a - b) <= epsilon, "a({a:?}) != b({b:?})"); + fn assert_float_compare(a: FixedU128, b: FixedU128, epsilon: FixedU128) { + dbg!(a, b, epsilon); + assert!( + a.into_inner().abs_diff(b.into_inner()) <= epsilon.into_inner(), + "a({a:?}) != b({b:?})" + ); } - fn assert_vec_compare(va: &[I96F32], vb: &[I96F32], epsilon: I96F32) { + fn assert_vec_compare(va: &[FixedU128], vb: &[FixedU128], epsilon: FixedU128) { assert!(va.len() == vb.len()); for (a, b) in va.iter().zip(vb.iter()) { assert_float_compare(*a, *b, epsilon); @@ -235,140 +218,90 @@ mod tests { } fn assert_sparse_mat_compare( - ma: &[Vec<(u16, I96F32)>], - mb: &[Vec<(u16, I96F32)>], - epsilon: I96F32, + ma: &[Vec<(u16, FixedU128)>], + mb: &[Vec<(u16, FixedU128)>], + epsilon: FixedU128, ) { assert!(ma.len() == mb.len()); for row in 0..ma.len() { assert!(ma[row].len() == mb[row].len()); for j in 0..ma[row].len() { assert!(ma[row][j].0 == mb[row][j].0); // u16 - assert_float_compare(ma[row][j].1, mb[row][j].1, epsilon) // I96F32 + assert_float_compare(ma[row][j].1, mb[row][j].1, epsilon) // FixedU128 } } } #[test] fn test_math_u64_normalization() { - let min: u64 = 1; - let mid: u64 = 10_500_000_000_000_000; - let max: u64 = 21_000_000_000_000_000; - let min_64: I96F32 = I96F32::from_num(min); - let mid_64: I96F32 = I96F32::from_num(mid); - let max_64: I96F32 = I96F32::from_num(max); - let max_sum: I96F32 = I96F32::from_num(max); - let min_frac: I96F32 = min_64 / max_sum; - assert_eq!(min_frac, I96F32::from_num(0.0000000000000000476)); - let half: I96F32 = mid_64 / max_sum; - assert_eq!(half, I96F32::from_num(0.5)); - let one: I96F32 = max_64 / max_sum; - assert_eq!(one, I96F32::from_num(1)); + let min: u128 = 1; + let mid: u128 = 10_500_000_000_000_000; + let max: u128 = 21_000_000_000_000_000; + let min_64: FixedU128 = FixedU128::from_inner(min); + let mid_64: FixedU128 = FixedU128::from_inner(mid); + let max_64: FixedU128 = FixedU128::from_inner(max); + let max_sum: FixedU128 = FixedU128::from_inner(max); + let min_frac: FixedU128 = min_64 / max_sum; + assert_eq!(min_frac, FixedU128::from_float(0.0000000000000000476)); + let half: FixedU128 = mid_64 / max_sum; + assert_eq!(half, FixedU128::from_float(0.5)); + let one: FixedU128 = max_64 / max_sum; + assert_eq!(one, FixedU128::from_u32(1)); } #[test] - fn test_math_to_num() { - let val: I96F32 = I96F32::from_num(u16::MAX); - let res: u16 = val.to_num::(); - assert_eq!(res, u16::MAX); - let vector: Vec = vec![val; 1000]; - let target: Vec = vec![u16::MAX; 1000]; - let output: Vec = vector.iter().map(|e: &I96F32| e.to_num::()).collect(); - assert_eq!(output, target); - let output: Vec = vector - .iter() - .map(|e: &I96F32| (*e).to_num::()) - .collect(); - assert_eq!(output, target); - let val: I96F32 = I96F32::max_value(); - let res: u128 = val.to_num::(); - let vector: Vec = vec![val; 1000]; - let target: Vec = vec![res; 1000]; - let output: Vec = vector.iter().map(|e: &I96F32| e.to_num()).collect(); - assert_eq!(output, target); - let output: Vec = vector.iter().map(|e: &I96F32| e.to_num()).collect(); - assert_eq!(output, target); - let val: I96F32 = I96F32::from_num(0); - let res: u64 = val.to_num::(); - let vector: Vec = vec![val; 1000]; - let target: Vec = vec![res; 1000]; - let output: Vec = vector.iter().map(|e: &I96F32| e.to_num::()).collect(); - assert_eq!(output, target); - let output: Vec = vector - .iter() - .map(|e: &I96F32| (*e).to_num::()) - .collect(); - assert_eq!(output, target); - let val: U64F64 = U64F64::from_num(u64::MAX); - let res: u64 = val.to_num::(); - assert_eq!(res, u64::MAX); - let vector: Vec = vec![val; 1000]; - let target: Vec = vec![u64::MAX; 1000]; - let output: Vec = vector.iter().map(|e: &U64F64| e.to_num::()).collect(); - assert_eq!(output, target); - let output: Vec = vector - .iter() - .map(|e: &U64F64| (*e).to_num::()) - .collect(); - assert_eq!(output, target); + fn test_math_vec_to_sparse_mat_fixed2() { + // ... (This test remains unchanged) + let test_cases = vec![ + ( + vec![0., 1., 2., 0., 10., 100.], + vec![ + vec![(1, FixedU128::from_u32(1)), (2, FixedU128::from_u32(2))], + vec![(1, FixedU128::from_u32(10)), (2, FixedU128::from_u32(100))], + ], + false, + ), + (vec![0., 0.], vec![vec![], vec![]], false), + ( + vec![0., 1., 2., 0., 10., 100.], + vec![ + vec![], + vec![(0, FixedU128::from_u32(1)), (1, FixedU128::from_u32(10))], + vec![(0, FixedU128::from_u32(2)), (1, FixedU128::from_u32(100))], + ], + true, + ), + (vec![0., 0.], vec![vec![]], true), + ]; + + for (vector, target, transpose) in test_cases { + let mat = vec_to_sparse_mat_fixed(&vector, 2, transpose); + assert_sparse_mat_compare(&mat, &target, FixedU128::from_inner(0)); + } } #[test] - fn test_math_vec_to_sparse_mat_fixed() { - let vector: Vec = vec![0., 1., 2., 0., 10., 100.]; - let target: Vec> = vec![ - vec![(1, I96F32::from_num(1.)), (2, I96F32::from_num(2.))], - vec![(1, I96F32::from_num(10.)), (2, I96F32::from_num(100.))], - ]; - let mat = vec_to_sparse_mat_fixed(&vector, 2, false); - assert_sparse_mat_compare(&mat, &target, I96F32::from_num(0)); - let vector: Vec = vec![0., 0.]; - let target: Vec> = vec![vec![], vec![]]; - let mat = vec_to_sparse_mat_fixed(&vector, 2, false); - assert_sparse_mat_compare(&mat, &target, I96F32::from_num(0)); - let vector: Vec = vec![0., 1., 2., 0., 10., 100.]; - let target: Vec> = vec![ - vec![], - vec![(0, I96F32::from_num(1.)), (1, I96F32::from_num(10.))], - vec![(0, I96F32::from_num(2.)), (1, I96F32::from_num(100.))], + fn test_math_normalize2() { + let epsilon = FixedU128::from_float(0.0001); + + let test_cases = vec![ + (vec![], vec![]), + ( + fixed_vec![1.0, 10.0, 30.0], + fixed_vec![0.0243902437, 0.243902439, 0.7317073171], + ), ]; - let mat = vec_to_sparse_mat_fixed(&vector, 2, true); - assert_sparse_mat_compare(&mat, &target, I96F32::from_num(0)); - let vector: Vec = vec![0., 0.]; - let target: Vec> = vec![vec![]]; - let mat = vec_to_sparse_mat_fixed(&vector, 2, true); - assert_sparse_mat_compare(&mat, &target, I96F32::from_num(0)); - } - #[test] - fn test_math_normalize() { - let epsilon: I96F32 = I96F32::from_num(0.0001); - let x: Vec = vec![]; - let y: Vec = normalize(x.clone()); - assert_vec_compare(&x, &y, epsilon); - let x: Vec = fixed_vec![1.0, 10.0, 30.0,]; - let y: Vec = normalize(x.clone()); - assert_vec_compare( - &y, - &[ - I96F32::from_num(0.0243902437), - I96F32::from_num(0.243902439), - I96F32::from_num(0.7317073171), - ], - epsilon, - ); - assert_float_compare(y.iter().sum(), I96F32::from_num(1.0), epsilon); - let x: Vec = fixed_vec![-1.0, 10.0, 30.0]; - let y: Vec = normalize(x.clone()); - assert_vec_compare( - &y, - &[ - I96F32::from_num(-0.0256410255), - I96F32::from_num(0.2564102563), - I96F32::from_num(0.769230769), - ], - epsilon, - ); - assert_float_compare(y.iter().sum(), I96F32::from_num(1.0), epsilon); + for (x, expected_y) in test_cases { + let y = normalize(x.clone()); + assert_vec_compare(&y, &expected_y, epsilon); + + if !y.is_empty() { + let sum = y + .iter() + .fold(FixedU128::from_inner(0), |acc, &val| acc + val); // Use from_inner(0) + assert_float_compare(sum, FixedU128::from_u32(1), epsilon); + } + } } } diff --git a/pallets/emission0/tests/distribution.rs b/pallets/emission0/tests/distribution.rs index 5c2a0ad..93044b9 100644 --- a/pallets/emission0/tests/distribution.rs +++ b/pallets/emission0/tests/distribution.rs @@ -8,9 +8,8 @@ use pallet_emission0::{ use polkadot_sdk::{ pallet_balances, sp_core::Get, - sp_runtime::{BoundedVec, Perbill, Percent}, + sp_runtime::{BoundedVec, FixedU128, Perbill, Percent}, }; -use substrate_fixed::{traits::ToFixed, types::I96F32}; use test_utils::{ add_balance, add_stake, pallet_governance::{Allocators, TreasuryEmissionFee}, @@ -102,8 +101,8 @@ fn weights_are_filtered_and_normalized() { assert_eq!( weights, vec![ - (1, 0.3333333333f64.to_fixed()), - (2, 0.6666666665f64.to_fixed()) + (1, FixedU128::from_rational(1, 3)), + (2, FixedU128::from_rational(1, 3) * 2.into()) ] ) }); @@ -123,7 +122,7 @@ fn creates_member_input_correctly() { weights: vec![], stakes: vec![], total_stake: 0, - normalized_stake: 0.to_fixed(), + normalized_stake: FixedU128::from_inner(0), delegating_to: None, registered: false } @@ -142,7 +141,7 @@ fn creates_member_input_correctly() { member.update_weights(BoundedVec::truncate_from(vec![(0, 0), (1, 10)])); let input = ConsensusMemberInput::::from_agent(0, member.weights.clone(), 15); - assert_eq!(input.total_stake, I96F32::from_num(30)); + assert_eq!(input.total_stake, 30); assert!(input.validator_permit); assert_eq!(input.weights.len(), 1); }); @@ -192,10 +191,10 @@ fn creates_list_of_all_member_inputs_for_rewards() { ConsensusMemberInput { agent_id: validator, validator_permit: true, - weights: vec![(miner, 1.to_fixed())], + weights: vec![(miner, FixedU128::from_u32(1))], stakes: vec![(staker, stake * 3)], total_stake: stake * 3, - normalized_stake: 0.75f64.to_fixed(), + normalized_stake: FixedU128::from_float(0.75f64), delegating_to: None, registered: true, } @@ -209,7 +208,7 @@ fn creates_list_of_all_member_inputs_for_rewards() { weights: vec![], stakes: vec![], total_stake: 0, - normalized_stake: 0.to_fixed(), + normalized_stake: FixedU128::from_inner(0), delegating_to: None, registered: true, } @@ -220,10 +219,10 @@ fn creates_list_of_all_member_inputs_for_rewards() { ConsensusMemberInput { agent_id: delegating_registered, validator_permit: true, - weights: vec![(miner, 1.to_fixed())], + weights: vec![(miner, FixedU128::from_u32(1))], stakes: vec![(staker, stake)], total_stake: stake, - normalized_stake: 0.25f64.to_fixed(), + normalized_stake: FixedU128::from_float(0.25f64), delegating_to: Some(validator), registered: true, } @@ -237,7 +236,7 @@ fn creates_list_of_all_member_inputs_for_rewards() { weights: vec![], stakes: vec![], total_stake: 0, - normalized_stake: 0.to_fixed(), + normalized_stake: FixedU128::from_inner(0), delegating_to: Some(validator), registered: false, } @@ -251,7 +250,7 @@ fn creates_list_of_all_member_inputs_for_rewards() { weights: vec![], stakes: vec![], total_stake: 0, - normalized_stake: 0.to_fixed(), + normalized_stake: FixedU128::from_inner(0), delegating_to: Some(validator), registered: false, } @@ -265,7 +264,7 @@ fn creates_list_of_all_member_inputs_for_rewards() { weights: vec![], stakes: vec![], total_stake: 0, - normalized_stake: 0.to_fixed(), + normalized_stake: FixedU128::from_inner(0), delegating_to: None, registered: true, } diff --git a/pallets/governance/Cargo.toml b/pallets/governance/Cargo.toml index 05560c3..a10f988 100644 --- a/pallets/governance/Cargo.toml +++ b/pallets/governance/Cargo.toml @@ -12,7 +12,6 @@ std = [ "codec/std", "polkadot-sdk/std", "scale-info/std", - "substrate-fixed/std", "pallet-torus0/std", "pallet-emission0/std", ] @@ -38,7 +37,6 @@ polkadot-sdk = { workspace = true, features = [ "runtime", "pallet-sudo", ] } -substrate-fixed = { workspace = true } pallet-torus0.workspace = true pallet-emission0.workspace = true diff --git a/pallets/governance/src/proposal.rs b/pallets/governance/src/proposal.rs index 060f858..c316cfd 100644 --- a/pallets/governance/src/proposal.rs +++ b/pallets/governance/src/proposal.rs @@ -9,11 +9,10 @@ use polkadot_sdk::{ }, polkadot_sdk_frame::{prelude::BlockNumberFor, traits::CheckedAdd}, sp_core::{ConstU32, U256}, - sp_runtime::{BoundedBTreeMap, DispatchError, Percent}, + sp_runtime::{traits::Saturating, BoundedBTreeMap, DispatchError, FixedU128, Percent}, sp_std::{collections::btree_set::BTreeSet, vec::Vec}, sp_tracing::error, }; -use substrate_fixed::types::I92F36; use crate::{ frame::traits::ExistenceRequirement, AccountIdOf, BalanceOf, BoundedBTreeSet, BoundedVec, @@ -631,9 +630,9 @@ pub fn tick_proposal_rewards(block_number: BlockNumberFor) return; } - let mut n: u16 = 0; + let mut n = 0u16; let mut account_stakes: AccountStakes = BoundedBTreeMap::new(); - let mut total_allocation: I92F36 = I92F36::from_num(0); + let mut total_allocation = FixedU128::from_inner(0); for (proposal_id, unrewarded_proposal) in UnrewardedProposals::::iter() { let proposal_block: u64 = unrewarded_proposal .block @@ -658,9 +657,7 @@ pub fn tick_proposal_rewards(block_number: BlockNumberFor) } match get_reward_allocation::(&governance_config, n) { - Ok(allocation) => { - total_allocation = total_allocation.saturating_add(allocation); - } + Ok(allocation) => total_allocation = total_allocation.saturating_add(allocation), Err(err) => { error!("could not get reward allocation for proposal {proposal_id}: {err:?}"); continue; @@ -682,50 +679,47 @@ pub fn tick_proposal_rewards(block_number: BlockNumberFor) pub fn get_reward_allocation( governance_config: &GovernanceConfiguration, n: u16, -) -> Result { +) -> Result { let treasury_address = DaoTreasuryAddress::::get(); let treasury_balance = ::Currency::free_balance(&treasury_address); - let treasury_balance = I92F36::from_num(treasury_balance); - let allocation_percentage = I92F36::from_num( - governance_config - .proposal_reward_treasury_allocation - .deconstruct(), + let allocation_percentage = governance_config.proposal_reward_treasury_allocation; + let max_allocation = governance_config.max_proposal_reward_treasury_allocation; + + let mut allocation = FixedU128::from_inner( + allocation_percentage + .mul_floor(treasury_balance) + .min(max_allocation), ); - let max_allocation = - I92F36::from_num(governance_config.max_proposal_reward_treasury_allocation); - let mut allocation = treasury_balance - .checked_mul(allocation_percentage) - .unwrap_or_default() - .min(max_allocation); if n > 0 { - let mut base = I92F36::from_num(1.5); - let mut result = I92F36::from_num(1); + let mut base = FixedU128::from_float(1.5); + let mut result = FixedU128::from_float(1.); let mut remaining = n; while remaining > 0 { if remaining % 2 == 1 { - result = result.checked_mul(base).unwrap_or(result); + result = result.const_checked_mul(base).unwrap_or(result); } - base = base.checked_mul(base).unwrap_or_default(); + base = base.const_checked_mul(base).unwrap_or_default(); remaining /= 2; } - allocation = allocation.checked_div(result).unwrap_or(allocation); + allocation = allocation.const_checked_div(result).unwrap_or(allocation); } + Ok(allocation) } /// Distributes the proposal rewards in a quadratic formula to all voters. fn distribute_proposal_rewards( account_stakes: AccountStakes, - total_allocation: I92F36, + total_allocation: FixedU128, max_proposal_reward_treasury_allocation: BalanceOf, ) { // This is just a sanity check, making sure we can never allocate more than the // max - if total_allocation > I92F36::from_num(max_proposal_reward_treasury_allocation) { + if total_allocation > FixedU128::from_inner(max_proposal_reward_treasury_allocation) { error!("total allocation exceeds max proposal reward treasury allocation"); return; } @@ -739,17 +733,17 @@ fn distribute_proposal_rewards( .collect(); let total_stake: BalanceOf = account_sqrt_stakes.iter().map(|(_, stake)| *stake).sum(); - let total_stake = I92F36::from_num(total_stake); + let total_stake = FixedU128::from_inner(total_stake); for (acc_id, stake) in account_sqrt_stakes.into_iter() { - let percentage = I92F36::from_num(stake) - .checked_div(total_stake) + let percentage = FixedU128::from_inner(stake) + .const_checked_div(total_stake) .unwrap_or_default(); - let reward: BalanceOf = total_allocation - .checked_mul(percentage) + let reward = total_allocation + .const_checked_mul(percentage) .unwrap_or_default() - .to_num(); + .into_inner(); // Transfer the proposal reward to the accounts from treasury if let Err(err) = ::Currency::transfer( diff --git a/pallets/governance/tests/voting.rs b/pallets/governance/tests/voting.rs index ef8cbe7..b2d9758 100644 --- a/pallets/governance/tests/voting.rs +++ b/pallets/governance/tests/voting.rs @@ -524,7 +524,7 @@ fn rewards_wont_exceed_treasury() { pallet_governance::proposal::get_reward_allocation::(&governance_config, n) .unwrap(); assert_eq!( - allocation.to_num::(), + allocation.into_inner(), governance_config.max_proposal_reward_treasury_allocation ); }); diff --git a/pallets/torus0/Cargo.toml b/pallets/torus0/Cargo.toml index 820db7a..8d915c4 100644 --- a/pallets/torus0/Cargo.toml +++ b/pallets/torus0/Cargo.toml @@ -12,7 +12,6 @@ std = [ "codec/std", "polkadot-sdk/std", "scale-info/std", - "substrate-fixed/std", "pallet-torus0-api/std", ] runtime-benchmarks = [ @@ -26,7 +25,6 @@ try-runtime = ["polkadot-sdk/try-runtime", "pallet-torus0-api/try-runtime"] codec = { workspace = true, features = ["derive"] } scale-info = { workspace = true, features = ["derive"] } polkadot-sdk = { workspace = true, features = ["experimental", "runtime"] } -substrate-fixed = { workspace = true } pallet-torus0-api.workspace = true pallet-emission0-api.workspace = true diff --git a/pallets/torus0/src/burn.rs b/pallets/torus0/src/burn.rs index 38b571b..924cc5f 100644 --- a/pallets/torus0/src/burn.rs +++ b/pallets/torus0/src/burn.rs @@ -1,10 +1,11 @@ use codec::{Decode, Encode, MaxEncodedLen}; use polkadot_sdk::{ - frame_election_provider_support::Get, frame_support::DebugNoBound, + frame_election_provider_support::Get, + frame_support::DebugNoBound, polkadot_sdk_frame::prelude::BlockNumberFor, + sp_runtime::{traits::Saturating, FixedU128}, }; use scale_info::{prelude::marker::PhantomData, TypeInfo}; -use substrate_fixed::types::I110F18; use crate::BalanceOf; @@ -67,36 +68,36 @@ pub fn adjust_burn(current_block: u64) { return; } - let updated_burn: I110F18 = I110F18::from_num(current_burn) - .checked_mul(I110F18::from_num( - registrations_this_interval.saturating_add(target_registrations_per_interval), + let updated_burn = FixedU128::from_inner(current_burn) + .const_checked_mul(FixedU128::from_u32( + registrations_this_interval.saturating_add(target_registrations_per_interval) as u32, )) .unwrap_or_default() - .checked_div(I110F18::from_num( - target_registrations_per_interval.saturating_add(target_registrations_per_interval), + .const_checked_div(FixedU128::from_u32( + target_registrations_per_interval.saturating_mul(2) as u32, )) .unwrap_or_default(); - let alpha: I110F18 = I110F18::from_num(adjustment_alpha) - .checked_div(I110F18::from_num(u64::MAX)) - .unwrap_or_else(|| I110F18::from_num(0)); + let alpha = FixedU128::from_inner(adjustment_alpha as u128) + .const_checked_div(FixedU128::from_inner(u64::MAX as u128)) + .unwrap_or_else(|| FixedU128::from_inner(0)); - let next_value: I110F18 = alpha - .checked_mul(I110F18::from_num(current_burn)) - .unwrap_or_else(|| I110F18::from_num(0)) + let next_value = alpha + .const_checked_mul(FixedU128::from_inner(current_burn)) + .unwrap_or_else(|| FixedU128::from_inner(0)) .saturating_add( - I110F18::from_num(1.0) + FixedU128::from_u32(1) .saturating_sub(alpha) - .checked_mul(updated_burn) - .unwrap_or_else(|| I110F18::from_num(0)), + .const_checked_mul(updated_burn) + .unwrap_or_else(|| FixedU128::from_inner(0)), ); - let new_burn = if next_value >= I110F18::from_num(max_burn) { + let new_burn = if next_value >= FixedU128::from_inner(max_burn) { max_burn - } else if next_value <= I110F18::from_num(min_burn) { + } else if next_value <= FixedU128::from_inner(min_burn) { min_burn } else { - next_value.to_num::>() + next_value.into_inner() }; crate::Burn::::set(new_burn);