snark_verifier/system/
halo2.rs

1//! [`halo2_proofs`](crate::halo2_proofs) proof system
2
3use crate::halo2_proofs::{
4    plonk::{self, Any, ConstraintSystem, FirstPhase, SecondPhase, ThirdPhase, VerifyingKey},
5    poly::{self, commitment::Params},
6    transcript::{EncodedChallenge, Transcript},
7};
8use crate::{
9    util::{
10        arithmetic::{root_of_unity, CurveAffine, Domain, PrimeField, Rotation},
11        Itertools,
12    },
13    verifier::plonk::protocol::{
14        CommonPolynomial, Expression, InstanceCommittingKey, PlonkProtocol, Query,
15        QuotientPolynomial,
16    },
17};
18use num_integer::Integer;
19use std::{io, iter, mem::size_of};
20
21pub mod strategy;
22pub mod transcript;
23
24/// Configuration for converting a [`VerifyingKey`] of [`halo2_proofs`](crate::halo2_proofs) into
25/// [`PlonkProtocol`].
26#[derive(Clone, Debug, Default)]
27pub struct Config {
28    zk: bool,
29    query_instance: bool,
30    num_proof: usize,
31    num_instance: Vec<usize>,
32    accumulator_indices: Option<Vec<(usize, usize)>>,
33}
34
35impl Config {
36    /// Returns [`Config`] with `query_instance` set to `false`.
37    pub fn kzg() -> Self {
38        Self { zk: true, query_instance: false, num_proof: 1, ..Default::default() }
39    }
40
41    /// Returns [`Config`] with `query_instance` set to `true`.
42    pub fn ipa() -> Self {
43        Self { zk: true, query_instance: true, num_proof: 1, ..Default::default() }
44    }
45
46    /// Set `zk`
47    pub fn set_zk(mut self, zk: bool) -> Self {
48        self.zk = zk;
49        self
50    }
51
52    /// Set `query_instance`
53    pub fn set_query_instance(mut self, query_instance: bool) -> Self {
54        self.query_instance = query_instance;
55        self
56    }
57
58    /// Set `num_proof`
59    pub fn with_num_proof(mut self, num_proof: usize) -> Self {
60        assert!(num_proof > 0);
61        self.num_proof = num_proof;
62        self
63    }
64
65    /// Set `num_instance`
66    pub fn with_num_instance(mut self, num_instance: Vec<usize>) -> Self {
67        self.num_instance = num_instance;
68        self
69    }
70
71    /// Set `accumulator_indices`
72    pub fn with_accumulator_indices(
73        mut self,
74        accumulator_indices: Option<Vec<(usize, usize)>>,
75    ) -> Self {
76        self.accumulator_indices = accumulator_indices;
77        self
78    }
79}
80
81/// Convert a [`VerifyingKey`] of [`halo2_proofs`](crate::halo2_proofs) into [`PlonkProtocol`].
82pub fn compile<'a, C: CurveAffine, P: Params<'a, C>>(
83    params: &P,
84    vk: &VerifyingKey<C>,
85    config: Config,
86) -> PlonkProtocol<C> {
87    assert_eq!(vk.get_domain().k(), params.k());
88
89    let cs = vk.cs();
90    let Config { zk, query_instance, num_proof, num_instance, accumulator_indices } = config;
91
92    let k = params.k() as usize;
93    let domain = Domain::new(k, root_of_unity(k));
94
95    let preprocessed = vk
96        .fixed_commitments()
97        .iter()
98        .chain(vk.permutation().commitments().iter())
99        .cloned()
100        .collect();
101
102    let polynomials = &Polynomials::new(cs, zk, query_instance, num_instance, num_proof);
103
104    let evaluations = iter::empty()
105        .chain((0..num_proof).flat_map(move |t| polynomials.instance_queries(t)))
106        .chain((0..num_proof).flat_map(move |t| polynomials.advice_queries(t)))
107        .chain(polynomials.fixed_queries())
108        .chain(polynomials.random_query())
109        .chain(polynomials.permutation_fixed_queries())
110        .chain((0..num_proof).flat_map(move |t| polynomials.permutation_z_queries::<true>(t)))
111        .chain((0..num_proof).flat_map(move |t| polynomials.lookup_queries::<true>(t)))
112        .collect();
113    // `quotient_query()` is not needed in evaluations because the verifier can compute it itself from the other evaluations.
114    let queries = (0..num_proof)
115        .flat_map(|t| {
116            iter::empty()
117                .chain(polynomials.instance_queries(t))
118                .chain(polynomials.advice_queries(t))
119                .chain(polynomials.permutation_z_queries::<false>(t))
120                .chain(polynomials.lookup_queries::<false>(t))
121        })
122        .chain(polynomials.fixed_queries())
123        .chain(polynomials.permutation_fixed_queries())
124        .chain(iter::once(polynomials.quotient_query()))
125        .chain(polynomials.random_query())
126        .collect();
127
128    let transcript_initial_state = transcript_initial_state::<C>(vk);
129
130    let instance_committing_key = query_instance.then(|| {
131        instance_committing_key(
132            params,
133            polynomials.num_instance().into_iter().max().unwrap_or_default(),
134        )
135    });
136
137    let accumulator_indices = accumulator_indices
138        .map(|accumulator_indices| polynomials.accumulator_indices(accumulator_indices))
139        .unwrap_or_default();
140
141    PlonkProtocol {
142        domain,
143        domain_as_witness: None,
144        preprocessed,
145        num_instance: polynomials.num_instance(),
146        num_witness: polynomials.num_witness(),
147        num_challenge: polynomials.num_challenge(),
148        evaluations,
149        queries,
150        quotient: polynomials.quotient(),
151        transcript_initial_state: Some(transcript_initial_state),
152        instance_committing_key,
153        linearization: None,
154        accumulator_indices,
155    }
156}
157
158impl From<poly::Rotation> for Rotation {
159    fn from(rotation: poly::Rotation) -> Rotation {
160        Rotation(rotation.0)
161    }
162}
163
164struct Polynomials<'a, F: PrimeField> {
165    cs: &'a ConstraintSystem<F>,
166    zk: bool,
167    query_instance: bool,
168    num_proof: usize,
169    num_fixed: usize,
170    num_permutation_fixed: usize,
171    num_instance: Vec<usize>,
172    num_advice: Vec<usize>,
173    num_challenge: Vec<usize>,
174    advice_index: Vec<usize>,
175    challenge_index: Vec<usize>,
176    num_lookup_permuted: usize,
177    permutation_chunk_size: usize,
178    num_permutation_z: usize,
179    num_lookup_z: usize,
180}
181
182impl<'a, F: PrimeField> Polynomials<'a, F> {
183    fn new(
184        cs: &'a ConstraintSystem<F>,
185        zk: bool,
186        query_instance: bool,
187        num_instance: Vec<usize>,
188        num_proof: usize,
189    ) -> Self {
190        // TODO: Re-enable optional-zk when it's merged in pse/halo2.
191        let degree = if zk { cs.degree() } else { unimplemented!() };
192        let permutation_chunk_size = if zk || cs.permutation().get_columns().len() >= degree {
193            degree - 2
194        } else {
195            degree - 1
196        };
197
198        let num_phase = *cs.advice_column_phase().iter().max().unwrap_or(&0) as usize + 1;
199        let remapping = |phase: Vec<u8>| {
200            let num = phase.iter().fold(vec![0; num_phase], |mut num, phase| {
201                num[*phase as usize] += 1;
202                num
203            });
204            let index = phase
205                .iter()
206                .scan(vec![0; num_phase], |state, phase| {
207                    let index = state[*phase as usize];
208                    state[*phase as usize] += 1;
209                    Some(index)
210                })
211                .collect::<Vec<_>>();
212            (num, index)
213        };
214
215        let (num_advice, advice_index) = remapping(cs.advice_column_phase());
216        let (num_challenge, challenge_index) = remapping(cs.challenge_phase());
217        assert_eq!(num_advice.iter().sum::<usize>(), cs.num_advice_columns());
218        assert_eq!(num_challenge.iter().sum::<usize>(), cs.num_challenges());
219
220        Self {
221            cs,
222            zk,
223            query_instance,
224            num_proof,
225            num_fixed: cs.num_fixed_columns(),
226            num_permutation_fixed: cs.permutation().get_columns().len(),
227            num_instance,
228            num_advice,
229            num_challenge,
230            advice_index,
231            challenge_index,
232            num_lookup_permuted: 2 * cs.lookups().len(),
233            permutation_chunk_size,
234            num_permutation_z: Integer::div_ceil(
235                &cs.permutation().get_columns().len(),
236                &permutation_chunk_size,
237            ),
238            num_lookup_z: cs.lookups().len(),
239        }
240    }
241
242    fn num_preprocessed(&self) -> usize {
243        self.num_fixed + self.num_permutation_fixed
244    }
245
246    fn num_instance(&self) -> Vec<usize> {
247        iter::repeat(self.num_instance.clone()).take(self.num_proof).flatten().collect()
248    }
249
250    fn num_witness(&self) -> Vec<usize> {
251        iter::empty()
252            .chain(self.num_advice.clone().iter().map(|num| self.num_proof * num))
253            .chain([
254                self.num_proof * self.num_lookup_permuted,
255                self.num_proof * (self.num_permutation_z + self.num_lookup_z) + self.zk as usize,
256            ])
257            .collect()
258    }
259
260    fn num_challenge(&self) -> Vec<usize> {
261        let mut num_challenge = self.num_challenge.clone();
262        *num_challenge.last_mut().unwrap() += 1; // theta
263        iter::empty()
264            .chain(num_challenge)
265            .chain([
266                2, // beta, gamma
267                1, // alpha
268            ])
269            .collect()
270    }
271
272    fn instance_offset(&self) -> usize {
273        self.num_preprocessed()
274    }
275
276    fn witness_offset(&self) -> usize {
277        self.instance_offset() + self.num_instance().len()
278    }
279
280    fn cs_witness_offset(&self) -> usize {
281        self.witness_offset() + self.num_witness().iter().take(self.num_advice.len()).sum::<usize>()
282    }
283
284    fn query<C: Into<Any> + Copy, R: Into<Rotation>>(
285        &self,
286        column_type: C,
287        mut column_index: usize,
288        rotation: R,
289        t: usize,
290    ) -> Query {
291        let offset = match column_type.into() {
292            Any::Fixed => 0,
293            Any::Instance => self.instance_offset() + t * self.num_instance.len(),
294            Any::Advice(advice) => {
295                column_index = self.advice_index[column_index];
296                let phase_offset = self.num_proof
297                    * self.num_advice[..advice.phase() as usize].iter().sum::<usize>();
298                self.witness_offset() + phase_offset + t * self.num_advice[advice.phase() as usize]
299            }
300        };
301        Query::new(offset + column_index, rotation.into())
302    }
303
304    fn instance_queries(&'a self, t: usize) -> impl IntoIterator<Item = Query> + 'a {
305        self.query_instance
306            .then(|| {
307                self.cs.instance_queries().iter().map(move |(column, rotation)| {
308                    self.query(*column.column_type(), column.index(), *rotation, t)
309                })
310            })
311            .into_iter()
312            .flatten()
313    }
314
315    fn advice_queries(&'a self, t: usize) -> impl IntoIterator<Item = Query> + 'a {
316        self.cs.advice_queries().iter().map(move |(column, rotation)| {
317            self.query(*column.column_type(), column.index(), *rotation, t)
318        })
319    }
320
321    fn fixed_queries(&'a self) -> impl IntoIterator<Item = Query> + 'a {
322        self.cs.fixed_queries().iter().map(move |(column, rotation)| {
323            self.query(*column.column_type(), column.index(), *rotation, 0)
324        })
325    }
326
327    fn permutation_fixed_queries(&'a self) -> impl IntoIterator<Item = Query> + 'a {
328        (0..self.num_permutation_fixed).map(|i| Query::new(self.num_fixed + i, 0))
329    }
330
331    fn permutation_poly(&'a self, t: usize, i: usize) -> usize {
332        let z_offset = self.cs_witness_offset() + self.num_witness()[self.num_advice.len()];
333        z_offset + t * self.num_permutation_z + i
334    }
335
336    fn permutation_z_queries<const EVAL: bool>(
337        &'a self,
338        t: usize,
339    ) -> impl IntoIterator<Item = Query> + 'a {
340        match (self.zk, EVAL) {
341            (true, true) => (0..self.num_permutation_z)
342                .flat_map(move |i| {
343                    let z = self.permutation_poly(t, i);
344                    iter::empty().chain([Query::new(z, 0), Query::new(z, 1)]).chain(
345                        if i == self.num_permutation_z - 1 {
346                            None
347                        } else {
348                            Some(Query::new(z, self.rotation_last()))
349                        },
350                    )
351                })
352                .collect_vec(),
353            (true, false) => iter::empty()
354                .chain((0..self.num_permutation_z).flat_map(move |i| {
355                    let z = self.permutation_poly(t, i);
356                    [Query::new(z, 0), Query::new(z, 1)]
357                }))
358                .chain((0..self.num_permutation_z).rev().skip(1).map(move |i| {
359                    let z = self.permutation_poly(t, i);
360                    Query::new(z, self.rotation_last())
361                }))
362                .collect_vec(),
363            (false, _) => (0..self.num_permutation_z)
364                .flat_map(move |i| {
365                    let z = self.permutation_poly(t, i);
366                    [Query::new(z, 0), Query::new(z, 1)]
367                })
368                .collect_vec(),
369        }
370    }
371
372    fn lookup_poly(&'a self, t: usize, i: usize) -> (usize, usize, usize) {
373        let permuted_offset = self.cs_witness_offset();
374        let z_offset = permuted_offset
375            + self.num_witness()[self.num_advice.len()]
376            + self.num_proof * self.num_permutation_z;
377        let z = z_offset + t * self.num_lookup_z + i;
378        let permuted_input = permuted_offset + 2 * (t * self.num_lookup_z + i);
379        let permuted_table = permuted_input + 1;
380        (z, permuted_input, permuted_table)
381    }
382
383    fn lookup_queries<const EVAL: bool>(
384        &'a self,
385        t: usize,
386    ) -> impl IntoIterator<Item = Query> + 'a {
387        (0..self.num_lookup_z).flat_map(move |i| {
388            let (z, permuted_input, permuted_table) = self.lookup_poly(t, i);
389            if EVAL {
390                [
391                    Query::new(z, 0),
392                    Query::new(z, 1),
393                    Query::new(permuted_input, 0),
394                    Query::new(permuted_input, -1),
395                    Query::new(permuted_table, 0),
396                ]
397            } else {
398                [
399                    Query::new(z, 0),
400                    Query::new(permuted_input, 0),
401                    Query::new(permuted_table, 0),
402                    Query::new(permuted_input, -1),
403                    Query::new(z, 1),
404                ]
405            }
406        })
407    }
408
409    fn quotient_query(&self) -> Query {
410        Query::new(self.witness_offset() + self.num_witness().iter().sum::<usize>(), 0)
411    }
412
413    fn random_query(&self) -> Option<Query> {
414        self.zk.then(|| {
415            Query::new(self.witness_offset() + self.num_witness().iter().sum::<usize>() - 1, 0)
416        })
417    }
418
419    fn convert(&self, expression: &plonk::Expression<F>, t: usize) -> Expression<F> {
420        expression.evaluate(
421            &|scalar| Expression::Constant(scalar),
422            &|_| unreachable!(),
423            &|query| self.query(Any::Fixed, query.column_index(), query.rotation(), t).into(),
424            &|query| {
425                self.query(
426                    match query.phase() {
427                        0 => Any::advice_in(FirstPhase),
428                        1 => Any::advice_in(SecondPhase),
429                        2 => Any::advice_in(ThirdPhase),
430                        _ => unreachable!(),
431                    },
432                    query.column_index(),
433                    query.rotation(),
434                    t,
435                )
436                .into()
437            },
438            &|query| self.query(Any::Instance, query.column_index(), query.rotation(), t).into(),
439            &|challenge| {
440                let phase_offset =
441                    self.num_challenge[..challenge.phase() as usize].iter().sum::<usize>();
442                Expression::Challenge(phase_offset + self.challenge_index[challenge.index()])
443            },
444            &|a| -a,
445            &|a, b| a + b,
446            &|a, b| a * b,
447            &|a, scalar| a * scalar,
448        )
449    }
450
451    fn gate_constraints(&'a self, t: usize) -> impl IntoIterator<Item = Expression<F>> + 'a {
452        self.cs.gates().iter().flat_map(move |gate| {
453            gate.polynomials().iter().map(move |expression| self.convert(expression, t))
454        })
455    }
456
457    fn rotation_last(&self) -> Rotation {
458        Rotation(-((self.cs.blinding_factors() + 1) as i32))
459    }
460
461    fn l_last(&self) -> Expression<F> {
462        if self.zk {
463            Expression::CommonPolynomial(CommonPolynomial::Lagrange(self.rotation_last().0))
464        } else {
465            Expression::CommonPolynomial(CommonPolynomial::Lagrange(-1))
466        }
467    }
468
469    fn l_blind(&self) -> Expression<F> {
470        (self.rotation_last().0 + 1..0)
471            .map(CommonPolynomial::Lagrange)
472            .map(Expression::CommonPolynomial)
473            .sum()
474    }
475
476    fn l_active(&self) -> Expression<F> {
477        Expression::Constant(F::ONE) - self.l_last() - self.l_blind()
478    }
479
480    fn system_challenge_offset(&self) -> usize {
481        self.num_challenge.iter().sum()
482    }
483
484    fn theta(&self) -> Expression<F> {
485        Expression::Challenge(self.system_challenge_offset())
486    }
487
488    fn beta(&self) -> Expression<F> {
489        Expression::Challenge(self.system_challenge_offset() + 1)
490    }
491
492    fn gamma(&self) -> Expression<F> {
493        Expression::Challenge(self.system_challenge_offset() + 2)
494    }
495
496    fn alpha(&self) -> Expression<F> {
497        Expression::Challenge(self.system_challenge_offset() + 3)
498    }
499
500    fn permutation_constraints(&'a self, t: usize) -> impl IntoIterator<Item = Expression<F>> + 'a {
501        let one = &Expression::Constant(F::ONE);
502        let l_0 = &Expression::<F>::CommonPolynomial(CommonPolynomial::Lagrange(0));
503        let l_last = &self.l_last();
504        let l_active = &self.l_active();
505        let identity = &Expression::<F>::CommonPolynomial(CommonPolynomial::Identity);
506        let beta = &self.beta();
507        let gamma = &self.gamma();
508
509        let polys = self
510            .cs
511            .permutation()
512            .get_columns()
513            .iter()
514            .map(|column| self.query(*column.column_type(), column.index(), 0, t))
515            .map(Expression::<F>::Polynomial)
516            .collect_vec();
517        let permutation_fixeds = (0..self.num_permutation_fixed)
518            .map(|i| Query::new(self.num_fixed + i, 0))
519            .map(Expression::<F>::Polynomial)
520            .collect_vec();
521        let zs = (0..self.num_permutation_z)
522            .map(|i| {
523                let z = self.permutation_poly(t, i);
524                (
525                    Expression::<F>::Polynomial(Query::new(z, 0)),
526                    Expression::<F>::Polynomial(Query::new(z, 1)),
527                    Expression::<F>::Polynomial(Query::new(z, self.rotation_last())),
528                )
529            })
530            .collect_vec();
531
532        iter::empty()
533            .chain(zs.first().map(|(z_0, _, _)| l_0 * (one - z_0)))
534            .chain(zs.last().and_then(|(z_l, _, _)| self.zk.then(|| l_last * (z_l * z_l - z_l))))
535            .chain(if self.zk {
536                zs.iter()
537                    .skip(1)
538                    .zip(zs.iter())
539                    .map(|((z, _, _), (_, _, z_prev_last))| l_0 * (z - z_prev_last))
540                    .collect_vec()
541            } else {
542                Vec::new()
543            })
544            .chain(
545                zs.iter()
546                    .zip(zs.iter().cycle().skip(1))
547                    .zip(polys.chunks(self.permutation_chunk_size))
548                    .zip(permutation_fixeds.chunks(self.permutation_chunk_size))
549                    .enumerate()
550                    .map(
551                        |(
552                            i,
553                            ((((z, z_omega, _), (_, z_next_omega, _)), polys), permutation_fixeds),
554                        )| {
555                            let left = if self.zk || zs.len() == 1 {
556                                z_omega.clone()
557                            } else {
558                                z_omega + l_last * (z_next_omega - z_omega)
559                            } * polys
560                                .iter()
561                                .zip(permutation_fixeds.iter())
562                                .map(|(poly, permutation_fixed)| {
563                                    poly + beta * permutation_fixed + gamma
564                                })
565                                .reduce(|acc, expr| acc * expr)
566                                .unwrap();
567                            let right = z * polys
568                                .iter()
569                                .zip(
570                                    iter::successors(
571                                        Some(F::DELTA.pow_vartime([
572                                            (i * self.permutation_chunk_size) as u64,
573                                        ])),
574                                        |delta| Some(F::DELTA * delta),
575                                    )
576                                    .map(Expression::Constant),
577                                )
578                                .map(|(poly, delta)| poly + beta * delta * identity + gamma)
579                                .reduce(|acc, expr| acc * expr)
580                                .unwrap();
581                            if self.zk {
582                                l_active * (left - right)
583                            } else {
584                                left - right
585                            }
586                        },
587                    ),
588            )
589            .collect_vec()
590    }
591
592    fn lookup_constraints(&'a self, t: usize) -> impl IntoIterator<Item = Expression<F>> + 'a {
593        let one = &Expression::Constant(F::ONE);
594        let l_0 = &Expression::<F>::CommonPolynomial(CommonPolynomial::Lagrange(0));
595        let l_last = &self.l_last();
596        let l_active = &self.l_active();
597        let beta = &self.beta();
598        let gamma = &self.gamma();
599
600        let polys = (0..self.num_lookup_z)
601            .map(|i| {
602                let (z, permuted_input, permuted_table) = self.lookup_poly(t, i);
603                (
604                    Expression::<F>::Polynomial(Query::new(z, 0)),
605                    Expression::<F>::Polynomial(Query::new(z, 1)),
606                    Expression::<F>::Polynomial(Query::new(permuted_input, 0)),
607                    Expression::<F>::Polynomial(Query::new(permuted_input, -1)),
608                    Expression::<F>::Polynomial(Query::new(permuted_table, 0)),
609                )
610            })
611            .collect_vec();
612
613        let compress = |expressions: &'a [plonk::Expression<F>]| {
614            Expression::DistributePowers(
615                expressions.iter().map(|expression| self.convert(expression, t)).collect(),
616                self.theta().into(),
617            )
618        };
619
620        self.cs
621            .lookups()
622            .iter()
623            .zip(polys.iter())
624            .flat_map(
625                |(
626                    lookup,
627                    (z, z_omega, permuted_input, permuted_input_omega_inv, permuted_table),
628                )| {
629                    let input = compress(lookup.input_expressions());
630                    let table = compress(lookup.table_expressions());
631                    iter::empty()
632                        .chain(Some(l_0 * (one - z)))
633                        .chain(self.zk.then(|| l_last * (z * z - z)))
634                        .chain(Some(if self.zk {
635                            l_active
636                                * (z_omega * (permuted_input + beta) * (permuted_table + gamma)
637                                    - z * (input + beta) * (table + gamma))
638                        } else {
639                            z_omega * (permuted_input + beta) * (permuted_table + gamma)
640                                - z * (input + beta) * (table + gamma)
641                        }))
642                        .chain(self.zk.then(|| l_0 * (permuted_input - permuted_table)))
643                        .chain(Some(if self.zk {
644                            l_active
645                                * (permuted_input - permuted_table)
646                                * (permuted_input - permuted_input_omega_inv)
647                        } else {
648                            (permuted_input - permuted_table)
649                                * (permuted_input - permuted_input_omega_inv)
650                        }))
651                },
652            )
653            .collect_vec()
654    }
655
656    fn quotient(&self) -> QuotientPolynomial<F> {
657        let constraints = (0..self.num_proof)
658            .flat_map(|t| {
659                iter::empty()
660                    .chain(self.gate_constraints(t))
661                    .chain(self.permutation_constraints(t))
662                    .chain(self.lookup_constraints(t))
663            })
664            .collect_vec();
665        let numerator = Expression::DistributePowers(constraints, self.alpha().into());
666        QuotientPolynomial { chunk_degree: 1, numerator }
667    }
668
669    fn accumulator_indices(
670        &self,
671        accumulator_indices: Vec<(usize, usize)>,
672    ) -> Vec<Vec<(usize, usize)>> {
673        (0..self.num_proof)
674            .map(|t| {
675                accumulator_indices
676                    .iter()
677                    .cloned()
678                    .map(|(poly, row)| (poly + t * self.num_instance.len(), row))
679                    .collect()
680            })
681            .collect()
682    }
683}
684
685struct MockChallenge;
686
687impl<C: CurveAffine> EncodedChallenge<C> for MockChallenge {
688    type Input = ();
689
690    fn new(_: &Self::Input) -> Self {
691        unreachable!()
692    }
693
694    fn get_scalar(&self) -> C::Scalar {
695        unreachable!()
696    }
697}
698
699#[derive(Default)]
700struct MockTranscript<F: PrimeField>(F);
701
702impl<C: CurveAffine> Transcript<C, MockChallenge> for MockTranscript<C::Scalar> {
703    fn squeeze_challenge(&mut self) -> MockChallenge {
704        unreachable!()
705    }
706
707    fn common_point(&mut self, _: C) -> io::Result<()> {
708        unreachable!()
709    }
710
711    fn common_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {
712        self.0 = scalar;
713        Ok(())
714    }
715}
716
717/// Returns the transcript initial state of the [VerifyingKey].
718/// Roundabout way to do it because [VerifyingKey] doesn't expose the field.
719pub fn transcript_initial_state<C: CurveAffine>(vk: &VerifyingKey<C>) -> C::Scalar {
720    let mut transcript = MockTranscript::default();
721    vk.hash_into(&mut transcript).unwrap();
722    transcript.0
723}
724
725fn instance_committing_key<'a, C: CurveAffine, P: Params<'a, C>>(
726    params: &P,
727    len: usize,
728) -> InstanceCommittingKey<C> {
729    let buf = {
730        let mut buf = Vec::new();
731        params.write(&mut buf).unwrap();
732        buf
733    };
734
735    let repr = C::Repr::default();
736    let repr_len = repr.as_ref().len();
737    let offset = size_of::<u32>() + (1 << params.k()) * repr_len;
738
739    let bases = (offset..)
740        .step_by(repr_len)
741        .map(|offset| {
742            let mut repr = C::Repr::default();
743            repr.as_mut().copy_from_slice(&buf[offset..offset + repr_len]);
744            C::from_bytes(&repr).unwrap()
745        })
746        .take(len)
747        .collect();
748
749    let w = {
750        let offset = size_of::<u32>() + (2 << params.k()) * repr_len;
751        let mut repr = C::Repr::default();
752        repr.as_mut().copy_from_slice(&buf[offset..offset + repr_len]);
753        C::from_bytes(&repr).unwrap()
754    };
755
756    InstanceCommittingKey { bases, constant: Some(w) }
757}