Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uint256 implementation #1059

Merged
merged 14 commits into from
Aug 30, 2021
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ and this project adheres to

### Added

- Added `From<Addr>` and `From<&Addr>` conversions for `Cow<Addr>`.
- cosmwasm-std: Added `From<Addr>` and `From<&Addr>` conversions for
`Cow<Addr>`.
- cosmwasm-std: Added a new `Uint256` type.
- cosmwasm-std: Added implementations of `Isqrt` (integer square root) for
`Uint64`, `Uint128`, and `Uint256`.

## [0.16.0] - 2021-08-05

Expand Down
5 changes: 4 additions & 1 deletion packages/std/src/errors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ mod system_error;
mod verification_error;

pub use recover_pubkey_error::RecoverPubkeyError;
pub use std_error::{DivideByZeroError, OverflowError, OverflowOperation, StdError, StdResult};
pub use std_error::{
ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, StdError,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConversionOverflowError also need to be exported in lib.rs because it is used in StdError now.

StdResult,
};
pub use system_error::SystemError;
pub use verification_error::VerificationError;
45 changes: 45 additions & 0 deletions packages/std/src/errors/std_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ pub enum StdError {
#[cfg(feature = "backtraces")]
backtrace: Backtrace,
},
#[error("Conversion error: ")]
ConversionOverflow {
#[from]
source: ConversionOverflowError,
#[cfg(feature = "backtraces")]
backtrace: Backtrace,
},
}

impl StdError {
Expand Down Expand Up @@ -376,6 +383,22 @@ impl PartialEq<StdError> for StdError {
false
}
}
StdError::ConversionOverflow {
source,
#[cfg(feature = "backtraces")]
backtrace: _,
} => {
if let StdError::ConversionOverflow {
source: rhs_source,
#[cfg(feature = "backtraces")]
backtrace: _,
} = rhs
{
source == rhs_source
} else {
false
}
}
}
}
}
Expand Down Expand Up @@ -455,6 +478,28 @@ impl OverflowError {
}
}

#[derive(Error, Debug, PartialEq, Eq)]
#[error("Error converting {source_type} to {target_type} for {value}")]
pub struct ConversionOverflowError {
pub source_type: &'static str,
pub target_type: &'static str,
pub value: String,
}

impl ConversionOverflowError {
pub fn new(
source_type: &'static str,
target_type: &'static str,
value: impl Into<String>,
) -> Self {
Self {
source_type,
target_type,
value: value.into(),
}
}
}

