noah_crypto/
field_simulation.rs

1use noah_algebra::bls12_381::BLSScalar;
2use noah_algebra::prelude::*;
3use noah_algebra::str::FromStr;
4use num_bigint::BigUint;
5use num_integer::Integer;
6use std::marker::PhantomData;
7
8/// The trait for parameters for field simulation.
9pub trait SimFrParams: Clone + Default {
10    /// The number of limbs in the simulated field element representation.
11    const NUM_OF_LIMBS: usize;
12    /// The expected number of bits for non-top limbs.
13    const BIT_PER_LIMB: usize;
14    /// The expected number of bits for the top limb.
15    const BIT_IN_TOP_LIMB: usize;
16    /// The number of limbs in the intermediate representation.
17    const NUM_OF_LIMBS_MUL: usize = Self::NUM_OF_LIMBS * 2 - 1;
18    /// The number of groups used during the zero-checking algorithm.
19    const NUM_OF_GROUPS: usize;
20
21    /// This is the `BigUint` of the scalar field modulus.
22    fn scalar_field_in_biguint() -> BigUint;
23
24    /// This is the limbs of the scalar field modulus.
25    fn scalar_field_in_limbs() -> Vec<BLSScalar>;
26
27    /// This is the limbs of the scalar field modulus being adjusted
28    /// so that each limb is more than 2^{BIT_PER_LIMB} (except the last one, 2^{BIT_IN_TOP_LIMB}).
29    ///
30    /// We use it in subtraction, and we call it sub pad.
31    fn scalar_field_sub_pad_in_limbs() -> Vec<BLSScalar>;
32
33    /// This is the `BigUint` representation of the sub pad.
34    fn scalar_field_sub_pad_in_biguint() -> BigUint;
35}
36
37/// The parameters for field simulation for Ristretto.
38#[derive(Clone, Default, Eq, PartialEq, Debug)]
39pub struct SimFrParamsRistretto;
40
41impl SimFrParams for SimFrParamsRistretto {
42    const NUM_OF_LIMBS: usize = 6;
43    const BIT_PER_LIMB: usize = 43;
44    const BIT_IN_TOP_LIMB: usize = 38;
45    const NUM_OF_GROUPS: usize = 6;
46
47    fn scalar_field_in_biguint() -> BigUint {
48        BigUint::from_str(
49            "7237005577332262213973186563042994240857116359379907606001950938285454250989",
50        )
51        .unwrap()
52    }
53
54    fn scalar_field_in_limbs() -> Vec<BLSScalar> {
55        [
56            BLSScalar::from_str("3411763647469").unwrap(),
57            BLSScalar::from_str("7643343815244").unwrap(),
58            BLSScalar::from_str("358561053323").unwrap(),
59            BLSScalar::from_str("0").unwrap(),
60            BLSScalar::from_str("0").unwrap(),
61            BLSScalar::from_str("137438953472").unwrap(),
62        ]
63        .to_vec()
64    }
65
66    fn scalar_field_sub_pad_in_limbs() -> Vec<BLSScalar> {
67        [
68            BLSScalar::from_str("10235290942407").unwrap(),
69            BLSScalar::from_str("14133938423524").unwrap(),
70            BLSScalar::from_str("9871776182178").unwrap(),
71            BLSScalar::from_str("17592186044415").unwrap(),
72            BLSScalar::from_str("17592186044414").unwrap(),
73            BLSScalar::from_str("412316860414").unwrap(),
74        ]
75        .to_vec()
76    }
77
78    fn scalar_field_sub_pad_in_biguint() -> BigUint {
79        BigUint::from_str(
80            "21711016731996786641919559689128982722571349078139722818005852814856362752967",
81        )
82        .unwrap()
83    }
84}
85
86/// The parameters for field simulation for the secq256k1 scalar field.
87#[derive(Clone, Default, Eq, PartialEq, Debug)]
88pub struct SimFrParamsSecq256k1;
89
90impl SimFrParams for SimFrParamsSecq256k1 {
91    const NUM_OF_LIMBS: usize = 6;
92    const BIT_PER_LIMB: usize = 44;
93    const BIT_IN_TOP_LIMB: usize = 36;
94    const NUM_OF_GROUPS: usize = 6;
95
96    fn scalar_field_in_biguint() -> BigUint {
97        BigUint::from_str(
98            "115792089237316195423570985008687907853269984665640564039457584007908834671663",
99        )
100        .unwrap()
101    }
102
103    fn scalar_field_in_limbs() -> Vec<BLSScalar> {
104        [
105            BLSScalar::from_str("17587891076143").unwrap(),
106            BLSScalar::from_str("17592186044415").unwrap(),
107            BLSScalar::from_str("17592186044415").unwrap(),
108            BLSScalar::from_str("17592186044415").unwrap(),
109            BLSScalar::from_str("17592186044415").unwrap(),
110            BLSScalar::from_str("68719476735").unwrap(),
111        ]
112        .to_vec()
113    }
114
115    fn scalar_field_sub_pad_in_limbs() -> Vec<BLSScalar> {
116        [
117            BLSScalar::from_str("35175782152286").unwrap(),
118            BLSScalar::from_str("35184372088830").unwrap(),
119            BLSScalar::from_str("35184372088830").unwrap(),
120            BLSScalar::from_str("35184372088830").unwrap(),
121            BLSScalar::from_str("35184372088830").unwrap(),
122            BLSScalar::from_str("137438953470").unwrap(),
123        ]
124        .to_vec()
125    }
126
127    fn scalar_field_sub_pad_in_biguint() -> BigUint {
128        BigUint::from_str(
129            "231584178474632390847141970017375815706539969331281128078915168015817669343326",
130        )
131        .unwrap()
132    }
133}
134
135/// The parameters for field simulation for the zorro scalar field.
136#[derive(Clone, Default, Eq, PartialEq, Debug)]
137pub struct SimFrParamsZorro;
138
139impl SimFrParams for SimFrParamsZorro {
140    const NUM_OF_LIMBS: usize = 6;
141    const BIT_PER_LIMB: usize = 44;
142    const BIT_IN_TOP_LIMB: usize = 36;
143    const NUM_OF_GROUPS: usize = 6;
144
145    fn scalar_field_in_biguint() -> BigUint {
146        BigUint::from_str(
147            "57896044618658097711785492504343953926634992332820282019728792003956564819949",
148        )
149        .unwrap()
150    }
151
152    fn scalar_field_in_limbs() -> Vec<BLSScalar> {
153        [
154            BLSScalar::from_str("17592186044397").unwrap(),
155            BLSScalar::from_str("17592186044415").unwrap(),
156            BLSScalar::from_str("17592186044415").unwrap(),
157            BLSScalar::from_str("17592186044415").unwrap(),
158            BLSScalar::from_str("17592186044415").unwrap(),
159            BLSScalar::from_str("34359738367").unwrap(),
160        ]
161        .to_vec()
162    }
163
164    fn scalar_field_sub_pad_in_limbs() -> Vec<BLSScalar> {
165        [
166            BLSScalar::from_str("35184372088794").unwrap(),
167            BLSScalar::from_str("35184372088830").unwrap(),
168            BLSScalar::from_str("35184372088830").unwrap(),
169            BLSScalar::from_str("35184372088830").unwrap(),
170            BLSScalar::from_str("35184372088830").unwrap(),
171            BLSScalar::from_str("68719476734").unwrap(),
172        ]
173        .to_vec()
174    }
175
176    fn scalar_field_sub_pad_in_biguint() -> BigUint {
177        BigUint::from_str(
178            "115792089237316195423570985008687907853269984665640564039457584007913129639898",
179        )
180        .unwrap()
181    }
182}
183
184/// A precise indicator of the reducibility in a simulate element.
185#[derive(Eq, PartialEq, Clone)]
186pub enum SimReducibility {
187    /// For public input or constant, the field element is in the normalized form.
188    StrictlyNotReducible,
189    /// For witness, meaning that the field element is either `x` or `x + p`.
190    AtMostReducibleByOne,
191    /// The field element might have been added this number of times.
192    Others(BigUint),
193}
194
195impl<'a> From<&'a SimReducibility> for BigUint {
196    fn from(src: &'a SimReducibility) -> Self {
197        match src {
198            SimReducibility::StrictlyNotReducible => BigUint::zero(),
199            SimReducibility::AtMostReducibleByOne => BigUint::one(),
200            SimReducibility::Others(x) => x.clone(),
201        }
202    }
203}
204
205/// `SimFr` is the simulated scalar field element
206/// over BLS12-381 scalar field.
207#[derive(Clone)]
208pub struct SimFr<P: SimFrParams> {
209    /// The limbs of a simulated field element.
210    pub limbs: Vec<BLSScalar>,
211    /// The actual value of the simulated field element.
212    pub val: BigUint,
213    /// The reducibility of this simulated field element.
214    pub num_of_additions_over_normal_form: SimReducibility,
215    /// PhantomData for the parameters.
216    pub params_phantom: PhantomData<P>,
217}
218
219impl<P: SimFrParams> Default for SimFr<P> {
220    fn default() -> Self {
221        Self {
222            limbs: vec![BLSScalar::zero(); P::NUM_OF_LIMBS],
223            val: BigUint::zero(),
224            num_of_additions_over_normal_form: SimReducibility::StrictlyNotReducible,
225            params_phantom: PhantomData::default(),
226        }
227    }
228}
229
230impl<P: SimFrParams> Sub<&SimFr<P>> for &SimFr<P> {
231    type Output = SimFr<P>;
232
233    fn sub(self, rhs: &SimFr<P>) -> SimFr<P> {
234        // For simplicity, given that our use case involves only one subtraction,
235        // we require the value to be subtracted by a simulated field element
236        // with at most one addition.
237        //
238        assert!(
239            rhs.num_of_additions_over_normal_form == SimReducibility::StrictlyNotReducible
240                || rhs.num_of_additions_over_normal_form == SimReducibility::AtMostReducibleByOne
241        );
242
243        let mut res = SimFr::<P>::default();
244        let r_limbs = P::scalar_field_sub_pad_in_limbs();
245        let r_biguint = P::scalar_field_sub_pad_in_biguint();
246
247        for i in 0..P::NUM_OF_LIMBS {
248            res.limbs[i] = self.limbs[i].add(&r_limbs[i]).sub(&rhs.limbs[i]);
249        }
250        res.val = (&self.val).add(&r_biguint).sub(&rhs.val);
251
252        res.num_of_additions_over_normal_form = SimReducibility::Others(
253            BigUint::from(&self.num_of_additions_over_normal_form) + BigUint::from(3u32),
254        );
255
256        res
257    }
258}
259
260impl<P: SimFrParams> Mul<&SimFr<P>> for &SimFr<P> {
261    type Output = SimFrMul<P>;
262
263    fn mul(self, rhs: &SimFr<P>) -> SimFrMul<P> {
264        let mut mul_res = SimFrMul::<P>::default();
265        for i in 0..P::NUM_OF_LIMBS {
266            for j in 0..P::NUM_OF_LIMBS {
267                mul_res.limbs[i + j].add_assign(&self.limbs[i].mul(&rhs.limbs[j]));
268            }
269        }
270        mul_res.val = (&self.val).mul(&rhs.val);
271        mul_res.prod_of_num_of_additions = BigUint::from(&self.num_of_additions_over_normal_form)
272            .add(&BigUint::one())
273            .mul(&BigUint::from(&rhs.num_of_additions_over_normal_form).add(&BigUint::one()));
274
275        mul_res
276    }
277}
278
279impl<P: SimFrParams> From<&BigUint> for SimFr<P> {
280    fn from(src: &BigUint) -> Self {
281        let mut rem = src.clone();
282
283        let step = BigUint::from(1u32).shl(P::BIT_PER_LIMB);
284
285        let mut res = SimFr::<P>::default();
286        res.val = rem.clone();
287        for i in 0..P::NUM_OF_LIMBS {
288            let (new_rem, limb) = rem.div_rem(&step);
289            rem = new_rem;
290            res.limbs[i] = BLSScalar::from(&limb);
291        }
292        res.num_of_additions_over_normal_form = SimReducibility::StrictlyNotReducible;
293
294        res
295    }
296}
297
298impl<P: SimFrParams> Into<BigUint> for &SimFr<P> {
299    fn into(self) -> BigUint {
300        let step = BigUint::from(1u32).shl(P::BIT_PER_LIMB);
301        let mut res = BigUint::zero();
302        for limb in self.limbs.iter().rev() {
303            res.mul_assign(&step);
304            res.add_assign(&limb.clone().into());
305        }
306        assert_eq!(res, self.val);
307        res
308    }
309}
310
311impl<P: SimFrParams> SimFr<P> {
312    /// Check if the *actual* value of the simulated field element is zero.
313    /// Note: One cannot simply require each limb to be zero, because the limbs
314    ///   could be representing k * p where k is a positive integer.
315    pub fn is_zero(&self) -> bool {
316        let self_biguint: BigUint = self.into();
317        let r_biguint = P::scalar_field_in_biguint();
318
319        let (_, rem) = self_biguint.div_rem(&r_biguint);
320        rem.is_zero()
321    }
322}
323
324/// `SimFrMul` is the intermediate representation for
325/// the product of two simulated scalar field elements
326/// over BLS12-381 scalar field.
327#[derive(Clone)]
328pub struct SimFrMul<P: SimFrParams> {
329    /// The limbs of this intermediate representation.
330    pub limbs: Vec<BLSScalar>,
331    /// The actual value of this intermediate representation.
332    pub val: BigUint,
333    /// The product of the num of additions over two original field elements.
334    pub prod_of_num_of_additions: BigUint,
335    /// PhantomData for the parameters.
336    pub params_phantom: PhantomData<P>,
337}
338
339impl<P: SimFrParams> Default for SimFrMul<P> {
340    fn default() -> Self {
341        Self {
342            limbs: vec![BLSScalar::zero(); P::NUM_OF_LIMBS_MUL],
343            val: BigUint::zero(),
344            prod_of_num_of_additions: BigUint::zero(),
345            params_phantom: PhantomData::default(),
346        }
347    }
348}
349
350impl<P: SimFrParams> Into<BigUint> for &SimFrMul<P> {
351    fn into(self) -> BigUint {
352        let step = BigUint::from(1u32).shl(P::BIT_PER_LIMB);
353        let mut res = BigUint::zero();
354        for limb in self.limbs.iter().rev() {
355            res.mul_assign(&step);
356            res.add_assign(&limb.clone().into());
357        }
358        assert_eq!(self.val, res);
359        res
360    }
361}
362
363impl<P: SimFrParams> Add<&SimFrMul<P>> for &SimFrMul<P> {
364    type Output = SimFrMul<P>;
365
366    fn add(self, rhs: &SimFrMul<P>) -> SimFrMul<P> {
367        let mut res = (*self).clone();
368
369        for i in 0..P::NUM_OF_LIMBS_MUL {
370            res.limbs[i] = res.limbs[i].add(&rhs.limbs[i]);
371        }
372        res.val = &res.val + &rhs.val;
373        res.prod_of_num_of_additions =
374            &res.prod_of_num_of_additions + &rhs.prod_of_num_of_additions;
375
376        res
377    }
378}
379
380impl<P: SimFrParams> Sub<&SimFr<P>> for &SimFrMul<P> {
381    type Output = SimFrMul<P>;
382
383    fn sub(self, rhs: &SimFr<P>) -> SimFrMul<P> {
384        // For simplicity, we require the value to be subtracted
385        // by a simulated field element with at most four additions.
386        //
387        assert!(BigUint::from(&rhs.num_of_additions_over_normal_form) <= BigUint::from(4u32));
388
389        let mut res = (*self).clone();
390        let r_limbs = P::scalar_field_sub_pad_in_limbs();
391        let r_biguint = P::scalar_field_sub_pad_in_biguint();
392
393        for i in 0..P::NUM_OF_LIMBS {
394            res.limbs[i] = res.limbs[i]
395                .add(&r_limbs[i])
396                .add(&r_limbs[i])
397                .add(&r_limbs[i])
398                .add(&r_limbs[i])
399                .sub(&rhs.limbs[i]);
400        }
401        res.val = &res.val + &r_biguint + &r_biguint + &r_biguint + &r_biguint - &rhs.val;
402        res.prod_of_num_of_additions = &res.prod_of_num_of_additions + &BigUint::from(12u32);
403
404        res
405    }
406}
407
408impl<P: SimFrParams> SimFrMul<P> {
409    /// The `enforce_zero` function uses the techniques from two works:
410    ///
411    /// [KPS18](https://akosba.github.io/papers/xjsnark.pdf):
412    /// A. E. Kosba, C. Papamanthou, and E. Shi.
413    /// "xJsnark: a framework for efficient verifiable computation,"
414    /// in S&P 2018
415    ///
416    /// [OWWB20](https://eprint.iacr.org/2019/1494.pdf):
417    /// A. Ozdemir, R. S. Wahby, B. Whitehat, and D. Boneh.
418    /// "Scaling verifiable computation using efficient set accumulators,"
419    /// in USENIX Security 2020
420    ///
421    /// And the code is from xJsnark, bellman-bignat, and arkworks-rs.
422    ///
423    pub fn enforce_zero(&self) {
424        // For safety, since in our use case we are only doing very few algebraic operations,
425        // we limit the `prod_of_num_of_additions` to be smaller than 32.
426        assert!(self.prod_of_num_of_additions.bits() as usize <= 5);
427        let surfeit = 5; // for safety
428
429        let cur_val: BigUint = self.into();
430        let r_biguint = P::scalar_field_in_biguint();
431
432        // The idea is to show that there exists `k * r` that is exactly the current number
433        // in `SimFrMul`, by a few shifts.
434
435        // compute `k`
436        let (k, rem) = cur_val.div_rem(&r_biguint);
437        assert!(rem.is_zero());
438        assert!(k <= r_biguint.clone().shl(5u32));
439
440        // compute the limbs for `k * r`
441        let r_limbs = P::scalar_field_in_limbs().to_vec();
442        let k_limbs = SimFr::<P>::from(&k).limbs.to_vec();
443
444        let mut rk_limbs = vec![BLSScalar::zero(); P::NUM_OF_LIMBS_MUL];
445        for i in 0..P::NUM_OF_LIMBS {
446            for j in 0..P::NUM_OF_LIMBS {
447                rk_limbs[i + j] = rk_limbs[i + j].add(&r_limbs[i].mul(&k_limbs[j]));
448            }
449        }
450
451        // group the limbs of `self` and the limbs of `k * r` together
452        // this step is for efficiency, so that carry adders have fewer steps
453        let mut left_group = Vec::with_capacity(P::NUM_OF_GROUPS);
454        let mut right_group = Vec::with_capacity(P::NUM_OF_GROUPS);
455        let mut num_limbs_in_group = Vec::with_capacity(P::NUM_OF_GROUPS);
456
457        let step = BLSScalar::from(&BigUint::from(1u32).shl(P::BIT_PER_LIMB));
458        for chunk in self.limbs.chunks(2) {
459            if chunk.len() == 2 {
460                left_group.push(chunk[0].add(&chunk[1].mul(&step)));
461            } else {
462                left_group.push(chunk[0]);
463            }
464            num_limbs_in_group.push(chunk.len());
465        }
466        for chunk in rk_limbs.chunks(2) {
467            if chunk.len() == 2 {
468                right_group.push(chunk[0].add(&chunk[1].mul(&step)));
469            } else {
470                right_group.push(chunk[0]);
471            }
472        }
473
474        // Perform the following checking
475        //      left_group_limb + pad_limb + carry_in - right_group_limb
476        //   =  carry shift by (BIT_PER_LIMB * num_limb_in_group) + remainder
477
478        let mut carry_in = BLSScalar::zero();
479        let mut accumulated_extra = BigUint::zero();
480        for (group_id, ((left_group_limb, right_group_limb), num_limbs_in_this_group)) in left_group
481            .iter()
482            .zip(right_group.iter())
483            .zip(num_limbs_in_group.iter())
484            .enumerate()
485        {
486            let pad = BigUint::from(1u32).shl(
487                (num_limbs_in_this_group + 1) * P::BIT_PER_LIMB + num_limbs_in_this_group + surfeit,
488            );
489            let pad_limb = BLSScalar::from(&pad);
490            assert!(pad > <BLSScalar as Into<BigUint>>::into(right_group_limb.clone()));
491
492            // Compute the carry number for the next cycle
493            let mut carry = left_group_limb
494                .add(&carry_in)
495                .add(&pad_limb)
496                .sub(&right_group_limb);
497            let carry_biguint: BigUint = carry.into();
498            carry = BLSScalar::from(&carry_biguint.shr(num_limbs_in_this_group * P::BIT_PER_LIMB));
499            accumulated_extra += BigUint::from_bytes_le(&pad_limb.to_bytes());
500
501            let (new_accumulated_extra, remainder_biguint) = accumulated_extra
502                .div_rem(&BigUint::from(1u64).shl(P::BIT_PER_LIMB * num_limbs_in_this_group));
503            let remainder = BLSScalar::from(&remainder_biguint);
504
505            let eqn_left = left_group_limb
506                .add(&pad_limb)
507                .add(&carry_in)
508                .sub(&right_group_limb);
509
510            let eqn_right = (&carry)
511                .mul(&(&BigUint::from(1u32).shl(P::BIT_PER_LIMB * num_limbs_in_this_group)).into())
512                .add(&remainder);
513
514            assert_eq!(eqn_left, eqn_right);
515
516            accumulated_extra = new_accumulated_extra;
517            carry_in = carry;
518
519            if group_id == left_group.len() - 1 {
520                assert_eq!(carry, (&accumulated_extra).into());
521            } else {
522                // bound the size of carry
523                assert!(BigUint::from(1u32)
524                    .shl(surfeit + P::BIT_PER_LIMB * 2)
525                    .gt(&carry.into()));
526            }
527        }
528    }
529}
530
531#[cfg(test)]
532mod test_ristretto {
533    use crate::field_simulation::{SimFr, SimFrParams, SimFrParamsRistretto};
534    use noah_algebra::prelude::*;
535    use num_bigint::{BigUint, RandBigInt};
536    use num_integer::Integer;
537
538    type SimFrTest = SimFr<SimFrParamsRistretto>;
539
540    #[test]
541    fn test_sim_fr_biguint_conversion() {
542        let mut prng = test_rng();
543        let r_biguint = SimFrParamsRistretto::scalar_field_in_biguint();
544
545        for _ in 0..100 {
546            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
547            let a_sim_fr = SimFrTest::from(&a);
548            let a_recovered: BigUint = (&a_sim_fr).into();
549
550            assert_eq!(a, a_recovered);
551        }
552    }
553
554    #[test]
555    fn test_sub() {
556        let mut prng = test_rng();
557        let r_biguint = SimFrParamsRistretto::scalar_field_in_biguint();
558
559        for _ in 0..100 {
560            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
561            let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
562
563            let a_sim_fr = SimFrTest::from(&a);
564            let b_sim_fr = SimFrTest::from(&b);
565            let sum_sim_fr = &a_sim_fr - &b_sim_fr;
566
567            let (_, sum) = a.add(&r_biguint).sub(&b).div_rem(&r_biguint);
568            let (_, sum_recovered) =
569                <&SimFrTest as Into<BigUint>>::into(&sum_sim_fr).div_rem(&r_biguint);
570
571            assert_eq!(sum, sum_recovered);
572        }
573    }
574
575    #[test]
576    fn test_mul() {
577        let mut prng = test_rng();
578        let r_biguint = SimFrParamsRistretto::scalar_field_in_biguint();
579
580        for _ in 0..100 {
581            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
582            let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
583
584            let a_sim_fr = SimFrTest::from(&a);
585            let b_sim_fr = SimFrTest::from(&b);
586
587            let prod_sim_fr_mul = a_sim_fr.mul(&b_sim_fr);
588            let prod_sim_fr_mul_recovered: BigUint = (&prod_sim_fr_mul).into();
589
590            let prod = &a * &b;
591
592            assert_eq!(prod, prod_sim_fr_mul_recovered);
593        }
594    }
595
596    #[test]
597    fn test_enforce_zero_trivial() {
598        let zero_fr = SimFrTest::from(&BigUint::zero());
599        let zero_fr_mul = (&zero_fr) * (&zero_fr);
600
601        zero_fr_mul.enforce_zero();
602    }
603
604    #[test]
605    fn test_enforce_zero() {
606        let mut prng = test_rng();
607        let r_biguint = SimFrParamsRistretto::scalar_field_in_biguint();
608
609        for _ in 0..1000 {
610            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
611            let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
612
613            let a_fr = SimFrTest::from(&a);
614            let b_fr = SimFrTest::from(&b);
615
616            let ab_fr_mul = &a_fr * &b_fr;
617            let ab_fr = &a * &b;
618            assert_eq!(ab_fr, (&ab_fr_mul).into());
619
620            let ab_fr_reduced = &ab_fr % &r_biguint;
621            let ab_reduced = SimFrTest::from(&ab_fr_reduced);
622
623            let zero_supposed = &ab_fr_mul - &ab_reduced;
624            let zero_supposed_biguint: BigUint = (&zero_supposed).into();
625            assert_eq!(BigUint::zero(), &zero_supposed_biguint % &r_biguint);
626            zero_supposed.enforce_zero();
627        }
628    }
629
630    #[test]
631    #[should_panic]
632    fn test_enforce_zero_panic() {
633        let mut prng = test_rng();
634        let r_biguint = SimFrParamsRistretto::scalar_field_in_biguint();
635
636        let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
637        let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
638
639        let a_fr = SimFrTest::from(&a);
640        let b_fr = SimFrTest::from(&b);
641
642        let ab_fr_mul = &a_fr * &b_fr;
643        let ab_fr = &a * &b;
644        assert_eq!(ab_fr, (&ab_fr_mul).into());
645
646        let ab_fr_reduced_manipulated = &ab_fr % &r_biguint + &BigUint::from(10u64);
647        let ab_reduced_manipulated = SimFrTest::from(&ab_fr_reduced_manipulated);
648
649        let zero_supposed_manipulated = &ab_fr_mul - &ab_reduced_manipulated;
650        zero_supposed_manipulated.enforce_zero();
651    }
652}
653
654#[cfg(test)]
655mod test_secq256k1 {
656    use crate::field_simulation::{SimFr, SimFrParams, SimFrParamsSecq256k1};
657    use noah_algebra::prelude::*;
658    use num_bigint::{BigUint, RandBigInt};
659    use num_integer::Integer;
660
661    type SimFrTest = SimFr<SimFrParamsSecq256k1>;
662
663    #[test]
664    fn test_sim_fr_biguint_conversion() {
665        let mut prng = test_rng();
666        let r_biguint = SimFrParamsSecq256k1::scalar_field_in_biguint();
667
668        for _ in 0..100 {
669            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
670            let a_sim_fr = SimFrTest::from(&a);
671            let a_recovered: BigUint = (&a_sim_fr).into();
672
673            assert_eq!(a, a_recovered);
674        }
675    }
676
677    #[test]
678    fn test_sub() {
679        let mut prng = test_rng();
680        let r_biguint = SimFrParamsSecq256k1::scalar_field_in_biguint();
681
682        for _ in 0..100 {
683            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
684            let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
685
686            let a_sim_fr = SimFrTest::from(&a);
687            let b_sim_fr = SimFrTest::from(&b);
688            let sum_sim_fr = &a_sim_fr - &b_sim_fr;
689
690            let (_, sum) = a.add(&r_biguint).sub(&b).div_rem(&r_biguint);
691            let (_, sum_recovered) =
692                <&SimFrTest as Into<BigUint>>::into(&sum_sim_fr).div_rem(&r_biguint);
693
694            assert_eq!(sum, sum_recovered);
695        }
696    }
697
698    #[test]
699    fn test_mul() {
700        let mut prng = test_rng();
701        let r_biguint = SimFrParamsSecq256k1::scalar_field_in_biguint();
702
703        for _ in 0..100 {
704            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
705            let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
706
707            let a_sim_fr = SimFrTest::from(&a);
708            let b_sim_fr = SimFrTest::from(&b);
709
710            let prod_sim_fr_mul = a_sim_fr.mul(&b_sim_fr);
711            let prod_sim_fr_mul_recovered: BigUint = (&prod_sim_fr_mul).into();
712
713            let prod = &a * &b;
714
715            assert_eq!(prod, prod_sim_fr_mul_recovered);
716        }
717    }
718
719    #[test]
720    fn test_enforce_zero_trivial() {
721        let zero_fr = SimFrTest::from(&BigUint::zero());
722        let zero_fr_mul = (&zero_fr) * (&zero_fr);
723
724        zero_fr_mul.enforce_zero();
725    }
726
727    #[test]
728    fn test_enforce_zero() {
729        let mut prng = test_rng();
730        let r_biguint = SimFrParamsSecq256k1::scalar_field_in_biguint();
731
732        for _ in 0..1000 {
733            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
734            let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
735
736            let a_fr = SimFrTest::from(&a);
737            let b_fr = SimFrTest::from(&b);
738
739            let ab_fr_mul = &a_fr * &b_fr;
740            let ab_fr = &a * &b;
741            assert_eq!(ab_fr, (&ab_fr_mul).into());
742
743            let ab_fr_reduced = &ab_fr % &r_biguint;
744            let ab_reduced = SimFrTest::from(&ab_fr_reduced);
745
746            let zero_supposed = &ab_fr_mul - &ab_reduced;
747            let zero_supposed_biguint: BigUint = (&zero_supposed).into();
748            assert_eq!(BigUint::zero(), &zero_supposed_biguint % &r_biguint);
749            zero_supposed.enforce_zero();
750        }
751    }
752
753    #[test]
754    #[should_panic]
755    fn test_enforce_zero_panic() {
756        let mut prng = test_rng();
757        let r_biguint = SimFrParamsSecq256k1::scalar_field_in_biguint();
758
759        let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
760        let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
761
762        let a_fr = SimFrTest::from(&a);
763        let b_fr = SimFrTest::from(&b);
764
765        let ab_fr_mul = &a_fr * &b_fr;
766        let ab_fr = &a * &b;
767        assert_eq!(ab_fr, (&ab_fr_mul).into());
768
769        let ab_fr_reduced_manipulated = &ab_fr % &r_biguint + &BigUint::from(10u64);
770        let ab_reduced_manipulated = SimFrTest::from(&ab_fr_reduced_manipulated);
771
772        let zero_supposed_manipulated = &ab_fr_mul - &ab_reduced_manipulated;
773        zero_supposed_manipulated.enforce_zero();
774    }
775}
776
777#[cfg(test)]
778mod test_zorro {
779    use crate::field_simulation::{SimFr, SimFrParams, SimFrParamsZorro};
780    use noah_algebra::prelude::*;
781    use num_bigint::{BigUint, RandBigInt};
782    use num_integer::Integer;
783
784    type SimFrTest = SimFr<SimFrParamsZorro>;
785
786    #[test]
787    fn test_sim_fr_biguint_conversion() {
788        let mut prng = test_rng();
789        let r_biguint = SimFrParamsZorro::scalar_field_in_biguint();
790
791        for _ in 0..100 {
792            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
793            let a_sim_fr = SimFrTest::from(&a);
794            let a_recovered: BigUint = (&a_sim_fr).into();
795
796            assert_eq!(a, a_recovered);
797        }
798    }
799
800    #[test]
801    fn test_sub() {
802        let mut prng = test_rng();
803        let r_biguint = SimFrParamsZorro::scalar_field_in_biguint();
804
805        for _ in 0..100 {
806            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
807            let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
808
809            let a_sim_fr = SimFrTest::from(&a);
810            let b_sim_fr = SimFrTest::from(&b);
811            let sum_sim_fr = &a_sim_fr - &b_sim_fr;
812
813            let (_, sum) = a.add(&r_biguint).sub(&b).div_rem(&r_biguint);
814            let (_, sum_recovered) =
815                <&SimFrTest as Into<BigUint>>::into(&sum_sim_fr).div_rem(&r_biguint);
816
817            assert_eq!(sum, sum_recovered);
818        }
819    }
820
821    #[test]
822    fn test_mul() {
823        let mut prng = test_rng();
824        let r_biguint = SimFrParamsZorro::scalar_field_in_biguint();
825
826        for _ in 0..100 {
827            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
828            let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
829
830            let a_sim_fr = SimFrTest::from(&a);
831            let b_sim_fr = SimFrTest::from(&b);
832
833            let prod_sim_fr_mul = a_sim_fr.mul(&b_sim_fr);
834            let prod_sim_fr_mul_recovered: BigUint = (&prod_sim_fr_mul).into();
835
836            let prod = &a * &b;
837
838            assert_eq!(prod, prod_sim_fr_mul_recovered);
839        }
840    }
841
842    #[test]
843    fn test_enforce_zero_trivial() {
844        let zero_fr = SimFrTest::from(&BigUint::zero());
845        let zero_fr_mul = (&zero_fr) * (&zero_fr);
846
847        zero_fr_mul.enforce_zero();
848    }
849
850    #[test]
851    fn test_enforce_zero() {
852        let mut prng = test_rng();
853        let r_biguint = SimFrParamsZorro::scalar_field_in_biguint();
854
855        for _ in 0..1000 {
856            let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
857            let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
858
859            let a_fr = SimFrTest::from(&a);
860            let b_fr = SimFrTest::from(&b);
861
862            let ab_fr_mul = &a_fr * &b_fr;
863            let ab_fr = &a * &b;
864            assert_eq!(ab_fr, (&ab_fr_mul).into());
865
866            let ab_fr_reduced = &ab_fr % &r_biguint;
867            let ab_reduced = SimFrTest::from(&ab_fr_reduced);
868
869            let zero_supposed = &ab_fr_mul - &ab_reduced;
870            let zero_supposed_biguint: BigUint = (&zero_supposed).into();
871            assert_eq!(BigUint::zero(), &zero_supposed_biguint % &r_biguint);
872            zero_supposed.enforce_zero();
873        }
874    }
875
876    #[test]
877    #[should_panic]
878    fn test_enforce_zero_panic() {
879        let mut prng = test_rng();
880        let r_biguint = SimFrParamsZorro::scalar_field_in_biguint();
881
882        let a = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
883        let b = prng.gen_biguint_range(&BigUint::zero(), &r_biguint);
884
885        let a_fr = SimFrTest::from(&a);
886        let b_fr = SimFrTest::from(&b);
887
888        let ab_fr_mul = &a_fr * &b_fr;
889        let ab_fr = &a * &b;
890        assert_eq!(ab_fr, (&ab_fr_mul).into());
891
892        let ab_fr_reduced_manipulated = &ab_fr % &r_biguint + &BigUint::from(10u64);
893        let ab_reduced_manipulated = SimFrTest::from(&ab_fr_reduced_manipulated);
894
895        let zero_supposed_manipulated = &ab_fr_mul - &ab_reduced_manipulated;
896        zero_supposed_manipulated.enforce_zero();
897    }
898}