1use std::rc::Rc;
2
3use ark_ff::{Field, One};
4use ark_poly::Radix2EvaluationDomain;
5use kimchi::{
6 circuits::expr::RowOffset,
7 curve::KimchiCurve,
8 proof::{PointEvaluations, ProofEvaluations},
9};
10use mina_curves::pasta::{Fp, Fq};
11
12use crate::{
13 proofs::{
14 field::{field, Boolean, FieldWitness},
15 public_input::plonk_checks::scalars::MinimalForScalar,
16 step::step_verifier::PlonkDomain,
17 to_field_elements::ToFieldElements,
18 witness::Witness,
19 wrap::{wrap_verifier::PlonkWithField, AllFeatureFlags},
20 },
21 scan_state::transaction_logic::local_state::LazyValue,
22};
23
24#[derive(Clone, Debug)]
25pub struct PlonkMinimal<F: FieldWitness, const NLIMB: usize = 2> {
26 pub alpha: F,
27 pub beta: F,
28 pub gamma: F,
29 pub zeta: F,
30 pub joint_combiner: Option<F>,
31 pub alpha_bytes: [u64; NLIMB],
32 pub beta_bytes: [u64; NLIMB],
33 pub gamma_bytes: [u64; NLIMB],
34 pub zeta_bytes: [u64; NLIMB],
35 pub joint_combiner_bytes: Option<[u64; NLIMB]>,
36 pub feature_flags: crate::proofs::step::FeatureFlags<bool>,
37}
38
39type TwoFields = [Fp; 2];
40
41pub struct ScalarsEnv<F: FieldWitness> {
42 pub zk_polynomial: F,
43 pub zeta_to_n_minus_1: F,
44 pub srs_length_log2: u64,
45 pub domain: Rc<dyn PlonkDomain<F>>,
46 pub omega_to_minus_zk_rows: F,
47 pub feature_flags: Option<AllFeatureFlags<F>>,
48 pub unnormalized_lagrange_basis: Option<Box<dyn Fn(RowOffset, &mut Witness<F>) -> F>>,
49 pub vanishes_on_zero_knowledge_and_previous_rows: F,
50 pub zeta_to_srs_length: LazyValue<F, Witness<F>>,
51}
52
53#[derive(Debug)]
55pub struct InCircuit<F: FieldWitness> {
56 pub alpha: F,
57 pub beta: F,
58 pub gamma: F,
59 pub zeta: F,
60 pub zeta_to_domain_size: F::Shifting,
61 pub zeta_to_srs_length: F::Shifting,
62 pub perm: F::Shifting,
63 pub lookup: Option<F>,
64 pub feature_flags: crate::proofs::step::FeatureFlags<bool>,
65}
66
67pub trait ShiftingValue<F: Field> {
68 type MyShift;
69 fn shift() -> Self::MyShift;
70 fn of_field(field: F) -> Self;
71 fn shifted_to_field(&self) -> F;
72 fn shifted_raw(&self) -> F;
73 fn of_raw(shifted: F) -> Self;
74}
75
76impl ShiftingValue<Fp> for ShiftedValue<Fp> {
77 type MyShift = Shift<Fp>;
78
79 fn shift() -> Self::MyShift {
80 type MyShift = Shift<Fp>;
81
82 cache_one! {MyShift, {
83 let c = (0..255).fold(Fp::one(), |accum, _| accum + accum) + Fp::one();
84
85 let scale: Fp = 2.into();
86 let scale = scale.inverse().unwrap();
87
88 Shift { c, scale }
89 }}
90 }
91
92 fn of_field(field: Fp) -> Self {
93 let shift = Self::shift();
94 Self {
95 shifted: (field - shift.c) * shift.scale,
96 }
97 }
98
99 fn shifted_to_field(&self) -> Fp {
100 let shift = Self::shift();
101 self.shifted + self.shifted + shift.c
102 }
103
104 fn shifted_raw(&self) -> Fp {
105 self.shifted
106 }
107
108 fn of_raw(shifted: Fp) -> Self {
109 Self { shifted }
110 }
111}
112
113impl ShiftingValue<Fq> for ShiftedValue<Fq> {
114 type MyShift = ShiftFq;
115
116 fn shift() -> Self::MyShift {
117 cache_one! {ShiftFq, {
118 ShiftFq {
119 shift: (0..255).fold(Fq::one(), |accum, _| accum + accum),
120 }
121 }}
122 }
123
124 fn of_field(field: Fq) -> Self {
125 let shift = Self::shift();
126 Self {
127 shifted: field - shift.shift,
128 }
129 }
130
131 fn shifted_to_field(&self) -> Fq {
132 let shift = Self::shift();
133 self.shifted + shift.shift
134 }
135
136 fn shifted_raw(&self) -> Fq {
137 self.shifted
138 }
139
140 fn of_raw(shifted: Fq) -> Self {
141 Self { shifted }
142 }
143}
144
145#[derive(Clone, Debug)]
146pub struct ShiftFq {
147 shift: Fq,
148}
149
150#[derive(Clone, Debug)]
151pub struct Shift<F: Field> {
152 c: F,
153 scale: F,
154}
155
156impl<F> Shift<F>
157where
158 F: Field + From<i32>,
159{
160 pub fn create() -> Self {
162 let c = (0..255).fold(F::one(), |accum, _| accum + accum) + F::one();
163
164 let scale: F = 2.into();
165 let scale = scale.inverse().unwrap();
166
167 Self { c, scale } }
169}
170
171#[derive(Clone, Debug)]
172pub struct ShiftedValue<F: Field> {
173 pub shifted: F,
174}
175
176impl<F: FieldWitness, F2: FieldWitness + ToFieldElements<F>> ToFieldElements<F>
189 for ShiftedValue<F2>
190{
191 fn to_field_elements(&self, fields: &mut Vec<F>) {
192 let Self { shifted } = self;
193 shifted.to_field_elements(fields);
194 }
195}
196
197impl<F> ShiftedValue<F>
198where
199 F: Field,
200{
201 pub fn new(field: F) -> Self {
203 Self { shifted: field }
204 }
205
206 }
219
220pub const PERM_ALPHA0: usize = 21;
222
223pub const NPOWERS_OF_ALPHA: usize = PERM_ALPHA0 + 3;
224
225pub fn powers_of_alpha<F: FieldWitness>(alpha: F) -> Box<[F; NPOWERS_OF_ALPHA]> {
227 let mut alphas = Box::new([F::one(); NPOWERS_OF_ALPHA]);
229
230 alphas[1] = alpha;
231 for i in 2..alphas.len() {
232 alphas[i] = alpha * alphas[i - 1];
233 }
234
235 alphas
236}
237
238pub fn derive_plonk<F: FieldWitness, const NLIMB: usize>(
239 env: &ScalarsEnv<F>,
240 evals: &ProofEvaluations<PointEvaluations<F>>,
241 minimal: &PlonkMinimal<F, NLIMB>,
242) -> InCircuit<F> {
243 let PlonkMinimal {
244 alpha,
245 beta,
246 gamma,
247 joint_combiner,
248 feature_flags: actual_feature_flags,
249 ..
250 } = minimal;
251
252 let zkp = env.zk_polynomial;
253 let powers_of_alpha = powers_of_alpha(*alpha);
254 let alpha_pow = |i: usize| powers_of_alpha[i];
255 let w0 = evals.w.map(|point| point.zeta);
256
257 let beta = *beta;
258 let gamma = *gamma;
259
260 let perm = evals.s.iter().enumerate().fold(
262 evals.z.zeta_omega * beta * alpha_pow(PERM_ALPHA0) * zkp,
263 |accum, (index, elem)| accum * (gamma + (beta * elem.zeta) + w0[index]),
264 );
265 let perm = -perm;
266
267 let zeta_to_domain_size = env.zeta_to_n_minus_1 + F::one();
268
269 let zeta_to_srs_length = {
270 let mut unused_w = Witness::empty();
271 *env.zeta_to_srs_length.get(&mut unused_w)
272 };
273
274 let shift = |f: F| F::Shifting::of_field(f);
276
277 InCircuit {
278 alpha: minimal.alpha,
279 beta: minimal.beta,
280 gamma: minimal.gamma,
281 zeta: minimal.zeta,
282 zeta_to_domain_size: shift(zeta_to_domain_size),
283 zeta_to_srs_length: shift(zeta_to_srs_length),
284 perm: shift(perm),
285 lookup: joint_combiner
286 .as_ref()
287 .map(|joint_combiner| *joint_combiner),
288 feature_flags: actual_feature_flags.clone(),
289 }
290}
291
292pub fn derive_plonk_checked<F: FieldWitness>(
294 env: &ScalarsEnv<F>,
295 evals: &ProofEvaluations<PointEvaluations<F>>,
296 minimal: &PlonkWithField<F>,
297 w: &mut Witness<F>,
298) -> InCircuit<F> {
299 let zkp = env.zk_polynomial;
302 let powers_of_alpha = powers_of_alpha(minimal.alpha);
303 let alpha_pow = |i: usize| powers_of_alpha[i];
304 let w0 = evals.w.map(|point| point.zeta);
305
306 let beta = minimal.beta;
307 let gamma = minimal.gamma;
308
309 let perm = evals.s.iter().enumerate().fold(
310 field::muls(&[evals.z.zeta_omega, beta, alpha_pow(PERM_ALPHA0), zkp], w),
311 |accum, (index, elem)| {
312 let beta_elem = field::mul(beta, elem.zeta, w);
314 field::mul(accum, gamma + beta_elem + w0[index], w)
315 },
316 );
317 let perm = -perm;
318
319 let zeta_to_domain_size = env.zeta_to_n_minus_1 + F::one();
320 let zeta_to_srs_length = *env.zeta_to_srs_length.get(w);
336
337 let shift = |f: F| F::Shifting::of_field(f);
339
340 InCircuit {
341 alpha: minimal.alpha,
342 beta: minimal.beta,
343 gamma: minimal.gamma,
344 zeta: minimal.zeta,
345 zeta_to_domain_size: shift(zeta_to_domain_size),
346 zeta_to_srs_length: shift(zeta_to_srs_length),
347 perm: shift(perm),
348 lookup: None,
349 feature_flags: crate::proofs::step::FeatureFlags::empty_bool(),
350 }
351}
352
353pub fn checked<F: FieldWitness>(
354 env: &ScalarsEnv<F>,
355 evals: &ProofEvaluations<PointEvaluations<F>>,
356 plonk: &PlonkWithField<F>,
357 w: &mut Witness<F>,
358) -> Boolean {
359 let actual = derive_plonk_checked(env, evals, plonk, w);
360
361 let list = [
362 field::equal(plonk.perm.shifted, actual.perm.shifted_raw(), w),
366 ];
367
368 Boolean::all(&list[..], w)
369}
370
371pub fn make_shifts<F: FieldWitness>(
372 domain: &Radix2EvaluationDomain<F>,
373) -> kimchi::circuits::polynomials::permutation::Shifts<F> {
374 kimchi::circuits::polynomials::permutation::Shifts::new(domain)
377}
378
379pub fn ft_eval0<F: FieldWitness, const NLIMB: usize>(
380 env: &ScalarsEnv<F>,
381 evals: &ProofEvaluations<PointEvaluations<F>>,
382 minimal: &PlonkMinimal<F, NLIMB>,
383 p_eval0: &[F],
384) -> F {
385 const PLONK_TYPES_PERMUTS_MINUS_1_N: usize = 6;
386
387 let e0_s: Vec<_> = evals.s.iter().map(|s| s.zeta).collect();
388 let zkp = env.zk_polynomial;
389 let powers_of_alpha = powers_of_alpha(minimal.alpha);
390 let alpha_pow = |i: usize| powers_of_alpha[i];
391
392 let zeta1m1 = env.zeta_to_n_minus_1;
393
394 let mut unused_w = Witness::empty();
395 let p_eval0 = p_eval0
396 .iter()
397 .copied()
398 .rfold(None, |acc, p_eval0| match acc {
399 None => Some(p_eval0),
400 Some(acc) => {
401 let zeta1 = *env.zeta_to_srs_length.get(&mut unused_w);
402 Some(p_eval0 + (zeta1 * acc))
403 }
404 })
405 .unwrap(); let w0: Vec<_> = evals.w.iter().map(|w| w.zeta).collect();
408
409 let ft_eval0 = {
410 let a0 = alpha_pow(PERM_ALPHA0);
411 let w_n = w0[PLONK_TYPES_PERMUTS_MINUS_1_N];
412 let init = (w_n + minimal.gamma) * evals.z.zeta_omega * a0 * zkp;
413 e0_s.iter().enumerate().fold(init, |acc, (i, s)| {
414 ((minimal.beta * s) + w0[i] + minimal.gamma) * acc
415 })
416 };
417
418 let shifts = env.domain.shifts();
419 let ft_eval0 = ft_eval0 - p_eval0;
420
421 let ft_eval0 = ft_eval0
422 - shifts.iter().enumerate().fold(
423 alpha_pow(PERM_ALPHA0) * zkp * evals.z.zeta,
424 |acc, (i, s)| acc * (minimal.gamma + (minimal.beta * minimal.zeta * s) + w0[i]),
425 );
426
427 let nominator =
428 (zeta1m1 * alpha_pow(PERM_ALPHA0 + 1) * (minimal.zeta - env.omega_to_minus_zk_rows)
429 + (zeta1m1 * alpha_pow(PERM_ALPHA0 + 2) * (minimal.zeta - F::one())))
430 * (F::one() - evals.z.zeta);
431
432 let denominator = (minimal.zeta - env.omega_to_minus_zk_rows) * (minimal.zeta - F::one());
433 let ft_eval0 = ft_eval0 + (nominator / denominator);
434
435 let minimal = MinimalForScalar {
436 alpha: minimal.alpha,
437 beta: minimal.beta,
438 gamma: minimal.gamma,
439 lookup: minimal.joint_combiner,
440 };
441 let mut w = Witness::empty();
442 let constant_term = scalars::compute(None, &minimal, evals, env, &mut w);
443
444 ft_eval0 - constant_term
445}
446
447fn get_feature_flag<F: FieldWitness>(
448 feature_flags: &AllFeatureFlags<F>,
449 feature: &kimchi::circuits::expr::FeatureFlag,
450 w: &mut Witness<F>,
451) -> Option<Boolean> {
452 use kimchi::circuits::{expr::FeatureFlag::*, lookup::lookups::LookupPattern};
453
454 match feature {
455 RangeCheck0 => Some(feature_flags.features.range_check0),
456 RangeCheck1 => Some(feature_flags.features.range_check1),
457 ForeignFieldAdd => Some(feature_flags.features.foreign_field_add),
458 ForeignFieldMul => Some(feature_flags.features.foreign_field_mul),
459 Xor => Some(feature_flags.features.xor),
460 Rot => Some(feature_flags.features.rot),
461 LookupTables => Some(*feature_flags.lookup_tables.get(w)),
462 RuntimeLookupTables => Some(feature_flags.features.runtime_tables),
463 TableWidth(3) => Some(*feature_flags.table_width_3.get(w)),
464 TableWidth(2) => Some(*feature_flags.table_width_at_least_2.get(w)),
465 TableWidth(i) if *i <= 1 => Some(*feature_flags.table_width_at_least_1.get(w)),
466 TableWidth(_) => None,
467 LookupsPerRow(4) => Some(*feature_flags.lookups_per_row_4.get(w)),
468 LookupsPerRow(i) if *i <= 3 => Some(*feature_flags.lookups_per_row_3.get(w)),
469 LookupsPerRow(_) => None,
470 LookupPattern(LookupPattern::Lookup) => Some(feature_flags.features.lookup),
471 LookupPattern(LookupPattern::Xor) => Some(*feature_flags.lookup_pattern_xor.get(w)),
472 LookupPattern(LookupPattern::RangeCheck) => {
473 Some(*feature_flags.lookup_pattern_range_check.get(w))
474 }
475 LookupPattern(LookupPattern::ForeignFieldMul) => {
476 Some(feature_flags.features.foreign_field_mul)
477 }
478 }
479}
480
481mod scalars {
482 use std::collections::BTreeMap;
483
484 use kimchi::{
485 circuits::{
486 constraints::FeatureFlags,
487 expr::{CacheId, Column, ConstantExpr, Constants, Expr, ExprError, Op2, Variable},
488 gate::{CurrOrNext, GateType},
489 lookup::lookups::{LookupFeatures, LookupPatterns},
490 },
491 proof::PointEvaluations,
492 };
493
494 use crate::proofs::transaction::endos;
495
496 use super::*;
497
498 fn var_evaluate<F: FieldWitness>(
500 v: &Variable,
501 evals: &ProofEvaluations<PointEvaluations<F>>,
502 ) -> Result<F, ExprError> {
503 let point_evaluations = {
504 use kimchi::circuits::lookup::lookups::LookupPattern;
505 use Column::*;
506
507 match v.col {
508 Witness(i) => Ok(evals.w[i]),
509 Z => Ok(evals.z),
510 LookupSorted(i) => {
511 evals.lookup_sorted[i].ok_or(ExprError::MissingIndexEvaluation(v.col))
512 }
513 LookupAggreg => evals
514 .lookup_aggregation
515 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
516 LookupTable => evals
517 .lookup_table
518 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
519 LookupRuntimeTable => evals
520 .runtime_lookup_table
521 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
522 Index(GateType::Poseidon) => Ok(evals.poseidon_selector),
523 Index(GateType::Generic) => Ok(evals.generic_selector),
524 Index(GateType::CompleteAdd) => Ok(evals.complete_add_selector),
525 Index(GateType::VarBaseMul) => Ok(evals.mul_selector),
526 Index(GateType::EndoMul) => Ok(evals.emul_selector),
527 Index(GateType::EndoMulScalar) => Ok(evals.endomul_scalar_selector),
528 Index(GateType::RangeCheck0) => evals
529 .range_check0_selector
530 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
531 Index(GateType::RangeCheck1) => evals
532 .range_check1_selector
533 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
534 Index(GateType::ForeignFieldAdd) => evals
535 .foreign_field_add_selector
536 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
537 Index(GateType::ForeignFieldMul) => evals
538 .foreign_field_mul_selector
539 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
540 Index(GateType::Xor16) => evals
541 .xor_selector
542 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
543 Index(GateType::Rot64) => evals
544 .rot_selector
545 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
546 Permutation(i) => Ok(evals.s[i]),
547 Coefficient(i) => Ok(evals.coefficients[i]),
548 Column::LookupKindIndex(LookupPattern::Xor) => evals
549 .xor_lookup_selector
550 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
551 Column::LookupKindIndex(LookupPattern::Lookup) => evals
552 .lookup_gate_lookup_selector
553 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
554 Column::LookupKindIndex(LookupPattern::RangeCheck) => evals
555 .range_check_lookup_selector
556 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
557 Column::LookupKindIndex(LookupPattern::ForeignFieldMul) => evals
558 .foreign_field_mul_lookup_selector
559 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
560 Column::LookupRuntimeSelector => evals
561 .runtime_lookup_table_selector
562 .ok_or(ExprError::MissingIndexEvaluation(v.col)),
563 Index(_) => Err(ExprError::MissingIndexEvaluation(v.col)),
564 }
565 }?;
566 match v.row {
567 CurrOrNext::Curr => Ok(point_evaluations.zeta),
568 CurrOrNext::Next => Ok(point_evaluations.zeta_omega),
569 }
570 }
571
572 fn pow<F: FieldWitness>(x: F, n: u64, w: &mut Witness<F>) -> F {
573 if n == 0 {
574 F::one()
575 } else if n == 1 {
576 x
577 } else {
578 let y = pow(field::square(x, w), n / 2, w);
579 if n % 2 == 0 {
580 y
581 } else {
582 field::mul(x, y, w)
583 }
584 }
585 }
586
587 fn pow_const<F: FieldWitness>(x: F, n: u64) -> F {
588 if n == 0 {
589 F::one()
590 } else if n == 1 {
591 x
592 } else {
593 (0..n - 1).fold(x, |acc, _| x * acc)
594 }
595 }
596
597 pub struct EvalContext<'a, F: FieldWitness> {
598 pub evals: &'a ProofEvaluations<PointEvaluations<F>>,
599 pub constants: &'a Constants<F>,
600 pub cache: BTreeMap<CacheId, F>,
601 pub env: &'a ScalarsEnv<F>,
602 pub w: &'a mut Witness<F>,
603 }
604
605 fn is_const<F: FieldWitness>(e: &Expr<ConstantExpr<F>>) -> bool {
607 use ConstantExpr::*;
608 match e {
609 Expr::Constant(c) => matches!(c, EndoCoefficient | Literal(_) | Mds { .. }),
610 Expr::BinOp(_, x, y) => is_const(x) && is_const(y),
611 Expr::Pow(x, _) => is_const(x),
612 _ => false,
613 }
614 }
615
616 pub fn eval<F: FieldWitness>(e: &Expr<ConstantExpr<F>>, ctx: &mut EvalContext<F>) -> F {
617 use Expr::*;
618 match e {
619 Double(x) => {
620 let v = eval(x, ctx);
621 v.double()
622 }
623 Constant(x) => {
624 let v = x.value(ctx.constants);
625 if let ConstantExpr::Mul(_, _) = x {
626 ctx.w.exists_no_check(v);
627 };
628 v
629 }
630 Pow(x, p) => {
631 let p = *p;
632 let v = eval(x, ctx);
633
634 if is_const(x) {
635 pow_const(v, p)
636 } else {
637 pow(v, p, ctx.w)
638 }
639 }
640 BinOp(Op2::Mul, x, y) => {
641 let is_x_const = is_const(x);
642 let is_y_const = is_const(y);
643 let y = eval(y, ctx);
644 let x = eval(x, ctx);
645 if is_x_const || is_y_const {
646 x * y
647 } else {
648 field::mul(x, y, ctx.w)
649 }
650 }
651 Square(x) => {
652 let is_x_const = is_const(x);
653 let x = eval(x, ctx);
654 if is_x_const {
655 x * x
656 } else {
657 field::mul(x, x, ctx.w)
658 }
659 }
660 BinOp(Op2::Add, x, y) => {
661 let y = eval(y, ctx);
662 let x = eval(x, ctx);
663 x + y
664 }
665 BinOp(Op2::Sub, x, y) => {
666 let y = eval(y, ctx);
667 let x = eval(x, ctx);
668 x - y
669 }
670 VanishesOnZeroKnowledgeAndPreviousRows => {
671 ctx.env.vanishes_on_zero_knowledge_and_previous_rows
672 }
673 UnnormalizedLagrangeBasis(i) => {
674 let unnormalized_lagrange_basis =
675 ctx.env.unnormalized_lagrange_basis.as_ref().unwrap();
676 unnormalized_lagrange_basis(*i, ctx.w)
677 }
678 Cell(v) => {
679 var_evaluate(v, ctx.evals).unwrap_or_else(|_| F::zero()) }
681 Cache(id, _e) => {
682 ctx.cache.get(id).copied().unwrap() }
684 IfFeature(feature, e1, e2) => match ctx.env.feature_flags.as_ref() {
685 None => eval(e2, ctx),
686 Some(feature_flags) => {
687 let is_feature_enabled = match get_feature_flag(feature_flags, feature, ctx.w) {
688 None => return eval(e2, ctx),
689 Some(enabled) => enabled,
690 };
691
692 let on_false = eval(e2, ctx);
693 let on_true = eval(e1, ctx);
694
695 ctx.w.exists_no_check(match is_feature_enabled {
696 Boolean::True => on_true,
697 Boolean::False => on_false,
698 })
699 }
700 },
701 }
702 }
703
704 #[derive(Default)]
705 pub struct Cached<F: FieldWitness> {
706 expr: BTreeMap<CacheId, (Box<Cached<F>>, Box<Expr<ConstantExpr<F>>>)>,
708 }
709
710 #[inline(never)]
711 pub fn extract_caches<F: FieldWitness>(e: &Expr<ConstantExpr<F>>, cache: &mut Cached<F>) {
712 use Expr::*;
713 match e {
714 Double(x) => {
715 extract_caches(x, cache);
716 }
717 Constant(_x) => (),
718 Pow(x, _p) => {
719 extract_caches(x, cache);
720 }
721 BinOp(Op2::Mul, x, y) => {
722 extract_caches(y, cache);
723 extract_caches(x, cache);
724 }
725 Square(x) => {
726 extract_caches(x, cache);
727 }
728 BinOp(Op2::Add, x, y) => {
729 extract_caches(y, cache);
730 extract_caches(x, cache);
731 }
732 BinOp(Op2::Sub, x, y) => {
733 extract_caches(y, cache);
734 extract_caches(x, cache);
735 }
736 VanishesOnZeroKnowledgeAndPreviousRows => todo!(),
737 UnnormalizedLagrangeBasis(_i) => todo!(),
738 Cell(_v) => (),
739 Cache(id, e) => {
740 let mut cached = Cached::default();
741 extract_caches(e, &mut cached);
742 cache.expr.insert(*id, (Box::new(cached), e.clone()));
743 }
744 IfFeature(_feature, e1, e2) => {
745 if false {
746 extract_caches(e1, cache)
747 } else {
748 extract_caches(e2, cache)
749 }
750 }
751 }
752 }
753
754 fn eval_cache<F: FieldWitness>(cached_exprs: &Cached<F>, ctx: &mut EvalContext<F>) {
755 for (id, (cache, expr)) in &cached_exprs.expr {
757 let mut old_cache = std::mem::take(&mut ctx.cache);
758 eval_cache::<F>(cache, ctx);
759 old_cache.insert(*id, eval::<F>(expr, ctx));
760 ctx.cache = old_cache;
761 }
762 }
763
764 pub struct MinimalForScalar<F> {
765 pub alpha: F,
766 pub beta: F,
767 pub gamma: F,
768 pub lookup: Option<F>,
769 }
770
771 pub fn compute<F: FieldWitness>(
772 gate: Option<GateType>,
773 minimal: &MinimalForScalar<F>,
774 evals: &ProofEvaluations<PointEvaluations<F>>,
775 env: &ScalarsEnv<F>,
776 w: &mut Witness<F>,
777 ) -> F {
778 let (constant_term, index_terms) = &*{
779 type TermsMap<F> = BTreeMap<Column, Expr<ConstantExpr<F>>>;
780 type Const<F> = Expr<ConstantExpr<F>>;
781 type Terms<F> = Rc<(Const<F>, TermsMap<F>)>;
782 cache! {
783 Terms::<F>, {
784 let is_fp = std::any::TypeId::of::<F>() == std::any::TypeId::of::<Fp>();
787
788 let features = if is_fp {
789 None
790 } else {
791 Some(FeatureFlags {
792 range_check0: false,
793 range_check1: false,
794 foreign_field_add: false,
795 foreign_field_mul: false,
796 xor: false,
797 rot: false,
798 lookup_features: LookupFeatures {
799 patterns: LookupPatterns {
800 xor: false,
801 lookup: false,
802 range_check: false,
803 foreign_field_mul: false,
804 },
805 joint_lookup_used: false,
806 uses_runtime_tables: false,
807 },
808 })
809 };
810
811 let evaluated_cols =
812 kimchi::linearization::linearization_columns::<F>(features.as_ref());
813 let (linearization, _powers_of_alpha) =
814 kimchi::linearization::constraints_expr::<F>(features.as_ref(), true);
815
816 let kimchi::circuits::expr::Linearization {
817 constant_term,
818 index_terms,
819 } = linearization.linearize(evaluated_cols).unwrap();
820
821 let index_terms = index_terms.into_iter().collect::<TermsMap<F>>();
822 Rc::new((constant_term, index_terms))
823 }
824 }
825 };
826
827 let constants = kimchi::circuits::expr::Constants::<F> {
828 alpha: minimal.alpha,
829 beta: minimal.beta,
830 gamma: minimal.gamma,
831 joint_combiner: minimal.lookup,
832 endo_coefficient: {
833 let (base, _) = endos::<F>();
834 base
835 },
836 mds: &<F::OtherCurve>::sponge_params().mds,
837 zk_rows: 3,
838 };
839
840 let mut ctx = EvalContext {
841 evals,
842 constants: &constants,
843 cache: BTreeMap::new(),
844 env,
845 w,
846 };
847
848 let term = match gate {
849 Some(gate) => index_terms.get(&Column::Index(gate)).unwrap(),
850 None => constant_term,
851 };
852
853 let mut cached_exprs = Cached::default();
855 extract_caches(term, &mut cached_exprs);
856 eval_cache(&cached_exprs, &mut ctx);
857
858 eval(term, &mut ctx)
860 }
861}
862
863pub fn ft_eval0_checked<F: FieldWitness, const NLIMB: usize>(
865 env: &ScalarsEnv<F>,
866 evals: &ProofEvaluations<PointEvaluations<F>>,
867 minimal: &PlonkMinimal<F, NLIMB>,
868 lookup: Option<F>,
869 p_eval0: &[F],
870 w: &mut Witness<F>,
871) -> F {
872 const PLONK_TYPES_PERMUTS_MINUS_1_N: usize = 6;
873
874 let e0_s: Vec<_> = evals.s.iter().map(|s| s.zeta).collect();
875 let zkp = env.zk_polynomial;
876 let powers_of_alpha = powers_of_alpha(minimal.alpha);
877 let alpha_pow = |i: usize| powers_of_alpha[i];
878
879 let zeta1m1 = env.zeta_to_n_minus_1;
880 let p_eval0 = p_eval0
881 .iter()
882 .copied()
883 .rfold(None, |acc, p_eval0| match acc {
884 None => Some(p_eval0),
885 Some(acc) => {
886 let zeta1 = *env.zeta_to_srs_length.get(w);
887 Some(p_eval0 + field::mul(zeta1, acc, w))
888 }
889 })
890 .unwrap(); let w0: Vec<_> = evals.w.iter().map(|w| w.zeta).collect();
892
893 let ft_eval0 = {
894 let a0 = alpha_pow(PERM_ALPHA0);
895 let w_n = w0[PLONK_TYPES_PERMUTS_MINUS_1_N];
896 let init = field::muls(&[(w_n + minimal.gamma), evals.z.zeta_omega, a0, zkp], w);
897 e0_s.iter().enumerate().fold(init, |acc, (i, s)| {
898 let beta_s = field::mul(minimal.beta, *s, w);
900 field::mul(beta_s + w0[i] + minimal.gamma, acc, w)
901 })
902 };
903
904 let shifts = env.domain.shifts();
905 let ft_eval0 = ft_eval0 - p_eval0;
906
907 let ft_eval0 = ft_eval0
908 - shifts.iter().enumerate().fold(
909 field::muls(&[alpha_pow(PERM_ALPHA0), zkp, evals.z.zeta], w),
910 |acc, (i, s)| {
911 let beta_zeta = field::mul(minimal.beta, minimal.zeta, w);
912 field::mul(acc, minimal.gamma + (beta_zeta * s) + w0[i], w)
913 },
914 );
915
916 let a = field::muls(
918 &[
919 zeta1m1,
920 alpha_pow(PERM_ALPHA0 + 2),
921 (minimal.zeta - F::one()),
922 ],
923 w,
924 );
925 let b = field::muls(
926 &[
927 zeta1m1,
928 alpha_pow(PERM_ALPHA0 + 1),
929 (minimal.zeta - env.omega_to_minus_zk_rows),
930 ],
931 w,
932 );
933 let nominator = field::mul(a + b, F::one() - evals.z.zeta, w);
934
935 let denominator = field::mul(
936 minimal.zeta - env.omega_to_minus_zk_rows,
937 minimal.zeta - F::one(),
938 w,
939 );
940 let ft_eval0 = ft_eval0 + field::div_by_inv(nominator, denominator, w);
941
942 let minimal = MinimalForScalar {
943 alpha: minimal.alpha,
944 beta: minimal.beta,
945 gamma: minimal.gamma,
946 lookup,
947 };
948 let constant_term = scalars::compute(None, &minimal, evals, env, w);
949 ft_eval0 - constant_term
950}
951
952#[cfg(test)]
953mod tests {
954 use std::str::FromStr;
955
956 use crate::FpExt;
957
958 use super::*;
959
960 #[cfg(target_family = "wasm")]
961 use wasm_bindgen_test::wasm_bindgen_test as test;
962
963 #[test]
1191 fn test_alphas() {
1192 let n = Fp::from_str(
1193 "27274897876953793245985525242013286410205575357216365244783619058623821516088",
1194 )
1195 .unwrap();
1196
1197 let alphas: Box<[Fp; NPOWERS_OF_ALPHA]> = powers_of_alpha(n);
1198 let alphas_str: Vec<String> = alphas.iter().map(|f| f.to_decimal()).collect();
1199
1200 const OCAML_RESULT: &[&str] = &[
1201 "1",
1202 "27274897876953793245985525242013286410205575357216365244783619058623821516088",
1203 "5856243499679297994261942705106783326584825647279332525318074626467168425175",
1204 "26908526253468636093650206549302380737071523922183255477383956748755441012366",
1205 "21276200075660690362913766498168565850417909161152737384646582509540496229450",
1206 "3843731251681147173193384676587074004662025496739119332721571982426684387560",
1207 "12392606098341916760701161625583524765199435768082801118718099569066567820086",
1208 "5932489972119399045562481763112253944218195162891420406370178296693572483896",
1209 "1375846522483390900802414356841133463956126287864007136159978297384640659584",
1210 "5356524575738460513076981906288272723856440519543693179836517878630162813220",
1211 "23319398249603527452857836680743813857193813763032214190196631633251915644825",
1212 "10921184148344839491052929288136821627436657352065581423854521247501001908351",
1213 "13053560967285308651226207033123539702290413328361716005386653453569329750313",
1214 "8298101552564684053050013414211292674866114224797784754887740268228151928335",
1215 "715072795965317694491886715913315968459520650830405802156784401283709943505",
1216 "25198551493059869063561311792478884528738012039746184861146867788131566740666",
1217 "27161703551928606962685117055547438689494792119791879693135179256422752270728",
1218 "28799358614011589987311924793640447939591189984280570017428244220659375622447",
1219 "4488279652568453906961591843014473441709515392753701104095475657832824041646",
1220 "4641946865609115816676535679719511429699894348223929677606063307711524129548",
1221 "995093492640264169583875280706844374785298168266651011740457078469635678163",
1222 "17429526728376789811772110265115435172515921536052154380599101096979177652072",
1223 "22850194394147425267881995863659224768162140900074856912248813188202263996579",
1224 "26770317988143355422138083990683491489315652370177339626307684951664128480053",
1225 ];
1226
1227 assert_eq!(alphas_str, OCAML_RESULT);
1228 }
1229}