From f18650a43e8eb32f24ec519af5c569214b096c42 Mon Sep 17 00:00:00 2001 From: lemunozm Date: Thu, 11 Jul 2024 16:38:46 +0200 Subject: [PATCH] num-wrapper implementation --- Cargo.lock | 3 + libs/primitives/Cargo.toml | 4 + libs/primitives/src/lib.rs | 32 ++- libs/traits/Cargo.toml | 4 + libs/traits/src/interest.rs | 4 +- libs/traits/src/lib.rs | 26 +- libs/utils/Cargo.toml | 2 + libs/utils/src/lib.rs | 86 ++++++ libs/utils/src/num_wrapper.rs | 477 ++++++++++++++++++++++++++++++++++ 9 files changed, 600 insertions(+), 38 deletions(-) create mode 100644 libs/utils/src/num_wrapper.rs diff --git a/Cargo.lock b/Cargo.lock index 73d3c0c03e..50c4d2923a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1640,6 +1640,7 @@ dependencies = [ name = "cfg-primitives" version = "2.0.0" dependencies = [ + "cfg-utils", "cumulus-primitives-core", "frame-support", "frame-system", @@ -1682,6 +1683,7 @@ version = "0.1.0" dependencies = [ "cfg-mocks", "cfg-primitives", + "cfg-utils", "frame-support", "frame-system", "impl-trait-for-tuples", @@ -1728,6 +1730,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", + "serde", "sp-arithmetic", "sp-consensus-aura", "sp-runtime", diff --git a/libs/primitives/Cargo.toml b/libs/primitives/Cargo.toml index f8c83f3c8d..2dea224ae9 100644 --- a/libs/primitives/Cargo.toml +++ b/libs/primitives/Cargo.toml @@ -24,6 +24,7 @@ sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +cfg-utils = { workspace = true } # substrate frame dependencies frame-support = { workspace = true } @@ -57,6 +58,7 @@ std = [ "sp-std/std", "staging-xcm-executor/std", "staging-xcm/std", + "cfg-utils/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", @@ -65,6 +67,7 @@ runtime-benchmarks = [ "pallet-membership/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "staging-xcm-executor/runtime-benchmarks", + "cfg-utils/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", @@ -72,4 +75,5 @@ try-runtime = [ "pallet-collective/try-runtime", "pallet-membership/try-runtime", "sp-runtime/try-runtime", + "cfg-utils/try-runtime", ] diff --git a/libs/primitives/src/lib.rs b/libs/primitives/src/lib.rs index 0d463ccad5..a18e781ec0 100644 --- a/libs/primitives/src/lib.rs +++ b/libs/primitives/src/lib.rs @@ -158,6 +158,12 @@ pub mod types { /// The type for outbound LP message nonces. pub type OutboundMessageNonce = u64; + + /// The type to represent milliseconds + pub type Millis = cfg_utils::time::Millis; + + /// The type to represent seconds + pub type Seconds = cfg_utils::time::Seconds; } /// Common constants for all runtimes @@ -165,7 +171,7 @@ pub mod constants { use frame_support::weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}; use sp_runtime::Perbill; - use super::types::{Balance, BlockNumber}; + use super::types::{Balance, BlockNumber, Millis, Seconds}; /// This determines the average expected block time that we are targeting. /// Blocks will be produced at a minimum duration defined by @@ -174,24 +180,24 @@ pub mod constants { /// slot_duration()`. /// /// Change this to adjust the block time. - pub const MILLISECS_PER_BLOCK: u64 = 12000; - pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + pub const MILLISECS_PER_BLOCK: Millis = Millis::from(12000); + pub const SLOT_DURATION: Millis = MILLISECS_PER_BLOCK; // Time is measured by number of blocks. - pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); + pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK.inner as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; - /// Milliseconds per day - pub const MILLISECS_PER_DAY: u64 = SECONDS_PER_DAY * 1000; - // Seconds units - pub const SECONDS_PER_MINUTE: u64 = 60; - pub const SECONDS_PER_HOUR: u64 = SECONDS_PER_MINUTE * 60; - pub const SECONDS_PER_DAY: u64 = SECONDS_PER_HOUR * 24; - pub const SECONDS_PER_WEEK: u64 = SECONDS_PER_DAY * 7; - pub const SECONDS_PER_MONTH: u64 = SECONDS_PER_DAY * 30; - pub const SECONDS_PER_YEAR: u64 = SECONDS_PER_DAY * 365; + pub const SECONDS_PER_MINUTE: Seconds = Seconds::from(60); + pub const SECONDS_PER_HOUR: Seconds = SECONDS_PER_MINUTE.mul(60); + pub const SECONDS_PER_DAY: Seconds = SECONDS_PER_HOUR.mul(24); + pub const SECONDS_PER_WEEK: Seconds = SECONDS_PER_DAY.mul(7); + pub const SECONDS_PER_MONTH: Seconds = SECONDS_PER_DAY.mul(30); + pub const SECONDS_PER_YEAR: Seconds = SECONDS_PER_DAY.mul(365); + + /// Milliseconds per day + pub const MILLISECS_PER_DAY: Millis = SECONDS_PER_DAY.into_millis(); /// We assume that ~5% of the block weight is consumed by `on_initialize` /// handlers. This is used to limit the maximal weight of a single diff --git a/libs/traits/Cargo.toml b/libs/traits/Cargo.toml index e8592f6295..5e4e04aad3 100644 --- a/libs/traits/Cargo.toml +++ b/libs/traits/Cargo.toml @@ -23,6 +23,7 @@ sp-arithmetic = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } strum = { workspace = true } +cfg-utils = { workspace = true } [dev-dependencies] cfg-mocks = { workspace = true, default_features = true } @@ -41,15 +42,18 @@ std = [ "scale-info/std", "strum/std", "orml-traits/std", + "cfg-utils/std", ] runtime-benchmarks = [ "cfg-primitives/runtime-benchmarks", "frame-support/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "cfg-mocks/runtime-benchmarks", + "cfg-utils/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "cfg-primitives/try-runtime", "sp-runtime/try-runtime", + "cfg-utils/try-runtime", ] diff --git a/libs/traits/src/interest.rs b/libs/traits/src/interest.rs index 5502263f61..78d330dc54 100644 --- a/libs/traits/src/interest.rs +++ b/libs/traits/src/interest.rs @@ -1,4 +1,4 @@ -use cfg_primitives::SECONDS_PER_YEAR; +use cfg_primitives::{Seconds, SECONDS_PER_YEAR}; use frame_support::{ dispatch::DispatchResult, pallet_prelude::{RuntimeDebug, TypeInfo}, @@ -14,8 +14,6 @@ use sp_runtime::{ DispatchError, }; -use crate::Seconds; - #[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebug, MaxEncodedLen)] pub enum CompoundingSchedule { /// Interest compounds every second diff --git a/libs/traits/src/lib.rs b/libs/traits/src/lib.rs index d7eb9042b0..398a4b8d8d 100644 --- a/libs/traits/src/lib.rs +++ b/libs/traits/src/lib.rs @@ -55,6 +55,8 @@ pub mod swaps; /// Traits related to benchmarking tooling. pub mod benchmarking; +pub use cfg_utils::time::{IntoMillis, IntoSeconds}; + /// A trait that can be used to fetch the nav and update nav for a given pool pub trait PoolNAV { type ClassId; @@ -380,35 +382,15 @@ pub trait TryConvert { fn try_convert(a: A) -> Result; } -// TODO: Probably these should be in a future cfg-utils. -// Issue: https://github.com/centrifuge/centrifuge-chain/issues/1380 - -/// Type to represent milliseconds -pub type Millis = u64; - -/// Type to represent seconds -pub type Seconds = u64; - /// Trait to obtain the time as seconds pub trait TimeAsSecs: UnixTime { - fn now() -> Seconds { - ::now().as_secs() + fn now() -> cfg_primitives::Seconds { + ::now().as_secs().into() } } impl TimeAsSecs for T {} -/// Trait to convert into seconds -pub trait IntoSeconds { - fn into_seconds(self) -> Seconds; -} - -impl IntoSeconds for Millis { - fn into_seconds(self) -> Seconds { - self / 1000 - } -} - pub trait ValueProvider { type Value; diff --git a/libs/utils/Cargo.toml b/libs/utils/Cargo.toml index 31c1f028b1..af2c08c983 100644 --- a/libs/utils/Cargo.toml +++ b/libs/utils/Cargo.toml @@ -24,6 +24,7 @@ sp-arithmetic = { workspace = true } sp-consensus-aura = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +serde = { workspace = true } [features] default = ["std"] @@ -39,6 +40,7 @@ std = [ "scale-info/std", "sp-consensus-aura/std", "hex/std", + "serde/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", diff --git a/libs/utils/src/lib.rs b/libs/utils/src/lib.rs index a1f8bc7412..c7a8c1d99c 100644 --- a/libs/utils/src/lib.rs +++ b/libs/utils/src/lib.rs @@ -16,6 +16,8 @@ use parity_scale_codec::Encode; use sp_std::cmp::min; +pub mod num_wrapper; + /// Build a fixed-size array using as many elements from `src` as possible /// without overflowing and ensuring that the array is 0 padded in the case /// where `src.len()` is smaller than S. @@ -209,6 +211,90 @@ pub mod math { } } +pub mod time { + use crate::num_wrapper::NumWrapper; + + /// Trait to convert into seconds + pub trait IntoSeconds { + type Seconds; + fn into_seconds(self) -> Self::Seconds; + } + + /// Trait to convert into millis + pub trait IntoMillis { + type Millis; + fn into_millis(self) -> Self::Millis; + } + + /// Type to distinguish NumWrapper as millis + pub struct MillisType; + + /// Type to represent milliseconds + pub type Millis = NumWrapper; + + macro_rules! into_seconds { + ($type_name:ident < $t:ty >) => { + impl $type_name<$t> { + pub const fn into_seconds(self) -> Seconds<$t> { + Seconds::from(self.inner / 1000) + } + + /// Constant factor multiplication + pub const fn mul(self, other: $t) -> Self { + Self::from(self.inner * other) + } + } + + impl IntoSeconds for $type_name<$t> { + type Seconds = Seconds<$t>; + + fn into_seconds(self) -> Seconds<$t> { + self.into_seconds() + } + } + }; + } + + into_seconds!(Millis); + into_seconds!(Millis); + into_seconds!(Millis); + into_seconds!(Millis); + + /// Type to distinguish NumWrapper as seconds + pub struct SecondsType; + + /// Type to represent seconds + pub type Seconds = NumWrapper; + + macro_rules! into_millis { + ($type_name:ident < $t:ty >) => { + impl $type_name<$t> { + pub const fn into_millis(self) -> Millis<$t> { + Millis::from(self.inner.saturating_mul(1000)) + } + + /// Constant factor multiplication + pub const fn mul(self, other: $t) -> Self { + Self::from(self.inner * other) + } + } + + impl IntoMillis for $type_name<$t> { + type Millis = Millis<$t>; + + fn into_millis(self) -> Millis<$t> { + self.into_millis() + } + } + }; + } + + into_millis!(Seconds); + into_millis!(Seconds); + into_millis!(Seconds); + into_millis!(Seconds); +} + #[cfg(test)] mod tests { use super::*; diff --git a/libs/utils/src/num_wrapper.rs b/libs/utils/src/num_wrapper.rs new file mode 100644 index 0000000000..18941d3456 --- /dev/null +++ b/libs/utils/src/num_wrapper.rs @@ -0,0 +1,477 @@ +use parity_scale_codec::{ + Compact, CompactAs, CompactRef, Decode, Encode, EncodeAsRef, EncodeLike, HasCompact, Input, + MaxEncodedLen, Ref, WrapperTypeDecode, WrapperTypeEncode, +}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_arithmetic::traits::{ + Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, + CheckedSub, IntegerSquareRoot, One, Saturating, Zero, +}; +use sp_std::{ + cmp::Ordering, + fmt::{self, Debug}, + marker::PhantomData, + ops::{ + Add, AddAssign, Deref, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, Shr, Sub, + SubAssign, + }, +}; + +/// Type that allows to create different typed numbers with the same inner +/// type: +/// +/// ``` +/// # use crate::num_wrapper::NumWrapper; +/// +/// struct Id1; +/// struct Id2; +/// +/// type FooU64 = NumWrapper; +/// type BarU64 = NumWrapper; +/// ``` +#[derive(TypeInfo, Serialize, Deserialize)] +#[scale_info(skip_type_params(T, I))] +pub struct NumWrapper { + pub inner: T, + _instance: PhantomData, +} + +impl NumWrapper { + pub const fn from(value: T) -> Self { + NumWrapper { + inner: value, + _instance: PhantomData, + } + } +} + +/* +impl, I> From for NumWrapper { + fn from(other: u8) -> Self { + Self::from((other).into()) + } +} + +impl, I> From for NumWrapper { + fn from(other: u16) -> Self { + Self::from((other).into()) + } +} + +impl, I> From for NumWrapper { + fn from(other: u32) -> Self { + Self::from((other).into()) + } +} + +impl, I> From for NumWrapper { + fn from(other: u64) -> Self { + Self::from((other).into()) + } +} + +impl, I> From for NumWrapper { + fn from(other: u128) -> Self { + Self::from((other).into()) + } +} +*/ + +impl, I> TryInto for NumWrapper { + type Error = T::Error; + + fn try_into(self) -> Result { + Ok(self.inner.try_into()?) + } +} + +impl, I> TryInto for NumWrapper { + type Error = T::Error; + + fn try_into(self) -> Result { + Ok(self.inner.try_into()?) + } +} + +impl, I> TryInto for NumWrapper { + type Error = T::Error; + + fn try_into(self) -> Result { + Ok(self.inner.try_into()?) + } +} + +impl, I> TryInto for NumWrapper { + type Error = T::Error; + + fn try_into(self) -> Result { + Ok(self.inner.try_into()?) + } +} + +impl, I> TryInto for NumWrapper { + type Error = T::Error; + + fn try_into(self) -> Result { + Ok(self.inner.try_into()?) + } +} + +impl Default for NumWrapper { + fn default() -> Self { + Self::from(T::default()) + } +} + +impl Clone for NumWrapper { + fn clone(&self) -> Self { + Self::from(self.inner.clone()) + } +} + +impl Copy for NumWrapper {} + +impl PartialEq for NumWrapper { + fn eq(&self, other: &Self) -> bool { + self.inner.eq(&other.inner) + } +} + +impl Eq for NumWrapper {} + +impl PartialOrd for NumWrapper { + fn partial_cmp(&self, other: &Self) -> Option { + self.inner.partial_cmp(&other.inner) + } +} + +impl Ord for NumWrapper { + fn cmp(&self, other: &Self) -> Ordering { + self.inner.cmp(&other.inner) + } +} + +impl Debug for NumWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.inner.fmt(f) + } +} + +impl, I> Add for NumWrapper { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self::from(self.inner.add(rhs.inner)) + } +} + +impl, I> Sub for NumWrapper { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Self::from(self.inner.sub(rhs.inner)) + } +} + +impl, I> Mul for NumWrapper { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + Self::from(self.inner.mul(rhs.inner)) + } +} + +impl, I> Div for NumWrapper { + type Output = Self; + + fn div(self, rhs: Self) -> Self { + Self::from(self.inner.div(rhs.inner)) + } +} + +impl, I> Rem for NumWrapper { + type Output = Self; + + fn rem(self, rhs: Self) -> Self { + Self::from(self.inner.rem(rhs.inner)) + } +} + +impl, I> Shl for NumWrapper { + type Output = Self; + + fn shl(self, rhs: u32) -> Self { + Self::from(self.inner.shl(rhs)) + } +} + +impl, I> Shr for NumWrapper { + type Output = Self; + + fn shr(self, rhs: u32) -> Self { + Self::from(self.inner.shr(rhs)) + } +} + +impl AddAssign for NumWrapper { + fn add_assign(&mut self, rhs: Self) { + self.inner.add_assign(rhs.inner) + } +} + +impl SubAssign for NumWrapper { + fn sub_assign(&mut self, rhs: Self) { + self.inner.sub_assign(rhs.inner) + } +} + +impl MulAssign for NumWrapper { + fn mul_assign(&mut self, rhs: Self) { + self.inner.mul_assign(rhs.inner) + } +} + +impl DivAssign for NumWrapper { + fn div_assign(&mut self, rhs: Self) { + self.inner.div_assign(rhs.inner) + } +} + +impl RemAssign for NumWrapper { + fn rem_assign(&mut self, rhs: Self) { + self.inner.rem_assign(rhs.inner) + } +} + +impl CheckedAdd for NumWrapper { + fn checked_add(&self, rhs: &Self) -> Option { + Some(Self::from(self.inner.checked_add(&rhs.inner)?)) + } +} + +impl CheckedSub for NumWrapper { + fn checked_sub(&self, rhs: &Self) -> Option { + Some(Self::from(self.inner.checked_sub(&rhs.inner)?)) + } +} + +impl CheckedMul for NumWrapper { + fn checked_mul(&self, rhs: &Self) -> Option { + Some(Self::from(self.inner.checked_mul(&rhs.inner)?)) + } +} + +impl CheckedDiv for NumWrapper { + fn checked_div(&self, rhs: &Self) -> Option { + Some(Self::from(self.inner.checked_div(&rhs.inner)?)) + } +} + +impl CheckedRem for NumWrapper { + fn checked_rem(&self, rhs: &Self) -> Option { + Some(Self::from(self.inner.checked_rem(&rhs.inner)?)) + } +} + +impl CheckedShl for NumWrapper { + fn checked_shl(&self, rhs: u32) -> Option { + Some(Self::from(self.inner.checked_shl(rhs)?)) + } +} + +impl CheckedShr for NumWrapper { + fn checked_shr(&self, rhs: u32) -> Option { + Some(Self::from(self.inner.checked_shr(rhs)?)) + } +} + +impl CheckedNeg for NumWrapper { + fn checked_neg(&self) -> Option { + Some(Self::from(self.inner.checked_neg()?)) + } +} + +impl Saturating for NumWrapper { + fn saturating_add(self, rhs: Self) -> Self { + Self::from(self.inner.saturating_add(rhs.inner)) + } + + fn saturating_sub(self, rhs: Self) -> Self { + Self::from(self.inner.saturating_sub(rhs.inner)) + } + + fn saturating_mul(self, rhs: Self) -> Self { + Self::from(self.inner.saturating_mul(rhs.inner)) + } + + fn saturating_pow(self, exp: usize) -> Self { + Self::from(self.inner.saturating_pow(exp)) + } +} + +impl Zero for NumWrapper { + fn zero() -> Self { + Self::from(T::zero()) + } + + fn is_zero(&self) -> bool { + self.inner.is_zero() + } +} + +impl One for NumWrapper { + fn one() -> Self { + Self::from(T::one()) + } + + fn is_one(&self) -> bool { + self.inner.is_one() + } +} + +impl IntegerSquareRoot for NumWrapper { + fn integer_sqrt_checked(&self) -> Option { + Some(Self::from(self.inner.integer_sqrt_checked()?)) + } +} + +impl Bounded for NumWrapper { + fn min_value() -> Self { + Self::from(T::min_value()) + } + + fn max_value() -> Self { + Self::from(T::max_value()) + } +} + +/* +impl Encode for NumWrapper { + fn encode(&self) -> Vec { + self.inner.encode() + } +} + +impl Decode for NumWrapper { + fn decode(input: &mut In) -> Result { + Ok(Self::from(T::decode(input)?)) + } +} +*/ + +impl Deref for NumWrapper { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl WrapperTypeEncode for NumWrapper {} + +impl, I> WrapperTypeDecode for NumWrapper { + type Wrapped = T; +} + +impl From for NumWrapper { + fn from(other: T) -> Self { + Self::from(other) + } +} + +impl From> for NumWrapper { + fn from(other: Compact) -> Self { + Self::from(other.0) + } +} + +impl From> for NumWrapper { + fn from(other: Compact) -> Self { + other.0 + } +} + +impl CompactAs for NumWrapper { + type As = T::As; + + fn encode_as(&self) -> &Self::As { + self.inner.encode_as() + } + + fn decode_from(x: Self::As) -> Result { + Ok(Self::from(T::decode_from(x)?)) + } +} + +impl MaxEncodedLen for NumWrapper { + fn max_encoded_len() -> usize { + T::max_encoded_len() + } +} + +impl<'a, T: 'a, I: 'a> EncodeAsRef<'a, Self> for NumWrapper +where + CompactRef<'a, T>: Encode + From<&'a Self>, +{ + type RefType = CompactRef<'a, T>; +} + +impl EncodeLike for NumWrapper {} +impl EncodeLike for NumWrapper {} +impl EncodeLike for &NumWrapper {} + +#[cfg(test)] +mod tests { + use sp_arithmetic::traits::BaseArithmetic; + + use super::*; + + /* + #[derive(Debug, PartialEq, Encode, Decode)] + enum TestGenericHasCompact { + A { + #[codec(compact)] + a: NumWrapper, + }, + } + */ + + fn is_has_compact() {} + + #[test] + fn ensure_is_has_compact() { + /* + #[derive(Encode, Decode, MaxEncodedLen)] + struct Example { + #[codec(compact)] + a: NumWrapper, + } + + let example = Example { a: 256.into() }; + + let encoded = (&example).encode(); + + dbg!(encoded); + panic!() + */ + + is_has_compact::>(); + } + + /* + #[test] + fn foo() { + Compact + } EncodeAsRef, + */ + + /* + fn is_base_arithmetic() {} + + #[test] + fn ensure_is_base_arithmetic() { + is_base_arithmetic::>(); + } + */ +}