diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c74e9d1b3..20639693a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to the contract call was executed in. - cosmwasm-std: Implement `ops::Mul` for `Decimal` and `Decimal256`. - cosmwasm-std: New const methods `Uint128::to_be_bytes`/`::to_le_bytes`. +- cosmwasm-std: New const conversion methods `Uint256::from_uint128` and + `Uint512::from_uint256`. ### Changed @@ -34,6 +36,7 @@ and this project adheres to such that `Decimal::numerator` and `::denominator` now return `Uint128`. - cosmwasm-std: Make methods `Uint256::to_be_bytes`/`::to_le_bytes` const. - cosmwasm-std: Make methods `Uint512::to_be_bytes`/`::to_le_bytes` const. +- cosmwasm-std: Make method `Uint512::from_le_bytes` const. ### Removed diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index e851d3711b..6cc197a36f 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -21,10 +21,6 @@ impl Decimal { const DECIMAL_FRACTIONAL: Uint128 = Uint128::new(1_000_000_000_000_000_000u128); // 1*10**18 const DECIMAL_FRACTIONAL_SQUARED: Uint128 = Uint128::new(1_000_000_000_000_000_000_000_000_000_000_000_000u128); // (1*10**18)**2 = 1*10**36 - const DECIMAL_FRACTIONAL_UINT256: Uint256 = Uint256::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 224, 182, 179, - 167, 100, 0, 0, - ]); // Python: `[b for b in (1*10**18).to_bytes(32, "big")]` const DECIMAL_PLACES: usize = 18; // This needs to be an even number. pub const MAX: Self = Self(Uint128::MAX); @@ -220,8 +216,8 @@ impl ops::Mul for Decimal { // (a.numerator() * b.numerator()) / (a.denominator() * b.denominator()) // = (a.numerator() * b.numerator()) / a.denominator() / b.denominator() - let result_as_uint256 = - self.numerator().full_mul(other.numerator()) / Self::DECIMAL_FRACTIONAL_UINT256; + let result_as_uint256 = self.numerator().full_mul(other.numerator()) + / Uint256::from_uint128(Self::DECIMAL_FRACTIONAL); // from_uint128 is a const method and should be "free" match result_as_uint256.try_into() { Ok(result) => Self(result), Err(_) => panic!("attempt to multiply with overflow"), diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index 07d6dac880..4203e7d124 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -32,11 +32,6 @@ impl Decimal256 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 151, 206, 123, 201, 7, 21, 179, 75, 159, 16, 0, 0, 0, 0, ]); - const DECIMAL_FRACTIONAL_UINT512: Uint512 = Uint512::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 224, 182, - 179, 167, 100, 0, 0, - ]); // Python: `[b for b in (1*10**18).to_bytes(32, "big")]` pub const MAX: Self = Self(Uint256::MAX); @@ -231,8 +226,8 @@ impl ops::Mul for Decimal256 { // (a.numerator() * b.numerator()) / (a.denominator() * b.denominator()) // = (a.numerator() * b.numerator()) / a.denominator() / b.denominator() - let result_as_uint512 = - self.numerator().full_mul(other.numerator()) / Self::DECIMAL_FRACTIONAL_UINT512; + let result_as_uint512 = self.numerator().full_mul(other.numerator()) + / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL); // from_uint256 is a const method and should be "free" match result_as_uint512.try_into() { Ok(result) => Self(result), Err(_) => panic!("attempt to multiply with overflow"), diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index 933ea574de..d60662abee 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -47,7 +47,7 @@ impl Uint128 { } /// Returns a copy of the internal data - pub fn u128(&self) -> u128 { + pub const fn u128(&self) -> u128 { self.0 } diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index 96dbfc68b4..cd422bc28f 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -99,6 +99,18 @@ impl Uint256 { Uint256(U256(words)) } + /// A conversion from `Uint128` that, unlike the one provided by the `From` trait, + /// can be used in a `const` context. + pub const fn from_uint128(num: Uint128) -> Self { + let bytes = num.to_le_bytes(); + + Self::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + } + /// Returns a copy of the number as big endian bytes. pub const fn to_be_bytes(self) -> [u8; 32] { let words = [ @@ -1006,6 +1018,19 @@ mod tests { ); } + #[test] + fn uint256_from_uint128() { + assert_eq!( + Uint256::from_uint128(Uint128::new(123)), + Uint256::from_str("123").unwrap() + ); + + assert_eq!( + Uint256::from_uint128(Uint128::new(9785746283745)), + Uint256::from_str("9785746283745").unwrap() + ); + } + #[test] fn uint256_implements_display() { let a = Uint256::from(12345u32); diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index 0b159337c7..1329989bb6 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -95,8 +95,48 @@ impl Uint512 { Self(U512(words)) } - pub fn from_le_bytes(value: [u8; 64]) -> Self { - Uint512(U512::from_little_endian(&value)) + pub const fn from_le_bytes(data: [u8; 64]) -> Self { + let words: [u64; 8] = [ + u64::from_le_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]), + u64::from_le_bytes([ + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], + ]), + u64::from_le_bytes([ + data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23], + ]), + u64::from_le_bytes([ + data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], + ]), + u64::from_le_bytes([ + data[32], data[33], data[34], data[35], data[36], data[37], data[38], data[39], + ]), + u64::from_le_bytes([ + data[40], data[41], data[42], data[43], data[44], data[45], data[46], data[47], + ]), + u64::from_le_bytes([ + data[48], data[49], data[50], data[51], data[52], data[53], data[54], data[55], + ]), + u64::from_le_bytes([ + data[56], data[57], data[58], data[59], data[60], data[61], data[62], data[63], + ]), + ]; + Self(U512(words)) + } + + /// A conversion from `Uint256` that, unlike the one provided by the `From` trait, + /// can be used in a `const` context. + pub const fn from_uint256(num: Uint256) -> Self { + let bytes = num.to_le_bytes(); + Self::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], + bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23], + bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31], + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]) } /// Returns a copy of the number as big endian bytes. @@ -719,6 +759,32 @@ mod tests { ); } + #[test] + fn uint512_from_uint256() { + assert_eq!( + Uint512::from_uint256(Uint256::from_str("123").unwrap()), + Uint512::from_str("123").unwrap() + ); + + assert_eq!( + Uint512::from_uint256(Uint256::from_str("9785746283745").unwrap()), + Uint512::from_str("9785746283745").unwrap() + ); + + assert_eq!( + Uint512::from_uint256( + Uint256::from_str( + "97857462837575757832978493758398593853985452378423874623874628736482736487236" + ) + .unwrap() + ), + Uint512::from_str( + "97857462837575757832978493758398593853985452378423874623874628736482736487236" + ) + .unwrap() + ); + } + #[test] fn uint512_implements_display() { let a = Uint512::from(12345u32);