sui_sdk_types/
u256.rs

1// Before we can expose this in the public interface it likely needs to be wrapped so that the type
2// from our dependency doesn't leak
3pub(crate) type U256 = bnum::BUintD8<32>;
4
5// This is a constant time assert to ensure that the backing storage for U256 is 32 bytes long
6#[allow(unused)]
7const ASSERT_32_BYTES: () = {
8    let u256 = U256::ZERO;
9
10    let _digits: &[u8; 32] = u256.digits();
11};
12
13// This is a constant time assert to ensure endianness of the underlying storage is as expected
14#[allow(unused)]
15const ASSERT_ENDIANNESS: () = {
16    const fn const_bytes_equal(lhs: &[u8], rhs: &[u8]) -> bool {
17        if lhs.len() != rhs.len() {
18            return false;
19        }
20        let mut i = 0;
21        while i < lhs.len() {
22            if lhs[i] != rhs[i] {
23                return false;
24            }
25            i += 1;
26        }
27        true
28    }
29
30    let one_platform = U256::ONE;
31    let one_le = {
32        let mut buf = [0; 32];
33        buf[0] = 1;
34        buf
35    };
36
37    let one_be = {
38        let mut buf = [0; 32];
39        buf[31] = 1;
40        buf
41    };
42
43    // To little endian
44    let le = one_platform.to_le();
45    assert!(const_bytes_equal(&one_le, le.digits().as_slice()));
46
47    // To big endian
48    let be = one_platform.to_be();
49    assert!(const_bytes_equal(&one_be, be.digits().as_slice()));
50
51    // From little endian
52    assert!(const_bytes_equal(
53        one_platform.digits().as_slice(),
54        U256::from_le(U256::from_digits(one_le)).digits().as_slice()
55    ));
56
57    // From big endian
58    assert!(const_bytes_equal(
59        one_platform.digits().as_slice(),
60        U256::from_be(U256::from_digits(one_be)).digits().as_slice()
61    ));
62};
63
64#[cfg(test)]
65mod test {
66    use super::*;
67    use num_bigint::BigUint;
68    use proptest::prelude::*;
69    use std::str::FromStr;
70    use test_strategy::proptest;
71
72    #[cfg(target_arch = "wasm32")]
73    use wasm_bindgen_test::wasm_bindgen_test as test;
74
75    #[test]
76    fn endianness() {
77        let one_platform = U256::ONE;
78        let one_le = {
79            let mut buf = [0; 32];
80            buf[0] = 1;
81            buf
82        };
83
84        let one_be = {
85            let mut buf = [0; 32];
86            buf[31] = 1;
87            buf
88        };
89
90        // To little endian
91        let le = one_platform.to_le();
92        assert_eq!(one_le, *le.digits());
93
94        // To big endian
95        let be = one_platform.to_be();
96        assert_eq!(one_be, *be.digits());
97
98        // From little endian
99        assert_eq!(one_platform, U256::from_le(U256::from_digits(one_le)));
100        // From big endian
101        assert_eq!(one_platform, U256::from_be(U256::from_digits(one_be)));
102    }
103
104    #[proptest]
105    fn dont_crash_on_large_inputs(
106        #[strategy(proptest::collection::vec(any::<u8>(), 33..1024))] bytes: Vec<u8>,
107    ) {
108        let big_int = BigUint::from_bytes_be(&bytes);
109        let radix10 = big_int.to_str_radix(10);
110
111        // doesn't crash
112        let _ = U256::from_str_radix(&radix10, 10);
113    }
114
115    #[proptest]
116    fn valid_u256_strings(
117        #[strategy(proptest::collection::vec(any::<u8>(), 1..=32))] bytes: Vec<u8>,
118    ) {
119        let big_int = BigUint::from_bytes_be(&bytes);
120        let radix10 = big_int.to_str_radix(10);
121
122        let u256 = U256::from_str_radix(&radix10, 10).unwrap();
123
124        assert_eq!(radix10, u256.to_str_radix(10));
125
126        let from_str = U256::from_str(&radix10).unwrap();
127        assert_eq!(from_str, u256);
128        assert_eq!(radix10, from_str.to_string());
129    }
130}