Skip to main content

iris_ztd/
bignum.rs

1use crate::ftas;
2#[cfg(feature = "wasm")]
3use alloc::{boxed::Box, string::ToString};
4use alloc::{format, vec, vec::Vec};
5use core::fmt;
6use ibig::UBig;
7use iris_ztd_derive::*;
8use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
9
10/// Big integer, with noun encoding as 32-bit sized limbs in LSB
11///
12/// In wasm, this is encoded as a string of hex digits, with no padding. This is the same as Noun's atom case.
13#[derive(NounEncode, NounDecode, Hashable, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
14#[wasm_noun_codec]
15#[cfg_attr(feature = "wasm", tsify(type = "string & { __tag_bignum: undefined }"))]
16pub struct Bignum {
17    tag: ftas!("bn"),
18    vals: Vec<u32>,
19}
20
21impl From<Vec<u32>> for Bignum {
22    fn from(vals: Vec<u32>) -> Self {
23        Bignum {
24            tag: Default::default(),
25            vals,
26        }
27    }
28}
29
30impl From<&UBig> for Bignum {
31    fn from(ibig: &UBig) -> Self {
32        let bytes = ibig.to_le_bytes();
33        let mut vals = vec![];
34        for b in bytes.chunks(4) {
35            let mut val = [0u8; 4];
36            val[..b.len()].copy_from_slice(b);
37            vals.push(u32::from_le_bytes(val));
38        }
39        Bignum {
40            tag: Default::default(),
41            vals,
42        }
43    }
44}
45
46impl From<&Bignum> for UBig {
47    fn from(bignum: &Bignum) -> Self {
48        let bytes = unsafe { bignum.vals.align_to::<u8>().1 };
49        ibig::UBig::from_le_bytes(bytes)
50    }
51}
52
53impl Serialize for Bignum {
54    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55    where
56        S: Serializer,
57    {
58        serializer.serialize_str(&format!("{:x}", UBig::from(self)))
59    }
60}
61
62impl<'de> Deserialize<'de> for Bignum {
63    fn deserialize<D>(de: D) -> Result<Self, D::Error>
64    where
65        D: Deserializer<'de>,
66    {
67        struct BnVisitor;
68        impl<'de> de::Visitor<'de> for BnVisitor {
69            type Value = Bignum;
70
71            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
72                write!(formatter, "a hex string representing the big integer")
73            }
74
75            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
76            where
77                E: de::Error,
78            {
79                let bigint = ibig::UBig::from_str_radix(s, 16)
80                    .map_err(|_| de::Error::custom("not hex string"))?;
81                Ok(Bignum::from(&bigint))
82            }
83        }
84
85        de.deserialize_str(BnVisitor)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_bignum() {
95        let bignum = Bignum::from(&UBig::from(12345678901234567890u128));
96        assert_eq!(UBig::from(&bignum), UBig::from(12345678901234567890u128));
97        let json = serde_json::to_string(&bignum).unwrap();
98        assert_eq!(json, "\"ab54a98ceb1f0ad2\"");
99        let bignum2 = serde_json::from_str(&json).unwrap();
100        assert_eq!(bignum, bignum2);
101    }
102
103    #[test]
104    fn test_bignum_p3() {
105        let bignum = Bignum::from(vec![1, 4294967293, 5, 4294967289, 5, 4294967293]);
106        let json = serde_json::to_string(&bignum).unwrap();
107        assert_eq!(json, "\"fffffffd00000005fffffff900000005fffffffd00000001\"");
108        let bignum2 = serde_json::from_str(&json).unwrap();
109        assert_eq!(bignum, bignum2);
110    }
111}