Skip to content

Commit

Permalink
U256 Subtraction fix (FuelLabs#3510)
Browse files Browse the repository at this point in the history
I found a bug in case when one of the words of the rhs `U256` values if
bigger than the corresponding words in the lhs `U256` values, but the
next to the left value of the lhs `U256` word is zero.

Co-authored-by: Toby Hutton <[email protected]>
  • Loading branch information
rostyslavtyshko and otrho authored Dec 6, 2022
1 parent 020f2e9 commit d605418
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 6 deletions.
35 changes: 29 additions & 6 deletions sway-lib-std/src/u256.sw
Original file line number Diff line number Diff line change
Expand Up @@ -356,34 +356,57 @@ impl core::ops::Add for U256 {
impl core::ops::Subtract for U256 {
/// Subtract a `U256` from a `U256`. Panics of overflow.
fn subtract(self, other: Self) -> Self {
if self == other {
return Self::min();
} else if other == Self::min() {
return self;
}
// If trying to subtract a larger number, panic.
assert(!(self < other));

assert(self > other);
let (word_1, word_2, word_3, word_4) = self.decompose();
let (other_word_1, other_word_2, other_word_3, other_word_4) = other.decompose();

let mut result_a = word_1 - other_word_1;

let mut result_b = 0;
if word_2 < other_word_2 {
result_b = u64::max() - (other_word_2 - word_2 - 1);
// we assume that result_a > 0, as in case result_a <= 0 means that lhs of the operation is smaller than rhs,
// which we ruled out at the beginning of the function.
result_a -= 1;
} else {
result_b = word_2 - other_word_2;
}

let mut result_c = 0;
if word_3 < other_word_3 {
result_c = u64::max() - (other_word_3 - word_3 - 1);
result_b -= 1;
if result_b > 0 {
result_b -= 1;
} else {
// we assume that result_a > 0, as in case result_a <= 0 means that lhs of the operation is smaller than rhs,
// which we ruled out at the beginning of the function.
result_a -= 1;
result_b = u64::max();
}
} else {
result_c = word_3 - other_word_3;
}

let mut result_d = 0;
if word_4 < other_word_4 {
result_d = u64::max() - (other_word_4 - word_4 - 1);
result_c -= 1;
if result_c > 0 {
result_c -= 1;
} else {
if result_b > 0 {
result_b -= 1;
} else {
// we assume that result_a > 0, as in case result_a <= 0 means that lhs of the operation is smaller than rhs,
// which we ruled out at the beginning of the function.
result_a -= 1;
result_b = u64::max();
}
result_c = u64::max();
}
} else {
result_d = word_4 - other_word_4;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ fn main() -> bool {
assert(sub_max_again.c == 0);
assert(sub_max_again.d == u64::max());

let zero_in_between = U256::from((0, 1, 0, 10));
let d_nonzero = U256::from((0, 0, 0, 12));

let res = zero_in_between - d_nonzero;

assert(res.c == u64::max());
assert(res.d == u64::max() - 1);

let one_upper = U256::from((0, 0, 1, 0));

let right_shift_one_upper = one_upper >> 1;
Expand Down

0 comments on commit d605418

Please sign in to comment.