mina_tree/proofs/
field.rs

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