Skip to main content

nox_core/protocol/
serialization.rs

1use ark_ff::{BigInteger, PrimeField};
2use serde::{self, Deserialize, Deserializer, Serializer};
3
4pub use darkpool_crypto::field::{deserialize_fr, serialize_fr};
5
6pub fn serialize_scalar<S>(field: &ark_ed_on_bn254::Fr, serializer: S) -> Result<S::Ok, S::Error>
7where
8    S: Serializer,
9{
10    let bytes = field.into_bigint().to_bytes_be();
11    serializer.serialize_str(&format!("0x{}", hex::encode(bytes)))
12}
13
14pub fn deserialize_scalar<'de, D>(deserializer: D) -> Result<ark_ed_on_bn254::Fr, D::Error>
15where
16    D: Deserializer<'de>,
17{
18    let s = String::deserialize(deserializer)?;
19    let clean_s = s.trim_start_matches("0x");
20    let bytes = hex::decode(clean_s).map_err(serde::de::Error::custom)?;
21
22    if bytes.len() > 32 {
23        return Err(serde::de::Error::custom(
24            "Scalar field element exceeds 32 bytes",
25        ));
26    }
27
28    let mut padded = [0u8; 32];
29    padded[32 - bytes.len()..].copy_from_slice(&bytes);
30
31    let val = ark_ed_on_bn254::Fr::from_be_bytes_mod_order(&padded);
32
33    let round_trip = val.into_bigint().to_bytes_be();
34    if round_trip != padded {
35        return Err(serde::de::Error::custom(
36            "Scalar value exceeds field modulus",
37        ));
38    }
39
40    Ok(val)
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use serde::{Deserialize, Serialize};
47
48    #[derive(Serialize, Deserialize, Debug, PartialEq)]
49    struct FrWrapper {
50        #[serde(serialize_with = "serialize_fr", deserialize_with = "deserialize_fr")]
51        val: ark_bn254::Fr,
52    }
53
54    #[derive(Serialize, Deserialize, Debug, PartialEq)]
55    struct ScalarWrapper {
56        #[serde(
57            serialize_with = "serialize_scalar",
58            deserialize_with = "deserialize_scalar"
59        )]
60        val: ark_ed_on_bn254::Fr,
61    }
62
63    #[test]
64    fn test_fr_round_trip_zero() {
65        let original = FrWrapper {
66            val: ark_bn254::Fr::from(0u64),
67        };
68        let json = serde_json::to_string(&original).unwrap();
69        let parsed: FrWrapper = serde_json::from_str(&json).unwrap();
70        assert_eq!(original, parsed);
71    }
72
73    #[test]
74    fn test_fr_round_trip_one() {
75        let original = FrWrapper {
76            val: ark_bn254::Fr::from(1u64),
77        };
78        let json = serde_json::to_string(&original).unwrap();
79        let parsed: FrWrapper = serde_json::from_str(&json).unwrap();
80        assert_eq!(original, parsed);
81    }
82
83    #[test]
84    fn test_fr_round_trip_large_value() {
85        let original = FrWrapper {
86            val: ark_bn254::Fr::from(u64::MAX),
87        };
88        let json = serde_json::to_string(&original).unwrap();
89        let parsed: FrWrapper = serde_json::from_str(&json).unwrap();
90        assert_eq!(original, parsed);
91    }
92
93    #[test]
94    fn test_fr_round_trip_max_valid() {
95        let original = FrWrapper {
96            val: -ark_bn254::Fr::from(1u64),
97        };
98        let json = serde_json::to_string(&original).unwrap();
99        let parsed: FrWrapper = serde_json::from_str(&json).unwrap();
100        assert_eq!(original, parsed);
101    }
102
103    #[test]
104    fn test_fr_rejects_over_modulus() {
105        let over_modulus =
106            r#"{"val":"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000002"}"#;
107        let result: Result<FrWrapper, _> = serde_json::from_str(over_modulus);
108        assert!(result.is_err(), "Should reject over-modulus value");
109    }
110
111    #[test]
112    fn test_fr_rejects_too_long() {
113        let too_long =
114            r#"{"val":"0x0030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"}"#;
115        let result: Result<FrWrapper, _> = serde_json::from_str(too_long);
116        assert!(result.is_err(), "Should reject >32 byte value");
117    }
118
119    #[test]
120    fn test_fr_accepts_with_0x_prefix() {
121        let with_prefix = r#"{"val":"0x01"}"#;
122        let parsed: FrWrapper = serde_json::from_str(with_prefix).unwrap();
123        assert_eq!(parsed.val, ark_bn254::Fr::from(1u64));
124    }
125
126    #[test]
127    fn test_fr_accepts_without_0x_prefix() {
128        let without_prefix = r#"{"val":"01"}"#;
129        let parsed: FrWrapper = serde_json::from_str(without_prefix).unwrap();
130        assert_eq!(parsed.val, ark_bn254::Fr::from(1u64));
131    }
132
133    #[test]
134    fn test_scalar_round_trip_zero() {
135        let original = ScalarWrapper {
136            val: ark_ed_on_bn254::Fr::from(0u64),
137        };
138        let json = serde_json::to_string(&original).unwrap();
139        let parsed: ScalarWrapper = serde_json::from_str(&json).unwrap();
140        assert_eq!(original, parsed);
141    }
142
143    #[test]
144    fn test_scalar_round_trip_large_value() {
145        let original = ScalarWrapper {
146            val: ark_ed_on_bn254::Fr::from(u64::MAX),
147        };
148        let json = serde_json::to_string(&original).unwrap();
149        let parsed: ScalarWrapper = serde_json::from_str(&json).unwrap();
150        assert_eq!(original, parsed);
151    }
152
153    #[test]
154    fn test_scalar_rejects_over_modulus() {
155        let over_modulus =
156            r#"{"val":"0x060c89ce5c263405370a08b6d0302b0bab3eedb83920ee0a677297dc392126f2"}"#;
157        let result: Result<ScalarWrapper, _> = serde_json::from_str(over_modulus);
158        assert!(result.is_err(), "Should reject over-modulus scalar value");
159    }
160}