From 1a64db984992e6700b3c3c558fdb2b22239d88fd Mon Sep 17 00:00:00 2001 From: Nick Furfaro Date: Tue, 28 Jun 2022 10:47:49 -0600 Subject: [PATCH] Add the U256 type (#2103) * feat: add new bare u256 module to std * feat: Add base type + from,into traits * test: setup testing for U256 * cleanup * feat: add impl U256 max, min, bits, new * test: add new test assertions * style: fmt * test: generate oracle file * Update sway-lib-std/src/u256.sw Co-authored-by: John Adler * fix: remove * import * fix: improve error handling * test: better test coverage * style: fmt * docs: add test comments * test: add more test cases for to_u64() * fix: remove #r prefix from storage lib * test: remove redundant test * refactor: rename to_u64 to as_u64 * Revert "fix: remove #r prefix from storage lib" This reverts commit 8dd0738de0fef716193b6effee9b9956822157a7. Co-authored-by: John Adler --- sway-lib-std/src/lib.sw | 1 + sway-lib-std/src/u256.sw | 90 +++++++++++++++ .../should_pass/stdlib/u256_test/.gitignore | 2 + .../should_pass/stdlib/u256_test/Forc.lock | 14 +++ .../should_pass/stdlib/u256_test/Forc.toml | 8 ++ .../stdlib/u256_test/json_abi_oracle.json | 14 +++ .../should_pass/stdlib/u256_test/src/main.sw | 103 ++++++++++++++++++ .../should_pass/stdlib/u256_test/test.toml | 3 + 8 files changed, 235 insertions(+) create mode 100644 sway-lib-std/src/u256.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/.gitignore create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/json_abi_oracle.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/test.toml diff --git a/sway-lib-std/src/lib.sw b/sway-lib-std/src/lib.sw index a7df9546283..aa54fcd82ac 100644 --- a/sway-lib-std/src/lib.sw +++ b/sway-lib-std/src/lib.sw @@ -26,6 +26,7 @@ dep reentrancy; dep vm/mod; dep flags; dep u128; +dep u256; dep vec; use core::*; diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw new file mode 100644 index 00000000000..5d9547c3deb --- /dev/null +++ b/sway-lib-std/src/u256.sw @@ -0,0 +1,90 @@ +library u256; + +use core::num::*; +use ::result::Result; + +/// The 256-bit unsigned integer type. +/// Represented as four 64-bit components: `(a, b, c, d)`, where `value = (a << 192) + (b << 128) + (c << 64) + d`. +pub struct U256 { + a: u64, + b: u64, + c: u64, + d: u64, +} + +pub enum U256Error { + LossOfPrecision: (), +} + +pub trait From { + /// Function for creating a U256 from its u64 components. + pub fn from(a: u64, b: u64, c: u64, d: u64) -> Self; +} { + /// Function for extracting 4 u64s from a U256. + fn into(val: U256) -> (u64, u64, u64, u64) { + (val.a, val.b, val.c, val.d) + } +} + +impl From for U256 { + pub fn from(a: u64, b: u64, c: u64, d: u64) -> U256 { + U256 { + a, b, c, d, + } + } +} + +impl core::ops::Eq for U256 { + /// Function for comparing 2 U256s for equality + pub fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b && self.c == other.c && self.d == other.d + } +} + +impl U256 { + /// Initializes a new, zeroed U256. + pub fn new() -> U256 { + U256 { + a: 0, + b: 0, + c: 0, + d: 0, + } + } + + /// Safely downcast to `u64` without loss of precision. + /// Returns Err if the number > ~u64::max() + pub fn as_u64(self) -> Result { + if self.a == 0 && self.b == 0 && self.c == 0 { + Result::Ok(self.d) + } else { + Result::Err(U256Error::LossOfPrecision) + } + } + + /// The smallest value that can be represented by this integer type. + pub fn min() -> U256 { + U256 { + a: 0, + b: 0, + c: 0, + d: 0, + } + } + + /// The largest value that can be represented by this type, + /// 2256 - 1. + pub fn max() -> U256 { + U256 { + a: ~u64::max(), + b: ~u64::max(), + c: ~u64::max(), + d: ~u64::max(), + } + } + + /// The size of this type in bits. + pub fn bits() -> u32 { + 256 + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/.gitignore b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.lock new file mode 100644 index 00000000000..26ae4e45e26 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-31E9CFD36AA5AC17' +dependencies = [] + +[[package]] +name = 'std' +source = 'path+from-root-31E9CFD36AA5AC17' +dependencies = ['core'] + +[[package]] +name = 'u256_test' +source = 'root' +dependencies = ['std'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.toml new file mode 100644 index 00000000000..b1faffa56f5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "u256_test" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/json_abi_oracle.json new file mode 100644 index 00000000000..f0586b752c7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "bool" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/src/main.sw new file mode 100644 index 00000000000..4cfe0acfb08 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/src/main.sw @@ -0,0 +1,103 @@ +script; + +use core::num::*; +use std::{assert::assert, result::Result, u256::{U256, U256Error}}; + +fn main() -> bool { + // test new() + let new = ~U256::new(); + let empty = U256 { + a: 0, + b: 0, + c: 0, + d: 0, + }; + assert(new == empty); + + // test from() & into() + let(l, m, n, o) = new.into(); + assert(l == 0); + assert(m == 0); + assert(n == 0); + assert(o == 0); + + let a = 11; + let b = 42; + let c = 101; + let d = 69; + let x = ~U256::from(a, b, c, d); + let y = ~U256::from(a, b, c, d); + + assert(x.a == a); + assert(x.a != b); + assert(x.b == b); + assert(x.b != c); + assert(x.c == c); + assert(x.c != d); + assert(x.d == d); + assert(x.d != a); + + let(e, f, g, h) = x.into(); + assert(e == a); + assert(f == b); + assert(g == c); + assert(h == d); + + assert(x == y); + + // test min() & max() + let max = ~U256::max(); + let min = ~U256::min(); + let(one, two, three, four) = max.into(); + assert(one == ~u64::max()); + assert(two == ~u64::max()); + assert(three == ~u64::max()); + assert(four == ~u64::max()); + + let(min_1, min_2, min_3, min_4) = min.into(); + assert(min_1 == ~u64::min()); + assert(min_2 == ~u64::min()); + assert(min_3 == ~u64::min()); + assert(min_4 == ~u64::min()); + + // test as_u64() + let err_1 = ~U256::from(42, 0, 0, 11).as_u64(); + assert(match err_1 { + Result::Err(U256Error::LossOfPrecision) => { + true + }, + _ => { + false + }, + }); + + let err_2 = ~U256::from(0, 42, 0, 11).as_u64(); + assert(match err_2 { + Result::Err(U256Error::LossOfPrecision) => { + true + }, + _ => { + false + }, + }); + + let err_3 = ~U256::from(0, 0, 42, 11).as_u64(); + assert(match err_3 { + Result::Err(U256Error::LossOfPrecision) => { + true + }, + _ => { + false + }, + }); + + + let eleven = ~U256::from(0, 0, 0, 11); + let unwrapped = eleven.as_u64().unwrap(); + assert(unwrapped == 11); + + // test bits() + assert(~U256::bits() == 256u32); + + true +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true