diff --git a/.github/workflows/ci_main.yml b/.github/workflows/ci_main.yml index 400bff09..df387bc3 100644 --- a/.github/workflows/ci_main.yml +++ b/.github/workflows/ci_main.yml @@ -59,7 +59,7 @@ jobs: command: tarpaulin args: --all-features --timeout 600 --out Xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v3.1.4 doc-links: name: Intra-doc links diff --git a/halo2_proofs/src/circuit/floor_planner/single_pass.rs b/halo2_proofs/src/circuit/floor_planner/single_pass.rs index 54268006..86802b0e 100644 --- a/halo2_proofs/src/circuit/floor_planner/single_pass.rs +++ b/halo2_proofs/src/circuit/floor_planner/single_pass.rs @@ -434,6 +434,7 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> TableLayouter offset: usize, to: &'v mut (dyn FnMut() -> Value> + 'v), ) -> Result<(), Error> { + // Cannot use the same column for different lookups ? if self.used_columns.contains(&column) { return Err(Error::Synthesis); // TODO better error } diff --git a/halo2_proofs/src/lib.rs b/halo2_proofs/src/lib.rs index 52676ddb..961108cb 100644 --- a/halo2_proofs/src/lib.rs +++ b/halo2_proofs/src/lib.rs @@ -27,6 +27,7 @@ pub mod arithmetic; pub mod circuit; +mod protostar; pub use halo2curves; mod multicore; pub mod plonk; diff --git a/halo2_proofs/src/plonk/circuit.rs b/halo2_proofs/src/plonk/circuit.rs index 1c55eb5c..db00ff8f 100644 --- a/halo2_proofs/src/plonk/circuit.rs +++ b/halo2_proofs/src/plonk/circuit.rs @@ -1500,7 +1500,7 @@ pub struct Gate { /// We track queried selectors separately from other cells, so that we can use them to /// trigger debug checks on gates. queried_selectors: Vec, - queried_cells: Vec, + queried_cells: Vec, // w_1 at i+1, w_2 at i, q_1 at i } impl Gate { @@ -1709,10 +1709,14 @@ impl ConstraintSystem { name: S, table_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression, TableColumn)>, ) -> usize { + // `cells` is named `meta` in `table_map`. We give it to the closure so that + // we get information about which columns are queried. + // Essentially extracts all the variables from `Expression` let mut cells = VirtualCells::new(self); let table_map = table_map(&mut cells) .into_iter() .map(|(mut input, table)| { + // why is table not used. if input.contains_simple_selector() { panic!("expression containing simple selector supplied to lookup argument"); } @@ -1920,6 +1924,7 @@ impl ConstraintSystem { // expressions in gates, as lookup arguments cannot support simple // selectors. Selectors that are complex or do not appear in any gates // will have degree zero. + // For each polynomial in each gate, get the unique simple selector let mut degrees = vec![0; selectors.len()]; for expr in self.gates.iter().flat_map(|gate| gate.polys.iter()) { if let Some(selector) = expr.extract_simple_selector() { diff --git a/halo2_proofs/src/plonk/evaluation.rs b/halo2_proofs/src/plonk/evaluation.rs index d5fb9840..d3b2eb37 100644 --- a/halo2_proofs/src/plonk/evaluation.rs +++ b/halo2_proofs/src/plonk/evaluation.rs @@ -180,6 +180,7 @@ impl Calculation { } /// Evaluator +/// Probably want a Homogenized Evaluator #[derive(Clone, Default, Debug)] pub struct Evaluator { /// Custom gates evalution @@ -224,11 +225,13 @@ impl Evaluator { pub fn new(cs: &ConstraintSystem) -> Self { let mut ev = Evaluator::default(); + // good place to homoegenize? + // Custom gates let mut parts = Vec::new(); for gate in cs.gates.iter() { parts.extend( - gate.polynomials() + gate.polynomials() // expressions .iter() .map(|poly| ev.custom_gates.add_expression(poly)), ); @@ -568,12 +571,15 @@ impl GraphEvaluator { /// resulting value so the result can be reused when that calculation /// is done multiple times. fn add_calculation(&mut self, calculation: Calculation) -> ValueSource { + // attempt to find this calculation in the set of existing calculation let existing_calculation = self .calculations .iter() .find(|c| c.calculation == calculation); match existing_calculation { + // if it exists, then reuse it Some(existing_calculation) => ValueSource::Intermediate(existing_calculation.target), + // else create a new calculation None => { let target = self.num_intermediates; self.calculations.push(CalculationInfo { @@ -599,6 +605,8 @@ impl GraphEvaluator { ))) } Expression::Advice(query) => { + // our calc depends on this specific query. + // e.g. w[1][i+1] let rot_idx = self.add_rotation(&query.rotation); self.add_calculation(Calculation::Store(ValueSource::Advice( query.column_index, diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index 6314481d..4907fa6f 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -321,6 +321,7 @@ where let mut witness = WitnessCollection { k: params.k(), current_phase, + // Seems inefficient to recreate all this data advice: vec![domain.empty_lagrange_assigned(); meta.num_advice_columns], instances, challenges: &challenges, diff --git a/halo2_proofs/src/protostar.rs b/halo2_proofs/src/protostar.rs new file mode 100644 index 00000000..f694441d --- /dev/null +++ b/halo2_proofs/src/protostar.rs @@ -0,0 +1,3 @@ +mod error_check; +mod expression; +mod witness; diff --git a/halo2_proofs/src/protostar/error_check.rs b/halo2_proofs/src/protostar/error_check.rs new file mode 100644 index 00000000..7aab8546 --- /dev/null +++ b/halo2_proofs/src/protostar/error_check.rs @@ -0,0 +1,159 @@ +struct Accumulator {} +struct Transcript {} + +const D: usize = 5; +const NUM_ADVICE_VARS: usize = 5; +const NUM_FIXED_COLS: usize = 5; +const NUM_CHALLENGES: usize = 5; + +// Represents the data required to evaluate e_i(X) +struct Row { + // not sure we need the index here + idx: usize, + // beta_0 * beta_1 = \beta^i + beta_0: u64, + beta_1: u64, + + // We want to populate this not only with the current row, but also all the rotations. + // I.e., the constraint G may depend on rows i+1, i-1 etc. + advice: [u64; NUM_ADVICE_VARS], +} + +struct ChallengeRow { + // slack = [mu, mu+1, ..., mu+D] + slack: u64, + // Evaluation of acc.challenge + X*tx.challenges for X = 0,1,..,D + challenges_eval: [u64; NUM_CHALLENGES], +} + +// index of active gate, and the Lagrange normalization factor for q(q-1)...(q-l) +struct SelectorActivation(usize, u64); + +struct FixedRow { + // index of the only active + // represents the active row. + active_constraint: usize, + + // constants for this row + fixed: [u64; NUM_FIXED_COLS], +} + +struct ErrorRow { + // Evaluations + evaluations: [u64; D], +} + +// represents G, and evaluates it +fn eval_constraint(constants: FixedRow, row: Row, challenges: &ChallengeRow) -> u64 { + 0 +} + +// at a given row i, compute acc.row[i] + X * tx.row[i] +fn eval_error_at_row( + constants: FixedRow, + acc_row: &Row, + tx_row: &Row, + challenges: &[ChallengeRow; D], + degree: usize, +) -> ErrorRow { + let mut error_row: [u64; D]; + + // Here, acc and tx rows would just contain info about where the actual values in acc and tx are located + // We can do something like row.get(i) to actually grab the data from memory. + // something like in ValueSource::get. + // If the row constants contains information about which gate is active, + // then we only need to grab the data that is relevant for this row + // (if the selector for a gate is 0, then we don't compute anything so we don't need the data either) + let mut tmp_row = acc_row; + + // compute e_i(0) + error_row[0] = eval_constraint(constants, tmp_row, &challenges[0]); + for l in 1..D { + // need to impl AddAssign + // tmp_row += acc_row; + + // compute e_i(l), using the + error_row[l] = eval_constraint(constants, tmp_row, &challenges[l]); + } + ErrorRow { + evaluations: error_row, + } +} + +fn prove(accumulator: Accumulator, transcript: Transcript) { + // Ensure iop_transcript is well formed/rederive challenges + + // Sample `alpha`/ValueSource::Y() for random-linear combination of gates? + // Not necessary with proper selector structure. + + // / // prepare evaluations of challenges () + // / challenges: [FF;d] + // / challenges[0] = acc.challenges + // / for l : [1..d] { + // / challenges[l] = challenges[l-1] + tx.challenges + // / } + + // / interpolate each row over 1,...,d-1 + // / for row_i : transcript.rows { + // / acc_i = accumulator.row + // / for l : d-1 { + // / acc_i += row_i + // / + // / } + // / } +} + +use std::rc::Rc; + +#[derive(Clone)] +enum Node { + Var(String), + Sum(Vec>), + Product(Vec>), + Power(Rc, i32), +} + +fn degree(node: &Rc) -> i32 { + match &**node { + Node::Var(_) => 1, + Node::Sum(children) => children.iter().map(degree).max().unwrap(), + Node::Product(children) => children.iter().map(degree).sum(), + Node::Power(base, exponent) => degree(base) * exponent, + } +} + +fn homogenize(node: Rc, total_degree: i32, new_var: &str) -> Rc { + match &*node { + Node::Var(_) => node.clone(), + Node::Sum(children) => { + let mut new_children = vec![]; + for child in children { + let child_degree = degree(child); + if child_degree < total_degree { + new_children.push(Rc::new(Node::Product(vec![ + homogenize(child.clone(), total_degree, new_var), + Rc::new(Node::Power( + Rc::new(Node::Var(new_var.to_string())), + total_degree - child_degree, + )), + ]))); + } else { + new_children.push(homogenize(child.clone(), total_degree, new_var)); + } + } + Rc::new(Node::Sum(new_children)) + } + Node::Product(children) => Rc::new(Node::Product( + children + .iter() + .map(|child| homogenize(child.clone(), total_degree, new_var)) + .collect(), + )), + Node::Power(_, _) => panic!("Power nodes are not supported in the original polynomial"), + } +} + +fn homogenize_ast(root: Rc, new_var: &str) -> Rc { + let total_degree = degree(&root); + homogenize(root, total_degree, new_var) +} diff --git a/halo2_proofs/src/protostar/expression.rs b/halo2_proofs/src/protostar/expression.rs new file mode 100644 index 00000000..572b1e44 --- /dev/null +++ b/halo2_proofs/src/protostar/expression.rs @@ -0,0 +1,115 @@ +use ff::Field; + +use crate::plonk::{AdviceQuery, Challenge, Expression, FixedQuery, InstanceQuery, Selector}; + +#[derive(Clone)] +struct Slack; + +/// Low-degree expression representing an identity that must hold over the committed columns. +#[derive(Clone)] +pub enum HomogenizedExpression { + Slack(), + /// This is a constant polynomial + Constant(F), + /// This is a virtual selector + Selector(Selector), + /// This is a fixed column queried at a certain relative location + Fixed(FixedQuery), + /// This is an advice (witness) column queried at a certain relative location + Advice(AdviceQuery), + /// This is an instance (external) column queried at a certain relative location + Instance(InstanceQuery), + /// This is a challenge, where the second parameter represents the power of a Challenge + ChallengePower(Challenge, usize), + /// This is a negated polynomial + Negated(Box>), + /// This is the sum of two polynomials + Sum(Box>, Box>), + /// This is the product of two polynomials + Product(Box>, Box>), + /// This is a scaled polynomial + Scaled(Box>, F), +} + +impl Expression { + fn homogenize(self) -> HomogenizedExpression { + match self { + Expression::Constant(v) => HomogenizedExpression::Constant(v), + Expression::Selector(v) => HomogenizedExpression::Selector(v), + Expression::Fixed(v) => HomogenizedExpression::Fixed(v), + Expression::Advice(v) => HomogenizedExpression::Advice(v), + Expression::Instance(v) => HomogenizedExpression::Instance(v), + Expression::Challenge(c) => HomogenizedExpression::ChallengePower(v, 1), + Expression::Negated(e) => HomogenizedExpression::Negated(Box::new(e.homogenize())), + Expression::Sum(e1, e2) => { + HomogenizedExpression::Sum(Box::new(e1.homogenize()), Box::new(e2.homogenize())) + } + Expression::Product(e1, e2) => { + HomogenizedExpression::Product(Box::new(e1.homogenize()), Box::new(e2.homogenize())) + } + Expression::Scaled(e, v) => HomogenizedExpression::Scaled(Box::new(e.homogenize()), v), + } + } +} + +impl HomogenizedExpression { + fn distribute_challenge_powers(self, challenge: &Challenge, power: usize) -> Self { + match self { + HomogenizedExpression::ChallengePower(other_challenge, d) => { + if other_challenge == *challenge { + if d == 1 { + panic!("Challenge power cannot be other than 1") + } + HomogenizedExpression::ChallengePower(*challenge, 1 + power) + } else { + } + } + HomogenizedExpression::Negated(e) => HomogenizedExpression::Negated(Box::new( + e.distribute_challenge_powers(challenge, power), + )), + HomogenizedExpression::Sum(e1, e2) => HomogenizedExpression::Sum( + Box::new(e1.distribute_challenge_powers(challenge, power)), + Box::new(e2.distribute_challenge_powers(challenge, power)), + ), + HomogenizedExpression::Product(e1, e2) => { + if power == 0 { + HomogenizedExpression::Product( + Box::new(e1.distribute_challenge_powers(challenge, power)), + Box::new(e2.distribute_challenge_powers(challenge, power)), + ) + } else { + HomogenizedExpression::Product( + Box::new(e1.distribute_challenge_powers(challenge, power)), + Box::new(e2.distribute_challenge_powers(challenge, power)), + ) + } + } + HomogenizedExpression::Scaled(v, f) => HomogenizedExpression::Scaled( + Box::new(v.distribute_challenge_powers(challenge, power)), + f, + ), + v => { + if power == 0 { + v + } else { + HomogenizedExpression::Product( + Box::new(v), + Box::new(HomogenizedExpression::ChallengePower(*challenge, power)), + ) + } + } + } + } + + fn flatten_challenge_powers(self, challenges: &[Challenge]) -> Self { + let mut expr = self; + for challenge in challenges { + (expr, _) = expr.flatten_challenge_powers_inner(challenge); + } + expr + } + + fn homogenize(self) -> Self { + HomogenizedExpression::Slack() + } +} diff --git a/halo2_proofs/src/protostar/witness.rs b/halo2_proofs/src/protostar/witness.rs new file mode 100644 index 00000000..63ea1914 --- /dev/null +++ b/halo2_proofs/src/protostar/witness.rs @@ -0,0 +1,395 @@ +use crate::{ + arithmetic::{eval_polynomial, CurveAffine}, + circuit::{layouter::SyncDeps, Value}, + plonk::{ + sealed::{self, Phase, SealedPhase}, + Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, ConstraintSystem, Error, + FirstPhase, Fixed, FloorPlanner, Instance, ProvingKey, Selector, VerifyingKey, + }, + poly::batch_invert_assigned, + poly::{ + self, + commitment::{Blind, CommitmentScheme, Params, Prover}, + Basis, Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, ProverQuery, + }, + transcript::{EncodedChallenge, TranscriptWrite}, +}; +use ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; +use group::{prime::PrimeCurveAffine, Curve}; +use rand_core::RngCore; +use std::{ + collections::{BTreeSet, HashMap}, + ops::RangeTo, +}; + +/// +struct WitnessCollection<'a, F: Field> { + k: u32, + current_phase: sealed::Phase, + advice: Vec, LagrangeCoeff>>, + challenges: &'a HashMap, + instances: &'a [&'a [F]], + usable_rows: RangeTo, + _marker: std::marker::PhantomData, +} + +impl<'a, F: Field> SyncDeps for WitnessCollection<'a, F> {} + +impl<'a, F: Field> Assignment for WitnessCollection<'a, F> { + fn enter_region(&mut self, _: N) + where + NR: Into, + N: FnOnce() -> NR, + { + // Do nothing; we don't care about regions in this context. + } + + fn exit_region(&mut self) { + // Do nothing; we don't care about regions in this context. + } + + fn enable_selector(&mut self, _: A, _: &Selector, _: usize) -> Result<(), Error> + where + A: FnOnce() -> AR, + AR: Into, + { + // We only care about advice columns here + + Ok(()) + } + + fn annotate_column(&mut self, _annotation: A, _column: Column) + where + A: FnOnce() -> AR, + AR: Into, + { + // Do nothing + } + + fn query_instance(&self, column: Column, row: usize) -> Result, Error> { + if !self.usable_rows.contains(&row) { + return Err(Error::not_enough_rows_available(self.k)); + } + + self.instances + .get(column.index()) + .and_then(|column| column.get(row)) + .map(|v| Value::known(*v)) + .ok_or(Error::BoundsFailure) + } + + fn assign_advice( + &mut self, + _: A, + column: Column, + row: usize, + to: V, + ) -> Result<(), Error> + where + V: FnOnce() -> Value, + VR: Into>, + A: FnOnce() -> AR, + AR: Into, + { + // let current_phase = self.current_phase.unwrap(); + // Ignore assignment of advice column in different phase than current one. + if self.current_phase != column.column_type().phase { + return Ok(()); + } + + if !self.usable_rows.contains(&row) { + return Err(Error::not_enough_rows_available(self.k)); + } + + *self + .advice + .get_mut(column.index()) + .and_then(|v| v.get_mut(row)) + .ok_or(Error::BoundsFailure)? = to().into_field().assign()?; + + Ok(()) + } + + fn assign_fixed( + &mut self, + _: A, + _: Column, + _: usize, + _: V, + ) -> Result<(), Error> + where + V: FnOnce() -> Value, + VR: Into>, + A: FnOnce() -> AR, + AR: Into, + { + // We only care about advice columns here + + Ok(()) + } + + fn copy(&mut self, _: Column, _: usize, _: Column, _: usize) -> Result<(), Error> { + // We only care about advice columns here + + Ok(()) + } + + fn fill_from_row( + &mut self, + _: Column, + _: usize, + _: Value>, + ) -> Result<(), Error> { + Ok(()) + } + + fn get_challenge(&self, challenge: Challenge) -> Value { + self.challenges + .get(&challenge.index()) + .cloned() + .map(Value::known) + .unwrap_or_else(Value::unknown) + } + + fn push_namespace(&mut self, _: N) + where + NR: Into, + N: FnOnce() -> NR, + { + // Do nothing; we don't care about namespaces in this context. + } + + fn pop_namespace(&mut self, _: Option) { + // Do nothing; we don't care about namespaces in this context. + } +} + +/// Returns an empty (zero) polynomial in the Lagrange coefficient basis +fn empty_lagrange(n: usize) -> Polynomial { + Polynomial { + values: vec![F::ZERO; n], + _marker: std::marker::PhantomData, + } +} + +/// Returns an empty (zero) polynomial in the Lagrange coefficient basis, with +/// deferred inversions. +fn empty_lagrange_assigned(n: usize) -> Polynomial, LagrangeCoeff> { + Polynomial { + values: vec![F::ZERO.into(); n], + _marker: std::marker::PhantomData, + } +} + +/// Advice polynomials sent by the prover during the first phases of +/// the IOP protocol. +struct GateTranscript { + pub instance_polys: Vec>, + pub advice_polys: Vec>, + // blinding values for advice_polys, same length as advice_polys + pub advice_blinds: Vec>, + pub challenges: Vec, +} + +/// Runs the witness generation for the first phase of the protocol +pub fn create_gate_transcript< + 'params, + Scheme: CommitmentScheme, + P: Prover<'params, Scheme>, + E: EncodedChallenge, + R: RngCore, + T: TranscriptWrite, + ConcreteCircuit: Circuit, +>( + params: &'params Scheme::ParamsProver, + vk: &VerifyingKey, + circuit: ConcreteCircuit, + // raw instance columns + instances: &[&[Scheme::Scalar]], + mut rng: R, + transcript: &mut T, +) -> Result, Error> +where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, +{ + if instances.len() != vk.cs().num_instance_columns { + return Err(Error::InvalidInstances); + } + + // Hash verification key into transcript + vk.hash_into(transcript)?; + + let mut meta = ConstraintSystem::default(); + + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut meta, circuit.params()); + #[cfg(not(feature = "circuit-params"))] + let config = ConcreteCircuit::configure(&mut meta); + + // Selector optimizations cannot be applied here; use the ConstraintSystem + // from the verification key. + let meta = &vk.cs(); + + // generate polys for instance columns + // NOTE(@adr1anh): In the case where the verifier does not query the instance, + // we do not need to create a Lagrange polynomial of size n. + let instance_polys = instances + .iter() + .map(|values| { + let mut poly = empty_lagrange(params.n() as usize); + + if values.len() > (poly.len() - (meta.blinding_factors() + 1)) { + return Err(Error::InstanceTooLarge); + } + for (poly, value) in poly.iter_mut().zip(values.iter()) { + // The instance is part of the transcript + if !P::QUERY_INSTANCE { + transcript.common_scalar(*value)?; + } + *poly = *value; + } + Ok(poly) + }) + .collect::, _>>()?; + + // For large instances, we send a commitment to it and open it with PCS + if P::QUERY_INSTANCE { + let instance_commitments_projective: Vec<_> = instance_polys + .iter() + .map(|poly| params.commit_lagrange(poly, Blind::default())) + .collect(); + let mut instance_commitments = + vec![Scheme::Curve::identity(); instance_commitments_projective.len()]; + ::CurveExt::batch_normalize( + &instance_commitments_projective, + &mut instance_commitments, + ); + let instance_commitments = instance_commitments; + drop(instance_commitments_projective); + + for commitment in &instance_commitments { + transcript.common_point(*commitment)?; + } + } + + // Synthesize the circuit over multiple iterations + let (advice_polys, advice_blinds, challenges) = { + let mut advice_polys = vec![empty_lagrange(params.n() as usize); meta.num_advice_columns]; + let mut advice_blinds = vec![Blind::default(); meta.num_advice_columns]; + let mut challenges = HashMap::::with_capacity(meta.num_challenges); + + let unusable_rows_start = params.n() as usize - (meta.blinding_factors() + 1); + + // implements Assignment so that we can + let mut witness = WitnessCollection { + k: params.k(), + current_phase: FirstPhase.to_sealed(), + advice: vec![empty_lagrange_assigned(params.n() as usize); meta.num_advice_columns], + instances, + challenges: &challenges, + // The prover will not be allowed to assign values to advice + // cells that exist within inactive rows, which include some + // number of blinding factors and an extra row for use in the + // permutation argument. + usable_rows: ..unusable_rows_start, + _marker: std::marker::PhantomData, + }; + + // For each phase + for current_phase in vk.cs().phases() { + witness.current_phase = current_phase; + let column_indices = meta + .advice_column_phase + .iter() + .enumerate() + .filter_map(|(column_index, phase)| { + if current_phase == *phase { + Some(column_index) + } else { + None + } + }) + .collect::>(); + + // Synthesize the circuit to obtain the witness and other information. + ConcreteCircuit::FloorPlanner::synthesize( + &mut witness, + &circuit, + config.clone(), + meta.constants.clone(), + )?; + + let mut advice_values = batch_invert_assigned::( + witness + .advice + .into_iter() + .enumerate() + .filter_map(|(column_index, advice)| { + if column_indices.contains(&column_index) { + Some(advice) + } else { + None + } + }) + .collect(), + ); + + // Add blinding factors to advice columns + for advice_values in &mut advice_values { + for cell in &mut advice_values[unusable_rows_start..] { + *cell = Scheme::Scalar::random(&mut rng); + } + } + + // Compute commitments to advice column polynomials + let blinds: Vec<_> = advice_values + .iter() + .map(|_| Blind(Scheme::Scalar::random(&mut rng))) + .collect(); + let advice_commitments_projective: Vec<_> = advice_values + .iter() + .zip(blinds.iter()) + .map(|(poly, blind)| params.commit_lagrange(poly, *blind)) + .collect(); + let mut advice_commitments = + vec![Scheme::Curve::identity(); advice_commitments_projective.len()]; + ::CurveExt::batch_normalize( + &advice_commitments_projective, + &mut advice_commitments, + ); + let advice_commitments = advice_commitments; + drop(advice_commitments_projective); + + for commitment in &advice_commitments { + transcript.write_point(*commitment)?; + } + for ((column_index, advice_values), blind) in + column_indices.iter().zip(advice_values).zip(blinds) + { + advice_polys[*column_index] = advice_values; + advice_blinds[*column_index] = blind; + } + + for (index, phase) in meta.challenge_phase.iter().enumerate() { + if current_phase == *phase { + let existing = + challenges.insert(index, *transcript.squeeze_challenge_scalar::<()>()); + assert!(existing.is_none()); + } + } + } + + assert_eq!(challenges.len(), meta.num_challenges); + let challenges = (0..meta.num_challenges) + .map(|index| challenges.remove(&index).unwrap()) + .collect::>(); + + (advice_polys, advice_blinds, challenges) + }; + Ok(GateTranscript { + instance_polys, + advice_polys, + advice_blinds, + challenges, + }) +} diff --git a/rust-toolchain b/rust-toolchain index 94057304..832e9afb 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.64.0 +1.70.0