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