diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index d7b326164b7d3..f1f8fc8b59830 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -50,6 +50,34 @@ pub use rational::{Rational128, RationalInfinite}; use sp_std::{cmp::Ordering, fmt::Debug, prelude::*}; use traits::{BaseArithmetic, One, SaturatedConversion, Unsigned, Zero}; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +/// Arithmetic errors. +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum ArithmeticError { + /// Underflow. + Underflow, + /// Overflow. + Overflow, + /// Division by zero. + DivisionByZero, +} + +impl From for &'static str { + fn from(e: ArithmeticError) -> &'static str { + match e { + ArithmeticError::Underflow => "An underflow would occur", + ArithmeticError::Overflow => "An overflow would occur", + ArithmeticError::DivisionByZero => "Division by zero", + } + } +} + /// Trait for comparing two numbers with an threshold. /// /// Returns: diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index 466d5696c7136..7fa64d28669d4 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -18,6 +18,11 @@ //! Primitive traits for the runtime arithmetic. use codec::HasCompact; +pub use ensure::{ + Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign, EnsureFixedPointNumber, + EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp, EnsureOpAssign, EnsureSub, + EnsureSubAssign, +}; pub use integer_sqrt::IntegerSquareRoot; pub use num_traits::{ checked_pow, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, @@ -302,3 +307,527 @@ pub trait SaturatedConversion { } } impl SaturatedConversion for T {} + +/// Arithmetic operations with safe error handling. +/// +/// This module provide a readable way to do safe arithmetics, turning this: +/// +/// ``` +/// # use sp_arithmetic::{traits::EnsureSub, ArithmeticError}; +/// # fn foo() -> Result<(), ArithmeticError> { +/// # let mut my_value: i32 = 1; +/// # let other_value: i32 = 1; +/// my_value = my_value.checked_sub(other_value).ok_or(ArithmeticError::Overflow)?; +/// # Ok(()) +/// # } +/// ``` +/// +/// into this: +/// +/// ``` +/// # use sp_arithmetic::{traits::EnsureSubAssign, ArithmeticError}; +/// # fn foo() -> Result<(), ArithmeticError> { +/// # let mut my_value: i32 = 1; +/// # let other_value: i32 = 1; +/// my_value.ensure_sub_assign(other_value)?; +/// # Ok(()) +/// # } +/// ``` +/// +/// choosing the correct [`ArithmeticError`](crate::ArithmeticError) it should return in case of +/// fail. +/// +/// The *EnsureOps* family functions follows the same behavior as *CheckedOps* but +/// returning an [`ArithmeticError`](crate::ArithmeticError) instead of `None`. +mod ensure { + use super::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Zero}; + use crate::{ArithmeticError, FixedPointNumber, FixedPointOperand}; + + /// Performs addition that returns [`ArithmeticError`] instead of wrapping around on overflow. + pub trait EnsureAdd: CheckedAdd + PartialOrd + Zero + Copy { + /// Adds two numbers, checking for overflow. + /// + /// If it fails, [`ArithmeticError`] is returned. + /// + /// Similar to [`CheckedAdd::checked_add()`] but returning an [`ArithmeticError`] error. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureAdd, ArithmeticError}; + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// u32::MAX.ensure_add(1)?; + /// Ok(()) + /// } + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// i32::MIN.ensure_add(-1)?; + /// Ok(()) + /// } + /// + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// ``` + fn ensure_add(self, v: Self) -> Result { + self.checked_add(&v).ok_or_else(|| error::equivalent(v)) + } + } + + /// Performs subtraction that returns [`ArithmeticError`] instead of wrapping around on + /// underflow. + pub trait EnsureSub: CheckedSub + PartialOrd + Zero + Copy { + /// Subtracts two numbers, checking for overflow. + /// + /// If it fails, [`ArithmeticError`] is returned. + /// + /// Similar to [`CheckedSub::checked_sub()`] but returning an [`ArithmeticError`] error. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureSub, ArithmeticError}; + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// 0u32.ensure_sub(1)?; + /// Ok(()) + /// } + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// i32::MAX.ensure_sub(-1)?; + /// Ok(()) + /// } + /// + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// ``` + fn ensure_sub(self, v: Self) -> Result { + self.checked_sub(&v).ok_or_else(|| error::inverse(v)) + } + } + + /// Performs multiplication that returns [`ArithmeticError`] instead of wrapping around on + /// overflow. + pub trait EnsureMul: CheckedMul + PartialOrd + Zero + Copy { + /// Multiplies two numbers, checking for overflow. + /// + /// If it fails, [`ArithmeticError`] is returned. + /// + /// Similar to [`CheckedMul::checked_mul()`] but returning an [`ArithmeticError`] error. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureMul, ArithmeticError}; + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// u32::MAX.ensure_mul(2)?; + /// Ok(()) + /// } + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// i32::MAX.ensure_mul(-2)?; + /// Ok(()) + /// } + /// + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// ``` + fn ensure_mul(self, v: Self) -> Result { + self.checked_mul(&v).ok_or_else(|| error::multiplication(self, v)) + } + } + + /// Performs division that returns [`ArithmeticError`] instead of wrapping around on overflow. + pub trait EnsureDiv: CheckedDiv + PartialOrd + Zero + Copy { + /// Divides two numbers, checking for overflow. + /// + /// If it fails, [`ArithmeticError`] is returned. + /// + /// Similar to [`CheckedDiv::checked_div()`] but returning an [`ArithmeticError`] error. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureDiv, ArithmeticError}; + /// + /// fn extrinsic_zero() -> Result<(), ArithmeticError> { + /// 1.ensure_div(0)?; + /// Ok(()) + /// } + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// i64::MIN.ensure_div(-1)?; + /// Ok(()) + /// } + /// + /// assert_eq!(extrinsic_zero(), Err(ArithmeticError::DivisionByZero)); + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// ``` + fn ensure_div(self, v: Self) -> Result { + self.checked_div(&v).ok_or_else(|| error::division(self, v)) + } + } + + impl EnsureAdd for T {} + impl EnsureSub for T {} + impl EnsureMul for T {} + impl EnsureDiv for T {} + + /// Meta trait that supports all immutable arithmetic `Ensure*` operations + pub trait EnsureOp: EnsureAdd + EnsureSub + EnsureMul + EnsureDiv {} + impl EnsureOp for T {} + + /// Performs self addition that returns [`ArithmeticError`] instead of wrapping around on + /// overflow. + pub trait EnsureAddAssign: EnsureAdd { + /// Adds two numbers overwriting the left hand one, checking for overflow. + /// + /// If it fails, [`ArithmeticError`] is returned. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureAddAssign, ArithmeticError}; + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// let mut max = u32::MAX; + /// max.ensure_add_assign(1)?; + /// Ok(()) + /// } + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// let mut max = i32::MIN; + /// max.ensure_add_assign(-1)?; + /// Ok(()) + /// } + /// + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// ``` + fn ensure_add_assign(&mut self, v: Self) -> Result<(), ArithmeticError> { + *self = self.ensure_add(v)?; + Ok(()) + } + } + + /// Performs self subtraction that returns [`ArithmeticError`] instead of wrapping around on + /// underflow. + pub trait EnsureSubAssign: EnsureSub { + /// Subtracts two numbers overwriting the left hand one, checking for overflow. + /// + /// If it fails, [`ArithmeticError`] is returned. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureSubAssign, ArithmeticError}; + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// let mut zero: u32 = 0; + /// zero.ensure_sub_assign(1)?; + /// Ok(()) + /// } + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// let mut zero = i32::MAX; + /// zero.ensure_sub_assign(-1)?; + /// Ok(()) + /// } + /// + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// ``` + fn ensure_sub_assign(&mut self, v: Self) -> Result<(), ArithmeticError> { + *self = self.ensure_sub(v)?; + Ok(()) + } + } + + /// Performs self multiplication that returns [`ArithmeticError`] instead of wrapping around on + /// overflow. + pub trait EnsureMulAssign: EnsureMul { + /// Multiplies two numbers overwriting the left hand one, checking for overflow. + /// + /// If it fails, [`ArithmeticError`] is returned. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureMulAssign, ArithmeticError}; + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// let mut max = u32::MAX; + /// max.ensure_mul_assign(2)?; + /// Ok(()) + /// } + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// let mut max = i32::MAX; + /// max.ensure_mul_assign(-2)?; + /// Ok(()) + /// } + /// + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// ``` + fn ensure_mul_assign(&mut self, v: Self) -> Result<(), ArithmeticError> { + *self = self.ensure_mul(v)?; + Ok(()) + } + } + + /// Performs self division that returns [`ArithmeticError`] instead of wrapping around on + /// overflow. + pub trait EnsureDivAssign: EnsureDiv { + /// Divides two numbers overwriting the left hand one, checking for overflow. + /// + /// If it fails, [`ArithmeticError`] is returned. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureDivAssign, ArithmeticError, FixedI64}; + /// + /// fn extrinsic_zero() -> Result<(), ArithmeticError> { + /// let mut one = 1; + /// one.ensure_div_assign(0)?; + /// Ok(()) + /// } + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// let mut min = FixedI64::from(i64::MIN); + /// min.ensure_div_assign(FixedI64::from(-1))?; + /// Ok(()) + /// } + /// + /// assert_eq!(extrinsic_zero(), Err(ArithmeticError::DivisionByZero)); + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// ``` + fn ensure_div_assign(&mut self, v: Self) -> Result<(), ArithmeticError> { + *self = self.ensure_div(v)?; + Ok(()) + } + } + + impl EnsureAddAssign for T {} + impl EnsureSubAssign for T {} + impl EnsureMulAssign for T {} + impl EnsureDivAssign for T {} + + /// Meta trait that supports all assigned arithmetic `Ensure*` operations + pub trait EnsureOpAssign: + EnsureAddAssign + EnsureSubAssign + EnsureMulAssign + EnsureDivAssign + { + } + impl EnsureOpAssign + for T + { + } + + /// Meta trait that supports all arithmetic operations + pub trait Ensure: EnsureOp + EnsureOpAssign {} + impl Ensure for T {} + + /// Extends [`FixedPointNumber`] with the Ensure family functions. + pub trait EnsureFixedPointNumber: FixedPointNumber { + /// Creates `self` from a rational number. Equal to `n / d`. + /// + /// Returns [`ArithmeticError`] if `d == 0` or `n / d` exceeds accuracy. + /// + /// Similar to [`FixedPointNumber::checked_from_rational()`] but returning an + /// [`ArithmeticError`] error. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureFixedPointNumber, ArithmeticError, FixedI64}; + /// + /// fn extrinsic_zero() -> Result<(), ArithmeticError> { + /// FixedI64::ensure_from_rational(1, 0)?; + /// Ok(()) + /// } + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// FixedI64::ensure_from_rational(i64::MAX, -1)?; + /// Ok(()) + /// } + /// + /// assert_eq!(extrinsic_zero(), Err(ArithmeticError::DivisionByZero)); + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// ``` + fn ensure_from_rational( + n: N, + d: D, + ) -> Result { + ::checked_from_rational(n, d) + .ok_or_else(|| error::division(n, d)) + } + + /// Ensure multiplication for integer type `N`. Equal to `self * n`. + /// + /// Returns [`ArithmeticError`] if the result does not fit in `N`. + /// + /// Similar to [`FixedPointNumber::checked_mul_int()`] but returning an [`ArithmeticError`] + /// error. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureFixedPointNumber, ArithmeticError, FixedI64}; + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// FixedI64::from(i64::MAX).ensure_mul_int(2)?; + /// Ok(()) + /// } + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// FixedI64::from(i64::MAX).ensure_mul_int(-2)?; + /// Ok(()) + /// } + /// + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// ``` + fn ensure_mul_int(self, n: N) -> Result { + self.checked_mul_int(n).ok_or_else(|| error::multiplication(self, n)) + } + + /// Ensure division for integer type `N`. Equal to `self / d`. + /// + /// Returns [`ArithmeticError`] if the result does not fit in `N` or `d == 0`. + /// + /// Similar to [`FixedPointNumber::checked_div_int()`] but returning an [`ArithmeticError`] + /// error. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureFixedPointNumber, ArithmeticError, FixedI64}; + /// + /// fn extrinsic_zero() -> Result<(), ArithmeticError> { + /// FixedI64::from(1).ensure_div_int(0)?; + /// Ok(()) + /// } + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// FixedI64::from(i64::MIN).ensure_div_int(-1)?; + /// Ok(()) + /// } + /// + /// assert_eq!(extrinsic_zero(), Err(ArithmeticError::DivisionByZero)); + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// ``` + fn ensure_div_int(self, d: D) -> Result { + self.checked_div_int(d).ok_or_else(|| error::division(self, d)) + } + } + + impl EnsureFixedPointNumber for T {} + + /// Similar to [`TryFrom`] but returning an [`ArithmeticError`] error. + pub trait EnsureFrom: + TryFrom + PartialOrd + Zero + Copy + { + /// Performs the conversion returning an [`ArithmeticError`] if fails. + /// + /// Similar to [`TryFrom::try_from()`] but returning an [`ArithmeticError`] error. + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureFrom, ArithmeticError}; + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// let byte: u8 = u8::ensure_from(256u16)?; + /// Ok(()) + /// } + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// let byte: i8 = i8::ensure_from(-129i16)?; + /// Ok(()) + /// } + /// + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// ``` + fn ensure_from(other: T) -> Result { + Self::try_from(other).map_err(|_| error::equivalent(other)) + } + } + + /// Similar to [`TryInto`] but returning an [`ArithmeticError`] error. + pub trait EnsureInto: + TryInto + PartialOrd + Zero + Copy + { + /// Performs the conversion returning an [`ArithmeticError`] if fails. + /// + /// Similar to [`TryInto::try_into()`] but returning an [`ArithmeticError`] error + /// + /// ``` + /// use sp_arithmetic::{traits::EnsureInto, ArithmeticError}; + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// let byte: u8 = 256u16.ensure_into()?; + /// Ok(()) + /// } + /// + /// fn underflow() -> Result<(), ArithmeticError> { + /// let byte: i8 = (-129i16).ensure_into()?; + /// Ok(()) + /// } + /// + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); + /// ``` + fn ensure_into(self) -> Result { + self.try_into().map_err(|_| error::equivalent(self)) + } + } + + impl + PartialOrd + Zero + Copy, S: PartialOrd + Zero + Copy> EnsureFrom for T {} + impl + PartialOrd + Zero + Copy, S: PartialOrd + Zero + Copy> EnsureInto for T {} + + mod error { + use super::{ArithmeticError, Zero}; + + #[derive(PartialEq)] + enum Signum { + Negative, + Positive, + } + + impl From for Signum { + fn from(value: T) -> Self { + if value < Zero::zero() { + Signum::Negative + } else { + Signum::Positive + } + } + } + + impl sp_std::ops::Mul for Signum { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + if self != rhs { + Signum::Negative + } else { + Signum::Positive + } + } + } + + pub fn equivalent(r: R) -> ArithmeticError { + match Signum::from(r) { + Signum::Negative => ArithmeticError::Underflow, + Signum::Positive => ArithmeticError::Overflow, + } + } + + pub fn inverse(r: R) -> ArithmeticError { + match Signum::from(r) { + Signum::Negative => ArithmeticError::Overflow, + Signum::Positive => ArithmeticError::Underflow, + } + } + + pub fn multiplication( + l: L, + r: R, + ) -> ArithmeticError { + match Signum::from(l) * Signum::from(r) { + Signum::Negative => ArithmeticError::Underflow, + Signum::Positive => ArithmeticError::Overflow, + } + } + + pub fn division( + n: N, + d: D, + ) -> ArithmeticError { + if d.is_zero() { + ArithmeticError::DivisionByZero + } else { + multiplication(n, d) + } + } + } +} diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index e94efda86aa03..96fe7d2487f48 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -93,9 +93,9 @@ pub use sp_arithmetic::biguint; pub use sp_arithmetic::helpers_128bit; /// Re-export top-level arithmetic stuff. pub use sp_arithmetic::{ - traits::SaturatedConversion, FixedI128, FixedI64, FixedPointNumber, FixedPointOperand, - FixedU128, InnerOf, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, Rational128, - Rounding, UpperOf, + traits::SaturatedConversion, ArithmeticError, FixedI128, FixedI64, FixedPointNumber, + FixedPointOperand, FixedU128, InnerOf, PerThing, PerU16, Perbill, Percent, Permill, + Perquintill, Rational128, Rounding, UpperOf, }; pub use either::Either; @@ -641,28 +641,6 @@ impl From for DispatchError { } } -/// Arithmetic errors. -#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub enum ArithmeticError { - /// Underflow. - Underflow, - /// Overflow. - Overflow, - /// Division by zero. - DivisionByZero, -} - -impl From for &'static str { - fn from(e: ArithmeticError) -> &'static str { - match e { - ArithmeticError::Underflow => "An underflow would occur", - ArithmeticError::Overflow => "An overflow would occur", - ArithmeticError::DivisionByZero => "Division by zero", - } - } -} - impl From for DispatchError { fn from(e: ArithmeticError) -> DispatchError { Self::Arithmetic(e) diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 375475141b818..8978cdb11c0c6 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -33,8 +33,10 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use sp_application_crypto::AppKey; pub use sp_arithmetic::traits::{ AtLeast32Bit, AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedShl, - CheckedShr, CheckedSub, IntegerSquareRoot, One, SaturatedConversion, Saturating, - UniqueSaturatedFrom, UniqueSaturatedInto, Zero, + CheckedShr, CheckedSub, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign, + EnsureFixedPointNumber, EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp, + EnsureOpAssign, EnsureSub, EnsureSubAssign, IntegerSquareRoot, One, SaturatedConversion, + Saturating, UniqueSaturatedFrom, UniqueSaturatedInto, Zero, }; use sp_core::{self, storage::StateVersion, Hasher, RuntimeDebug, TypeId}; #[doc(hidden)]