Skip to main content

mina_p2p_messages/
bigint.rs

1use ark_ff::{BigInteger256, PrimeField};
2use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
3use malloc_size_of::MallocSizeOf;
4use rsexp::{OfSexp, SexpOf};
5use serde::{Deserialize, Serialize};
6
7// ---
8// This has been imported from a fork of arkworks/ff
9// We should probably revisit this structure in the future
10#[derive(Clone, Debug)]
11pub struct InvalidBigInt;
12
13impl core::fmt::Display for InvalidBigInt {
14    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
15        write!(f, "InvalidBigInt")
16    }
17}
18
19impl From<InvalidBigInt> for String {
20    fn from(_: InvalidBigInt) -> Self {
21        "InvalidBigInt".to_string()
22    }
23}
24
25impl std::error::Error for InvalidBigInt {}
26// ---
27
28#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, derive_more::From, derive_more::Into)]
29pub struct BigInt(BigInteger256);
30
31impl std::fmt::Debug for BigInt {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        let Self(bigint) = self;
34        // Avoid vertical alignment
35        f.write_fmt(format_args!("BigInt({:?})", bigint.0))
36    }
37}
38
39impl MallocSizeOf for BigInt {
40    fn size_of(&self, _ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
41        0
42    }
43}
44
45#[derive(Debug, thiserror::Error)]
46#[error("Invalid decimal number")]
47pub struct InvalidDecimalNumber;
48
49impl BigInt {
50    pub fn zero() -> Self {
51        mina_curves::pasta::Fp::from(0u64).into()
52    }
53
54    pub fn one() -> Self {
55        mina_curves::pasta::Fp::from(1u64).into()
56    }
57
58    pub fn to_field<F>(&self) -> Result<F, InvalidBigInt>
59    where
60        F: ark_ff::Field + From<BigInteger256>,
61    {
62        let Self(biginteger) = self;
63        Ok(F::from(*biginteger))
64    }
65
66    pub fn to_bytes(&self) -> [u8; 32] {
67        let mut bytes = Vec::with_capacity(32);
68        self.0.serialize_uncompressed(&mut bytes).unwrap(); // Never fail, there is 32 bytes
69        bytes.try_into().unwrap()
70    }
71
72    pub fn from_bytes(bytes: [u8; 32]) -> Self {
73        let value = BigInteger256::deserialize_uncompressed(&bytes[..]).expect("Don't fail");
74        Self(value) // Never fail, we read from 32 bytes
75    }
76
77    pub fn from_decimal(s: &str) -> Result<Self, InvalidDecimalNumber> {
78        num_bigint::BigInt::<4>::parse_bytes(s.as_bytes(), 10)
79            .map(|num| {
80                let mut bytes = num.to_bytes_be().1;
81                bytes.reverse();
82                bytes.resize(32, 0); // Ensure the byte vector has 32 bytes
83                BigInt::from_bytes(bytes.try_into().unwrap())
84            })
85            .ok_or(InvalidDecimalNumber)
86    }
87
88    pub fn to_decimal(&self) -> String {
89        let bigint: num_bigint::BigUint = self.0.into();
90        bigint.to_string()
91    }
92}
93
94impl AsRef<BigInteger256> for BigInt {
95    fn as_ref(&self) -> &BigInteger256 {
96        let Self(biginteger) = self;
97        biginteger
98    }
99}
100
101impl From<mina_curves::pasta::Fp> for BigInt {
102    fn from(field: mina_curves::pasta::Fp) -> Self {
103        Self(field.into_bigint())
104    }
105}
106
107impl From<mina_curves::pasta::Fq> for BigInt {
108    fn from(field: mina_curves::pasta::Fq) -> Self {
109        Self(field.into_bigint())
110    }
111}
112
113impl From<&mina_curves::pasta::Fp> for BigInt {
114    fn from(field: &mina_curves::pasta::Fp) -> Self {
115        Self(field.into_bigint())
116    }
117}
118
119impl From<&mina_curves::pasta::Fq> for BigInt {
120    fn from(field: &mina_curves::pasta::Fq) -> Self {
121        Self(field.into_bigint())
122    }
123}
124
125impl TryFrom<BigInt> for mina_curves::pasta::Fp {
126    type Error = InvalidBigInt;
127
128    fn try_from(bigint: BigInt) -> Result<Self, Self::Error> {
129        bigint.to_field()
130    }
131}
132
133impl TryFrom<BigInt> for mina_curves::pasta::Fq {
134    type Error = InvalidBigInt;
135
136    fn try_from(bigint: BigInt) -> Result<Self, Self::Error> {
137        bigint.to_field()
138    }
139}
140
141impl TryFrom<&BigInt> for mina_curves::pasta::Fp {
142    type Error = InvalidBigInt;
143
144    fn try_from(bigint: &BigInt) -> Result<Self, Self::Error> {
145        bigint.to_field()
146    }
147}
148
149impl TryFrom<&BigInt> for mina_curves::pasta::Fq {
150    type Error = InvalidBigInt;
151
152    fn try_from(bigint: &BigInt) -> Result<Self, Self::Error> {
153        bigint.to_field()
154    }
155}
156
157impl OfSexp for BigInt {
158    fn of_sexp(s: &rsexp::Sexp) -> Result<Self, rsexp::IntoSexpError>
159    where
160        Self: Sized,
161    {
162        let bytes = s.extract_atom("BigInt")?;
163        let hex_str = std::str::from_utf8(bytes).map_err(|_| {
164            rsexp::IntoSexpError::StringConversionError {
165                err: format!("Expected hex string with 0x prefix, got {bytes:?}"),
166            }
167        })?;
168
169        let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
170
171        let padded_hex = format!("{:0>64}", hex_str);
172
173        if padded_hex.len() != 64 {
174            return Err(rsexp::IntoSexpError::StringConversionError {
175                err: format!("Expected 64-character hex string, got {padded_hex:?}"),
176            });
177        }
178
179        let byte_vec: Vec<u8> = (0..padded_hex.len())
180            .step_by(2)
181            .map(|i| u8::from_str_radix(&padded_hex[i..i + 2], 16))
182            .rev()
183            .collect::<Result<Vec<u8>, _>>()
184            .map_err(|_| rsexp::IntoSexpError::StringConversionError {
185                err: format!("Failed to parse hex string: {padded_hex:?}"),
186            })?;
187
188        Ok(BigInt::from_bytes(byte_vec.try_into().unwrap()))
189    }
190}
191
192impl SexpOf for BigInt {
193    fn sexp_of(&self) -> rsexp::Sexp {
194        use std::fmt::Write;
195        let byte_vec = self.to_bytes();
196        let hex_str = byte_vec
197            .iter()
198            .rev()
199            .fold("0x".to_string(), |mut output, byte| {
200                let _ = write!(output, "{byte:02X}");
201                output
202            });
203
204        rsexp::Sexp::Atom(hex_str.into_bytes())
205    }
206}
207
208impl binprot::BinProtRead for BigInt {
209    fn binprot_read<R: std::io::Read + ?Sized>(r: &mut R) -> Result<Self, binprot::Error>
210    where
211        Self: Sized,
212    {
213        let mut bytes = [0u8; 32];
214        r.read_exact(&mut bytes)?;
215        Ok(Self::from_bytes(bytes))
216    }
217}
218
219impl binprot::BinProtWrite for BigInt {
220    fn binprot_write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
221        w.write_all(&self.to_bytes())
222    }
223}
224
225impl Serialize for BigInt {
226    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
227    where
228        S: serde::Serializer,
229    {
230        if serializer.is_human_readable() {
231            // TODO get rid of copying
232            let mut rev = self.to_bytes();
233            rev[..].reverse();
234            let mut hex = [0_u8; 32 * 2 + 2];
235            hex[..2].copy_from_slice(b"0x");
236            hex::encode_to_slice(rev, &mut hex[2..]).unwrap();
237            serializer.serialize_str(String::from_utf8_lossy(&hex).as_ref())
238        } else {
239            serializer.serialize_bytes(&self.to_bytes())
240        }
241    }
242}
243
244impl<'de> Deserialize<'de> for BigInt {
245    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
246    where
247        D: serde::Deserializer<'de>,
248    {
249        if deserializer.is_human_readable() {
250            struct V;
251            impl<'de> serde::de::Visitor<'de> for V {
252                type Value = Vec<u8>;
253
254                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
255                    formatter.write_str("hex string or numeric string")
256                }
257
258                fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
259                where
260                    E: serde::de::Error,
261                {
262                    match v.strip_prefix("0x") {
263                        Some(v) => hex::decode(v).map_err(|_| {
264                            serde::de::Error::custom(format!("failed to decode hex str: {v}"))
265                        }),
266                        None => {
267                            // Try to parse as a decimal number
268                            num_bigint::BigInt::<4>::parse_bytes(v.as_bytes(), 10)
269                                .map(|num| {
270                                    let mut bytes = num.to_bytes_be().1;
271                                    bytes.reverse();
272                                    bytes.resize(32, 0); // Ensure the byte vector has 32 bytes
273                                    bytes.reverse();
274                                    bytes
275                                })
276                                .ok_or_else(|| {
277                                    serde::de::Error::custom(
278                                        "failed to parse decimal number".to_string(),
279                                    )
280                                })
281                        }
282                    }
283                }
284
285                fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
286                where
287                    E: serde::de::Error,
288                {
289                    self.visit_borrowed_str(v)
290                }
291            }
292            let mut v = deserializer.deserialize_str(V)?;
293            v.reverse();
294            v.try_into()
295                .map_err(|_| serde::de::Error::custom("failed to convert vec to array".to_string()))
296        } else {
297            struct V;
298            impl serde::de::Visitor<'_> for V {
299                type Value = [u8; 32];
300
301                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
302                    formatter.write_str("sequence of 32 bytes")
303                }
304
305                fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
306                where
307                    E: serde::de::Error,
308                {
309                    let v: [u8; 32] = v
310                        .try_into()
311                        .map_err(|_| serde::de::Error::custom("expecting 32 bytes".to_string()))?;
312                    Ok(v)
313                }
314            }
315            deserializer.deserialize_bytes(V)
316        }
317        .map(Self::from_bytes)
318    }
319}
320
321impl mina_hasher::Hashable for BigInt {
322    type D = ();
323
324    fn to_roinput(&self) -> mina_hasher::ROInput {
325        mina_hasher::ROInput::new()
326            .append_field(self.to_field().expect("Failed to convert Hash into Fp"))
327    }
328
329    fn domain_string(_: Self::D) -> Option<String> {
330        None
331    }
332}
333
334// Manual ToSchema: serializes as hex string with 0x prefix in human-readable format
335#[cfg(feature = "openapi")]
336const _: () = {
337    use utoipa::{
338        openapi::schema::{Object, SchemaType, Type},
339        PartialSchema, ToSchema,
340    };
341    impl PartialSchema for BigInt {
342        fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
343            Object::builder()
344                .schema_type(SchemaType::Type(Type::String))
345                .description(Some("Unsigned 256-bit integer as 0x-prefixed hex string"))
346                .pattern(Some(r"^0x[0-9a-fA-F]{64}$"))
347                .build()
348                .into()
349        }
350    }
351    impl ToSchema for BigInt {
352        fn name() -> std::borrow::Cow<'static, str> {
353            std::borrow::Cow::Borrowed("BigInt")
354        }
355    }
356};
357
358#[cfg(test)]
359mod tests {
360    use super::BigInt;
361    use binprot::{BinProtRead, BinProtWrite};
362
363    fn to_binprot(v: &BigInt) -> Vec<u8> {
364        let mut w = Vec::new();
365        v.binprot_write(&mut w).unwrap();
366        w
367    }
368
369    fn from_binprot(mut b: &[u8]) -> BigInt {
370        BigInt::binprot_read(&mut b).unwrap()
371    }
372
373    /// Build a BigInt from a sequence of bytes.
374    /// If the input contains less than 32 bytes, the sequence is repeated until reaching 32 bytes
375    /// and filling the BigInt buffer.
376    fn from_bytes<'a, I>(it: I) -> BigInt
377    where
378        I: IntoIterator<Item = &'a u8>,
379        I::IntoIter: Clone,
380    {
381        let mut bytes = [0; 32];
382        let it = it.into_iter().cycle();
383        bytes.iter_mut().zip(it).for_each(|(b, i)| *b = *i);
384        BigInt::from_bytes(bytes)
385    }
386
387    #[test]
388    fn serialize_bigint() {
389        let bigints = [
390            BigInt::from_bytes([0; 32]),
391            BigInt::from_bytes([1; 32]),
392            BigInt::from_bytes([0xff; 32]),
393            from_bytes(&[0, 1, 2, 3, 4]),
394        ];
395
396        for bigint in bigints {
397            let binprot = to_binprot(&bigint);
398            assert_eq!(binprot.as_slice(), bigint.to_bytes());
399        }
400    }
401
402    #[test]
403    fn deserialize_bigint() {
404        let bigints = [
405            BigInt::from_bytes([0; 32]),
406            BigInt::from_bytes([1; 32]),
407            BigInt::from_bytes([0xff; 32]),
408            from_bytes(&[0, 1, 2, 3, 4]),
409        ];
410
411        for bigint in bigints {
412            let deser: BigInt = from_binprot(&bigint.to_bytes());
413            assert_eq!(&bigint.0, &deser.0);
414        }
415    }
416
417    #[test]
418    fn to_json() {
419        let bigints = [
420            BigInt::from_bytes([0; 32]),
421            BigInt::from_bytes([1; 32]),
422            BigInt::from_bytes([0xff; 32]),
423            from_bytes(&[0, 1, 2, 3, 4]),
424        ];
425
426        for bigint in bigints {
427            let json = serde_json::to_string(&bigint).unwrap();
428            let mut v = bigint.to_bytes();
429            v.reverse();
430            let json_exp = format!(r#""0x{}""#, hex::encode(v));
431            assert_eq!(json, json_exp);
432        }
433    }
434
435    #[test]
436    fn from_json() {
437        let bigints = [
438            BigInt::from_bytes([0; 32]),
439            BigInt::from_bytes([1; 32]),
440            BigInt::from_bytes([0xff; 32]),
441            from_bytes(&[0, 1, 2, 3, 4]),
442        ];
443
444        for bigint in bigints {
445            let mut be = bigint.to_bytes();
446            be.reverse();
447            let json = format!(r#""0x{}""#, hex::encode(be.as_ref()));
448            let bigint_exp = serde_json::from_str(&json).unwrap();
449            assert_eq!(bigint, bigint_exp);
450        }
451    }
452
453    #[test]
454    fn from_numeric_string() {
455        // Big endian encoding
456        let hex = "00000000000000000000000000000000000000000000000000000000075bcd15";
457        let deser: BigInt = serde_json::from_str(r#""123456789""#).unwrap();
458
459        let mut deser = deser.to_bytes();
460        deser.reverse();
461        let result_hex = hex::encode(deser);
462
463        assert_eq!(result_hex, hex.to_string());
464    }
465
466    #[test]
467    fn from_numeric_string_2() {
468        let rx =
469            r#""23298604903871047876308234794524469025218548053411207476198573374353464993732""#;
470        let s = r#""160863098041039391219472069845715442980741444645399750596310972807022542440""#;
471
472        let deser_rx: BigInt = serde_json::from_str(rx).unwrap();
473        let deser_s: BigInt = serde_json::from_str(s).unwrap();
474
475        println!("rx: {:?}", deser_rx);
476        println!("s: {:?}", deser_s);
477
478        let _ = deser_rx.to_field::<mina_curves::pasta::Fp>().unwrap();
479        println!("rx OK");
480        let _ = deser_s.to_field::<mina_curves::pasta::Fp>().unwrap();
481        println!("s OK");
482    }
483
484    use super::*;
485    use rsexp::Sexp;
486
487    #[test]
488    fn test_sexp_bigint() {
489        let hex_str = "0x248D179F4E92EA85C644CD99EF72187463B541D5F797943898C3D7A6CEEEC523";
490        let expected_array = [
491            0x98C3D7A6CEEEC523,
492            0x63B541D5F7979438,
493            0xC644CD99EF721874,
494            0x248D179F4E92EA85,
495        ];
496
497        let original_sexp = Sexp::Atom(hex_str.as_bytes().to_vec());
498
499        let result = BigInt::of_sexp(&original_sexp).expect("Failed to convert Sexp to BigInt");
500        let expected_result = BigInt(BigInteger256::new(expected_array));
501
502        assert_eq!(result, expected_result);
503
504        let produced_sexp = result.sexp_of();
505
506        assert_eq!(original_sexp, produced_sexp);
507    }
508}