#[derive(Error, Debug, PartialEq, Eq)]
#[error("Cannot devide {operand} by zero")]
pub struct DivideByZeroError {
Expand Down
2 changes: 1 addition & 1 deletion packages/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub use crate::ibc::{
#[cfg(feature = "iterator")]
#[allow(deprecated)]
pub use crate::iterator::{Order, Pair, KV};
pub use crate::math::{Decimal, Fraction, Uint128, Uint64};
pub use crate::math::{Decimal, Fraction, Uint128, Uint256, Uint64};
pub use crate::query::{
AllBalanceResponse, BalanceResponse, BankQuery, CustomQuery, QueryRequest, WasmQuery,
};
Expand Down
30 changes: 28 additions & 2 deletions packages/std/src/math/isqrt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::{cmp, ops};

use crate::{Uint128, Uint256, Uint64};

/// A trait for calculating the
/// [integer square root](https://en.wikipedia.org/wiki/Integer_square_root).
pub trait Isqrt {
Expand All @@ -12,7 +14,7 @@ where
I: Unsigned
+ ops::Add<I, Output = I>
+ ops::Div<I, Output = I>
+ ops::Shr<u8, Output = I>
+ ops::Shr<u32, Output = I>
maurolacy marked this conversation as resolved.
Show resolved Hide resolved
+ cmp::PartialOrd
+ Copy
+ From<u8>,
Expand Down Expand Up @@ -43,14 +45,19 @@ impl Unsigned for u16 {}
impl Unsigned for u32 {}
impl Unsigned for u64 {}
impl Unsigned for u128 {}
impl Unsigned for Uint64 {}
impl Unsigned for Uint128 {}
impl Unsigned for Uint256 {}
impl Unsigned for usize {}

#[cfg(test)]
mod tests {
use std::convert::TryFrom;

use super::*;

#[test]
fn isqrts() {
fn isqrt_primitives() {
// Let's check correctness.
assert_eq!(0u8.isqrt(), 0);
assert_eq!(1u8.isqrt(), 1);
Expand All @@ -66,4 +73,23 @@ mod tests {
assert_eq!(26u64.isqrt(), 5);
assert_eq!(26u128.isqrt(), 5);
}

#[test]
fn isqrt_uint64() {
assert_eq!(Uint64::new(24).isqrt(), Uint64::new(4));
}

#[test]
fn isqrt_uint128() {
assert_eq!(Uint128::new(24).isqrt(), Uint128::new(4));
}

#[test]
fn isqrt_uint256() {
assert_eq!(Uint256::from(24u32).isqrt(), Uint256::from(4u32));
uint marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(
(Uint256::from(u128::MAX) * Uint256::from(u128::MAX)).isqrt(),
Uint256::try_from("340282366920938463463374607431768211455").unwrap()
);
}
}
2 changes: 2 additions & 0 deletions packages/std/src/math/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ mod decimal;
mod fraction;
mod isqrt;
mod uint128;
mod uint256;
mod uint64;

pub use decimal::Decimal;
pub use fraction::Fraction;
pub use isqrt::Isqrt;
pub use uint128::Uint128;
pub use uint256::Uint256;
pub use uint64::Uint64;
81 changes: 63 additions & 18 deletions packages/std/src/math/uint128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::iter::Sum;
use std::ops;

use crate::errors::{DivideByZeroError, OverflowError, OverflowOperation, StdError};
use crate::Uint256;

/// A thin wrapper around u128 that is using strings for JSON encoding/decoding,
/// such that the full u128 range can be used for clients that convert JSON numbers to floats,
Expand Down Expand Up @@ -222,6 +223,38 @@ impl<'a> ops::Sub<&'a Uint128> for Uint128 {
}
}

impl ops::Div<Uint128> for Uint128 {
type Output = Self;

fn div(self, rhs: Self) -> Self::Output {
Self(self.u128().checked_div(rhs.u128()).unwrap())
}
}

impl<'a> ops::Div<&'a Uint128> for Uint128 {
type Output = Self;

fn div(self, rhs: &'a Uint128) -> Self::Output {
Self(self.u128().checked_div(rhs.u128()).unwrap())
}
}

impl ops::Shr<u32> for Uint128 {
type Output = Self;

fn shr(self, rhs: u32) -> Self::Output {
Self(self.u128().checked_shr(rhs).unwrap())
}
}

impl<'a> ops::Shr<&'a u32> for Uint128 {
type Output = Self;

fn shr(self, rhs: &'a u32) -> Self::Output {
Self(self.u128().checked_shr(*rhs).unwrap())
}
}

impl ops::AddAssign<Uint128> for Uint128 {
fn add_assign(&mut self, rhs: Uint128) {
self.0 = self.0.checked_add(rhs.u128()).unwrap();
Expand All @@ -246,6 +279,30 @@ impl<'a> ops::SubAssign<&'a Uint128> for Uint128 {
}
}

impl ops::DivAssign<Uint128> for Uint128 {
fn div_assign(&mut self, rhs: Self) {
self.0 = self.0.checked_div(rhs.u128()).unwrap();
}
}

impl<'a> ops::DivAssign<&'a Uint128> for Uint128 {
fn div_assign(&mut self, rhs: &'a Uint128) {
self.0 = self.0.checked_div(rhs.u128()).unwrap();
}
}

impl ops::ShrAssign<u32> for Uint128 {
fn shr_assign(&mut self, rhs: u32) {
self.0 = self.0.checked_shr(rhs).unwrap();
}
}

impl<'a> ops::ShrAssign<&'a u32> for Uint128 {
fn shr_assign(&mut self, rhs: &'a u32) {
self.0 = self.0.checked_shr(*rhs).unwrap();
}
}

impl Uint128 {
/// Returns `self * numerator / denominator`
pub fn multiply_ratio<A: Into<u128>, B: Into<u128>>(
Expand All @@ -258,15 +315,16 @@ impl Uint128 {
if denominator == 0 {
panic!("Denominator must not be zero");
}
let val: u128 = (self.full_mul(numerator) / denominator)
(self.full_mul(numerator) / Uint256::from(denominator))
.try_into()
.expect("multiplication overflow");
Uint128::from(val)
.expect("multiplication overflow")
}

/// Multiplies two u128 values without overflow.
fn full_mul(self, rhs: impl Into<u128>) -> U256 {
U256::from(self.u128()) * U256::from(rhs.into())
pub fn full_mul(self, rhs: impl Into<u128>) -> Uint256 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new API is nice. Could you add a CHANGELOG entry and an example doc for it?

Uint256::from(self.u128())
.checked_mul(Uint256::from(rhs.into()))
.unwrap()
}
}

Expand Down Expand Up @@ -322,19 +380,6 @@ impl<'a> Sum<&'a Uint128> for Uint128 {
}
}

/// This module is purely a workaround that lets us ignore lints for all the code
/// the `construct_uint!` macro generates.
#[allow(clippy::all)]
mod uints {
uint::construct_uint! {
pub struct U256(4);
}
}

/// Only used internally - namely to store the intermediate result of
/// multiplying two 128-bit uints.
use uints::U256;

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading