1use core::{fmt, str::FromStr};
12
13use ark_ff::{BigInteger, PrimeField};
14use num_bigint::BigUint;
15
16pub use ark_bn254::Fr;
18
19pub const FIELD_MODULUS_DEC: &str =
21 "21888242871839275222246405745257275088548364400416034343698204186575808495617";
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub struct Bn254Fr(Fr);
30
31#[derive(Clone, Debug, PartialEq, Eq)]
33pub enum Bn254FrError {
34 InvalidDecimal,
35 NonCanonicalDecimal,
36 OutOfRange,
37}
38
39impl fmt::Display for Bn254FrError {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 Self::InvalidDecimal => f.write_str("invalid unsigned decimal field element"),
43 Self::NonCanonicalDecimal => f.write_str("non-canonical decimal field element"),
44 Self::OutOfRange => {
45 f.write_str("field element is greater than or equal to the BN254 modulus")
46 }
47 }
48 }
49}
50
51impl std::error::Error for Bn254FrError {}
52
53impl Bn254Fr {
54 pub fn try_from_dec(s: &str) -> Result<Self, Bn254FrError> {
56 if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) {
57 return Err(Bn254FrError::InvalidDecimal);
58 }
59 if s.len() > 1 && s.starts_with('0') {
60 return Err(Bn254FrError::NonCanonicalDecimal);
61 }
62 let value = BigUint::parse_bytes(s.as_bytes(), 10).ok_or(Bn254FrError::InvalidDecimal)?;
63 let modulus =
64 BigUint::parse_bytes(FIELD_MODULUS_DEC.as_bytes(), 10).expect("valid BN254 modulus");
65 if value >= modulus {
66 return Err(Bn254FrError::OutOfRange);
67 }
68 Ok(Self(fr_from_biguint(&value)))
69 }
70
71 #[inline]
73 pub fn from_fr(value: Fr) -> Self {
74 Self(value)
75 }
76
77 #[inline]
78 pub fn as_fr(&self) -> &Fr {
79 &self.0
80 }
81
82 #[inline]
83 pub fn into_inner(self) -> Fr {
84 self.0
85 }
86
87 pub fn to_dec(self) -> String {
88 fr_to_dec(&self.0)
89 }
90
91 pub fn to_le_32(self) -> [u8; 32] {
92 let bytes = fr_to_biguint(&self.0).to_bytes_le();
93 let mut out = [0u8; 32];
94 out[..bytes.len()].copy_from_slice(&bytes);
95 out
96 }
97}
98
99pub fn fr_from_dec(s: &str) -> Fr {
110 Fr::from_str(s).unwrap_or_else(|_| panic!("invalid field decimal: {s:?}"))
111}
112
113pub fn fr_from_biguint(v: &BigUint) -> Fr {
115 Fr::from_be_bytes_mod_order(&v.to_bytes_be())
116}
117
118pub fn fr_to_dec(x: &Fr) -> String {
120 fr_to_biguint(x).to_str_radix(10)
121}
122
123pub fn fr_to_biguint(x: &Fr) -> BigUint {
125 BigUint::from_bytes_be(&x.into_bigint().to_bytes_be())
126}
127
128pub fn fr_to_be_32(x: &Fr) -> [u8; 32] {
131 let be = x.into_bigint().to_bytes_be(); debug_assert_eq!(be.len(), 32);
133 let mut out = [0u8; 32];
134 out.copy_from_slice(&be);
135 out
136}
137
138pub fn fr_from_be_bytes_mod(bytes: &[u8]) -> Fr {
140 Fr::from_be_bytes_mod_order(bytes)
141}
142
143pub fn fr_from_be_32_checked(bytes: &[u8]) -> Option<Fr> {
149 if bytes.len() != 32 {
150 return None;
151 }
152 let value = Fr::from_be_bytes_mod_order(bytes);
153 (fr_to_be_32(&value).as_slice() == bytes).then_some(value)
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn decimal_roundtrip() {
162 for s in [
163 "0",
164 "1",
165 "42",
166 "21888242871839275222246405745257275088548364400416034343698204186575808495616",
167 ] {
168 assert_eq!(fr_to_dec(&fr_from_dec(s)), s);
169 }
170 }
171
172 #[test]
173 fn from_dec_reduces_out_of_range() {
174 let p_plus_5 =
177 "21888242871839275222246405745257275088548364400416034343698204186575808495622";
178 assert_eq!(fr_from_dec(p_plus_5), Fr::from(5u64));
179 assert_eq!(fr_from_dec("-1"), -Fr::from(1u64));
180 }
181
182 #[test]
183 fn field_modulus_constant_matches_arkworks() {
184 let be = <Fr as PrimeField>::MODULUS.to_bytes_be();
186 let dec = BigUint::from_bytes_be(&be).to_str_radix(10);
187 assert_eq!(dec, FIELD_MODULUS_DEC);
188 }
189
190 #[test]
191 fn canonical_binary_decoder_rejects_reduction_and_wrong_lengths() {
192 let five = fr_to_be_32(&Fr::from(5u64));
193 assert_eq!(fr_from_be_32_checked(&five), Some(Fr::from(5u64)));
194 assert_eq!(fr_from_be_32_checked(&five[..31]), None);
195
196 let modulus = <Fr as PrimeField>::MODULUS.to_bytes_be();
197 assert_eq!(fr_from_be_32_checked(&modulus), None);
198 }
199
200 #[test]
201 fn checked_field_parser_rejects_reduction_and_noncanonical_decimal() {
202 assert_eq!(Bn254Fr::try_from_dec("42").unwrap().to_dec(), "42");
203 assert_eq!(
204 Bn254Fr::try_from_dec("00"),
205 Err(Bn254FrError::NonCanonicalDecimal)
206 );
207 assert_eq!(
208 Bn254Fr::try_from_dec("-1"),
209 Err(Bn254FrError::InvalidDecimal)
210 );
211 assert_eq!(
212 Bn254Fr::try_from_dec(FIELD_MODULUS_DEC),
213 Err(Bn254FrError::OutOfRange)
214 );
215 }
216}