nox_core/protocol/
serialization.rs1use 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}