Skip to main content

walletkit_core/
u256.rs

1use std::ops::Deref;
2
3use crate::error::WalletKitError;
4use ruint::aliases::U256;
5use serde::{Deserialize, Serialize};
6
7/// A wrapper around `U256` to represent a field element in the protocol. Wrapper enables FFI interoperability.
8///
9/// Most inputs and outputs from the zero-knowledge proofs are `U256` values.
10/// While using `U256` directly is convenient and recommended when working with the proofs, particularly in Rust,
11/// it is not a user-friendly type for interactions or communications in other languages / systems.
12///
13/// Particularly, when sending proof inputs/outputs as JSON on HTTP requests, the values SHOULD
14/// be represented as padded hex strings from Big Endian bytes.
15#[allow(clippy::module_name_repetitions)]
16#[derive(Debug, PartialEq, Eq, Clone, Copy, uniffi::Object)]
17pub struct U256Wrapper(pub U256);
18
19#[uniffi::export]
20impl U256Wrapper {
21    /// Outputs a hex string representation of the `U256` value padded to 32 bytes (plus two bytes for the `0x` prefix).
22    #[must_use]
23    pub fn to_hex_string(&self) -> String {
24        format!("{:#066x}", self.0)
25    }
26
27    /// Outputs the decimal string representation of the `U256` value.
28    #[must_use]
29    pub fn to_decimal_string(&self) -> String {
30        self.0.to_string()
31    }
32
33    /// Attempts to parse a hex string as a `U256` value (wrapped).
34    ///
35    /// # Errors
36    /// Will return an `Error::InvalidNumber` if the input is not a valid hex-string-presented number up to 256 bits.
37    #[uniffi::constructor]
38    pub fn try_from_hex_string(hex_string: &str) -> Result<Self, WalletKitError> {
39        let hex_string = hex_string.trim().trim_start_matches("0x");
40
41        let number = U256::from_str_radix(hex_string, 16)
42            .map_err(|_| WalletKitError::InvalidNumber)?;
43
44        Ok(Self(number))
45    }
46
47    /// Creates a `U256` value from a `u64` value.
48    ///
49    /// Logically this will only support values up to 64 bits. For larger values a different initialization should be used.
50    #[must_use]
51    #[uniffi::constructor]
52    pub fn from_u64(value: u64) -> Self {
53        Self(U256::from(value))
54    }
55
56    /// Creates a `U256` value from a `u32` value.
57    ///
58    /// Logically this will only support values up to 32 bits. For larger values a different initialization should be used.
59    #[must_use]
60    #[uniffi::constructor]
61    pub fn from_u32(value: u32) -> Self {
62        Self(U256::from(value))
63    }
64
65    /// Creates a `U256` value from an array of 4 `u64` values (little-endian). Least significant limb first.
66    ///
67    /// This is the same as the `U256::from_limbs` method, but exposed to foreign bindings.
68    ///
69    /// # Errors
70    ///
71    /// Will return an `Error::InvalidNumber` if the input is not a valid `U256` value.
72    #[uniffi::constructor]
73    pub fn from_limbs(limbs: Vec<u64>) -> Result<Self, WalletKitError> {
74        let limbs = limbs
75            .try_into()
76            .map_err(|_| WalletKitError::InvalidNumber)?;
77
78        Ok(Self(U256::from_limbs(limbs)))
79    }
80
81    /// Converts the `U256` value into a vector of 4 `u64` values (little-endian). Least significant limb first.
82    ///
83    /// Using a vector as an array cannot be lowered to foreign bindings.
84    #[must_use]
85    pub fn into_limbs(self) -> Vec<u64> {
86        self.0.into_limbs().to_vec()
87    }
88}
89
90impl From<U256Wrapper> for U256 {
91    fn from(val: U256Wrapper) -> Self {
92        val.0
93    }
94}
95
96impl From<U256> for U256Wrapper {
97    fn from(val: U256) -> Self {
98        Self(val)
99    }
100}
101
102impl std::fmt::Display for U256Wrapper {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        write!(f, "{}", self.to_hex_string())
105    }
106}
107
108impl Deref for U256Wrapper {
109    type Target = U256;
110
111    fn deref(&self) -> &Self::Target {
112        &self.0
113    }
114}
115
116impl Serialize for U256Wrapper {
117    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118    where
119        S: serde::Serializer,
120    {
121        serializer.serialize_str(&self.to_hex_string())
122    }
123}
124
125impl<'de> Deserialize<'de> for U256Wrapper {
126    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
127    where
128        D: serde::Deserializer<'de>,
129    {
130        let s = String::deserialize(deserializer)?;
131        Self::try_from_hex_string(&s).map_err(serde::de::Error::custom)
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    #![allow(clippy::unreadable_literal)]
138
139    use super::*;
140    use ruint::uint;
141
142    #[test]
143    fn test_to_hex_string_for_u256() {
144        assert_eq!(
145            U256Wrapper(U256::from(1)).to_hex_string(),
146            "0x0000000000000000000000000000000000000000000000000000000000000001"
147        );
148        assert_eq!(
149            U256Wrapper(U256::from(42)).to_hex_string(),
150            "0x000000000000000000000000000000000000000000000000000000000000002a"
151        );
152
153        assert_eq!(
154            U256Wrapper(uint!(999999_U256)).to_hex_string(),
155            "0x00000000000000000000000000000000000000000000000000000000000f423f"
156        );
157
158        assert_eq!(
159            U256Wrapper(uint!(
160                80084422859880547211683076133703299733277748156566366325829078699459944778998_U256
161            ))
162            .to_hex_string(),
163            "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"
164        );
165
166        assert_eq!(
167            U256Wrapper(uint!(
168                0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0_U256
169            ))
170            .to_hex_string(),
171            "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0"
172        );
173    }
174
175    #[test]
176    fn test_from_hex_string_for_u256() {
177        assert_eq!(
178            U256Wrapper::try_from_hex_string(
179                "0x0000000000000000000000000000000000000000000000000000000000000001"
180            )
181            .unwrap(),
182            U256Wrapper(U256::from(1))
183        );
184        assert_eq!(
185            U256Wrapper::try_from_hex_string(
186                "0x000000000000000000000000000000000000000000000000000000000000002a"
187            )
188            .unwrap(),
189            U256Wrapper(U256::from(42))
190        );
191
192        assert_eq!(
193            U256Wrapper::try_from_hex_string(
194                "0x00000000000000000000000000000000000000000000000000000000000f423f"
195            )
196            .unwrap(),
197            U256Wrapper(uint!(999999_U256))
198        );
199
200        assert_eq!(
201            U256Wrapper::try_from_hex_string("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6").unwrap(),
202            U256Wrapper(uint!(
203                80084422859880547211683076133703299733277748156566366325829078699459944778998_U256
204            ))
205        );
206
207        assert_eq!(
208            U256Wrapper::try_from_hex_string(
209                "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0"
210            )
211            .unwrap()
212            .0,
213            uint!(
214                0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0_U256
215            )
216        );
217    }
218
219    #[test]
220    fn test_invalid_hex_string() {
221        assert!(U256Wrapper::try_from_hex_string("0xZZZZ").is_err());
222        assert!(U256Wrapper::try_from_hex_string("1g").is_err());
223        assert!(U256Wrapper::try_from_hex_string("not a hex string").is_err());
224    }
225
226    #[test]
227    fn test_json_serializing() {
228        let number = U256Wrapper(uint!(
229            0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0_U256
230        ));
231
232        let json = serde_json::to_string(&number).unwrap();
233        assert_eq!(
234            json,
235            "\"0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0\""
236        );
237    }
238
239    #[test]
240    fn test_from_u64() {
241        assert_eq!(U256Wrapper::from_u64(1).0, uint!(1_U256));
242        assert_eq!(U256Wrapper::from_u64(42).0, uint!(42_U256));
243        assert_eq!(U256Wrapper::from_u64(999_999).0, uint!(999_999_U256));
244
245        assert_eq!(
246            U256Wrapper::from_u64(2).to_hex_string(),
247            "0x0000000000000000000000000000000000000000000000000000000000000002"
248        );
249        assert_eq!(
250            U256Wrapper::from_u64(u64::MAX).to_hex_string(),
251            "0x000000000000000000000000000000000000000000000000ffffffffffffffff"
252        );
253    }
254
255    #[test]
256    fn test_from_u32() {
257        assert_eq!(U256Wrapper::from_u32(1).0, U256::from(1));
258        assert_eq!(U256Wrapper::from_u32(42).0, U256::from(42));
259        assert_eq!(U256Wrapper::from_u32(999999).0, U256::from(999999));
260    }
261
262    #[test]
263    fn test_from_limbs() {
264        assert_eq!(
265            U256Wrapper::from_limbs(vec![1, 0, 0, 0]).unwrap().0,
266            U256::from(1)
267        );
268
269        assert_eq!(
270            U256Wrapper::from_limbs(vec![0, 0, 0, 2161727821137838080])
271                .unwrap()
272                .to_hex_string(),
273            "0x1e00000000000000000000000000000000000000000000000000000000000000"
274        );
275
276        assert_eq!(
277            U256Wrapper::from_limbs(vec![1, 0, 0, 2161727821137838080])
278                .unwrap()
279                .to_hex_string(),
280            "0x1e00000000000000000000000000000000000000000000000000000000000001"
281        );
282    }
283
284    #[test]
285    fn test_from_limbs_invalid_length() {
286        assert!(U256Wrapper::from_limbs(vec![1, 0, 0]).is_err()); // must be padded
287        assert!(U256Wrapper::from_limbs(vec![1, 0, 0, 0, 5]).is_err());
288        assert!(U256Wrapper::from_limbs(vec![1, 0, 0, 0, 0]).is_err());
289    }
290}