Skip to main content

primitives/algebra/
biguint.rs

1use std::mem::MaybeUninit;
2
3use derive_more::derive::{AsMut, AsRef, Deref, DerefMut, From};
4use serde::{Deserialize, Serialize};
5use wincode::{io::Writer, ReadResult, SchemaRead, SchemaWrite, WriteResult};
6
7// TODO: Remove once v0.7.0 is released
8
9#[cfg(not(target_pointer_width = "64"))]
10compile_error!("this crate builds on 64-bit platforms only");
11
12/// A wrapper around `crypto_bigint::BoxedUint` that implements `Serialize` and `Deserialize`.
13#[derive(
14    Debug, Clone, Deref, DerefMut, PartialEq, Eq, Hash, AsRef, AsMut, From, PartialOrd, Ord,
15)]
16#[as_ref(forward)]
17#[from(forward)]
18#[repr(transparent)]
19pub struct BoxedUint(crypto_bigint::BoxedUint);
20
21impl Serialize for BoxedUint {
22    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
23    where
24        S: serde::Serializer,
25    {
26        let bytes = self.0.to_le_bytes();
27        bytes.serialize(serializer)
28    }
29}
30
31impl<'de> Deserialize<'de> for BoxedUint {
32    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
33    where
34        D: serde::Deserializer<'de>,
35    {
36        let bytes = Vec::<u8>::deserialize(deserializer)?;
37        let bits_precision = bytes.len() * 8;
38        let boxed_uint = crypto_bigint::BoxedUint::from_le_slice(&bytes, bits_precision as u32)
39            .map_err(serde::de::Error::custom)?;
40        Ok(BoxedUint(boxed_uint))
41    }
42}
43
44impl SchemaWrite for BoxedUint {
45    type Src = Self;
46
47    fn size_of(src: &Self::Src) -> WriteResult<usize> {
48        let limbs = src
49            .0
50            .as_limbs()
51            .iter()
52            .map(|limb| limb.0)
53            .collect::<Box<[u64]>>();
54        <Box<[u64]> as SchemaWrite>::size_of(&limbs)
55    }
56
57    fn write(writer: &mut impl Writer, src: &Self::Src) -> WriteResult<()> {
58        let limbs = src
59            .0
60            .as_limbs()
61            .iter()
62            .map(|limb| limb.0)
63            .collect::<Box<[u64]>>();
64        <Box<[u64]> as SchemaWrite>::write(writer, &limbs)
65    }
66}
67
68impl<'de> SchemaRead<'de> for BoxedUint {
69    type Dst = Self;
70
71    fn read(
72        reader: &mut impl wincode::io::Reader<'de>,
73        dst: &mut MaybeUninit<Self::Dst>,
74    ) -> ReadResult<()> {
75        // SAFETY: Wrappers maintain the same memory layout as their inner type, even without
76        // #[repr(transparent)]. Reference: https://github.com/rust-lang/unsafe-code-guidelines/issues/34#issuecomment-431114929
77        // Additionally, we only build on 64-bit platforms, so u64 limbs are valid.
78        let dst_u64 = unsafe { &mut *dst.as_mut_ptr().cast::<MaybeUninit<Box<[u64]>>>() };
79        <Box<[u64]> as SchemaRead>::read(reader, dst_u64)?;
80        Ok(())
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    // Roundtrip test BoxedUint serialization
87    use bincode;
88    use crypto_bigint::U256;
89
90    use super::BoxedUint;
91    #[test]
92    fn test_boxed_uint_bincode() {
93        let original = BoxedUint(crypto_bigint::BoxedUint::from(U256::from_u64(123456789)));
94        let serialized = bincode::serialize(&original).expect("Serialization failed");
95        let deserialized: BoxedUint =
96            bincode::deserialize(&serialized).expect("Deserialization failed");
97        assert_eq!(original, deserialized);
98    }
99
100    #[test]
101    fn test_boxed_uint_wincode() {
102        let original = BoxedUint(crypto_bigint::BoxedUint::from(U256::from_u64(123456789)));
103        let serialized = wincode::serialize(&original).expect("Serialization failed");
104        let deserialized: BoxedUint =
105            wincode::deserialize(&serialized).expect("Deserialization failed");
106        assert_eq!(original, deserialized);
107    }
108}