mina_tree/proofs/
field.rs

1use ark_ec::{short_weierstrass::Projective, AffineRepr, CurveGroup};
2use ark_ff::{BigInteger256, FftField, Field, PrimeField};
3use kimchi::curve::KimchiCurve;
4use mina_curves::pasta::{
5    fields::fft::FpParameters as _, Fp, Fq, PallasParameters, ProjectivePallas, ProjectiveVesta,
6    VestaParameters,
7};
8use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSponge};
9
10use poseidon::SpongeParamsForField;
11
12use crate::proofs;
13
14use super::{
15    public_input::plonk_checks::{self, ShiftedValue},
16    to_field_elements::ToFieldElements,
17    transaction::Check,
18    witness::Witness,
19    BACKEND_TICK_ROUNDS_N, BACKEND_TOCK_ROUNDS_N,
20};
21
22pub type GroupAffine<F> = ark_ec::short_weierstrass::Affine<<F as FieldWitness>::Parameters>;
23
24/// All the generics we need during witness generation
25pub trait FieldWitness
26where
27    Self: Field
28        + Send
29        + Sync
30        + Into<BigInteger256>
31        + From<BigInteger256>
32        + Into<mina_p2p_messages::bigint::BigInt>
33        + From<i64>
34        + From<i32>
35        + ToFieldElements<Self>
36        + Check<Self>
37        + FromFpFq
38        + PrimeField
39        + FftField
40        + SpongeParamsForField<Self>
41        + std::fmt::Debug
42        + 'static,
43{
44    type Scalar: FieldWitness<Scalar = Self>;
45    type Affine: AffineRepr<
46            Group = Self::Projective,
47            BaseField = Self,
48            ScalarField = <Self as proofs::field::FieldWitness>::Scalar,
49        > + Into<GroupAffine<Self>>
50        + KimchiCurve
51        + std::fmt::Debug;
52    type Projective: CurveGroup<
53            Affine = Self::Affine,
54            BaseField = Self,
55            ScalarField = <Self as proofs::field::FieldWitness>::Scalar,
56        > + From<Projective<Self::Parameters>>
57        + std::fmt::Debug;
58    type Parameters: ark_ec::short_weierstrass::SWCurveConfig<
59            BaseField = Self,
60            ScalarField = <Self as proofs::field::FieldWitness>::Scalar,
61        > + Clone
62        + std::fmt::Debug;
63    type Shifting: plonk_checks::ShiftingValue<Self> + Clone + std::fmt::Debug;
64    type OtherCurve: KimchiCurve<
65        ScalarField = Self,
66        BaseField = <Self as proofs::field::FieldWitness>::Scalar,
67    >;
68    type FqSponge: Clone
69        + mina_poseidon::FqSponge<
70            <Self as proofs::field::FieldWitness>::Scalar,
71            Self::OtherCurve,
72            Self,
73        >;
74
75    const PARAMS: Params<Self>;
76    const SIZE: BigInteger256;
77    const NROUNDS: usize;
78    const SRS_DEPTH: usize;
79}
80
81pub struct Params<F> {
82    pub a: F,
83    pub b: F,
84}
85
86impl FieldWitness for Fp {
87    type Scalar = Fq;
88    type Parameters = PallasParameters;
89    type Affine = GroupAffine<Self>;
90    type Projective = ProjectivePallas;
91    type Shifting = ShiftedValue<Fp>;
92    type OtherCurve = GroupAffine<Fq>;
93    type FqSponge = DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>;
94
95    /// <https://github.com/openmina/mina/blob/46b6403cb7f158b66a60fc472da2db043ace2910/src/lib/crypto/kimchi_backend/pasta/basic/kimchi_pasta_basic.ml#L107>
96    const PARAMS: Params<Self> = Params::<Self> {
97        a: ark_ff::MontFp!("0"),
98        b: ark_ff::MontFp!("5"),
99    };
100    const SIZE: BigInteger256 = mina_curves::pasta::fields::FpParameters::MODULUS;
101    const NROUNDS: usize = BACKEND_TICK_ROUNDS_N;
102    const SRS_DEPTH: usize = 32768;
103}
104
105impl FieldWitness for Fq {
106    type Scalar = Fp;
107    type Parameters = VestaParameters;
108    type Affine = GroupAffine<Self>;
109    type Projective = ProjectiveVesta;
110    type Shifting = ShiftedValue<Fq>;
111    type OtherCurve = GroupAffine<Fp>;
112    type FqSponge = DefaultFqSponge<PallasParameters, PlonkSpongeConstantsKimchi>;
113
114    /// <https://github.com/openmina/mina/blob/46b6403cb7f158b66a60fc472da2db043ace2910/src/lib/crypto/kimchi_backend/pasta/basic/kimchi_pasta_basic.ml#L95>
115    const PARAMS: Params<Self> = Params::<Self> {
116        a: ark_ff::MontFp!("0"),
117        b: ark_ff::MontFp!("5"),
118    };
119    const SIZE: BigInteger256 = mina_curves::pasta::fields::FqParameters::MODULUS;
120    const NROUNDS: usize = BACKEND_TOCK_ROUNDS_N;
121    const SRS_DEPTH: usize = 65536;
122}
123
124/// Trait helping converting generics into concrete types
125pub trait FromFpFq {
126    fn from_fp(fp: Fp) -> Self;
127    fn from_fq(fp: Fq) -> Self;
128}
129
130impl FromFpFq for Fp {
131    fn from_fp(fp: Fp) -> Self {
132        fp
133    }
134    fn from_fq(_fq: Fq) -> Self {
135        // `Fq` cannot be converted into `Fp`.
136        // Caller must first split `Fq` into 2 parts (high & low bits)
137        // See `impl<F: FieldWitness> ToFieldElements<F> for Fq`
138        panic!("Attempt to convert a `Fq` into `Fp`")
139    }
140}
141
142impl FromFpFq for Fq {
143    fn from_fp(fp: Fp) -> Self {
144        // `Fp` is smaller than `Fq`, so the conversion is fine
145        let bigint: BigInteger256 = fp.into();
146        Self::from(bigint)
147    }
148    fn from_fq(fq: Fq) -> Self {
149        fq
150    }
151}
152
153/// Trait helping converting concrete types into generics
154pub trait IntoGeneric<F: FieldWitness> {
155    fn into_gen(self) -> F;
156}
157
158impl<F: FieldWitness> IntoGeneric<F> for Fp {
159    fn into_gen(self) -> F {
160        F::from_fp(self)
161    }
162}
163
164impl<F: FieldWitness> IntoGeneric<F> for Fq {
165    fn into_gen(self) -> F {
166        F::from_fq(self)
167    }
168}
169
170#[allow(clippy::module_inception)]
171pub mod field {
172    use crate::proofs::transaction::field_to_bits2;
173
174    use super::*;
175
176    // <https://github.com/o1-labs/snarky/blob/7edf13628872081fd7cad154de257dad8b9ba621/src/base/utils.ml#L99>
177    pub fn square<F>(field: F, w: &mut Witness<F>) -> F
178    where
179        F: FieldWitness,
180    {
181        w.exists(field.square())
182        // TODO: Rest of the function doesn't modify witness
183    }
184
185    pub fn mul<F>(x: F, y: F, w: &mut Witness<F>) -> F
186    where
187        F: FieldWitness,
188    {
189        w.exists(x * y)
190    }
191
192    pub fn const_mul<F>(x: F, y: F) -> F
193    where
194        F: FieldWitness,
195    {
196        x * y
197    }
198
199    pub fn muls<F>(xs: &[F], w: &mut Witness<F>) -> F
200    where
201        F: FieldWitness,
202    {
203        xs.iter()
204            .copied()
205            .reduce(|acc, v| w.exists(acc * v))
206            .expect("invalid param")
207    }
208
209    pub fn div<F>(x: F, y: F, w: &mut Witness<F>) -> F
210    where
211        F: FieldWitness,
212    {
213        w.exists(x / y)
214    }
215
216    // TODO: Do we need `div` above ?
217    pub fn div_by_inv<F>(x: F, y: F, w: &mut Witness<F>) -> F
218    where
219        F: FieldWitness,
220    {
221        let y_inv = w.exists(y.inverse().unwrap_or_else(F::zero));
222        mul(x, y_inv, w)
223    }
224
225    pub fn sub<F>(x: F, y: F, w: &mut Witness<F>) -> F
226    where
227        F: FieldWitness,
228    {
229        w.exists(x - y)
230    }
231
232    pub fn add<F>(x: F, y: F, w: &mut Witness<F>) -> F
233    where
234        F: FieldWitness,
235    {
236        w.exists(x + y)
237    }
238
239    pub fn const_add<F>(x: F, y: F) -> F
240    where
241        F: FieldWitness,
242    {
243        x + y
244    }
245
246    pub fn equal<F: FieldWitness>(x: F, y: F, w: &mut Witness<F>) -> Boolean {
247        let z = x - y;
248
249        let (boolean, r, inv) = if x == y {
250            (Boolean::True, F::one(), F::zero())
251        } else {
252            (Boolean::False, F::zero(), z.inverse().unwrap())
253        };
254        w.exists([r, inv]);
255
256        boolean
257    }
258
259    pub fn compare<F: FieldWitness>(
260        bit_length: u64,
261        a: F,
262        b: F,
263        w: &mut Witness<F>,
264    ) -> (Boolean, Boolean) {
265        let two_to_the = |n: usize| (0..n).fold(F::one(), |acc, _| acc.double());
266
267        let bit_length = bit_length as usize;
268        let alpha_packed = { two_to_the(bit_length) + b - a };
269        let alpha = w.exists(field_to_bits2(alpha_packed, bit_length + 1));
270        let (less_or_equal, prefix) = alpha.split_last().unwrap();
271
272        let less_or_equal = less_or_equal.to_boolean();
273        let prefix = prefix.iter().map(|b| b.to_boolean()).collect::<Vec<_>>();
274
275        let not_all_zeros = Boolean::any(&prefix, w);
276        let less = less_or_equal.and(&not_all_zeros, w);
277
278        (less, less_or_equal)
279    }
280
281    pub fn assert_lt<F: FieldWitness>(bit_length: u64, x: F, y: F, w: &mut Witness<F>) {
282        compare(bit_length, x, y, w);
283    }
284}
285
286pub trait ToBoolean {
287    fn to_boolean(&self) -> Boolean;
288}
289
290impl ToBoolean for bool {
291    fn to_boolean(&self) -> Boolean {
292        if *self {
293            Boolean::True
294        } else {
295            Boolean::False
296        }
297    }
298}
299
300#[derive(Clone, Copy, Debug, PartialEq, Eq)]
301pub enum Boolean {
302    True = 1,
303    False = 0,
304}
305
306impl Boolean {
307    pub fn of_field<F: FieldWitness>(field: F) -> Self {
308        if field.is_zero() {
309            Self::False
310        } else {
311            Self::True
312        }
313    }
314
315    pub fn as_bool(&self) -> bool {
316        match self {
317            Boolean::True => true,
318            Boolean::False => false,
319        }
320    }
321
322    pub fn neg(&self) -> Self {
323        match self {
324            Boolean::False => Boolean::True,
325            Boolean::True => Boolean::False,
326        }
327    }
328
329    pub fn to_field<F: FieldWitness>(self) -> F {
330        F::from(self.as_bool())
331    }
332
333    fn mul<F: FieldWitness>(&self, other: &Self, w: &mut Witness<F>) -> Self {
334        let result: F = self.to_field::<F>() * other.to_field::<F>();
335        w.exists(result);
336        if result.is_zero() {
337            Self::False
338        } else {
339            assert_eq!(result, F::one());
340            Self::True
341        }
342    }
343
344    pub fn and<F: FieldWitness>(&self, other: &Self, w: &mut Witness<F>) -> Self {
345        self.mul(other, w)
346    }
347
348    /// Same as `Self::and` but doesn't push values to the witness
349    pub fn const_and(&self, other: &Self) -> Self {
350        (self.as_bool() && other.as_bool()).to_boolean()
351    }
352
353    pub fn or<F: FieldWitness>(&self, other: &Self, w: &mut Witness<F>) -> Self {
354        let both_false = self.neg().and(&other.neg(), w);
355        both_false.neg()
356    }
357
358    /// Same as `Self::or` but doesn't push values to the witness
359    pub fn const_or(&self, other: &Self) -> Self {
360        (self.as_bool() || other.as_bool()).to_boolean()
361    }
362
363    pub fn all<F: FieldWitness>(x: &[Self], w: &mut Witness<F>) -> Self {
364        match x {
365            [] => Self::True,
366            [b1] => *b1,
367            [b1, b2] => b1.and(b2, w),
368            bs => {
369                let len = F::from(bs.len() as u64);
370                let sum = bs.iter().fold(0u64, |acc, b| {
371                    acc + match b {
372                        Boolean::True => 1,
373                        Boolean::False => 0,
374                    }
375                });
376                field::equal(len, F::from(sum), w)
377            }
378        }
379    }
380
381    pub fn const_all<F: FieldWitness>(x: &[Self]) -> Self {
382        match x {
383            [] => Self::True,
384            [b1] => *b1,
385            [b1, b2] => b1.const_and(b2),
386            bs => {
387                let len = F::from(bs.len() as u64);
388                let sum = bs.iter().fold(0u64, |acc, b| {
389                    acc + match b {
390                        Boolean::True => 1,
391                        Boolean::False => 0,
392                    }
393                });
394                (len == F::from(sum)).to_boolean()
395            }
396        }
397    }
398
399    // For non-constant
400    pub fn lxor<F: FieldWitness>(&self, other: &Self, w: &mut Witness<F>) -> Self {
401        let result = (self.as_bool() ^ other.as_bool()).to_boolean();
402        w.exists(result.to_field::<F>());
403        result
404    }
405
406    pub fn const_lxor(&self, other: &Self) -> Self {
407        (self.as_bool() ^ other.as_bool()).to_boolean()
408    }
409
410    pub fn equal<F: FieldWitness>(&self, other: &Self, w: &mut Witness<F>) -> Self {
411        self.lxor(other, w).neg()
412    }
413
414    pub fn const_equal(&self, other: &Self) -> Self {
415        (self.as_bool() == other.as_bool()).to_boolean()
416    }
417
418    pub fn const_any<F: FieldWitness>(x: &[Self]) -> Self {
419        match x {
420            [] => Self::False,
421            [b1] => *b1,
422            [b1, b2] => b1.const_or(b2),
423            bs => {
424                let sum = bs.iter().fold(0u64, |acc, b| {
425                    acc + match b {
426                        Boolean::True => 1,
427                        Boolean::False => 0,
428                    }
429                });
430                (F::from(sum) == F::zero()).to_boolean().neg()
431            }
432        }
433    }
434
435    pub fn any<F: FieldWitness>(x: &[Self], w: &mut Witness<F>) -> Self {
436        match x {
437            [] => Self::False,
438            [b1] => *b1,
439            [b1, b2] => b1.or(b2, w),
440            bs => {
441                let sum = bs.iter().fold(0u64, |acc, b| {
442                    acc + match b {
443                        Boolean::True => 1,
444                        Boolean::False => 0,
445                    }
446                });
447                field::equal(F::from(sum), F::zero(), w).neg()
448            }
449        }
450    }
451
452    // Part of utils.inv
453    pub fn assert_non_zero<F: FieldWitness>(v: F, w: &mut Witness<F>) {
454        if v.is_zero() {
455            w.exists(v);
456        } else {
457            w.exists(v.inverse().unwrap());
458        }
459    }
460
461    pub fn assert_any<F: FieldWitness>(bs: &[Self], w: &mut Witness<F>) {
462        let num_true = bs.iter().fold(0u64, |acc, b| {
463            acc + match b {
464                Boolean::True => 1,
465                Boolean::False => 0,
466            }
467        });
468        Self::assert_non_zero::<F>(F::from(num_true), w)
469    }
470
471    pub fn var(&self) -> CircuitVar<Boolean> {
472        CircuitVar::Var(*self)
473    }
474
475    pub fn constant(&self) -> CircuitVar<Boolean> {
476        CircuitVar::Constant(*self)
477    }
478}
479
480impl<F: FieldWitness> Check<F> for Boolean {
481    fn check(&self, _w: &mut Witness<F>) {
482        // Does not modify the witness
483    }
484}
485
486impl<F: FieldWitness> Check<F> for CircuitVar<Boolean> {
487    fn check(&self, _w: &mut Witness<F>) {
488        // Does not modify the witness
489    }
490}
491
492/// Our implementation of cvars (compare to OCaml) is incomplete, but sufficient
493/// for now (for witness generation).
494/// It's also sometimes not used correctly (`CircuitVar<GroupAffine<F>>` should be
495/// `GroupAffine<CircuitVar<F>>` instead)
496///
497/// Note that our implementation of `CircuitVar<Boolean>` is complete.
498#[derive(Clone, Copy, Debug)]
499pub enum CircuitVar<F> {
500    Var(F),
501    Constant(F),
502}
503
504impl<F: FieldWitness> CircuitVar<F> {
505    pub fn as_field(&self) -> F {
506        match self {
507            CircuitVar::Var(f) => *f,
508            CircuitVar::Constant(f) => *f,
509        }
510    }
511
512    fn scale(x: CircuitVar<F>, s: F) -> CircuitVar<F> {
513        use CircuitVar::*;
514
515        if s.is_zero() {
516            Constant(F::zero())
517        } else if s.is_one() {
518            x
519        } else {
520            match x {
521                Constant(x) => Constant(x * s),
522                Var(x) => Var(x * s),
523            }
524        }
525    }
526
527    fn mul(&self, other: &Self, w: &mut Witness<F>) -> CircuitVar<F> {
528        use CircuitVar::*;
529
530        let (x, y) = (*self, *other);
531        match (x, y) {
532            (Constant(x), Constant(y)) => Constant(x * y),
533            (Constant(x), _) => Self::scale(y, x),
534            (_, Constant(y)) => Self::scale(x, y),
535            (Var(x), Var(y)) => Var(w.exists(x * y)),
536        }
537    }
538
539    fn equal(x: &Self, y: &Self, w: &mut Witness<F>) -> CircuitVar<Boolean> {
540        match (x, y) {
541            (CircuitVar::Constant(x), CircuitVar::Constant(y)) => {
542                let eq = if x == y {
543                    Boolean::True
544                } else {
545                    Boolean::False
546                };
547                CircuitVar::Constant(eq)
548            }
549            _ => {
550                let x = x.as_field();
551                let y = y.as_field();
552                CircuitVar::Var(field::equal(x, y, w))
553            }
554        }
555    }
556}
557
558impl<T> CircuitVar<T> {
559    pub fn is_const(&self) -> bool {
560        match self {
561            CircuitVar::Var(_) => false,
562            CircuitVar::Constant(_) => true,
563        }
564    }
565
566    pub fn value(&self) -> &T {
567        match self {
568            CircuitVar::Var(v) => v,
569            CircuitVar::Constant(v) => v,
570        }
571    }
572
573    pub fn map<Fun, V>(&self, fun: Fun) -> CircuitVar<V>
574    where
575        Fun: Fn(&T) -> V,
576    {
577        match self {
578            CircuitVar::Var(v) => CircuitVar::Var(fun(v)),
579            CircuitVar::Constant(v) => CircuitVar::Constant(fun(v)),
580        }
581    }
582}
583
584impl CircuitVar<Boolean> {
585    pub fn as_boolean(&self) -> Boolean {
586        match self {
587            CircuitVar::Var(b) => *b,
588            CircuitVar::Constant(b) => *b,
589        }
590    }
591
592    fn as_cvar<F: FieldWitness>(&self) -> CircuitVar<F> {
593        self.map(|b| b.to_field::<F>())
594    }
595
596    pub fn of_cvar<F: FieldWitness>(cvar: CircuitVar<F>) -> Self {
597        cvar.map(|b| {
598            // TODO: Should we check for `is_one` or `is_zero` here ? To match OCaml behavior
599            if b.is_one() {
600                Boolean::True
601            } else {
602                Boolean::False
603            }
604        })
605    }
606
607    pub fn and<F: FieldWitness>(&self, other: &Self, w: &mut Witness<F>) -> Self {
608        self.as_cvar().mul(&other.as_cvar(), w).map(|v| {
609            // TODO: Should we check for `is_one` or `is_zero` here ? To match OCaml behavior
610            if v.is_one() {
611                Boolean::True
612            } else {
613                Boolean::False
614            }
615        })
616    }
617
618    fn boolean_sum<F: FieldWitness>(x: &[Self]) -> CircuitVar<F> {
619        let sum = x.iter().fold(0u64, |acc, b| {
620            acc + match b.as_boolean() {
621                Boolean::True => 1,
622                Boolean::False => 0,
623            }
624        });
625        if x.iter().all(|x| matches!(x, CircuitVar::Constant(_))) {
626            CircuitVar::Constant(F::from(sum))
627        } else {
628            CircuitVar::Var(F::from(sum))
629        }
630    }
631
632    pub fn any<F: FieldWitness>(x: &[Self], w: &mut Witness<F>) -> CircuitVar<Boolean> {
633        match x {
634            [] => CircuitVar::Constant(Boolean::False),
635            [b1] => *b1,
636            [b1, b2] => b1.or(b2, w),
637            bs => {
638                let sum = Self::boolean_sum(bs);
639                CircuitVar::equal(&sum, &CircuitVar::Constant(F::zero()), w).map(Boolean::neg)
640            }
641        }
642    }
643
644    pub fn all<F: FieldWitness>(x: &[Self], w: &mut Witness<F>) -> CircuitVar<Boolean> {
645        match x {
646            [] => CircuitVar::Constant(Boolean::True),
647            [b1] => *b1,
648            [b1, b2] => b1.and(b2, w),
649            bs => {
650                let sum = Self::boolean_sum(bs);
651                let len = F::from(bs.len() as u64);
652                CircuitVar::equal(&CircuitVar::Constant(len), &sum, w)
653            }
654        }
655    }
656
657    pub fn neg(&self) -> Self {
658        self.map(Boolean::neg)
659    }
660
661    pub fn or<F: FieldWitness>(&self, other: &Self, w: &mut Witness<F>) -> Self {
662        let both_false = self.neg().and(&other.neg(), w);
663        both_false.neg()
664    }
665
666    pub fn lxor<F: FieldWitness>(&self, other: &Self, w: &mut Witness<F>) -> Self {
667        match (self, other) {
668            (CircuitVar::Var(a), CircuitVar::Var(b)) => CircuitVar::Var(a.lxor(b, w)),
669            (CircuitVar::Constant(a), CircuitVar::Constant(b)) => {
670                CircuitVar::Constant(a.const_lxor(b))
671            }
672            (a, b) => CircuitVar::Var(a.as_boolean().const_lxor(&b.as_boolean())),
673        }
674    }
675
676    pub fn equal_bool<F: FieldWitness>(&self, other: &Self, w: &mut Witness<F>) -> Self {
677        self.lxor(other, w).neg()
678    }
679
680    pub fn assert_any<F: FieldWitness>(bs: &[Self], w: &mut Witness<F>) {
681        let num_true = bs.iter().fold(0u64, |acc, b| {
682            acc + match b.as_boolean() {
683                Boolean::True => 1,
684                Boolean::False => 0,
685            }
686        });
687        Boolean::assert_non_zero::<F>(F::from(num_true), w)
688    }
689}