From da5acf8826e93e7b7b930151a14dcaefb1190c6c Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 15 Mar 2023 17:31:23 +0100 Subject: [PATCH 1/8] Add FeePolynomial struct Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/lib.rs | 64 +++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 8a842307355bf..ae0eb13b9853c 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -30,7 +30,7 @@ use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use sp_arithmetic::{ - traits::{BaseArithmetic, SaturatedConversion, Saturating, Unsigned}, + traits::{BaseArithmetic, SaturatedConversion, Unsigned}, Perbill, }; use sp_core::Get; @@ -121,6 +121,45 @@ pub struct WeightToFeeCoefficient { /// A list of coefficients that represent one polynomial. pub type WeightToFeeCoefficients = SmallVec<[WeightToFeeCoefficient; 4]>; +/// A list of coefficients that represent a polynomial to convert a `u64` to a fee. +/// +/// Can be [eval](Self::eval)uated at a specific `u64` to get the fee. +pub struct FeePolynomial { + coefficients: SmallVec<[WeightToFeeCoefficient; 4]>, +} + +impl From> for FeePolynomial { + fn from(coefficients: WeightToFeeCoefficients) -> Self { + Self { coefficients } + } +} + +impl FeePolynomial +where + Balance: BaseArithmetic + From + Copy + Unsigned, +{ + pub fn eval(&self, x: u64) -> Balance { + self.coefficients.iter().fold(Balance::saturated_from(0u32), |mut acc, args| { + let w = Balance::saturated_from(x).saturating_pow(args.degree.into()); + + // The sum could get negative. Therefore we only sum with the accumulator. + // The Perbill Mul implementation is non overflowing. + let frac = args.coeff_frac * w; + let integer = args.coeff_integer.saturating_mul(w); + + if args.negative { + acc = acc.saturating_sub(frac); + acc = acc.saturating_sub(integer); + } else { + acc = acc.saturating_add(frac); + acc = acc.saturating_add(integer); + } + + acc + }) + } +} + /// A trait that describes the weight to fee calculation. pub trait WeightToFee { /// The type that is returned as result from calculation. @@ -157,27 +196,8 @@ where /// This should not be overridden in most circumstances. Calculation is done in the /// `Balance` type and never overflows. All evaluation is saturating. fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::polynomial() - .iter() - .fold(Self::Balance::saturated_from(0u32), |mut acc, args| { - let w = Self::Balance::saturated_from(weight.ref_time()) - .saturating_pow(args.degree.into()); - - // The sum could get negative. Therefore we only sum with the accumulator. - // The Perbill Mul implementation is non overflowing. - let frac = args.coeff_frac * w; - let integer = args.coeff_integer.saturating_mul(w); - - if args.negative { - acc = acc.saturating_sub(frac); - acc = acc.saturating_sub(integer); - } else { - acc = acc.saturating_add(frac); - acc = acc.saturating_add(integer); - } - - acc - }) + let poly: FeePolynomial = Self::polynomial().into(); + poly.eval(weight.ref_time()) } } From 7b697e009caf70b53895ed3160ce6e240ac366a2 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 15 Mar 2023 17:33:14 +0100 Subject: [PATCH 2/8] Add Weight::without_{ref_time, proof_size} Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/weight_v2.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/primitives/weights/src/weight_v2.rs b/primitives/weights/src/weight_v2.rs index ca137145920bb..76c40c16cbf03 100644 --- a/primitives/weights/src/weight_v2.rs +++ b/primitives/weights/src/weight_v2.rs @@ -74,6 +74,16 @@ impl Weight { &mut self.proof_size } + /// Return self but discard any reference time. + pub const fn without_ref_time(&self) -> Self { + Self { ref_time: 0, proof_size: self.proof_size } + } + + /// Return self but discard any proof size. + pub const fn without_proof_size(&self) -> Self { + Self { ref_time: self.ref_time, proof_size: 0 } + } + pub const MAX: Self = Self { ref_time: u64::MAX, proof_size: u64::MAX }; /// Get the conservative min of `self` and `other` weight. From 6add0b4a0963deb00b71dc27d2a106fdc6a2d47e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 15 Mar 2023 17:35:43 +0100 Subject: [PATCH 3/8] Docs Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index ae0eb13b9853c..185acd17005fd 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -121,7 +121,7 @@ pub struct WeightToFeeCoefficient { /// A list of coefficients that represent one polynomial. pub type WeightToFeeCoefficients = SmallVec<[WeightToFeeCoefficient; 4]>; -/// A list of coefficients that represent a polynomial to convert a `u64` to a fee. +/// A list of coefficients that represent a polynomial. /// /// Can be [eval](Self::eval)uated at a specific `u64` to get the fee. pub struct FeePolynomial { @@ -138,6 +138,7 @@ impl FeePolynomial where Balance: BaseArithmetic + From + Copy + Unsigned, { + /// Evaluate the polynomial at a specific `x`. pub fn eval(&self, x: u64) -> Balance { self.coefficients.iter().fold(Balance::saturated_from(0u32), |mut acc, args| { let w = Balance::saturated_from(x).saturating_pow(args.degree.into()); From e0489dece168cb4e190f0fdb95ae09665311233e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 15 Mar 2023 20:14:38 +0100 Subject: [PATCH 4/8] Cleanup code Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/lib.rs | 45 +++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 185acd17005fd..64642eb1aa83c 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -118,7 +118,31 @@ pub struct WeightToFeeCoefficient { pub degree: u8, } -/// A list of coefficients that represent one polynomial. +impl WeightToFeeCoefficient +where + Balance: BaseArithmetic + From + Copy + Unsigned, +{ + /// Evaluate the term at `x` and saturatingly amalgamate into `result`. + pub fn saturating_eval(&self, mut result: Balance, x: Balance) -> Balance { + let power = x.saturating_pow(self.degree.into()); + + let frac = self.coeff_frac * power; // Overflow safe. + let integer = self.coeff_integer.saturating_mul(power); + // Do not add them together here to avoid an underflow. + + if self.negative { + result = result.saturating_sub(frac); + result = result.saturating_sub(integer); + } else { + result = result.saturating_add(frac); + result = result.saturating_add(integer); + } + + result + } +} + +/// A list of coefficients that represent a polynomial. pub type WeightToFeeCoefficients = SmallVec<[WeightToFeeCoefficient; 4]>; /// A list of coefficients that represent a polynomial. @@ -140,23 +164,8 @@ where { /// Evaluate the polynomial at a specific `x`. pub fn eval(&self, x: u64) -> Balance { - self.coefficients.iter().fold(Balance::saturated_from(0u32), |mut acc, args| { - let w = Balance::saturated_from(x).saturating_pow(args.degree.into()); - - // The sum could get negative. Therefore we only sum with the accumulator. - // The Perbill Mul implementation is non overflowing. - let frac = args.coeff_frac * w; - let integer = args.coeff_integer.saturating_mul(w); - - if args.negative { - acc = acc.saturating_sub(frac); - acc = acc.saturating_sub(integer); - } else { - acc = acc.saturating_add(frac); - acc = acc.saturating_add(integer); - } - - acc + self.coefficients.iter().fold(Balance::zero(), |acc, term| { + term.saturating_eval(acc, Balance::saturated_from(x)) }) } } From 005138c9ba55a4c34a593d93dfa8b88cc29839e4 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 15 Mar 2023 20:14:49 +0100 Subject: [PATCH 5/8] Add docs Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/lib.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 64642eb1aa83c..502f02c4275cc 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -147,7 +147,18 @@ pub type WeightToFeeCoefficients = SmallVec<[WeightToFeeCoefficient; 4]>; /// A list of coefficients that represent a polynomial. /// -/// Can be [eval](Self::eval)uated at a specific `u64` to get the fee. +/// Can be [eval](Self::eval)uated at a specific `u64` to get the fee. The evaluations happens by +/// summing up all term [results](`WeightToFeeCoefficient::saturating_eval`). The order of the +/// coefficients matters since it uses saturating arithmetic. This struct does therefore not model a +/// polynomial in the mathematical sense (polynomial ring). +/// +/// For visualization purposes, the formula looks like this: +/// +/// ```ignore +/// (c[0].frac * x^(c[0].degree) + c[0].integer * x^(c[0].degree)) * (-1 * c[0].negative) +/// + (c[1].frac * x^(c[1].degree) + c[1].integer * x^(c[1].degree)) * (-1 * c[1].negative) +/// + ... +/// ``` pub struct FeePolynomial { coefficients: SmallVec<[WeightToFeeCoefficient; 4]>, } From 984cafa8d14fdccdf26035683c76ad806c381bda Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 15 Mar 2023 20:18:06 +0100 Subject: [PATCH 6/8] doc Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 502f02c4275cc..a64ae09fdc1b5 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -123,6 +123,12 @@ where Balance: BaseArithmetic + From + Copy + Unsigned, { /// Evaluate the term at `x` and saturatingly amalgamate into `result`. + /// + /// The term is calculated as: + /// + /// ```ignore + /// (frac * x^(degree) + integer * x^(degree)) * (-1 * c[0].negative) + /// ``` pub fn saturating_eval(&self, mut result: Balance, x: Balance) -> Balance { let power = x.saturating_pow(self.degree.into()); From 2bae1f789668cf33686fe833c2c3fd578a7940b3 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 15 Mar 2023 20:29:50 +0100 Subject: [PATCH 7/8] Fix docs Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/lib.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index a64ae09fdc1b5..0a58f869baed9 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -124,11 +124,11 @@ where { /// Evaluate the term at `x` and saturatingly amalgamate into `result`. /// - /// The term is calculated as: - /// + /// The unsigned value for the term is calculated as: /// ```ignore - /// (frac * x^(degree) + integer * x^(degree)) * (-1 * c[0].negative) + /// (frac * x^(degree) + integer * x^(degree)) /// ``` + /// Depending on the value of `negative`, it is added or subtracted from `result`. pub fn saturating_eval(&self, mut result: Balance, x: Balance) -> Balance { let power = x.saturating_pow(self.degree.into()); @@ -158,13 +158,14 @@ pub type WeightToFeeCoefficients = SmallVec<[WeightToFeeCoefficient; 4]>; /// coefficients matters since it uses saturating arithmetic. This struct does therefore not model a /// polynomial in the mathematical sense (polynomial ring). /// -/// For visualization purposes, the formula looks like this: +/// For visualization purposes, the formulas of the unsigned terms look like: /// /// ```ignore -/// (c[0].frac * x^(c[0].degree) + c[0].integer * x^(c[0].degree)) * (-1 * c[0].negative) -/// + (c[1].frac * x^(c[1].degree) + c[1].integer * x^(c[1].degree)) * (-1 * c[1].negative) -/// + ... +/// c[0].frac * x^(c[0].degree) + c[0].integer * x^(c[0].degree) +/// c[1].frac * x^(c[1].degree) + c[1].integer * x^(c[1].degree) +/// ... /// ``` +/// Depending on the value of `c[i].negative`, the term is added or subtracted from the result. pub struct FeePolynomial { coefficients: SmallVec<[WeightToFeeCoefficient; 4]>, } From 52251d39df4331061ec6fa7d9bb7d5ac5fc7f1d2 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 15 Mar 2023 20:31:57 +0100 Subject: [PATCH 8/8] docs Signed-off-by: Oliver Tale-Yazdi --- primitives/weights/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/primitives/weights/src/lib.rs b/primitives/weights/src/lib.rs index 0a58f869baed9..faa641a0f0c53 100644 --- a/primitives/weights/src/lib.rs +++ b/primitives/weights/src/lib.rs @@ -128,7 +128,7 @@ where /// ```ignore /// (frac * x^(degree) + integer * x^(degree)) /// ``` - /// Depending on the value of `negative`, it is added or subtracted from `result`. + /// Depending on the value of `negative`, it is added or subtracted from the `result`. pub fn saturating_eval(&self, mut result: Balance, x: Balance) -> Balance { let power = x.saturating_pow(self.degree.into()); @@ -161,11 +161,12 @@ pub type WeightToFeeCoefficients = SmallVec<[WeightToFeeCoefficient; 4]>; /// For visualization purposes, the formulas of the unsigned terms look like: /// /// ```ignore -/// c[0].frac * x^(c[0].degree) + c[0].integer * x^(c[0].degree) -/// c[1].frac * x^(c[1].degree) + c[1].integer * x^(c[1].degree) +/// (c[0].frac * x^(c[0].degree) + c[0].integer * x^(c[0].degree)) +/// (c[1].frac * x^(c[1].degree) + c[1].integer * x^(c[1].degree)) /// ... /// ``` -/// Depending on the value of `c[i].negative`, the term is added or subtracted from the result. +/// Depending on the value of `c[i].negative`, each term is added or subtracted from the result. +/// The result is initialized as zero. pub struct FeePolynomial { coefficients: SmallVec<[WeightToFeeCoefficient; 4]>, }