diff --git a/Cargo.lock b/Cargo.lock index 4a2d55f3..ef0455c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -219,12 +219,27 @@ dependencies = [ "shlex", ] +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec 0.6.3", +] + [[package]] name = "bit-vec" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -390,6 +405,16 @@ dependencies = [ "serde", ] +[[package]] +name = "c-uint256-tests" +version = "0.1.0" +dependencies = [ + "cc", + "cty", + "primitive-types", + "proptest", +] + [[package]] name = "cache-padded" version = "1.2.0" @@ -750,7 +775,7 @@ version = "0.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "048653106f001c8e0c4d26853d187fd9216d18ee64318dc7a0cda8676f95a31f" dependencies = [ - "bit-vec", + "bit-vec 0.5.1", "bitflags", "bytes 1.1.0", "ckb-channel", @@ -3494,6 +3519,26 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "proptest" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "quick-error 2.0.1", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_xorshift 0.3.0", + "regex-syntax", + "rusty-fork", + "tempfile", +] + [[package]] name = "prost" version = "0.9.0" @@ -3547,6 +3592,18 @@ dependencies = [ "prost", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.22.0" @@ -3586,7 +3643,7 @@ dependencies = [ "rand_jitter", "rand_os", "rand_pcg 0.1.2", - "rand_xorshift", + "rand_xorshift 0.1.1", "winapi 0.3.9", ] @@ -3758,6 +3815,15 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.3", +] + [[package]] name = "rayon" version = "1.5.1" @@ -3982,6 +4048,18 @@ dependencies = [ "semver", ] +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.9" @@ -5242,6 +5320,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.3.2" diff --git a/Cargo.toml b/Cargo.toml index 3f2f6eab..27ead1c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] members = [ "tests", + "c-uint256-tests", ] diff --git a/c-uint256-tests/Cargo.toml b/c-uint256-tests/Cargo.toml new file mode 100644 index 00000000..f2b5bad6 --- /dev/null +++ b/c-uint256-tests/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "c-uint256-tests" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cty = "0.2" + +[dev-dependencies] +proptest = "1.0" +primitive-types = { version = "0.10", default-features = false, features = [ "impl-serde", "impl-rlp" ] } + +[build-dependencies] +cc = "1.0" diff --git a/c-uint256-tests/build.rs b/c-uint256-tests/build.rs new file mode 100644 index 00000000..bd5faa32 --- /dev/null +++ b/c-uint256-tests/build.rs @@ -0,0 +1,20 @@ +fn main() { + let mut build = cc::Build::new(); + + // building + build + .file("../c/rust-binding/uint256_wrapper.c") + .static_flag(true) + .flag("-O3") + .flag("-Wl,-static") + .flag("-Wl,--gc-sections") + .include("../c/") + .flag("-Wall") + .flag("-Werror") + .flag("-Wno-unused-parameter") + .flag("-Wno-nonnull") + .flag("-Wno-nonnull-compare") + .flag("-Wno-unused-function") + .define("__SHARED_LIBRARY__", None) + .compile("c-uint256.a"); +} diff --git a/c-uint256-tests/src/bindings.rs b/c-uint256-tests/src/bindings.rs new file mode 100644 index 00000000..68fd6f90 --- /dev/null +++ b/c-uint256-tests/src/bindings.rs @@ -0,0 +1,168 @@ +/* automatically generated by rust-bindgen 0.59.2 */ + +pub const true_: u32 = 1; +pub const false_: u32 = 0; +pub const INT8_MIN: i32 = -128; +pub const INT16_MIN: i32 = -32768; +pub const INT32_MIN: i32 = -2147483648; +pub const INT64_MIN: i64 = -9223372036854775808; +pub const INT8_MAX: u32 = 127; +pub const INT16_MAX: u32 = 32767; +pub const INT32_MAX: u32 = 2147483647; +pub const INT64_MAX: u64 = 9223372036854775807; +pub const UINT8_MAX: u32 = 255; +pub const UINT16_MAX: u32 = 65535; +pub const UINT32_MAX: u32 = 4294967295; +pub const UINT64_MAX: i32 = -1; +pub const SIZE_MAX: i32 = -1; +pub type size_t = ::std::os::raw::c_ulong; +pub type ssize_t = ::std::os::raw::c_long; +extern "C" { + pub fn memset( + dest: *mut ::std::os::raw::c_void, + c: ::std::os::raw::c_int, + n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn memcpy( + dest: *mut ::std::os::raw::c_void, + src: *const ::std::os::raw::c_void, + n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn memcmp( + vl: *const ::std::os::raw::c_void, + vr: *const ::std::os::raw::c_void, + n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +pub type WT = size_t; +extern "C" { + pub fn memmove( + dest: *mut ::std::os::raw::c_void, + src: *const ::std::os::raw::c_void, + n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn strcpy( + d: *mut ::std::os::raw::c_char, + s: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strlen(s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_ulong; +} +extern "C" { + pub fn strcmp( + l: *const ::std::os::raw::c_char, + r: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn malloc(size: ::std::os::raw::c_ulong) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn free(ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn calloc( + nmemb: ::std::os::raw::c_ulong, + size: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn realloc( + ptr: *mut ::std::os::raw::c_void, + size: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +pub type cmpfun = ::std::option::Option< + unsafe extern "C" fn( + arg1: *const ::std::os::raw::c_void, + arg2: *const ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, +>; +extern "C" { + pub fn qsort(base: *mut ::std::os::raw::c_void, nel: size_t, width: size_t, cmp: cmpfun); +} +extern "C" { + pub fn bsearch( + key: *const ::std::os::raw::c_void, + base: *const ::std::os::raw::c_void, + nel: size_t, + width: size_t, + cmp: ::std::option::Option< + unsafe extern "C" fn( + arg1: *const ::std::os::raw::c_void, + arg2: *const ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn printf(format: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn _start(); +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct uint256_t { + pub array: [u32; 8usize], +} +#[test] +fn bindgen_test_layout_uint256_t() { + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(uint256_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(uint256_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).array as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(uint256_t), + "::", + stringify!(array) + ) + ); +} +extern "C" { + pub fn gw_uint256_zero(num: *mut uint256_t); +} +extern "C" { + pub fn gw_uint256_one(num: *mut uint256_t); +} +extern "C" { + pub fn gw_uint256_max(num: *mut uint256_t); +} +extern "C" { + pub fn gw_uint256_overflow_add( + a: uint256_t, + b: uint256_t, + sum: *mut uint256_t, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gw_uint256_underflow_sub( + a: uint256_t, + b: uint256_t, + rem: *mut uint256_t, + ) -> ::std::os::raw::c_int; +} +pub const GW_UINT256_SMALLER: ::std::os::raw::c_int = -1; +pub const GW_UINT256_EQUAL: ::std::os::raw::c_int = 0; +pub const GW_UINT256_LARGER: ::std::os::raw::c_int = 1; +pub type _bindgen_ty_1 = ::std::os::raw::c_int; +extern "C" { + pub fn gw_uint256_cmp(a: uint256_t, b: uint256_t) -> ::std::os::raw::c_int; +} diff --git a/c-uint256-tests/src/lib.rs b/c-uint256-tests/src/lib.rs new file mode 100644 index 00000000..3b25765b --- /dev/null +++ b/c-uint256-tests/src/lib.rs @@ -0,0 +1,162 @@ +use std::cmp::Ordering; + +use self::bindings::{ + gw_uint256_cmp, gw_uint256_one, gw_uint256_overflow_add, gw_uint256_underflow_sub, uint256_t, + GW_UINT256_EQUAL, GW_UINT256_LARGER, GW_UINT256_SMALLER, +}; + +// deref_nullptr in test code `fn bindgen_test_layout_uint256_t()`. +#[allow(dead_code)] +#[allow(non_camel_case_types)] +#[allow(non_upper_case_globals)] +#[allow(deref_nullptr)] +mod bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct U256(uint256_t); + +impl U256 { + pub fn from_le_bytes(bytes: [u8; 32]) -> Self { + let mut array = [0u32; 8]; + for i in 0..8 { + let start = i * 4; + let end = i * 4 + 4; + let u32_val = u32::from_le_bytes(bytes[start..end].try_into().unwrap()); + array[i] = u32_val; + } + U256(uint256_t { array }) + } + + pub fn to_le_bytes(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + for i in 0..8 { + let u32_val = self.0.array[i]; + let start = i * 4; + let end = i * 4 + 4; + bytes[start..end].copy_from_slice(&u32_val.to_le_bytes()); + } + + bytes + } + + pub fn zero() -> Self { + U256(uint256_t { array: [0u32; 8] }) + } + + pub fn one() -> Self { + let mut val = Self::zero(); + unsafe { gw_uint256_one(&mut val.0) }; + + val + } + + pub fn checked_add(&self, other: U256) -> Option { + let mut sum = U256::zero(); + match unsafe { gw_uint256_overflow_add(self.0, other.0, &mut sum.0) } { + 0 => Some(sum), + _err => None, + } + } + + pub fn checked_sub(&self, other: U256) -> Option { + let mut rem = U256::zero(); + match unsafe { gw_uint256_underflow_sub(self.0, other.0, &mut rem.0) } { + 0 => Some(rem), + _err => None, + } + } +} + +impl PartialOrd for U256 { + fn partial_cmp(&self, other: &Self) -> Option { + match unsafe { gw_uint256_cmp(self.0, other.0) } { + GW_UINT256_SMALLER => Some(Ordering::Less), + GW_UINT256_EQUAL => Some(Ordering::Equal), + GW_UINT256_LARGER => Some(Ordering::Greater), + _ => None, + } + } +} + +impl Ord for U256 { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::U256 as CU256; + use primitive_types::U256 as PU256; + use proptest::prelude::*; + + impl PartialEq for CU256 { + fn eq(&self, other: &PU256) -> bool { + let mut other_le_bytes = [0u8; 32]; + other.to_little_endian(&mut other_le_bytes); + + self.to_le_bytes() == other_le_bytes + } + } + + impl CU256 { + fn into_pu256(self) -> PU256 { + PU256::from_little_endian(&self.to_le_bytes()) + } + } + + #[test] + fn test_c_uint256_one() { + let one = CU256::one(); + assert_eq!(one.checked_sub(one), Some(CU256::zero())); + + let p_one = PU256::one(); + assert_eq!(one, p_one); + } + + proptest! { + #[test] + fn test_c_uint256_checked_add( + a in prop::array::uniform32(any::()), + b in prop::array::uniform32(any::()) + ) { + let ca = CU256::from_le_bytes(a); + let cb = CU256::from_le_bytes(b); + let csum = ca.checked_add(cb); + + let pa = PU256::from_little_endian(&a); + let pb = PU256::from_little_endian(&b); + let psum = pa.checked_add(pb); + prop_assert_eq!(csum.map(CU256::into_pu256), psum); + } + + #[test] + fn test_c_uint256_checked_sub( + a in prop::array::uniform32(any::()), + b in prop::array::uniform32(any::()) + ) { + let ca = CU256::from_le_bytes(a); + let cb = CU256::from_le_bytes(b); + let crem = ca.checked_sub(cb); + + let pa = PU256::from_little_endian(&a); + let pb = PU256::from_little_endian(&b); + let prem = pa.checked_sub(pb); + prop_assert_eq!(crem.map(CU256::into_pu256), prem); + } + + #[test] + fn test_c_uint256_cmp( + a in prop::array::uniform32(any::()), + b in prop::array::uniform32(any::()) + ) { + let ca = CU256::from_le_bytes(a); + let cb = CU256::from_le_bytes(b); + + let pa = PU256::from_little_endian(&a); + let pb = PU256::from_little_endian(&b); + + prop_assert_eq!(ca > cb, pa > pb); + } + } +} diff --git a/c/Makefile b/c/Makefile index 8c7115df..d42b1c94 100644 --- a/c/Makefile +++ b/c/Makefile @@ -153,3 +153,10 @@ bindgen: rust-binding/smt_wrapper.h --allowlist-type ^smt_.+ \ --allowlist-function ^smt_.+ \ -- ${CINCLUDES} + +bindgen_uint256: rust-binding/uint256_wrapper.h + bindgen rust-binding/uint256_wrapper.h \ + --with-derive-partialeq \ + --with-derive-eq \ + -o ../c-uint256-tests/src/bindings.rs \ + -- ${CINCLUDES} diff --git a/c/rust-binding/uint256_wrapper.c b/c/rust-binding/uint256_wrapper.c new file mode 100644 index 00000000..f839bacb --- /dev/null +++ b/c/rust-binding/uint256_wrapper.c @@ -0,0 +1 @@ +#include "uint256_wrapper.h" diff --git a/c/rust-binding/uint256_wrapper.h b/c/rust-binding/uint256_wrapper.h new file mode 100644 index 00000000..de6453a8 --- /dev/null +++ b/c/rust-binding/uint256_wrapper.h @@ -0,0 +1,3 @@ +#include + +#include "uint256.h"