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

Add increment functionality to b256 #4787

Closed
wants to merge 4 commits into from
Closed
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
43 changes: 42 additions & 1 deletion sway-lib-std/src/b256.sw
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ library;
use ::assert::assert;
use ::bytes::Bytes;
use ::convert::TryFrom;
use ::option::Option::{self, *};
use ::option::Option::{*, self};
use ::logging::log;
use ::math::*;

impl TryFrom<Bytes> for b256 {
fn try_from(b: Bytes) -> Option<Self> {
Expand All @@ -19,6 +20,25 @@ impl TryFrom<Bytes> for b256 {
}
}

impl b256 {
// Increments a b256 by a u64 amount
fn increment(ref mut self, amount: u64) {
// Decompose the b256 into 4 words
let (mut word1, mut word2, mut word3, mut word4) = asm(r1: self) { r1: (u64, u64, u64, u64) };

// Add the amount and carry the overflow to the next word
let (overflow4, result4) = word4.overflowing_add(amount);
let (overflow3, result3) = word3.overflowing_add(overflow4);
let (overflow2, result2) = word2.overflowing_add(overflow3);
let (overflow1, result1) = word1.overflowing_add(overflow2);
// If word1 overflows then we have reached past the max of a b256
assert(overflow1 == 0);

// Recompose the b256 and assign to self
self = asm(r1: (result1, result2, result3, result4)) { r1: b256 };
}
}

#[test]
fn test_b256_try_from() {
let mut initial_bytes = Bytes::with_capacity(32);
Expand Down Expand Up @@ -47,3 +67,24 @@ fn test_b256_try_from() {
assert(second_bytes.len() == 33);
assert(second_bytes.capacity() == 33);
}

#[test]
fn test_b256_increment() {
let mut val = 0x0000000000000000000000000000000000000000000000000000000000000000;

val.increment(1);
assert(val == 0x0000000000000000000000000000000000000000000000000000000000000001);

val.increment(1);
assert(val == 0x0000000000000000000000000000000000000000000000000000000000000002);

val.increment(8);
assert(val == 0x000000000000000000000000000000000000000000000000000000000000000a);

// force overflow
val.increment(u64::max());
assert(val == 0x0000000000000000000000000000000000000000000000010000000000000009);

val.increment(u64::max());
assert(val == 0x0000000000000000000000000000000000000000000000020000000000000008);
}
4 changes: 2 additions & 2 deletions sway-lib-std/src/lib.sw
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub mod contract_id;
pub mod constants;
pub mod external;
pub mod registers;
pub mod flags;
pub mod math;
pub mod call_frames;
pub mod context;
pub mod hash;
Expand All @@ -28,12 +30,10 @@ pub mod r#storage;
pub mod b256;
pub mod inputs;
pub mod auth;
pub mod math;
pub mod block;
pub mod token;
pub mod ecr;
pub mod vm;
pub mod flags;
pub mod u128;
pub mod u256;
pub mod message;
Expand Down
30 changes: 30 additions & 0 deletions sway-lib-std/src/math.sw
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Utilities for common math operations.
library;

use ::flags::{disable_panic_on_overflow, enable_panic_on_overflow};
use ::alloc::alloc;

/// Calculates the square root.
pub trait Root {
fn sqrt(self) -> Self;
Expand Down Expand Up @@ -163,3 +166,30 @@ impl BinaryLogarithm for u8 {
self.log(2u8)
}
}

impl u64 {
pub fn overflowing_add(self, right: Self) -> (Self, Self) {
// If either self or right is zero we do nothing
if self == 0 || right == 0 {
return (0, self + right);
}

disable_panic_on_overflow();
let result = alloc::<u64>(2);

asm(sum, overflow, left: self, right: right, result_ptr: result) {
// Add left and right.
add sum left right;
// Immediately copy the overflow of the addition from `$of` into
// `overflow` so that it's not lost.
move overflow of;
// Store the overflow into the first word of result.
sw result_ptr overflow i0;
// Store the sum into the second word of result.
sw result_ptr sum i1;
};
enable_panic_on_overflow();

asm(ptr: result) {ptr: (u64, u64)}
}
}
45 changes: 14 additions & 31 deletions sway-lib-std/src/u128.sw
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,6 @@ impl core::ops::Ord for U128 {
// impl core::ops::OrdEq for U128 {
// }
impl u64 {
pub fn overflowing_add(self, right: Self) -> U128 {
disable_panic_on_overflow();
let mut result = U128 {
upper: 0,
lower: 0,
};
asm(sum, overflow, left: self, right: right, result_ptr: result) {
// Add left and right.
add sum left right;
// Immediately copy the overflow of the addition from `$of` into
// `overflow` so that it's not lost.
move overflow of;
// Store the overflow into the first word of result.
sw result_ptr overflow i0;
// Store the sum into the second word of result.
sw result_ptr sum i1;
};
enable_panic_on_overflow();
result
}

pub fn overflowing_mul(self, right: Self) -> U128 {
disable_panic_on_overflow();
let mut result = U128 {
Expand Down Expand Up @@ -270,24 +249,28 @@ impl core::ops::Not for U128 {
impl core::ops::Add for U128 {
/// Add a `U128` to a `U128`. Panics on overflow.
fn add(self, other: Self) -> Self {
let mut upper_128 = self.upper.overflowing_add(other.upper);
let (overflow_upper, upper_128) = self.upper.overflowing_add(other.upper);

// If the upper overflows, then the number cannot fit in 128 bits, so panic.
assert(upper_128.upper == 0);
let lower_128 = self.lower.overflowing_add(other.lower);
assert(overflow_upper == 0);
let (overflow_lower, lower_128) = self.lower.overflowing_add(other.lower);

// If overflow has occurred in the lower component addition, carry.
// Note: carry can be at most 1.
if lower_128.upper > 0 {
upper_128 = upper_128.lower.overflowing_add(lower_128.upper);
if overflow_lower > 0 {
let (overflow_upper_2, upper_128_2) = upper_128.overflowing_add(overflow_lower);
// If overflow has occurred in the upper component addition, panic.
assert(overflow_upper_2 == 0);

return U128 {
upper: upper_128_2,
lower: lower_128,
};
}

// If overflow has occurred in the upper component addition, panic.
assert(upper_128.upper == 0);

U128 {
upper: upper_128.lower,
lower: lower_128.lower,
upper: upper_128,
lower: lower_128,
}
}
}
Expand Down
36 changes: 18 additions & 18 deletions sway-lib-std/src/u256.sw
Original file line number Diff line number Diff line change
Expand Up @@ -477,14 +477,14 @@ impl core::ops::Multiply for U256 {
let result_d_c = self.d.overflowing_mul(other.c);
let result_d_d = self.d.overflowing_mul(other.d);

let (overflow_of_c_to_b_1, mut c) = result_d_d.upper.overflowing_add(result_c_d.lower).into();
let (mut overflow_of_c_to_b_2, c) = c.overflowing_add(result_d_c.lower).into();
let (overflow_of_c_to_b_1, mut c) = result_d_d.upper.overflowing_add(result_c_d.lower);
let (mut overflow_of_c_to_b_2, c) = c.overflowing_add(result_d_c.lower);

let (overflow_of_b_to_a_0, overflow_of_c_to_b_2) = overflow_of_c_to_b_1.overflowing_add(overflow_of_c_to_b_2).into();
let (overflow_of_b_to_a_0, overflow_of_c_to_b_2) = overflow_of_c_to_b_1.overflowing_add(overflow_of_c_to_b_2);

let (overflow_of_b_to_a_1, mut b) = result_b_d.lower.overflowing_add(result_c_d.upper).into();
let (overflow_of_b_to_a_2, b) = b.overflowing_add(result_d_c.upper).into();
let (overflow_of_b_to_a_3, b) = b.overflowing_add(overflow_of_c_to_b_2).into();
let (overflow_of_b_to_a_1, mut b) = result_b_d.lower.overflowing_add(result_c_d.upper);
let (overflow_of_b_to_a_2, b) = b.overflowing_add(result_d_c.upper);
let (overflow_of_b_to_a_3, b) = b.overflowing_add(overflow_of_c_to_b_2);

U256::from((
self.b * other.c + result_b_d.upper + overflow_of_b_to_a_3 + overflow_of_b_to_a_2 + overflow_of_b_to_a_1 + overflow_of_b_to_a_0,
Expand All @@ -502,14 +502,14 @@ impl core::ops::Multiply for U256 {
let result_d_c = other.d.overflowing_mul(self.c);
let result_d_d = other.d.overflowing_mul(self.d);

let (overflow_of_c_to_b_1, mut c) = result_d_d.upper.overflowing_add(result_c_d.lower).into();
let (mut overflow_of_c_to_b_2, c) = c.overflowing_add(result_d_c.lower).into();
let (overflow_of_c_to_b_1, mut c) = result_d_d.upper.overflowing_add(result_c_d.lower);
let (mut overflow_of_c_to_b_2, c) = c.overflowing_add(result_d_c.lower);

let (overflow_of_b_to_a_0, overflow_of_c_to_b_2) = overflow_of_c_to_b_1.overflowing_add(overflow_of_c_to_b_2).into();
let (overflow_of_b_to_a_0, overflow_of_c_to_b_2) = overflow_of_c_to_b_1.overflowing_add(overflow_of_c_to_b_2);

let (overflow_of_b_to_a_1, mut b) = result_b_d.lower.overflowing_add(result_c_d.upper).into();
let (overflow_of_b_to_a_2, b) = b.overflowing_add(result_d_c.upper).into();
let (overflow_of_b_to_a_3, b) = b.overflowing_add(overflow_of_c_to_b_2).into();
let (overflow_of_b_to_a_1, mut b) = result_b_d.lower.overflowing_add(result_c_d.upper);
let (overflow_of_b_to_a_2, b) = b.overflowing_add(result_d_c.upper);
let (overflow_of_b_to_a_3, b) = b.overflowing_add(overflow_of_c_to_b_2);

U256::from((
other.b * self.c + result_b_d.upper + overflow_of_b_to_a_3 + overflow_of_b_to_a_2 + overflow_of_b_to_a_1 + overflow_of_b_to_a_0,
Expand All @@ -524,15 +524,15 @@ impl core::ops::Multiply for U256 {
let result_d_c = self.d.overflowing_mul(other.c);
let result_d_d = self.d.overflowing_mul(other.d);

let (overflow_of_c_to_b_1, mut c) = result_d_d.upper.overflowing_add(result_c_d.lower).into();
let (overflow_of_c_to_b_1, mut c) = result_d_d.upper.overflowing_add(result_c_d.lower);

let (mut overflow_of_c_to_b_2, c) = c.overflowing_add(result_d_c.lower).into();
let (mut overflow_of_c_to_b_2, c) = c.overflowing_add(result_d_c.lower);

let (overflow_of_b_to_a_0, overflow_of_c_to_b_2) = overflow_of_c_to_b_1.overflowing_add(overflow_of_c_to_b_2).into();
let (overflow_of_b_to_a_0, overflow_of_c_to_b_2) = overflow_of_c_to_b_1.overflowing_add(overflow_of_c_to_b_2);

let (overflow_of_b_to_a_1, mut b) = result_c_c.lower.overflowing_add(result_c_d.upper).into();
let (overflow_of_b_to_a_2, b) = b.overflowing_add(result_d_c.upper).into();
let (overflow_of_b_to_a_3, b) = b.overflowing_add(overflow_of_c_to_b_2).into();
let (overflow_of_b_to_a_1, mut b) = result_c_c.lower.overflowing_add(result_c_d.upper);
let (overflow_of_b_to_a_2, b) = b.overflowing_add(result_d_c.upper);
let (overflow_of_b_to_a_3, b) = b.overflowing_add(overflow_of_c_to_b_2);

U256::from((
// as overflow for a means overflow for the whole number, we are adding as is, not using `overflowing_add`
Expand Down