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

Const conversions Uint128 -> Uint256 and Uint256 -> Uint512 #1110

Merged
merged 8 commits into from
Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
8 changes: 2 additions & 6 deletions packages/std/src/math/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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"),
Expand Down
9 changes: 2 additions & 7 deletions packages/std/src/math/decimal256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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"),
Expand Down
2 changes: 1 addition & 1 deletion packages/std/src/math/uint128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
25 changes: 25 additions & 0 deletions packages/std/src/math/uint256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
])
}
Copy link
Member

Choose a reason for hiding this comment

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

Much nicer!


/// Returns a copy of the number as big endian bytes.
pub const fn to_be_bytes(self) -> [u8; 32] {
let words = [
Expand Down Expand Up @@ -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);
Expand Down
70 changes: 68 additions & 2 deletions packages/std/src/math/uint512.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down