iota_sdk_types/
u256.rs

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