From 897ec3b05371e90cbc8d56d7b61c671660a60757 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 4 Jun 2021 10:47:46 +0200 Subject: [PATCH 001/117] `Target` version of proof structs --- src/polynomial/commitment.rs | 6 +++- src/proof.rs | 61 ++++++++++++++++++------------------ src/recursive_verifier.rs | 2 +- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 06194ad9..9eee99cc 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -10,7 +10,7 @@ use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_powers; use crate::polynomial::polynomial::PolynomialCoeffs; -use crate::proof::{FriProof, Hash, OpeningSet}; +use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; @@ -272,6 +272,10 @@ impl, const D: usize> OpeningProof { } } +pub struct OpeningProofTarget { + fri_proof: FriProofTarget, +} + #[cfg(test)] mod tests { use anyhow::Result; diff --git a/src/proof.rs b/src/proof.rs index 46288808..332eb941 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,8 +1,10 @@ +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; +use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; use crate::merkle_proofs::{MerkleProof, MerkleProofTarget}; -use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof}; +use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof, OpeningProofTarget}; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; use std::convert::TryInto; @@ -63,39 +65,33 @@ pub struct Proof, const D: usize> { pub plonk_zs_root: Hash, /// Merkle root of LDEs of the quotient polynomial components. pub quotient_polys_root: Hash, - /// Purported values of each polynomial at the challenge point. pub openings: OpeningSet, - /// A FRI argument for each FRI query. pub opening_proof: OpeningProof, } -pub struct ProofTarget { - /// Merkle root of LDEs of wire values. +pub struct ProofTarget { pub wires_root: HashTarget, - /// Merkle root of LDEs of Z, in the context of Plonk's permutation argument. pub plonk_zs_root: HashTarget, - /// Merkle root of LDEs of the quotient polynomial components. pub quotient_polys_root: HashTarget, - - /// Purported values of each polynomial at each challenge point. - pub openings: Vec, - - /// A FRI argument for each FRI query. - pub fri_proofs: Vec, + pub openings: Vec>, + pub opening_proof: Vec>, } /// Evaluations and Merkle proof produced by the prover in a FRI query step. -// TODO: Implement FriQueryStepTarget pub struct FriQueryStep, const D: usize> { pub evals: Vec, pub merkle_proof: MerkleProof, } +pub struct FriQueryStepTarget { + pub evals: Vec>, + pub merkle_proof: MerkleProofTarget, +} + /// Evaluations and Merkle proofs of the original set of polynomials, /// before they are combined into a composition polynomial. -// TODO: Implement FriInitialTreeProofTarget pub struct FriInitialTreeProof { pub evals_proofs: Vec<(Vec, MerkleProof)>, } @@ -107,13 +103,21 @@ impl FriInitialTreeProof { } } +pub struct FriInitialTreeProofTarget { + pub evals_proofs: Vec<(Vec, MerkleProofTarget)>, +} + /// Proof for a FRI query round. -// TODO: Implement FriQueryRoundTarget pub struct FriQueryRound, const D: usize> { pub initial_trees_proof: FriInitialTreeProof, pub steps: Vec>, } +pub struct FriQueryRoundTarget { + pub initial_trees_proof: FriInitialTreeProofTarget, + pub steps: Vec>, +} + pub struct FriProof, const D: usize> { /// A Merkle root for each reduced polynomial in the commit phase. pub commit_phase_merkle_roots: Vec>, @@ -125,16 +129,11 @@ pub struct FriProof, const D: usize> { pub pow_witness: F, } -/// Represents a single FRI query, i.e. a path through the reduction tree. -pub struct FriProofTarget { - /// A Merkle root for each reduced polynomial in the commit phase. +pub struct FriProofTarget { pub commit_phase_merkle_roots: Vec, - /// Merkle proofs for the original purported codewords, i.e. the subject of the LDT. - pub initial_merkle_proofs: Vec, - /// Merkle proofs for the reduced polynomials that were sent in the commit phase. - pub intermediate_merkle_proofs: Vec, - /// The final polynomial in coefficient form. - pub final_poly: Vec, + pub query_round_proofs: Vec>, + pub final_poly: PolynomialCoeffsExtTarget, + pub pow_witness: Target, } /// The purported values of each polynomial at a single point. @@ -175,10 +174,10 @@ impl, const D: usize> OpeningSet { } /// The purported values of each polynomial at a single point. -pub struct OpeningSetTarget { - pub constants: Vec, - pub plonk_sigmas: Vec, - pub wires: Vec, - pub plonk_zs: Vec, - pub quotient_polys: Vec, +pub struct OpeningSetTarget { + pub constants: Vec>, + pub plonk_sigmas: Vec>, + pub wires: Vec>, + pub plonk_zs: Vec>, + pub quotient_polys: Vec>, } diff --git a/src/recursive_verifier.rs b/src/recursive_verifier.rs index a9d37553..c1005a67 100644 --- a/src/recursive_verifier.rs +++ b/src/recursive_verifier.rs @@ -13,7 +13,7 @@ pub fn add_recursive_verifier, const D: usize>( inner_config: CircuitConfig, inner_circuit: VerifierCircuitTarget, inner_gates: Vec>, - inner_proof: ProofTarget, + inner_proof: ProofTarget, ) { assert!(builder.config.num_wires >= MIN_WIRES); assert!(builder.config.num_wires >= MIN_ROUTED_WIRES); From 42d5b80a7a5f1ec96e08f3082fd733b23e1108ac Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 4 Jun 2021 15:40:54 +0200 Subject: [PATCH 002/117] `BaseSum` gate --- src/fri/mod.rs | 1 + src/fri/prover.rs | 2 +- src/fri/recursive_verifier.rs | 306 ++++++++++++++++++++++++++++++++++ src/fri/verifier.rs | 3 +- src/gates/base_sum.rs | 169 +++++++++++++++++++ src/gates/mod.rs | 1 + src/plonk_challenger.rs | 14 ++ src/plonk_common.rs | 11 +- 8 files changed, 501 insertions(+), 6 deletions(-) create mode 100644 src/fri/recursive_verifier.rs create mode 100644 src/gates/base_sum.rs diff --git a/src/fri/mod.rs b/src/fri/mod.rs index c147e8c6..6351a2af 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -1,6 +1,7 @@ use crate::polynomial::commitment::SALT_SIZE; pub mod prover; +mod recursive_verifier; pub mod verifier; /// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns, diff --git a/src/fri/prover.rs b/src/fri/prover.rs index d1eeadcf..a919b559 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -106,7 +106,7 @@ fn fri_proof_of_work(current_hash: Hash, config: &FriConfig) -> F { false, ) .to_canonical_u64() - .leading_zeros() + .trailing_zeros() >= config.proof_of_work_bits }) .map(F::from_canonical_u64) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs new file mode 100644 index 00000000..a64b02fe --- /dev/null +++ b/src/fri/recursive_verifier.rs @@ -0,0 +1,306 @@ +use anyhow::{ensure, Result}; +use itertools::izip; + +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::{flatten, Extendable, FieldExtension, OEF}; +use crate::field::field::Field; +use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; +use crate::fri::FriConfig; +use crate::hash::hash_n_to_1; +use crate::merkle_proofs::verify_merkle_proof; +use crate::plonk_challenger::{Challenger, RecursiveChallenger}; +use crate::plonk_common::reduce_with_iter; +use crate::proof::{ + FriInitialTreeProof, FriProof, FriProofTarget, FriQueryRound, Hash, OpeningSet, +}; +use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; + +impl, const D: usize> CircuitBuilder { + /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity + /// and P' is the FRI reduced polynomial. + fn compute_evaluation() { + todo!(); + // debug_assert_eq!(last_evals.len(), 1 << arity_bits); + // + // let g = F::primitive_root_of_unity(arity_bits); + // + // // The evaluation vector needs to be reordered first. + // let mut evals = last_evals.to_vec(); + // reverse_index_bits_in_place(&mut evals); + // evals.rotate_left(reverse_bits(old_x_index, arity_bits)); + // + // // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. + // let points = g + // .powers() + // .zip(evals) + // .map(|(y, e)| ((x * y).into(), e)) + // .collect::>(); + // let barycentric_weights = barycentric_weights(&points); + // interpolate(&points, beta, &barycentric_weights) + } + + fn fri_verify_proof_of_work( + &mut self, + proof: &FriProofTarget, + challenger: &mut RecursiveChallenger, + config: &FriConfig, + ) -> Result<()> { + let mut inputs = challenger.get_hash(self).elements.to_vec(); + inputs.push(proof.pow_witness); + + let hash = self.hash_n_to_m(inputs, 1, false)[0]; + let purported_hash_bits = self.split_le_virtual(hash, 64); + // for &b in &purported_hash_bits { + // self.generate_copy(b, self.zero()); + // } + // ensure!( + // hash.to_canonical_u64().trailing_zeros() >= config.proof_of_work_bits, + // "Invalid proof of work witness." + // ); + + Ok(()) + } + + // pub fn verify_fri_proof( + // purported_degree_log: usize, + // // Openings of the PLONK polynomials. + // os: &OpeningSet, + // // Point at which the PLONK polynomials are opened. + // zeta: F::Extension, + // // Scaling factor to combine polynomials. + // alpha: F::Extension, + // initial_merkle_roots: &[Hash], + // proof: &FriProof, + // challenger: &mut Challenger, + // config: &FriConfig, + // ) -> Result<()> { + // let total_arities = config.reduction_arity_bits.iter().sum::(); + // ensure!( + // purported_degree_log + // == log2_strict(proof.final_poly.len()) + total_arities - config.rate_bits, + // "Final polynomial has wrong degree." + // ); + // + // // Size of the LDE domain. + // let n = proof.final_poly.len() << total_arities; + // + // // Recover the random betas used in the FRI reductions. + // let betas = proof + // .commit_phase_merkle_roots + // .iter() + // .map(|root| { + // challenger.observe_hash(root); + // challenger.get_extension_challenge() + // }) + // .collect::>(); + // challenger.observe_extension_elements(&proof.final_poly.coeffs); + // + // // Check PoW. + // fri_verify_proof_of_work(proof, challenger, config)?; + // + // // Check that parameters are coherent. + // ensure!( + // config.num_query_rounds == proof.query_round_proofs.len(), + // "Number of query rounds does not match config." + // ); + // ensure!( + // !config.reduction_arity_bits.is_empty(), + // "Number of reductions should be non-zero." + // ); + // + // for round_proof in &proof.query_round_proofs { + // fri_verifier_query_round( + // os, + // zeta, + // alpha, + // initial_merkle_roots, + // &proof, + // challenger, + // n, + // &betas, + // round_proof, + // config, + // )?; + // } + // + // Ok(()) + // } + // + // fn fri_verify_initial_proof( + // x_index: usize, + // proof: &FriInitialTreeProof, + // initial_merkle_roots: &[Hash], + // ) -> Result<()> { + // for ((evals, merkle_proof), &root) in proof.evals_proofs.iter().zip(initial_merkle_roots) { + // verify_merkle_proof(evals.clone(), x_index, root, merkle_proof, false)?; + // } + // + // Ok(()) + // } + // + // fn fri_combine_initial, const D: usize>( + // proof: &FriInitialTreeProof, + // alpha: F::Extension, + // os: &OpeningSet, + // zeta: F::Extension, + // subgroup_x: F, + // config: &FriConfig, + // ) -> F::Extension { + // assert!(D > 1, "Not implemented for D=1."); + // let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; + // let subgroup_x = F::Extension::from_basefield(subgroup_x); + // let mut alpha_powers = alpha.powers(); + // let mut sum = F::Extension::ZERO; + // + // // We will add three terms to `sum`: + // // - one for polynomials opened at `x` only + // // - one for polynomials opened at `x` and `g x` + // // - one for polynomials opened at `x` and its conjugate + // + // let evals = [0, 1, 4] + // .iter() + // .flat_map(|&i| proof.unsalted_evals(i, config)) + // .map(|&e| F::Extension::from_basefield(e)); + // let openings = os + // .constants + // .iter() + // .chain(&os.plonk_sigmas) + // .chain(&os.quotient_polys); + // let numerator = izip!(evals, openings, &mut alpha_powers) + // .map(|(e, &o, a)| a * (e - o)) + // .sum::(); + // let denominator = subgroup_x - zeta; + // sum += numerator / denominator; + // + // let ev: F::Extension = proof + // .unsalted_evals(3, config) + // .iter() + // .zip(alpha_powers.clone()) + // .map(|(&e, a)| a * e.into()) + // .sum(); + // let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; + // let zs_interpol = interpolant(&[ + // (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), + // ( + // zeta_right, + // reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + // ), + // ]); + // let numerator = ev - zs_interpol.eval(subgroup_x); + // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); + // sum += numerator / denominator; + // + // let ev: F::Extension = proof + // .unsalted_evals(2, config) + // .iter() + // .zip(alpha_powers.clone()) + // .map(|(&e, a)| a * e.into()) + // .sum(); + // let zeta_frob = zeta.frobenius(); + // let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); + // let wires_interpol = interpolant(&[ + // (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), + // (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), + // ]); + // let numerator = ev - wires_interpol.eval(subgroup_x); + // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); + // sum += numerator / denominator; + // + // sum + // } + // + // fn fri_verifier_query_round, const D: usize>( + // os: &OpeningSet, + // zeta: F::Extension, + // alpha: F::Extension, + // initial_merkle_roots: &[Hash], + // proof: &FriProof, + // challenger: &mut Challenger, + // n: usize, + // betas: &[F::Extension], + // round_proof: &FriQueryRound, + // config: &FriConfig, + // ) -> Result<()> { + // let mut evaluations: Vec> = Vec::new(); + // let x = challenger.get_challenge(); + // let mut domain_size = n; + // let mut x_index = x.to_canonical_u64() as usize % n; + // fri_verify_initial_proof( + // x_index, + // &round_proof.initial_trees_proof, + // initial_merkle_roots, + // )?; + // let mut old_x_index = 0; + // // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. + // let log_n = log2_strict(n); + // let mut subgroup_x = F::MULTIPLICATIVE_GROUP_GENERATOR + // * F::primitive_root_of_unity(log_n).exp(reverse_bits(x_index, log_n) as u64); + // for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { + // let arity = 1 << arity_bits; + // let next_domain_size = domain_size >> arity_bits; + // let e_x = if i == 0 { + // fri_combine_initial( + // &round_proof.initial_trees_proof, + // alpha, + // os, + // zeta, + // subgroup_x, + // config, + // ) + // } else { + // let last_evals = &evaluations[i - 1]; + // // Infer P(y) from {P(x)}_{x^arity=y}. + // compute_evaluation( + // subgroup_x, + // old_x_index, + // config.reduction_arity_bits[i - 1], + // last_evals, + // betas[i - 1], + // ) + // }; + // let mut evals = round_proof.steps[i].evals.clone(); + // // Insert P(y) into the evaluation vector, since it wasn't included by the prover. + // evals.insert(x_index & (arity - 1), e_x); + // evaluations.push(evals); + // verify_merkle_proof( + // flatten(&evaluations[i]), + // x_index >> arity_bits, + // proof.commit_phase_merkle_roots[i], + // &round_proof.steps[i].merkle_proof, + // false, + // )?; + // + // if i > 0 { + // // Update the point x to x^arity. + // for _ in 0..config.reduction_arity_bits[i - 1] { + // subgroup_x = subgroup_x.square(); + // } + // } + // domain_size = next_domain_size; + // old_x_index = x_index; + // x_index >>= arity_bits; + // } + // + // let last_evals = evaluations.last().unwrap(); + // let final_arity_bits = *config.reduction_arity_bits.last().unwrap(); + // let purported_eval = compute_evaluation( + // subgroup_x, + // old_x_index, + // final_arity_bits, + // last_evals, + // *betas.last().unwrap(), + // ); + // for _ in 0..final_arity_bits { + // subgroup_x = subgroup_x.square(); + // } + // + // // Final check of FRI. After all the reductions, we check that the final polynomial is equal + // // to the one sent by the prover. + // ensure!( + // proof.final_poly.eval(subgroup_x.into()) == purported_eval, + // "Final polynomial evaluation is invalid." + // ); + // + // Ok(()) + // } +} diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index e6e8cae7..156d170f 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -56,8 +56,7 @@ fn fri_verify_proof_of_work, const D: usize>( false, ); ensure!( - hash.to_canonical_u64().leading_zeros() - >= config.proof_of_work_bits + F::ORDER.leading_zeros(), + hash.to_canonical_u64().trailing_zeros() >= config.proof_of_work_bits, "Invalid proof of work witness." ); diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs new file mode 100644 index 00000000..f7ebd3a0 --- /dev/null +++ b/src/gates/base_sum.rs @@ -0,0 +1,169 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::circuit_data::CircuitConfig; +use crate::field::extension_field::target::ExtensionTarget; +use crate::field::extension_field::Extendable; +use crate::field::field::Field; +use crate::gates::gate::{Gate, GateRef}; +use crate::generator::{SimpleGenerator, WitnessGenerator}; +use crate::plonk_common::{reduce_with_powers, reduce_with_powers_recursive}; +use crate::target::Target; +use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::wire::Wire; +use crate::witness::PartialWitness; +use std::ops::Range; + +/// A gate which can sum base W limbs. +#[derive(Debug)] +pub struct BaseSumGate { + num_limbs: usize, +} + +impl BaseSumGate { + pub fn new, const D: usize>(config: &CircuitConfig) -> GateRef { + GateRef::new(BaseSumGate:: { + num_limbs: config.num_routed_wires - 1, + }) + } + + pub const WIRE_SUM: usize = 0; + pub const WIRE_LIMBS_START: usize = 1; + + /// Returns the index of the `i`th limb wire. + pub fn limbs(&self) -> Range { + Self::WIRE_LIMBS_START..Self::WIRE_LIMBS_START + self.num_limbs + } +} + +impl, const D: usize, const B: usize> Gate for BaseSumGate { + fn id(&self) -> String { + format!("{:?}", self) + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + let sum = vars.local_wires[Self::WIRE_SUM]; + let limbs = vars.local_wires[self.limbs()].to_vec(); + let computed_sum = reduce_with_powers(&limbs, F::Extension::from_canonical_usize(B)); + let mut constraints = vec![computed_sum - sum]; + for limb in limbs { + constraints.push( + (0..B) + .map(|i| limb - F::Extension::from_canonical_usize(i)) + .product(), + ); + } + constraints + } + + fn eval_unfiltered_recursively( + &self, + builder: &mut CircuitBuilder, + vars: EvaluationTargets, + ) -> Vec> { + let base = builder.constant(F::from_canonical_usize(B)); + let sum = vars.local_wires[Self::WIRE_SUM]; + let limbs = vars.local_wires[self.limbs()].to_vec(); + let computed_sum = + reduce_with_powers_recursive(builder, &vars.local_wires[self.limbs()], base); + let mut constraints = vec![builder.sub_extension(computed_sum, sum)]; + for limb in limbs { + constraints.push({ + let mut acc = builder.one_extension(); + (0..B).for_each(|i| { + let it = builder.constant_extension(F::from_canonical_usize(i).into()); + let diff = builder.sub_extension(limb, it); + acc = builder.mul_extension(acc, diff); + }); + acc + }); + } + constraints + } + + fn generators( + &self, + gate_index: usize, + _local_constants: &[F], + ) -> Vec>> { + let gen = BaseSplitGenerator:: { + gate_index, + num_limbs: self.num_limbs, + }; + vec![Box::new(gen)] + } + + fn num_wires(&self) -> usize { + self.num_limbs + 1 + } + + fn num_constants(&self) -> usize { + 0 + } + + fn degree(&self) -> usize { + B + } + + fn num_constraints(&self) -> usize { + 1 + B + } +} + +#[derive(Debug)] +pub struct BaseSplitGenerator { + gate_index: usize, + num_limbs: usize, +} + +impl SimpleGenerator for BaseSplitGenerator { + fn dependencies(&self) -> Vec { + vec![Target::Wire(Wire { + gate: self.gate_index, + input: BaseSumGate::::WIRE_SUM, + })] + } + + fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + let mut sum_value = witness + .get_target(Target::Wire(Wire { + gate: self.gate_index, + input: BaseSumGate::::WIRE_SUM, + })) + .to_canonical_u64() as usize; + let limbs = (BaseSumGate::::WIRE_LIMBS_START + ..BaseSumGate::::WIRE_LIMBS_START + self.num_limbs) + .map(|i| { + Target::Wire(Wire { + gate: self.gate_index, + input: i, + }) + }); + + let mut result = PartialWitness::new(); + for b in limbs { + let b_value = sum_value % B; + result.set_target(b, F::from_canonical_usize(b_value)); + sum_value /= B; + } + + debug_assert_eq!( + sum_value, 0, + "Integer too large to fit in given number of bits" + ); + + result + } +} + +#[cfg(test)] +mod tests { + use crate::circuit_data::CircuitConfig; + use crate::field::crandall_field::CrandallField; + use crate::gates::base_sum::BaseSumGate; + use crate::gates::gate_testing::test_low_degree; + + #[test] + fn low_degree() { + let config = CircuitConfig::default(); + test_low_degree(BaseSumGate::<6>::new::(&config)) + } +} diff --git a/src/gates/mod.rs b/src/gates/mod.rs index ebcf6e3f..b25337ff 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -5,5 +5,6 @@ pub mod gmimc; mod interpolation; pub(crate) mod noop; +mod base_sum; #[cfg(test)] mod gate_testing; diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 69891761..2b1739d5 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -255,6 +255,20 @@ impl RecursiveChallenger { (0..n).map(|_| self.get_challenge(builder)).collect() } + pub fn get_hash, const D: usize>( + &mut self, + builder: &mut CircuitBuilder, + ) -> HashTarget { + HashTarget { + elements: [ + self.get_challenge(builder), + self.get_challenge(builder), + self.get_challenge(builder), + self.get_challenge(builder), + ], + } + } + /// Absorb any buffered inputs. After calling this, the input buffer will be empty. fn absorb_buffered_inputs, const D: usize>( &mut self, diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 990e6ea7..daa131ef 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -103,10 +103,15 @@ pub(crate) fn reduce_with_powers(terms: &[F], alpha: F) -> F { pub(crate) fn reduce_with_powers_recursive, const D: usize>( builder: &mut CircuitBuilder, - terms: Vec, + terms: &[ExtensionTarget], alpha: Target, -) -> Target { - todo!() +) -> ExtensionTarget { + let mut sum = builder.zero_extension(); + for &term in terms.iter().rev() { + sum = builder.scalar_mul_ext(alpha, sum); + sum = builder.add_extension(sum, term); + } + sum } pub(crate) fn reduce_with_iter(terms: &[F], coeffs: I) -> F From 51c06d74ee8759c386ff9976eb6d9581e98bebdf Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 4 Jun 2021 16:02:48 +0200 Subject: [PATCH 003/117] Recursive PoW --- src/circuit_builder.rs | 5 +++++ src/fri/recursive_verifier.rs | 9 +-------- src/gadgets/mod.rs | 1 + src/gadgets/split_base.rs | 33 +++++++++++++++++++++++++++++++++ src/gates/base_sum.rs | 11 ++++------- src/gates/mod.rs | 2 +- 6 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 src/gadgets/split_base.rs diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index a2c94f83..ccca4e2b 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -149,6 +149,11 @@ impl, const D: usize> CircuitBuilder { self.copy_constraints.push((x, y)); } + pub fn assert_zero(&mut self, x: Target) { + let zero = self.zero(); + self.assert_equal(x, zero); + } + pub fn add_generators(&mut self, generators: Vec>>) { self.generators.extend(generators); } diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index a64b02fe..6c245167 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -49,14 +49,7 @@ impl, const D: usize> CircuitBuilder { inputs.push(proof.pow_witness); let hash = self.hash_n_to_m(inputs, 1, false)[0]; - let purported_hash_bits = self.split_le_virtual(hash, 64); - // for &b in &purported_hash_bits { - // self.generate_copy(b, self.zero()); - // } - // ensure!( - // hash.to_canonical_u64().trailing_zeros() >= config.proof_of_work_bits, - // "Invalid proof of work witness." - // ); + self.assert_trailing_zeros(hash, config.proof_of_work_bits); Ok(()) } diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 9a6a728e..22bef43b 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -1,4 +1,5 @@ pub mod arithmetic; pub mod hash; pub mod polynomial; +pub mod split_base; pub(crate) mod split_join; diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs new file mode 100644 index 00000000..e622be75 --- /dev/null +++ b/src/gadgets/split_base.rs @@ -0,0 +1,33 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::Extendable; +use crate::field::field::Field; +use crate::gates::base_sum::{BaseSplitGenerator, BaseSumGate}; +use crate::generator::{SimpleGenerator, WitnessGenerator}; +use crate::target::Target; +use crate::util::ceil_div_usize; +use crate::wire::Wire; +use crate::witness::PartialWitness; + +impl, const D: usize> CircuitBuilder { + /// Split the given element into a list of 11 targets, where each one represents a + /// base-64 limb of the element, with little-endian ordering. + pub(crate) fn split_le_base64(&mut self, x: Target) -> Vec { + let gate = self.add_gate(BaseSumGate::<64>::new(11), vec![]); + let sum = Target::Wire(Wire { + gate, + input: BaseSumGate::<64>::WIRE_SUM, + }); + self.route(x, sum); + (BaseSumGate::<64>::WIRE_LIMBS_START..BaseSumGate::<64>::WIRE_LIMBS_START + 11) + .map(|i| Target::Wire(Wire { gate, input: i })) + .collect() + } + + /// Asserts that `x`'s bit representation has at least `trailing_zeros` trailing zeros. + pub(crate) fn assert_trailing_zeros(&mut self, x: Target, trailing_zeros: u32) { + let limbs = self.split_le_base64(x); + for i in 0..ceil_div_usize(trailing_zeros as usize, 6) { + self.assert_zero(limbs[i]); + } + } +} diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index f7ebd3a0..9190e19c 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -19,10 +19,8 @@ pub struct BaseSumGate { } impl BaseSumGate { - pub fn new, const D: usize>(config: &CircuitConfig) -> GateRef { - GateRef::new(BaseSumGate:: { - num_limbs: config.num_routed_wires - 1, - }) + pub fn new, const D: usize>(num_limbs: usize) -> GateRef { + GateRef::new(BaseSumGate:: { num_limbs }) } pub const WIRE_SUM: usize = 0; @@ -36,7 +34,7 @@ impl BaseSumGate { impl, const D: usize, const B: usize> Gate for BaseSumGate { fn id(&self) -> String { - format!("{:?}", self) + format!("{:?} + Base: {}", self, B) } fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { @@ -163,7 +161,6 @@ mod tests { #[test] fn low_degree() { - let config = CircuitConfig::default(); - test_low_degree(BaseSumGate::<6>::new::(&config)) + test_low_degree(BaseSumGate::<6>::new::(11)) } } diff --git a/src/gates/mod.rs b/src/gates/mod.rs index b25337ff..49c37bae 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -1,10 +1,10 @@ pub(crate) mod arithmetic; +pub mod base_sum; pub mod constant; pub(crate) mod gate; pub mod gmimc; mod interpolation; pub(crate) mod noop; -mod base_sum; #[cfg(test)] mod gate_testing; From 11698701637b17d2ca78ef7dcc0ee96e0ff65a81 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 4 Jun 2021 17:07:14 +0200 Subject: [PATCH 004/117] Added test --- src/fri/recursive_verifier.rs | 2 +- src/gadgets/split_base.rs | 76 +++++++++++++++++++++++++++++++---- src/gates/base_sum.rs | 2 +- 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 6c245167..81cc59ab 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -49,7 +49,7 @@ impl, const D: usize> CircuitBuilder { inputs.push(proof.pow_witness); let hash = self.hash_n_to_m(inputs, 1, false)[0]; - self.assert_trailing_zeros(hash, config.proof_of_work_bits); + self.assert_trailing_zeros::<64>(hash, config.proof_of_work_bits); Ok(()) } diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index e622be75..35d5ac93 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -11,23 +11,85 @@ use crate::witness::PartialWitness; impl, const D: usize> CircuitBuilder { /// Split the given element into a list of 11 targets, where each one represents a /// base-64 limb of the element, with little-endian ordering. - pub(crate) fn split_le_base64(&mut self, x: Target) -> Vec { - let gate = self.add_gate(BaseSumGate::<64>::new(11), vec![]); + pub(crate) fn split_le_base(&mut self, x: Target) -> Vec { + let num_limbs = num_limbs_to_check(64, B); + let gate = self.add_gate(BaseSumGate::::new(num_limbs), vec![]); let sum = Target::Wire(Wire { gate, - input: BaseSumGate::<64>::WIRE_SUM, + input: BaseSumGate::::WIRE_SUM, }); self.route(x, sum); - (BaseSumGate::<64>::WIRE_LIMBS_START..BaseSumGate::<64>::WIRE_LIMBS_START + 11) + (BaseSumGate::::WIRE_LIMBS_START..BaseSumGate::::WIRE_LIMBS_START + num_limbs) .map(|i| Target::Wire(Wire { gate, input: i })) .collect() } /// Asserts that `x`'s bit representation has at least `trailing_zeros` trailing zeros. - pub(crate) fn assert_trailing_zeros(&mut self, x: Target, trailing_zeros: u32) { - let limbs = self.split_le_base64(x); - for i in 0..ceil_div_usize(trailing_zeros as usize, 6) { + pub(crate) fn assert_trailing_zeros(&mut self, x: Target, trailing_zeros: u32) { + let limbs = self.split_le_base::(x); + let num_limbs_to_check = num_limbs_to_check(trailing_zeros, B); + assert!( + num_limbs_to_check < self.config.num_routed_wires, + "Not enough routed wires." + ); + for i in 0..num_limbs_to_check { self.assert_zero(limbs[i]); } } } + +/// Returns `k` such that any number with `k` trailing zeros in base `base` has at least +/// `n` trailing zeros in base 2. +fn num_limbs_to_check(n: u32, base: usize) -> usize { + (n as f64 * (2.0_f64.log(base as f64))).ceil() as usize +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::circuit_data::CircuitConfig; + use crate::field::crandall_field::CrandallField; + use crate::fri::FriConfig; + use crate::prover::PLONK_BLINDING; + use crate::verifier::verify; + use anyhow::Result; + + #[test] + fn test_split_base() -> Result<()> { + type F = CrandallField; + let config = CircuitConfig { + num_wires: 134, + num_routed_wires: 12, + security_bits: 128, + rate_bits: 3, + num_challenges: 3, + fri_config: FriConfig { + proof_of_work_bits: 1, + rate_bits: 3, + reduction_arity_bits: vec![1], + num_query_rounds: 1, + blinding: PLONK_BLINDING.to_vec(), + }, + }; + let mut builder = CircuitBuilder::::new(config); + let x = F::from_canonical_usize(0b10100000); // 160 =1120 in base 5. + let xt = builder.constant(x); + let limbs = builder.split_le_base::<5>(xt); + assert_eq!(limbs.len(), 28); // 5^27 < 2^64 <= 5^28 + let zero = builder.zero(); + let one = builder.one(); + let two = builder.two(); + builder.assert_equal(limbs[0], zero); + builder.assert_equal(limbs[1], two); + builder.assert_equal(limbs[2], one); + builder.assert_equal(limbs[3], one); + + builder.assert_trailing_zeros::<3>(xt, 4); + builder.assert_trailing_zeros::<3>(xt, 5); + builder.assert_trailing_zeros::<13>(xt, 5); + let data = builder.build(); + + let proof = data.prove(PartialWitness::new()); + verify(proof, &data.verifier_only, &data.common) + } +} diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index 9190e19c..8c8064a1 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -102,7 +102,7 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat } fn num_constraints(&self) -> usize { - 1 + B + 1 + self.num_limbs } } From eee117512be74f17178235e1641bcbe9f7a0ff6c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 4 Jun 2021 17:12:39 +0200 Subject: [PATCH 005/117] Fill `todo!` --- src/verifier.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/verifier.rs b/src/verifier.rs index 0ad7a1a0..98568c13 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -31,7 +31,7 @@ pub(crate) fn verify, const D: usize>( // TODO: Compute PI(zeta), Z_H(zeta), etc. and check the identity at zeta. - let evaluations = todo!(); + let evaluations = proof.openings; let merkle_roots = &[ verifier_data.constants_root, @@ -41,9 +41,13 @@ pub(crate) fn verify, const D: usize>( proof.quotient_polys_root, ]; - proof - .opening_proof - .verify(zeta, evaluations, merkle_roots, &mut challenger, fri_config)?; + proof.opening_proof.verify( + zeta, + &evaluations, + merkle_roots, + &mut challenger, + fri_config, + )?; Ok(()) } From f5dfe95b2e999f58d891b36c920d766eee780fc5 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 4 Jun 2021 17:36:48 +0200 Subject: [PATCH 006/117] Added recursive `powers` --- src/field/extension_field/target.rs | 7 ++ src/fri/recursive_verifier.rs | 170 ++++++++++++++-------------- src/gadgets/arithmetic.rs | 25 ++++ src/merkle_proofs.rs | 4 +- src/proof.rs | 8 ++ 5 files changed, 129 insertions(+), 85 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 7198ebf6..20cd3dcb 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -206,4 +206,11 @@ impl, const D: usize> CircuitBuilder { } b } + + pub fn convert_to_ext(&mut self, t: Target) -> ExtensionTarget { + let zero = self.zero(); + let mut arr = [zero; D]; + arr[0] = t; + ExtensionTarget(arr) + } } diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 81cc59ab..f0a2338e 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -2,6 +2,7 @@ use anyhow::{ensure, Result}; use itertools::izip; use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{flatten, Extendable, FieldExtension, OEF}; use crate::field::field::Field; use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; @@ -11,8 +12,10 @@ use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::{Challenger, RecursiveChallenger}; use crate::plonk_common::reduce_with_iter; use crate::proof::{ - FriInitialTreeProof, FriProof, FriProofTarget, FriQueryRound, Hash, OpeningSet, + FriInitialTreeProof, FriInitialTreeProofTarget, FriProof, FriProofTarget, FriQueryRound, Hash, + HashTarget, OpeningSet, OpeningSetTarget, }; +use crate::target::Target; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; impl, const D: usize> CircuitBuilder { @@ -119,88 +122,89 @@ impl, const D: usize> CircuitBuilder { // Ok(()) // } // - // fn fri_verify_initial_proof( - // x_index: usize, - // proof: &FriInitialTreeProof, - // initial_merkle_roots: &[Hash], - // ) -> Result<()> { - // for ((evals, merkle_proof), &root) in proof.evals_proofs.iter().zip(initial_merkle_roots) { - // verify_merkle_proof(evals.clone(), x_index, root, merkle_proof, false)?; - // } - // - // Ok(()) - // } - // - // fn fri_combine_initial, const D: usize>( - // proof: &FriInitialTreeProof, - // alpha: F::Extension, - // os: &OpeningSet, - // zeta: F::Extension, - // subgroup_x: F, - // config: &FriConfig, - // ) -> F::Extension { - // assert!(D > 1, "Not implemented for D=1."); - // let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; - // let subgroup_x = F::Extension::from_basefield(subgroup_x); - // let mut alpha_powers = alpha.powers(); - // let mut sum = F::Extension::ZERO; - // - // // We will add three terms to `sum`: - // // - one for polynomials opened at `x` only - // // - one for polynomials opened at `x` and `g x` - // // - one for polynomials opened at `x` and its conjugate - // - // let evals = [0, 1, 4] - // .iter() - // .flat_map(|&i| proof.unsalted_evals(i, config)) - // .map(|&e| F::Extension::from_basefield(e)); - // let openings = os - // .constants - // .iter() - // .chain(&os.plonk_sigmas) - // .chain(&os.quotient_polys); - // let numerator = izip!(evals, openings, &mut alpha_powers) - // .map(|(e, &o, a)| a * (e - o)) - // .sum::(); - // let denominator = subgroup_x - zeta; - // sum += numerator / denominator; - // - // let ev: F::Extension = proof - // .unsalted_evals(3, config) - // .iter() - // .zip(alpha_powers.clone()) - // .map(|(&e, a)| a * e.into()) - // .sum(); - // let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; - // let zs_interpol = interpolant(&[ - // (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), - // ( - // zeta_right, - // reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - // ), - // ]); - // let numerator = ev - zs_interpol.eval(subgroup_x); - // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); - // sum += numerator / denominator; - // - // let ev: F::Extension = proof - // .unsalted_evals(2, config) - // .iter() - // .zip(alpha_powers.clone()) - // .map(|(&e, a)| a * e.into()) - // .sum(); - // let zeta_frob = zeta.frobenius(); - // let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); - // let wires_interpol = interpolant(&[ - // (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), - // (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), - // ]); - // let numerator = ev - wires_interpol.eval(subgroup_x); - // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); - // sum += numerator / denominator; - // - // sum - // } + + fn fri_verify_initial_proof( + &mut self, + x_index: Target, + proof: &FriInitialTreeProofTarget, + initial_merkle_roots: &[HashTarget], + ) { + for ((evals, merkle_proof), &root) in proof.evals_proofs.iter().zip(initial_merkle_roots) { + self.verify_merkle_proof(evals.clone(), x_index, root, merkle_proof); + } + } + + fn fri_combine_initial( + &mut self, + proof: &FriInitialTreeProofTarget, + alpha: ExtensionTarget, + os: &OpeningSetTarget, + zeta: ExtensionTarget, + subgroup_x: Target, + ) -> ExtensionTarget { + assert!(D > 1, "Not implemented for D=1."); + let config = &self.config.fri_config; + let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; + let subgroup_x = self.convert_to_ext(subgroup_x); + let mut alpha_powers = self.powers(alpha); + let mut sum = self.zero_extension(); + + // We will add three terms to `sum`: + // - one for polynomials opened at `x` only + // - one for polynomials opened at `x` and `g x` + // - one for polynomials opened at `x` and its conjugate + + let evals = [0, 1, 4] + .iter() + .flat_map(|&i| proof.unsalted_evals(i, config)) + .map(|&e| F::Extension::from_basefield(e)); + let openings = os + .constants + .iter() + .chain(&os.plonk_sigmas) + .chain(&os.quotient_polys); + let numerator = izip!(evals, openings, &mut alpha_powers) + .map(|(e, &o, a)| a * (e - o)) + .sum::(); + let denominator = subgroup_x - zeta; + sum += numerator / denominator; + + let ev: F::Extension = proof + .unsalted_evals(3, config) + .iter() + .zip(alpha_powers.clone()) + .map(|(&e, a)| a * e.into()) + .sum(); + let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; + let zs_interpol = interpolant(&[ + (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), + ( + zeta_right, + reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + ), + ]); + let numerator = ev - zs_interpol.eval(subgroup_x); + let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); + sum += numerator / denominator; + + let ev: F::Extension = proof + .unsalted_evals(2, config) + .iter() + .zip(alpha_powers.clone()) + .map(|(&e, a)| a * e.into()) + .sum(); + let zeta_frob = zeta.frobenius(); + let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); + let wires_interpol = interpolant(&[ + (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), + (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), + ]); + let numerator = ev - wires_interpol.eval(subgroup_x); + let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); + sum += numerator / denominator; + + sum + } // // fn fri_verifier_query_round, const D: usize>( // os: &OpeningSet, diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index a214df3f..f152f77d 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -1,4 +1,5 @@ use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::gates::arithmetic::ArithmeticGate; @@ -226,6 +227,30 @@ impl, const D: usize> CircuitBuilder { } } +/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. +#[derive(Clone)] +pub struct PowersTarget { + base: ExtensionTarget, + current: ExtensionTarget, +} + +impl, const D: usize> PowersTarget { + fn next(&mut self, builder: &mut CircuitBuilder) -> Option> { + let result = self.current; + self.current = builder.mul_extension(self.base, self.current); + Some(result) + } +} + +impl, const D: usize> CircuitBuilder { + pub fn powers(&mut self, base: ExtensionTarget) -> PowersTarget { + PowersTarget { + base, + current: self.one_extension(), + } + } +} + struct QuotientGenerator { numerator: Target, denominator: Target, diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index d5ab8a78..7d50f466 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -62,7 +62,7 @@ impl, const D: usize> CircuitBuilder { leaf_data: Vec, leaf_index: Target, merkle_root: HashTarget, - proof: MerkleProofTarget, + proof: &MerkleProofTarget, ) { let zero = self.zero(); let height = proof.siblings.len(); @@ -71,7 +71,7 @@ impl, const D: usize> CircuitBuilder { let mut state: HashTarget = self.hash_or_noop(leaf_data); let mut acc_leaf_index = zero; - for (bit, sibling) in purported_index_bits.into_iter().zip(proof.siblings) { + for (bit, &sibling) in purported_index_bits.into_iter().zip(&proof.siblings) { let gate = self .add_gate_no_constants(GMiMCGate::::with_automatic_constants()); diff --git a/src/proof.rs b/src/proof.rs index 332eb941..65634f4c 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -35,6 +35,7 @@ impl Hash { } /// Represents a ~256 bit hash output. +#[derive(Copy, Clone, Debug)] pub struct HashTarget { pub(crate) elements: [Target; 4], } @@ -107,6 +108,13 @@ pub struct FriInitialTreeProofTarget { pub evals_proofs: Vec<(Vec, MerkleProofTarget)>, } +impl FriInitialTreeProofTarget { + pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> &[Target] { + let evals = &self.evals_proofs[i].0; + &evals[..evals.len() - config.salt_size(i)] + } +} + /// Proof for a FRI query round. pub struct FriQueryRound, const D: usize> { pub initial_trees_proof: FriInitialTreeProof, From 6f2275bc6d7ca6b137a8a03e73f9f6ee6bf86f8f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 7 Jun 2021 11:19:54 +0200 Subject: [PATCH 007/117] Progress --- src/fri/recursive_verifier.rs | 14 +-- src/gadgets/arithmetic.rs | 101 ++++++++++++++++----- src/gates/mod.rs | 1 + src/gates/mul_extension.rs | 160 ++++++++++++++++++++++++++++++++++ src/witness.rs | 11 +++ 5 files changed, 262 insertions(+), 25 deletions(-) create mode 100644 src/gates/mul_extension.rs diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index f0a2338e..6fe38b45 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -157,16 +157,20 @@ impl, const D: usize> CircuitBuilder { let evals = [0, 1, 4] .iter() .flat_map(|&i| proof.unsalted_evals(i, config)) - .map(|&e| F::Extension::from_basefield(e)); + .map(|&e| self.convert_to_ext(e)); let openings = os .constants .iter() .chain(&os.plonk_sigmas) .chain(&os.quotient_polys); - let numerator = izip!(evals, openings, &mut alpha_powers) - .map(|(e, &o, a)| a * (e - o)) - .sum::(); - let denominator = subgroup_x - zeta; + let mut numerator = self.zero_extension(); + for (e, &o) in izip!(evals, openings) { + let a = alpha_powers.next(self); + let diff = self.sub_extension(e, o); + numerator = self.mul_add_extension(a, diff, numerator); + } + let denominator = self.sub_extension(subgroup_x, zeta); + // let quotient = self.div_unsafe() sum += numerator / denominator; let ev: F::Extension = proof diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index f152f77d..a84caf03 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -225,29 +225,40 @@ impl, const D: usize> CircuitBuilder { q } -} -/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. -#[derive(Clone)] -pub struct PowersTarget { - base: ExtensionTarget, - current: ExtensionTarget, -} + /// Computes `q = x / y` by witnessing `q` and requiring that `q * y = x`. This can be unsafe in + /// some cases, as it allows `0 / 0 = `. + pub fn div_unsafe_extension( + &mut self, + x: ExtensionTarget, + y: ExtensionTarget, + ) -> ExtensionTarget { + // Add an `ArithmeticGate` to compute `q * y`. + let gate = self.add_gate(ArithmeticGate::new(), vec![F::ONE, F::ZERO]); -impl, const D: usize> PowersTarget { - fn next(&mut self, builder: &mut CircuitBuilder) -> Option> { - let result = self.current; - self.current = builder.mul_extension(self.base, self.current); - Some(result) - } -} + let wire_multiplicand_0 = Wire { + gate, + input: ArithmeticGate::WIRE_MULTIPLICAND_0, + }; + let wire_multiplicand_1 = Wire { + gate, + input: ArithmeticGate::WIRE_MULTIPLICAND_1, + }; + let wire_addend = Wire { + gate, + input: ArithmeticGate::WIRE_ADDEND, + }; + let wire_output = Wire { + gate, + input: ArithmeticGate::WIRE_OUTPUT, + }; -impl, const D: usize> CircuitBuilder { - pub fn powers(&mut self, base: ExtensionTarget) -> PowersTarget { - PowersTarget { - base, - current: self.one_extension(), - } + let q = Target::Wire(wire_multiplicand_0); + self.add_generator(QuotientGeneratorExtension { + numerator: x, + denominator: ExtensionTarget(), + quotient: ExtensionTarget(), + }) } } @@ -268,3 +279,53 @@ impl SimpleGenerator for QuotientGenerator { PartialWitness::singleton_target(self.quotient, num / den) } } + +struct QuotientGeneratorExtension { + numerator: ExtensionTarget, + denominator: ExtensionTarget, + quotient: ExtensionTarget, +} + +impl SimpleGenerator for QuotientGeneratorExtension { + fn dependencies(&self) -> Vec { + let mut deps = self.numerator.to_target_array().to_vec(); + deps.extend(&self.denominator.to_target_array()); + deps + } + + fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + let num = witness.get_extension_target(self.numerator); + let dem = witness.get_extension_target(self.denominator); + let quotient = num / dem; + let mut pw = PartialWitness::new(); + pw.set_ext_wires(self.quotient.to_target_array(), quotient); + pw + } +} + +/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. +#[derive(Clone)] +pub struct PowersTarget { + base: ExtensionTarget, + current: ExtensionTarget, +} + +impl PowersTarget { + pub fn next>( + &mut self, + builder: &mut CircuitBuilder, + ) -> ExtensionTarget { + let result = self.current; + self.current = builder.mul_extension(self.base, self.current); + result + } +} + +impl, const D: usize> CircuitBuilder { + pub fn powers(&mut self, base: ExtensionTarget) -> PowersTarget { + PowersTarget { + base, + current: self.one_extension(), + } + } +} diff --git a/src/gates/mod.rs b/src/gates/mod.rs index 49c37bae..f131b754 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -8,3 +8,4 @@ pub(crate) mod noop; #[cfg(test)] mod gate_testing; +mod mul_extension; diff --git a/src/gates/mul_extension.rs b/src/gates/mul_extension.rs new file mode 100644 index 00000000..ec5ba671 --- /dev/null +++ b/src/gates/mul_extension.rs @@ -0,0 +1,160 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; +use crate::field::extension_field::Extendable; +use crate::field::field::Field; +use crate::gates::gate::{Gate, GateRef}; +use crate::generator::{SimpleGenerator, WitnessGenerator}; +use crate::target::Target; +use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::wire::Wire; +use crate::witness::PartialWitness; +use std::ops::Range; + +/// A gate which can multiply to field extension elements. +/// TODO: Add an addend if `NUM_ROUTED_WIRES` is large enough. +#[derive(Debug)] +pub struct MulExtensionGate; + +impl MulExtensionGate { + pub fn new>() -> GateRef { + GateRef::new(MulExtensionGate) + } + + pub fn wires_multiplicand_0() -> Range { + 0..D + } + pub fn wires_multiplicand_1() -> Range { + D..2 * D + } + pub fn wires_output() -> Range { + 2 * D..3 * D + } +} + +impl, const D: usize> Gate for MulExtensionGate { + fn id(&self) -> String { + format!("{:?}", self) + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + let const_0 = vars.local_constants[0]; + let const_1 = vars.local_constants[1]; + let multiplicand_0 = vars.local_wires[Self::WIRE_MULTIPLICAND_0]; + let multiplicand_1 = vars.local_wires[Self::WIRE_MULTIPLICAND_1]; + let addend = vars.local_wires[Self::WIRE_ADDEND]; + let output = vars.local_wires[Self::WIRE_OUTPUT]; + let computed_output = const_0 * multiplicand_0 * multiplicand_1 + const_1 * addend; + vec![computed_output - output] + } + + fn eval_unfiltered_recursively( + &self, + builder: &mut CircuitBuilder, + vars: EvaluationTargets, + ) -> Vec> { + let const_0 = vars.local_constants[0]; + let const_1 = vars.local_constants[1]; + let multiplicand_0 = vars.local_wires[Self::WIRE_MULTIPLICAND_0]; + let multiplicand_1 = vars.local_wires[Self::WIRE_MULTIPLICAND_1]; + let addend = vars.local_wires[Self::WIRE_ADDEND]; + let output = vars.local_wires[Self::WIRE_OUTPUT]; + + let product_term = builder.mul_many_extension(&[const_0, multiplicand_0, multiplicand_1]); + let addend_term = builder.mul_extension(const_1, addend); + let computed_output = builder.add_many_extension(&[product_term, addend_term]); + vec![builder.sub_extension(computed_output, output)] + } + + fn generators( + &self, + gate_index: usize, + local_constants: &[F], + ) -> Vec>> { + let gen = ArithmeticGenerator { + gate_index, + const_0: local_constants[0], + const_1: local_constants[1], + }; + vec![Box::new(gen)] + } + + fn num_wires(&self) -> usize { + 4 + } + + fn num_constants(&self) -> usize { + 2 + } + + fn degree(&self) -> usize { + 3 + } + + fn num_constraints(&self) -> usize { + 1 + } +} + +struct ArithmeticGenerator { + gate_index: usize, + const_0: F, + const_1: F, +} + +impl SimpleGenerator for ArithmeticGenerator { + fn dependencies(&self) -> Vec { + vec![ + Target::Wire(Wire { + gate: self.gate_index, + input: ArithmeticGate::WIRE_MULTIPLICAND_0, + }), + Target::Wire(Wire { + gate: self.gate_index, + input: ArithmeticGate::WIRE_MULTIPLICAND_1, + }), + Target::Wire(Wire { + gate: self.gate_index, + input: ArithmeticGate::WIRE_ADDEND, + }), + ] + } + + fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + let multiplicand_0_target = Wire { + gate: self.gate_index, + input: ArithmeticGate::WIRE_MULTIPLICAND_0, + }; + let multiplicand_1_target = Wire { + gate: self.gate_index, + input: ArithmeticGate::WIRE_MULTIPLICAND_1, + }; + let addend_target = Wire { + gate: self.gate_index, + input: ArithmeticGate::WIRE_ADDEND, + }; + let output_target = Wire { + gate: self.gate_index, + input: ArithmeticGate::WIRE_OUTPUT, + }; + + let multiplicand_0 = witness.get_wire(multiplicand_0_target); + let multiplicand_1 = witness.get_wire(multiplicand_1_target); + let addend = witness.get_wire(addend_target); + + let output = self.const_0 * multiplicand_0 * multiplicand_1 + self.const_1 * addend; + + PartialWitness::singleton_wire(output_target, output) + } +} + +#[cfg(test)] +mod tests { + use crate::field::crandall_field::CrandallField; + use crate::gates::arithmetic::ArithmeticGate; + use crate::gates::gate_testing::test_low_degree; + + #[test] + fn low_degree() { + test_low_degree(ArithmeticGate::new::()) + } +} diff --git a/src/witness.rs b/src/witness.rs index a0b4b2a4..ebd85d9e 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,9 +1,11 @@ use std::collections::HashMap; +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::target::Target; use crate::wire::Wire; +use std::convert::TryInto; #[derive(Clone, Debug)] pub struct PartialWitness { @@ -39,6 +41,15 @@ impl PartialWitness { targets.iter().map(|&t| self.get_target(t)).collect() } + pub fn get_extension_target(&self, et: ExtensionTarget) -> F::Extension + where + F: Extendable, + { + F::Extension::from_basefield_array( + self.get_targets(&et.to_target_array()).try_into().unwrap(), + ) + } + pub fn try_get_target(&self, target: Target) -> Option { self.target_values.get(&target).cloned() } From a8da9b945ec799044a3f36bba5b3aadebfcf442a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 7 Jun 2021 17:09:53 +0200 Subject: [PATCH 008/117] Working MulExtensionGate --- src/fri/recursive_verifier.rs | 75 ++++++------ src/gadgets/arithmetic.rs | 22 ++-- src/gates/mul_extension.rs | 215 ++++++++++++++++++++++++---------- 3 files changed, 203 insertions(+), 109 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 6fe38b45..edb9b075 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -143,7 +143,7 @@ impl, const D: usize> CircuitBuilder { subgroup_x: Target, ) -> ExtensionTarget { assert!(D > 1, "Not implemented for D=1."); - let config = &self.config.fri_config; + let config = &self.config.fri_config.clone(); let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; let subgroup_x = self.convert_to_ext(subgroup_x); let mut alpha_powers = self.powers(alpha); @@ -157,7 +157,8 @@ impl, const D: usize> CircuitBuilder { let evals = [0, 1, 4] .iter() .flat_map(|&i| proof.unsalted_evals(i, config)) - .map(|&e| self.convert_to_ext(e)); + .map(|&e| self.convert_to_ext(e)) + .collect::>(); let openings = os .constants .iter() @@ -170,42 +171,42 @@ impl, const D: usize> CircuitBuilder { numerator = self.mul_add_extension(a, diff, numerator); } let denominator = self.sub_extension(subgroup_x, zeta); - // let quotient = self.div_unsafe() - sum += numerator / denominator; + let quotient = self.div_unsafe_extension(numerator, denominator); + let sum = self.add_extension(sum, quotient); - let ev: F::Extension = proof - .unsalted_evals(3, config) - .iter() - .zip(alpha_powers.clone()) - .map(|(&e, a)| a * e.into()) - .sum(); - let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; - let zs_interpol = interpolant(&[ - (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), - ( - zeta_right, - reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - ), - ]); - let numerator = ev - zs_interpol.eval(subgroup_x); - let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); - sum += numerator / denominator; - - let ev: F::Extension = proof - .unsalted_evals(2, config) - .iter() - .zip(alpha_powers.clone()) - .map(|(&e, a)| a * e.into()) - .sum(); - let zeta_frob = zeta.frobenius(); - let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); - let wires_interpol = interpolant(&[ - (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), - (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), - ]); - let numerator = ev - wires_interpol.eval(subgroup_x); - let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); - sum += numerator / denominator; + // let ev: F::Extension = proof + // .unsalted_evals(3, config) + // .iter() + // .zip(alpha_powers.clone()) + // .map(|(&e, a)| a * e.into()) + // .sum(); + // let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; + // let zs_interpol = interpolant(&[ + // (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), + // ( + // zeta_right, + // reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + // ), + // ]); + // let numerator = ev - zs_interpol.eval(subgroup_x); + // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); + // sum += numerator / denominator; + // + // let ev: F::Extension = proof + // .unsalted_evals(2, config) + // .iter() + // .zip(alpha_powers.clone()) + // .map(|(&e, a)| a * e.into()) + // .sum(); + // let zeta_frob = zeta.frobenius(); + // let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); + // let wires_interpol = interpolant(&[ + // (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), + // (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), + // ]); + // let numerator = ev - wires_interpol.eval(subgroup_x); + // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); + // sum += numerator / denominator; sum } diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index a84caf03..fd32c83d 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -1,6 +1,6 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; -use crate::field::extension_field::Extendable; +use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::gates::arithmetic::ArithmeticGate; use crate::generator::SimpleGenerator; @@ -254,11 +254,12 @@ impl, const D: usize> CircuitBuilder { }; let q = Target::Wire(wire_multiplicand_0); - self.add_generator(QuotientGeneratorExtension { - numerator: x, - denominator: ExtensionTarget(), - quotient: ExtensionTarget(), - }) + todo!() + // self.add_generator(QuotientGeneratorExtension { + // numerator: x, + // denominator: ExtensionTarget(), + // quotient: ExtensionTarget(), + // }) } } @@ -286,7 +287,7 @@ struct QuotientGeneratorExtension { quotient: ExtensionTarget, } -impl SimpleGenerator for QuotientGeneratorExtension { +impl, const D: usize> SimpleGenerator for QuotientGeneratorExtension { fn dependencies(&self) -> Vec { let mut deps = self.numerator.to_target_array().to_vec(); deps.extend(&self.denominator.to_target_array()); @@ -298,7 +299,12 @@ impl SimpleGenerator for QuotientGeneratorExtension let dem = witness.get_extension_target(self.denominator); let quotient = num / dem; let mut pw = PartialWitness::new(); - pw.set_ext_wires(self.quotient.to_target_array(), quotient); + for i in 0..D { + pw.set_target( + self.quotient.to_target_array()[i], + quotient.to_basefield_array()[i], + ); + } pw } } diff --git a/src/gates/mul_extension.rs b/src/gates/mul_extension.rs index ec5ba671..18ec5827 100644 --- a/src/gates/mul_extension.rs +++ b/src/gates/mul_extension.rs @@ -1,6 +1,6 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; -use crate::field::extension_field::Extendable; +use crate::field::extension_field::{Extendable, FieldExtension, OEF}; use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; @@ -8,9 +8,81 @@ use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::wire::Wire; use crate::witness::PartialWitness; +use std::convert::TryInto; use std::ops::Range; -/// A gate which can multiply to field extension elements. +// TODO: Replace this when https://github.com/mir-protocol/plonky2/issues/56 is resolved. +fn mul_vec(a: &[F], b: &[F], w: F) -> Vec { + let (a0, a1, a2, a3) = (a[0], a[1], a[2], a[3]); + let (b0, b1, b2, b3) = (b[0], b[1], b[2], b[3]); + + let c0 = a0 * b0 + w * (a1 * b3 + a2 * b2 + a3 * b1); + let c1 = a0 * b1 + a1 * b0 + w * (a2 * b3 + a3 * b2); + let c2 = a0 * b2 + a1 * b1 + a2 * b0 + w * a3 * b3; + let c3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + + vec![c0, c1, c2, c3] +} +impl, const D: usize> CircuitBuilder { + fn mul_vec( + &mut self, + a: &[ExtensionTarget], + b: &[ExtensionTarget], + w: ExtensionTarget, + ) -> Vec> { + let (a0, a1, a2, a3) = (a[0], a[1], a[2], a[3]); + let (b0, b1, b2, b3) = (b[0], b[1], b[2], b[3]); + + // TODO: Optimize this. + let c0 = { + let tmp0 = self.mul_extension(a0, b0); + let tmp1 = self.mul_extension(a1, b3); + let tmp2 = self.mul_extension(a2, b2); + let tmp3 = self.mul_extension(a3, b1); + let tmp = self.add_extension(tmp1, tmp2); + let tmp = self.add_extension(tmp, tmp3); + let tmp = self.mul_extension(w, tmp); + let tmp = self.add_extension(tmp0, tmp); + tmp + }; + let c1 = { + let tmp0 = self.mul_extension(a0, b1); + let tmp1 = self.mul_extension(a1, b0); + let tmp2 = self.mul_extension(a2, b3); + let tmp3 = self.mul_extension(a3, b2); + let tmp = self.add_extension(tmp2, tmp3); + let tmp = self.mul_extension(w, tmp); + let tmp = self.add_extension(tmp, tmp0); + let tmp = self.add_extension(tmp, tmp1); + tmp + }; + let c2 = { + let tmp0 = self.mul_extension(a0, b2); + let tmp1 = self.mul_extension(a1, b1); + let tmp2 = self.mul_extension(a2, b0); + let tmp3 = self.mul_extension(a3, b3); + let tmp = self.mul_extension(w, tmp3); + let tmp = self.add_extension(tmp, tmp2); + let tmp = self.add_extension(tmp, tmp1); + let tmp = self.add_extension(tmp, tmp0); + tmp + }; + let c3 = { + let tmp0 = self.mul_extension(a0, b3); + let tmp1 = self.mul_extension(a1, b2); + let tmp2 = self.mul_extension(a2, b1); + let tmp3 = self.mul_extension(a3, b0); + let tmp = self.add_extension(tmp3, tmp2); + let tmp = self.add_extension(tmp, tmp1); + let tmp = self.add_extension(tmp, tmp0); + tmp + }; + + vec![c0, c1, c2, c3] + } +} + +/// A gate which can multiply two field extension elements. /// TODO: Add an addend if `NUM_ROUTED_WIRES` is large enough. #[derive(Debug)] pub struct MulExtensionGate; @@ -38,13 +110,25 @@ impl, const D: usize> Gate for MulExtensionGate { fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let const_0 = vars.local_constants[0]; - let const_1 = vars.local_constants[1]; - let multiplicand_0 = vars.local_wires[Self::WIRE_MULTIPLICAND_0]; - let multiplicand_1 = vars.local_wires[Self::WIRE_MULTIPLICAND_1]; - let addend = vars.local_wires[Self::WIRE_ADDEND]; - let output = vars.local_wires[Self::WIRE_OUTPUT]; - let computed_output = const_0 * multiplicand_0 * multiplicand_1 + const_1 * addend; - vec![computed_output - output] + let multiplicand_0 = vars.local_wires[Self::wires_multiplicand_0()].to_vec(); + let multiplicand_1 = vars.local_wires[Self::wires_multiplicand_1()].to_vec(); + let output = vars.local_wires[Self::wires_output()].to_vec(); + let computed_output = mul_vec( + &[ + const_0, + F::Extension::ZERO, + F::Extension::ZERO, + F::Extension::ZERO, + ], + &multiplicand_0, + F::Extension::W.into(), + ); + let computed_output = mul_vec(&computed_output, &multiplicand_1, F::Extension::W.into()); + output + .into_iter() + .zip(computed_output) + .map(|(o, co)| o - co) + .collect() } fn eval_unfiltered_recursively( @@ -53,16 +137,18 @@ impl, const D: usize> Gate for MulExtensionGate { vars: EvaluationTargets, ) -> Vec> { let const_0 = vars.local_constants[0]; - let const_1 = vars.local_constants[1]; - let multiplicand_0 = vars.local_wires[Self::WIRE_MULTIPLICAND_0]; - let multiplicand_1 = vars.local_wires[Self::WIRE_MULTIPLICAND_1]; - let addend = vars.local_wires[Self::WIRE_ADDEND]; - let output = vars.local_wires[Self::WIRE_OUTPUT]; - - let product_term = builder.mul_many_extension(&[const_0, multiplicand_0, multiplicand_1]); - let addend_term = builder.mul_extension(const_1, addend); - let computed_output = builder.add_many_extension(&[product_term, addend_term]); - vec![builder.sub_extension(computed_output, output)] + let multiplicand_0 = vars.local_wires[Self::wires_multiplicand_0()].to_vec(); + let multiplicand_1 = vars.local_wires[Self::wires_multiplicand_1()].to_vec(); + let output = vars.local_wires[Self::wires_output()].to_vec(); + let w = builder.constant_extension(F::Extension::W.into()); + let zero = builder.zero_extension(); + let computed_output = builder.mul_vec(&[const_0, zero, zero, zero], &multiplicand_0, w); + let computed_output = builder.mul_vec(&computed_output, &multiplicand_1, w); + output + .into_iter() + .zip(computed_output) + .map(|(o, co)| builder.sub_extension(o, co)) + .collect() } fn generators( @@ -70,20 +156,19 @@ impl, const D: usize> Gate for MulExtensionGate { gate_index: usize, local_constants: &[F], ) -> Vec>> { - let gen = ArithmeticGenerator { + let gen = MulExtensionGenerator { gate_index, const_0: local_constants[0], - const_1: local_constants[1], }; vec![Box::new(gen)] } fn num_wires(&self) -> usize { - 4 + 12 } fn num_constants(&self) -> usize { - 2 + 1 } fn degree(&self) -> usize { @@ -91,59 +176,60 @@ impl, const D: usize> Gate for MulExtensionGate { } fn num_constraints(&self) -> usize { - 1 + D } } -struct ArithmeticGenerator { +struct MulExtensionGenerator, const D: usize> { gate_index: usize, const_0: F, - const_1: F, } -impl SimpleGenerator for ArithmeticGenerator { +impl, const D: usize> SimpleGenerator for MulExtensionGenerator { fn dependencies(&self) -> Vec { - vec![ - Target::Wire(Wire { - gate: self.gate_index, - input: ArithmeticGate::WIRE_MULTIPLICAND_0, - }), - Target::Wire(Wire { - gate: self.gate_index, - input: ArithmeticGate::WIRE_MULTIPLICAND_1, - }), - Target::Wire(Wire { - gate: self.gate_index, - input: ArithmeticGate::WIRE_ADDEND, - }), - ] + MulExtensionGate::::wires_multiplicand_0() + .chain(MulExtensionGate::::wires_multiplicand_1()) + .map(|i| { + Target::Wire(Wire { + gate: self.gate_index, + input: i, + }) + }) + .collect() } fn run_once(&self, witness: &PartialWitness) -> PartialWitness { - let multiplicand_0_target = Wire { - gate: self.gate_index, - input: ArithmeticGate::WIRE_MULTIPLICAND_0, - }; - let multiplicand_1_target = Wire { - gate: self.gate_index, - input: ArithmeticGate::WIRE_MULTIPLICAND_1, - }; - let addend_target = Wire { - gate: self.gate_index, - input: ArithmeticGate::WIRE_ADDEND, - }; - let output_target = Wire { - gate: self.gate_index, - input: ArithmeticGate::WIRE_OUTPUT, - }; + let multiplicand_0 = MulExtensionGate::::wires_multiplicand_0() + .map(|i| { + witness.get_wire(Wire { + gate: self.gate_index, + input: i, + }) + }) + .collect::>(); + let multiplicand_0 = F::Extension::from_basefield_array(multiplicand_0.try_into().unwrap()); + let multiplicand_1 = MulExtensionGate::::wires_multiplicand_1() + .map(|i| { + witness.get_wire(Wire { + gate: self.gate_index, + input: i, + }) + }) + .collect::>(); + let multiplicand_1 = F::Extension::from_basefield_array(multiplicand_1.try_into().unwrap()); + let output = MulExtensionGate::::wires_output() + .map(|i| Wire { + gate: self.gate_index, + input: i, + }) + .collect::>(); - let multiplicand_0 = witness.get_wire(multiplicand_0_target); - let multiplicand_1 = witness.get_wire(multiplicand_1_target); - let addend = witness.get_wire(addend_target); + let computed_output = + F::Extension::from_basefield(self.const_0) * multiplicand_0 * multiplicand_1; - let output = self.const_0 * multiplicand_0 * multiplicand_1 + self.const_1 * addend; - - PartialWitness::singleton_wire(output_target, output) + let mut pw = PartialWitness::new(); + pw.set_ext_wires(output, computed_output); + pw } } @@ -152,9 +238,10 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::gates::arithmetic::ArithmeticGate; use crate::gates::gate_testing::test_low_degree; + use crate::gates::mul_extension::MulExtensionGate; #[test] fn low_degree() { - test_low_degree(ArithmeticGate::new::()) + test_low_degree(MulExtensionGate::<4>::new::()) } } From 8cf2758b6c12b6448a8b105f80f26e869f059855 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 7 Jun 2021 17:55:27 +0200 Subject: [PATCH 009/117] Division gadget for extension field --- src/circuit_builder.rs | 13 +++++ src/gadgets/arithmetic.rs | 100 +++++++++++++++++++++++++++++--------- src/gates/mod.rs | 2 +- src/witness.rs | 20 ++++++++ 4 files changed, 110 insertions(+), 25 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index ccca4e2b..4a86ef48 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -8,6 +8,7 @@ use crate::circuit_data::{ VerifierCircuitData, VerifierOnlyCircuitData, }; use crate::field::cosets::get_unique_coset_shifts; +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::gates::constant::ConstantGate; @@ -130,6 +131,12 @@ impl, const D: usize> CircuitBuilder { self.assert_equal(src, dst); } + pub fn route_extension(&mut self, src: ExtensionTarget, dst: ExtensionTarget) { + for i in 0..D { + self.route(src.0[i], dst.0[i]); + } + } + /// Adds a generator which will copy `src` to `dst`. pub fn generate_copy(&mut self, src: Target, dst: Target) { self.add_generator(CopyGenerator { src, dst }); @@ -154,6 +161,12 @@ impl, const D: usize> CircuitBuilder { self.assert_equal(x, zero); } + pub fn assert_equal_extension(&mut self, x: ExtensionTarget, y: ExtensionTarget) { + for i in 0..D { + self.assert_equal(x.0[i], y.0[i]); + } + } + pub fn add_generators(&mut self, generators: Vec>>) { self.generators.extend(generators); } diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index fd32c83d..4a2e4bd3 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -3,10 +3,12 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::gates::arithmetic::ArithmeticGate; +use crate::gates::mul_extension::MulExtensionGate; use crate::generator::SimpleGenerator; use crate::target::Target; use crate::wire::Wire; use crate::witness::PartialWitness; +use std::convert::TryInto; impl, const D: usize> CircuitBuilder { /// Computes `-x`. @@ -234,32 +236,32 @@ impl, const D: usize> CircuitBuilder { y: ExtensionTarget, ) -> ExtensionTarget { // Add an `ArithmeticGate` to compute `q * y`. - let gate = self.add_gate(ArithmeticGate::new(), vec![F::ONE, F::ZERO]); + let gate = self.add_gate(MulExtensionGate::new(), vec![F::ONE]); - let wire_multiplicand_0 = Wire { - gate, - input: ArithmeticGate::WIRE_MULTIPLICAND_0, - }; - let wire_multiplicand_1 = Wire { - gate, - input: ArithmeticGate::WIRE_MULTIPLICAND_1, - }; - let wire_addend = Wire { - gate, - input: ArithmeticGate::WIRE_ADDEND, - }; - let wire_output = Wire { - gate, - input: ArithmeticGate::WIRE_OUTPUT, - }; + let multiplicand_0 = MulExtensionGate::::wires_multiplicand_0() + .map(|i| Target::Wire(Wire { gate, input: i })) + .collect::>(); + let multiplicand_0 = ExtensionTarget(multiplicand_0.try_into().unwrap()); + let multiplicand_1 = MulExtensionGate::::wires_multiplicand_1() + .map(|i| Target::Wire(Wire { gate, input: i })) + .collect::>(); + let multiplicand_1 = ExtensionTarget(multiplicand_1.try_into().unwrap()); + let output = MulExtensionGate::::wires_output() + .map(|i| Target::Wire(Wire { gate, input: i })) + .collect::>(); + let output = ExtensionTarget(output.try_into().unwrap()); - let q = Target::Wire(wire_multiplicand_0); - todo!() - // self.add_generator(QuotientGeneratorExtension { - // numerator: x, - // denominator: ExtensionTarget(), - // quotient: ExtensionTarget(), - // }) + self.add_generator(QuotientGeneratorExtension { + numerator: x, + denominator: y, + quotient: multiplicand_0, + }); + + self.route_extension(y, multiplicand_1); + + self.assert_equal_extension(output, x); + + multiplicand_0 } } @@ -335,3 +337,53 @@ impl, const D: usize> CircuitBuilder { } } } + +#[cfg(test)] +mod tests { + use crate::circuit_builder::CircuitBuilder; + use crate::circuit_data::CircuitConfig; + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::field::field::Field; + use crate::fri::FriConfig; + use crate::prover::PLONK_BLINDING; + use crate::witness::PartialWitness; + + #[test] + fn test_div_extension() { + type F = CrandallField; + type FF = QuarticCrandallField; + const D: usize = 4; + + let config = CircuitConfig { + num_wires: 134, + num_routed_wires: 12, + security_bits: 128, + rate_bits: 0, + num_challenges: 3, + fri_config: FriConfig { + proof_of_work_bits: 1, + rate_bits: 0, + reduction_arity_bits: vec![1], + num_query_rounds: 1, + blinding: PLONK_BLINDING.to_vec(), + }, + }; + + let mut builder = CircuitBuilder::::new(config); + + let x = FF::rand(); + let y = FF::rand(); + let x = FF::TWO; + let y = FF::ONE; + let z = x / y; + let xt = builder.constant_extension(x); + let yt = builder.constant_extension(y); + let zt = builder.constant_extension(z); + let comp_zt = builder.div_unsafe_extension(xt, yt); + builder.assert_equal_extension(zt, comp_zt); + + let data = builder.build(); + let proof = data.prove(PartialWitness::new()); + } +} diff --git a/src/gates/mod.rs b/src/gates/mod.rs index f131b754..3ac7bd74 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -4,8 +4,8 @@ pub mod constant; pub(crate) mod gate; pub mod gmimc; mod interpolation; +pub mod mul_extension; pub(crate) mod noop; #[cfg(test)] mod gate_testing; -mod mul_extension; diff --git a/src/witness.rs b/src/witness.rs index ebd85d9e..c5748c3f 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -81,6 +81,26 @@ impl PartialWitness { } } + pub fn set_extension_target( + &mut self, + et: ExtensionTarget, + value: F::Extension, + ) where + F: Extendable, + { + let limbs = value.to_basefield_array(); + for i in 0..D { + let opt_old_value = self.target_values.insert(et.0[i], limbs[i]); + if let Some(old_value) = opt_old_value { + assert_eq!( + old_value, limbs[i], + "Target was set twice with different values: {:?}", + et.0[i] + ); + } + } + } + pub fn set_wire(&mut self, wire: Wire, value: F) { self.set_target(Target::Wire(wire), value) } From e50d0aa63d20af951a3f71e0e4f8f80f625a0f12 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 7 Jun 2021 21:24:41 +0200 Subject: [PATCH 010/117] Interpolation of two points --- src/fri/recursive_verifier.rs | 45 ++++++++++++++++++++++------------- src/gadgets/interpolation.rs | 15 ++++++++++++ src/gadgets/mod.rs | 1 + 3 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 src/gadgets/interpolation.rs diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index edb9b075..bd5559a3 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -174,23 +174,34 @@ impl, const D: usize> CircuitBuilder { let quotient = self.div_unsafe_extension(numerator, denominator); let sum = self.add_extension(sum, quotient); - // let ev: F::Extension = proof - // .unsalted_evals(3, config) - // .iter() - // .zip(alpha_powers.clone()) - // .map(|(&e, a)| a * e.into()) - // .sum(); - // let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; - // let zs_interpol = interpolant(&[ - // (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), - // ( - // zeta_right, - // reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - // ), - // ]); - // let numerator = ev - zs_interpol.eval(subgroup_x); - // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); - // sum += numerator / denominator; + let evs = proof + .unsalted_evals(3, config) + .iter() + .map(|&e| self.convert_to_ext(e)) + .collect::>(); + let mut ev = self.zero_extension(); + for &e in &evs { + let a = alpha_powers.next(self); + let tmp = self.mul_extension(a, e); + ev = self.add_extension(ev, tmp); + } + + let g = self.constant_extension(F::Extension::primitive_root_of_unity(degree_log)); + let zeta_right = self.mul_extension(g, zeta); + let zs_interpol = self.interpolate2([ + (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), + ( + zeta_right, + reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + ), + ]); + let interpol_val = zs_interpol.eval(self, subgroup_x); + let numerator = self.sub_extension(ev, interpol_val); + let vanish = self.sub_extension(subgroup_x, zeta); + let vanish_right = self.sub_extension(subgroup_x, zeta_right); + let denominator = self.mul_extension(vanish, vanish_right); + let quotient = self.div_unsafe_extension(numerator, denominator); + let sum = self.add_extension(sum, quotient); // // let ev: F::Extension = proof // .unsalted_evals(2, config) diff --git a/src/gadgets/interpolation.rs b/src/gadgets/interpolation.rs new file mode 100644 index 00000000..6d44cd76 --- /dev/null +++ b/src/gadgets/interpolation.rs @@ -0,0 +1,15 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; +use crate::field::extension_field::Extendable; +use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; + +impl, const D: usize> CircuitBuilder { + /// Interpolate two points. No need for an `InterpolationGate` since the coefficients + /// of the linear interpolation polynomial can be easily computed with arithmetic operations. + pub fn interpolate2( + &mut self, + points: [(ExtensionTarget, ExtensionTarget); 2], + ) -> PolynomialCoeffsExtTarget { + todo!() + } +} diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 22bef43b..ee331012 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -1,5 +1,6 @@ pub mod arithmetic; pub mod hash; +pub mod interpolation; pub mod polynomial; pub mod split_base; pub(crate) mod split_join; From c6c71ef5747622adc6cbd3cd2134d9b3e7433751 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 8 Jun 2021 14:56:49 +0200 Subject: [PATCH 011/117] Working `fri_combine_initial` --- src/field/extension_field/target.rs | 14 ++++++ src/fri/recursive_verifier.rs | 72 +++++++++++++++++++---------- src/proof.rs | 1 + 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 20cd3dcb..971d8836 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -11,6 +11,20 @@ impl ExtensionTarget { pub fn to_target_array(&self) -> [Target; D] { self.0 } + + pub fn frobenius>(&self, builder: &mut CircuitBuilder) -> Self { + let arr = self.to_target_array(); + let k = (F::ORDER - 1) / (D as u64); + let z0 = builder.constant(F::Extension::W.exp(k)); + let mut z = builder.one(); + let mut res = [builder.zero(); D]; + for i in 0..D { + res[i] = builder.mul(arr[i], z); + z = builder.mul(z, z0); + } + + Self(res) + } } /// `Target`s representing an element of an extension of an extension field. diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index bd5559a3..2006d21f 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -179,22 +179,26 @@ impl, const D: usize> CircuitBuilder { .iter() .map(|&e| self.convert_to_ext(e)) .collect::>(); + // TODO: Would probably be more efficient using `CircuitBuilder::reduce_with_powers_recursive` let mut ev = self.zero_extension(); for &e in &evs { let a = alpha_powers.next(self); - let tmp = self.mul_extension(a, e); - ev = self.add_extension(ev, tmp); + ev = self.mul_add_extension(a, e, ev); } let g = self.constant_extension(F::Extension::primitive_root_of_unity(degree_log)); let zeta_right = self.mul_extension(g, zeta); - let zs_interpol = self.interpolate2([ - (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), - ( - zeta_right, - reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - ), - ]); + let mut ev_zeta = self.zero_extension(); + for &t in &os.plonk_zs { + let a = alpha_powers.next(self); + ev_zeta = self.mul_add_extension(a, t, ev_zeta); + } + let mut ev_zeta_right = self.zero_extension(); + for &t in &os.plonk_zs_right { + let a = alpha_powers.next(self); + ev_zeta_right = self.mul_add_extension(a, t, ev_zeta); + } + let zs_interpol = self.interpolate2([(zeta, ev_zeta), (zeta_right, ev_zeta_right)]); let interpol_val = zs_interpol.eval(self, subgroup_x); let numerator = self.sub_extension(ev, interpol_val); let vanish = self.sub_extension(subgroup_x, zeta); @@ -202,22 +206,40 @@ impl, const D: usize> CircuitBuilder { let denominator = self.mul_extension(vanish, vanish_right); let quotient = self.div_unsafe_extension(numerator, denominator); let sum = self.add_extension(sum, quotient); - // - // let ev: F::Extension = proof - // .unsalted_evals(2, config) - // .iter() - // .zip(alpha_powers.clone()) - // .map(|(&e, a)| a * e.into()) - // .sum(); - // let zeta_frob = zeta.frobenius(); - // let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); - // let wires_interpol = interpolant(&[ - // (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), - // (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), - // ]); - // let numerator = ev - wires_interpol.eval(subgroup_x); - // let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); - // sum += numerator / denominator; + + let evs = proof + .unsalted_evals(2, config) + .iter() + .map(|&e| self.convert_to_ext(e)) + .collect::>(); + let mut ev = self.zero_extension(); + for &e in &evs { + let a = alpha_powers.next(self); + ev = self.mul_add_extension(a, e, ev); + } + let zeta_frob = zeta.frobenius(self); + let wire_evals_frob = os + .wires + .iter() + .map(|e| e.frobenius(self)) + .collect::>(); + let mut ev_zeta = self.zero_extension(); + for &t in &os.wires { + let a = alpha_powers.next(self); + ev_zeta = self.mul_add_extension(a, t, ev_zeta); + } + let mut ev_zeta_frob = self.zero_extension(); + for &t in &wire_evals_frob { + let a = alpha_powers.next(self); + ev_zeta_right = self.mul_add_extension(a, t, ev_zeta); + } + let wires_interpol = self.interpolate2([(zeta, ev_zeta), (zeta_frob, ev_zeta_frob)]); + let interpol_val = wires_interpol.eval(self, subgroup_x); + let numerator = self.sub_extension(ev, interpol_val); + let vanish_frob = self.sub_extension(subgroup_x, zeta_frob); + let denominator = self.mul_extension(vanish, vanish_frob); + let quotient = self.div_unsafe_extension(numerator, denominator); + let sum = self.add_extension(sum, quotient); sum } diff --git a/src/proof.rs b/src/proof.rs index 65634f4c..51e32938 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -187,5 +187,6 @@ pub struct OpeningSetTarget { pub plonk_sigmas: Vec>, pub wires: Vec>, pub plonk_zs: Vec>, + pub plonk_zs_right: Vec>, pub quotient_polys: Vec>, } From e1e4bb36db6e3055b8e9790d64a3f3f47f62a82f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 8 Jun 2021 19:32:23 +0200 Subject: [PATCH 012/117] Started query round --- src/field/extension_field/target.rs | 19 +++ src/fri/recursive_verifier.rs | 202 ++++++++++++++-------------- 2 files changed, 122 insertions(+), 99 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 971d8836..c5b1fe53 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -228,3 +228,22 @@ impl, const D: usize> CircuitBuilder { ExtensionTarget(arr) } } + +/// Flatten the slice by sending every extension target to its D-sized canonical representation. +pub fn flatten_target(l: &[ExtensionTarget]) -> Vec { + l.iter() + .flat_map(|x| x.to_target_array().to_vec()) + .collect() +} + +/// Batch every D-sized chunks into extension targets. +pub fn unflatten_target(l: &[Target]) -> Vec> { + debug_assert_eq!(l.len() % D, 0); + l.chunks_exact(D) + .map(|c| { + let mut arr = Default::default(); + arr.copy_from_slice(c); + ExtensionTarget(arr) + }) + .collect() +} diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 2006d21f..38948bf7 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -2,7 +2,7 @@ use anyhow::{ensure, Result}; use itertools::izip; use crate::circuit_builder::CircuitBuilder; -use crate::field::extension_field::target::ExtensionTarget; +use crate::field::extension_field::target::{flatten_target, ExtensionTarget}; use crate::field::extension_field::{flatten, Extendable, FieldExtension, OEF}; use crate::field::field::Field; use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; @@ -12,8 +12,8 @@ use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::{Challenger, RecursiveChallenger}; use crate::plonk_common::reduce_with_iter; use crate::proof::{ - FriInitialTreeProof, FriInitialTreeProofTarget, FriProof, FriProofTarget, FriQueryRound, Hash, - HashTarget, OpeningSet, OpeningSetTarget, + FriInitialTreeProof, FriInitialTreeProofTarget, FriProof, FriProofTarget, FriQueryRound, + FriQueryRoundTarget, Hash, HashTarget, OpeningSet, OpeningSetTarget, }; use crate::target::Target; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; @@ -21,7 +21,7 @@ use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; impl, const D: usize> CircuitBuilder { /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity /// and P' is the FRI reduced polynomial. - fn compute_evaluation() { + fn compute_evaluation(&mut self) { todo!(); // debug_assert_eq!(last_evals.len(), 1 << arity_bits); // @@ -243,99 +243,103 @@ impl, const D: usize> CircuitBuilder { sum } - // - // fn fri_verifier_query_round, const D: usize>( - // os: &OpeningSet, - // zeta: F::Extension, - // alpha: F::Extension, - // initial_merkle_roots: &[Hash], - // proof: &FriProof, - // challenger: &mut Challenger, - // n: usize, - // betas: &[F::Extension], - // round_proof: &FriQueryRound, - // config: &FriConfig, - // ) -> Result<()> { - // let mut evaluations: Vec> = Vec::new(); - // let x = challenger.get_challenge(); - // let mut domain_size = n; - // let mut x_index = x.to_canonical_u64() as usize % n; - // fri_verify_initial_proof( - // x_index, - // &round_proof.initial_trees_proof, - // initial_merkle_roots, - // )?; - // let mut old_x_index = 0; - // // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. - // let log_n = log2_strict(n); - // let mut subgroup_x = F::MULTIPLICATIVE_GROUP_GENERATOR - // * F::primitive_root_of_unity(log_n).exp(reverse_bits(x_index, log_n) as u64); - // for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { - // let arity = 1 << arity_bits; - // let next_domain_size = domain_size >> arity_bits; - // let e_x = if i == 0 { - // fri_combine_initial( - // &round_proof.initial_trees_proof, - // alpha, - // os, - // zeta, - // subgroup_x, - // config, - // ) - // } else { - // let last_evals = &evaluations[i - 1]; - // // Infer P(y) from {P(x)}_{x^arity=y}. - // compute_evaluation( - // subgroup_x, - // old_x_index, - // config.reduction_arity_bits[i - 1], - // last_evals, - // betas[i - 1], - // ) - // }; - // let mut evals = round_proof.steps[i].evals.clone(); - // // Insert P(y) into the evaluation vector, since it wasn't included by the prover. - // evals.insert(x_index & (arity - 1), e_x); - // evaluations.push(evals); - // verify_merkle_proof( - // flatten(&evaluations[i]), - // x_index >> arity_bits, - // proof.commit_phase_merkle_roots[i], - // &round_proof.steps[i].merkle_proof, - // false, - // )?; - // - // if i > 0 { - // // Update the point x to x^arity. - // for _ in 0..config.reduction_arity_bits[i - 1] { - // subgroup_x = subgroup_x.square(); - // } - // } - // domain_size = next_domain_size; - // old_x_index = x_index; - // x_index >>= arity_bits; - // } - // - // let last_evals = evaluations.last().unwrap(); - // let final_arity_bits = *config.reduction_arity_bits.last().unwrap(); - // let purported_eval = compute_evaluation( - // subgroup_x, - // old_x_index, - // final_arity_bits, - // last_evals, - // *betas.last().unwrap(), - // ); - // for _ in 0..final_arity_bits { - // subgroup_x = subgroup_x.square(); - // } - // - // // Final check of FRI. After all the reductions, we check that the final polynomial is equal - // // to the one sent by the prover. - // ensure!( - // proof.final_poly.eval(subgroup_x.into()) == purported_eval, - // "Final polynomial evaluation is invalid." - // ); - // - // Ok(()) - // } + + fn fri_verifier_query_round( + &mut self, + os: &OpeningSetTarget, + zeta: ExtensionTarget, + alpha: ExtensionTarget, + initial_merkle_roots: &[HashTarget], + proof: &FriProofTarget, + challenger: &mut RecursiveChallenger, + n: usize, + betas: &[ExtensionTarget], + round_proof: &FriQueryRoundTarget, + config: &FriConfig, + ) -> Result<()> { + let mut evaluations: Vec>> = Vec::new(); + // TODO: Do we need to range check `x_index` to a target smaller than `p`? + let mut x_index = challenger.get_challenge(self); + let mut domain_size = n; + self.fri_verify_initial_proof( + x_index, + &round_proof.initial_trees_proof, + initial_merkle_roots, + ); + let mut old_x_index = self.zero(); + // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. + let log_n = log2_strict(n); + // TODO: The verifier will need to check these constants at some point (out of circuit). + let g = self.constant(F::MULTIPLICATIVE_GROUP_GENERATOR); + let phi = self.constant(F::primitive_root_of_unity(log_n)); + // TODO: Gate for this. + let reversed_x = self.reverse_bits(x_index, log_n); + let phi = self.exp(phi, reversed_x); + let mut subgroup_x = self.mul(g, phi); + for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { + let arity = 1 << arity_bits; + let next_domain_size = domain_size >> arity_bits; + let e_x = if i == 0 { + self.fri_combine_initial( + &round_proof.initial_trees_proof, + alpha, + os, + zeta, + subgroup_x, + ) + } else { + let last_evals = &evaluations[i - 1]; + // Infer P(y) from {P(x)}_{x^arity=y}. + self.compute_evaluation( + subgroup_x, + old_x_index, + config.reduction_arity_bits[i - 1], + last_evals, + betas[i - 1], + ) + }; + let mut evals = round_proof.steps[i].evals.clone(); + // Insert P(y) into the evaluation vector, since it wasn't included by the prover. + evals.insert(x_index & (arity - 1), e_x); + evaluations.push(evals); + self.verify_merkle_proof( + flatten_target(&evaluations[i]), + x_index >> arity_bits, + proof.commit_phase_merkle_roots[i], + &round_proof.steps[i].merkle_proof, + )?; + + if i > 0 { + // Update the point x to x^arity. + for _ in 0..config.reduction_arity_bits[i - 1] { + subgroup_x = self.mul(subgroup_x, subgroup_x); + } + } + domain_size = next_domain_size; + old_x_index = x_index; + x_index >>= arity_bits; + } + + let last_evals = evaluations.last().unwrap(); + let final_arity_bits = *config.reduction_arity_bits.last().unwrap(); + let purported_eval = self.compute_evaluation( + subgroup_x, + old_x_index, + final_arity_bits, + last_evals, + *betas.last().unwrap(), + ); + for _ in 0..final_arity_bits { + subgroup_x = self.mul(subgroup_x, subgroup_x); + } + + // Final check of FRI. After all the reductions, we check that the final polynomial is equal + // to the one sent by the prover. + ensure!( + proof.final_poly.eval(subgroup_x.into()) == purported_eval, + "Final polynomial evaluation is invalid." + ); + + Ok(()) + } } From 9adf5bb43f530f49962065b3d4be7494e909f043 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 9 Jun 2021 10:51:50 +0200 Subject: [PATCH 013/117] Use `ExtensionAlgebra` + new `CircuitBuilder::mul_extension` --- src/field/extension_field/mod.rs | 7 +- src/field/extension_field/target.rs | 59 +++++++++++--- src/fri/recursive_verifier.rs | 6 +- src/gadgets/arithmetic.rs | 16 ++-- src/gadgets/polynomial.rs | 2 +- src/gadgets/split_base.rs | 8 +- src/gates/arithmetic.rs | 2 +- src/gates/base_sum.rs | 2 +- src/gates/gmimc.rs | 6 +- src/gates/interpolation.rs | 4 +- src/gates/mul_extension.rs | 118 +++------------------------- src/target.rs | 5 ++ src/wire.rs | 5 ++ 13 files changed, 95 insertions(+), 145 deletions(-) diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 60d2b2e1..d706a341 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -1,4 +1,5 @@ use crate::field::field::Field; +use std::convert::TryInto; pub mod algebra; pub mod quadratic; @@ -88,10 +89,6 @@ where { debug_assert_eq!(l.len() % D, 0); l.chunks_exact(D) - .map(|c| { - let mut arr = [F::ZERO; D]; - arr.copy_from_slice(c); - F::Extension::from_basefield_array(arr) - }) + .map(|c| F::Extension::from_basefield_array(c.to_vec().try_into().unwrap())) .collect() } diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 901cf854..17d28172 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -2,7 +2,10 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::algebra::ExtensionAlgebra; use crate::field::extension_field::{Extendable, FieldExtension, OEF}; use crate::field::field::Field; +use crate::gates::mul_extension::MulExtensionGate; use crate::target::Target; +use std::convert::{TryFrom, TryInto}; +use std::ops::Range; /// `Target`s representing an element of an extension field. #[derive(Copy, Clone, Debug)] @@ -26,6 +29,19 @@ impl ExtensionTarget { Self(res) } + + pub fn from_range(gate: usize, range: Range) -> Self { + debug_assert_eq!(range.end - range.start, D); + Target::wires_from_range(gate, range).try_into().unwrap() + } +} + +impl TryFrom> for ExtensionTarget { + type Error = Vec; + + fn try_from(value: Vec) -> Result { + Ok(Self(value.try_into()?)) + } } /// `Target`s representing an element of an extension of an extension field. @@ -128,7 +144,34 @@ impl, const D: usize> CircuitBuilder { a } + pub fn mul_extension_with_const( + &mut self, + const_0: F, + multiplicand_0: ExtensionTarget, + multiplicand_1: ExtensionTarget, + ) -> ExtensionTarget { + let gate = self.add_gate(MulExtensionGate::new(), vec![const_0]); + + let wire_multiplicand_0 = + ExtensionTarget::from_range(gate, MulExtensionGate::::wires_multiplicand_0()); + let wire_multiplicand_1 = + ExtensionTarget::from_range(gate, MulExtensionGate::::wires_multiplicand_1()); + let wire_output = ExtensionTarget::from_range(gate, MulExtensionGate::::wires_output()); + + self.route_extension(multiplicand_0, wire_multiplicand_0); + self.route_extension(multiplicand_1, wire_multiplicand_1); + wire_output + } + pub fn mul_extension( + &mut self, + multiplicand_0: ExtensionTarget, + multiplicand_1: ExtensionTarget, + ) -> ExtensionTarget { + self.mul_extension_with_const(F::ONE, multiplicand_0, multiplicand_1) + } + + pub fn mul_extension_naive( &mut self, a: ExtensionTarget, b: ExtensionTarget, @@ -156,7 +199,7 @@ impl, const D: usize> CircuitBuilder { let w = self.constant(F::Extension::W); for i in 0..D { for j in 0..D { - let ai_bi = self.mul_extension(a.0[i], b.0[j]); + let ai_bi = self.mul_extension_naive(a.0[i], b.0[j]); res[(i + j) % D] = if i + j < D { self.add_extension(ai_bi, res[(i + j) % D]) } else { @@ -171,7 +214,7 @@ impl, const D: usize> CircuitBuilder { pub fn mul_many_extension(&mut self, terms: &[ExtensionTarget]) -> ExtensionTarget { let mut product = self.one_extension(); for term in terms { - product = self.mul_extension(product, *term); + product = self.mul_extension_naive(product, *term); } product } @@ -184,7 +227,7 @@ impl, const D: usize> CircuitBuilder { b: ExtensionTarget, c: ExtensionTarget, ) -> ExtensionTarget { - let product = self.mul_extension(a, b); + let product = self.mul_extension_naive(a, b); self.add_extension(product, c) } @@ -204,7 +247,7 @@ impl, const D: usize> CircuitBuilder { mut b: ExtensionAlgebraTarget, ) -> ExtensionAlgebraTarget { for i in 0..D { - b.0[i] = self.mul_extension(a, b.0[i]); + b.0[i] = self.mul_extension_naive(a, b.0[i]); } b } @@ -225,13 +268,9 @@ pub fn flatten_target(l: &[ExtensionTarget]) -> Vec { } /// Batch every D-sized chunks into extension targets. -pub fn unflatten_target(l: &[Target]) -> Vec> { +pub fn unflatten_target, const D: usize>(l: &[Target]) -> Vec> { debug_assert_eq!(l.len() % D, 0); l.chunks_exact(D) - .map(|c| { - let mut arr = Default::default(); - arr.copy_from_slice(c); - ExtensionTarget(arr) - }) + .map(|c| c.to_vec().try_into().unwrap()) .collect() } diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 38948bf7..2ae287f4 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -187,7 +187,7 @@ impl, const D: usize> CircuitBuilder { } let g = self.constant_extension(F::Extension::primitive_root_of_unity(degree_log)); - let zeta_right = self.mul_extension(g, zeta); + let zeta_right = self.mul_extension_naive(g, zeta); let mut ev_zeta = self.zero_extension(); for &t in &os.plonk_zs { let a = alpha_powers.next(self); @@ -203,7 +203,7 @@ impl, const D: usize> CircuitBuilder { let numerator = self.sub_extension(ev, interpol_val); let vanish = self.sub_extension(subgroup_x, zeta); let vanish_right = self.sub_extension(subgroup_x, zeta_right); - let denominator = self.mul_extension(vanish, vanish_right); + let denominator = self.mul_extension_naive(vanish, vanish_right); let quotient = self.div_unsafe_extension(numerator, denominator); let sum = self.add_extension(sum, quotient); @@ -237,7 +237,7 @@ impl, const D: usize> CircuitBuilder { let interpol_val = wires_interpol.eval(self, subgroup_x); let numerator = self.sub_extension(ev, interpol_val); let vanish_frob = self.sub_extension(subgroup_x, zeta_frob); - let denominator = self.mul_extension(vanish, vanish_frob); + let denominator = self.mul_extension_naive(vanish, vanish_frob); let quotient = self.div_unsafe_extension(numerator, denominator); let sum = self.add_extension(sum, quotient); diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 4a2e4bd3..98c4d0d0 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -238,17 +238,13 @@ impl, const D: usize> CircuitBuilder { // Add an `ArithmeticGate` to compute `q * y`. let gate = self.add_gate(MulExtensionGate::new(), vec![F::ONE]); - let multiplicand_0 = MulExtensionGate::::wires_multiplicand_0() - .map(|i| Target::Wire(Wire { gate, input: i })) - .collect::>(); + let multiplicand_0 = + Target::wires_from_range(gate, MulExtensionGate::::wires_multiplicand_0()); let multiplicand_0 = ExtensionTarget(multiplicand_0.try_into().unwrap()); - let multiplicand_1 = MulExtensionGate::::wires_multiplicand_1() - .map(|i| Target::Wire(Wire { gate, input: i })) - .collect::>(); + let multiplicand_1 = + Target::wires_from_range(gate, MulExtensionGate::::wires_multiplicand_1()); let multiplicand_1 = ExtensionTarget(multiplicand_1.try_into().unwrap()); - let output = MulExtensionGate::::wires_output() - .map(|i| Target::Wire(Wire { gate, input: i })) - .collect::>(); + let output = Target::wires_from_range(gate, MulExtensionGate::::wires_output()); let output = ExtensionTarget(output.try_into().unwrap()); self.add_generator(QuotientGeneratorExtension { @@ -324,7 +320,7 @@ impl PowersTarget { builder: &mut CircuitBuilder, ) -> ExtensionTarget { let result = self.current; - self.current = builder.mul_extension(self.base, self.current); + self.current = builder.mul_extension_naive(self.base, self.current); result } } diff --git a/src/gadgets/polynomial.rs b/src/gadgets/polynomial.rs index 543be834..9ccfc6a8 100644 --- a/src/gadgets/polynomial.rs +++ b/src/gadgets/polynomial.rs @@ -26,7 +26,7 @@ impl PolynomialCoeffsExtTarget { ) -> ExtensionTarget { let mut acc = builder.zero_extension(); for &c in self.0.iter().rev() { - let tmp = builder.mul_extension(point, acc); + let tmp = builder.mul_extension_naive(point, acc); acc = builder.add_extension(tmp, c); } acc diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 35d5ac93..810939fb 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -19,9 +19,11 @@ impl, const D: usize> CircuitBuilder { input: BaseSumGate::::WIRE_SUM, }); self.route(x, sum); - (BaseSumGate::::WIRE_LIMBS_START..BaseSumGate::::WIRE_LIMBS_START + num_limbs) - .map(|i| Target::Wire(Wire { gate, input: i })) - .collect() + + Target::wires_from_range( + gate, + BaseSumGate::::WIRE_LIMBS_START..BaseSumGate::::WIRE_LIMBS_START + num_limbs, + ) } /// Asserts that `x`'s bit representation has at least `trailing_zeros` trailing zeros. diff --git a/src/gates/arithmetic.rs b/src/gates/arithmetic.rs index 0d0fdd7c..8208f0f8 100644 --- a/src/gates/arithmetic.rs +++ b/src/gates/arithmetic.rs @@ -57,7 +57,7 @@ impl, const D: usize> Gate for ArithmeticGate { let output = vars.local_wires[Self::WIRE_OUTPUT]; let product_term = builder.mul_many_extension(&[const_0, multiplicand_0, multiplicand_1]); - let addend_term = builder.mul_extension(const_1, addend); + let addend_term = builder.mul_extension_naive(const_1, addend); let computed_output = builder.add_many_extension(&[product_term, addend_term]); vec![builder.sub_extension(computed_output, output)] } diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index 8c8064a1..f56519e1 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -69,7 +69,7 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat (0..B).for_each(|i| { let it = builder.constant_extension(F::from_canonical_usize(i).into()); let diff = builder.sub_extension(limb, it); - acc = builder.mul_extension(acc, diff); + acc = builder.mul_extension_naive(acc, diff); }); acc }); diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 19042d57..7c75951d 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -131,7 +131,7 @@ impl, const D: usize, const R: usize> Gate for GMiMCGate< let swap = vars.local_wires[Self::WIRE_SWAP]; let one_ext = builder.one_extension(); let not_swap = builder.sub_extension(swap, one_ext); - constraints.push(builder.mul_extension(swap, not_swap)); + constraints.push(builder.mul_extension_naive(swap, not_swap)); let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD]; let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW]; @@ -168,8 +168,8 @@ impl, const D: usize, const R: usize> Gate for GMiMCGate< let constant = builder.constant_extension(self.constants[r].into()); let cubing_input = builder.add_many_extension(&[state[active], addition_buffer, constant]); - let square = builder.mul_extension(cubing_input, cubing_input); - let f = builder.mul_extension(square, cubing_input); + let square = builder.mul_extension_naive(cubing_input, cubing_input); + let f = builder.mul_extension_naive(square, cubing_input); addition_buffer = builder.add_extension(addition_buffer, f); state[active] = builder.sub_extension(state[active], f); } diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index 9bf76c2c..b3fd35d0 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -357,9 +357,7 @@ mod tests { }; assert!( - gate.eval_unfiltered(vars.clone()) - .iter() - .all(|x| x.is_zero()), + gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()), "Gate constraints are not satisfied." ); } diff --git a/src/gates/mul_extension.rs b/src/gates/mul_extension.rs index 18ec5827..00a0ad8d 100644 --- a/src/gates/mul_extension.rs +++ b/src/gates/mul_extension.rs @@ -1,7 +1,6 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; -use crate::field::extension_field::{Extendable, FieldExtension, OEF}; -use crate::field::field::Field; +use crate::field::extension_field::{Extendable, FieldExtension}; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; @@ -11,77 +10,6 @@ use crate::witness::PartialWitness; use std::convert::TryInto; use std::ops::Range; -// TODO: Replace this when https://github.com/mir-protocol/plonky2/issues/56 is resolved. -fn mul_vec(a: &[F], b: &[F], w: F) -> Vec { - let (a0, a1, a2, a3) = (a[0], a[1], a[2], a[3]); - let (b0, b1, b2, b3) = (b[0], b[1], b[2], b[3]); - - let c0 = a0 * b0 + w * (a1 * b3 + a2 * b2 + a3 * b1); - let c1 = a0 * b1 + a1 * b0 + w * (a2 * b3 + a3 * b2); - let c2 = a0 * b2 + a1 * b1 + a2 * b0 + w * a3 * b3; - let c3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; - - vec![c0, c1, c2, c3] -} -impl, const D: usize> CircuitBuilder { - fn mul_vec( - &mut self, - a: &[ExtensionTarget], - b: &[ExtensionTarget], - w: ExtensionTarget, - ) -> Vec> { - let (a0, a1, a2, a3) = (a[0], a[1], a[2], a[3]); - let (b0, b1, b2, b3) = (b[0], b[1], b[2], b[3]); - - // TODO: Optimize this. - let c0 = { - let tmp0 = self.mul_extension(a0, b0); - let tmp1 = self.mul_extension(a1, b3); - let tmp2 = self.mul_extension(a2, b2); - let tmp3 = self.mul_extension(a3, b1); - let tmp = self.add_extension(tmp1, tmp2); - let tmp = self.add_extension(tmp, tmp3); - let tmp = self.mul_extension(w, tmp); - let tmp = self.add_extension(tmp0, tmp); - tmp - }; - let c1 = { - let tmp0 = self.mul_extension(a0, b1); - let tmp1 = self.mul_extension(a1, b0); - let tmp2 = self.mul_extension(a2, b3); - let tmp3 = self.mul_extension(a3, b2); - let tmp = self.add_extension(tmp2, tmp3); - let tmp = self.mul_extension(w, tmp); - let tmp = self.add_extension(tmp, tmp0); - let tmp = self.add_extension(tmp, tmp1); - tmp - }; - let c2 = { - let tmp0 = self.mul_extension(a0, b2); - let tmp1 = self.mul_extension(a1, b1); - let tmp2 = self.mul_extension(a2, b0); - let tmp3 = self.mul_extension(a3, b3); - let tmp = self.mul_extension(w, tmp3); - let tmp = self.add_extension(tmp, tmp2); - let tmp = self.add_extension(tmp, tmp1); - let tmp = self.add_extension(tmp, tmp0); - tmp - }; - let c3 = { - let tmp0 = self.mul_extension(a0, b3); - let tmp1 = self.mul_extension(a1, b2); - let tmp2 = self.mul_extension(a2, b1); - let tmp3 = self.mul_extension(a3, b0); - let tmp = self.add_extension(tmp3, tmp2); - let tmp = self.add_extension(tmp, tmp1); - let tmp = self.add_extension(tmp, tmp0); - tmp - }; - - vec![c0, c1, c2, c3] - } -} - /// A gate which can multiply two field extension elements. /// TODO: Add an addend if `NUM_ROUTED_WIRES` is large enough. #[derive(Debug)] @@ -110,25 +38,11 @@ impl, const D: usize> Gate for MulExtensionGate { fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let const_0 = vars.local_constants[0]; - let multiplicand_0 = vars.local_wires[Self::wires_multiplicand_0()].to_vec(); - let multiplicand_1 = vars.local_wires[Self::wires_multiplicand_1()].to_vec(); - let output = vars.local_wires[Self::wires_output()].to_vec(); - let computed_output = mul_vec( - &[ - const_0, - F::Extension::ZERO, - F::Extension::ZERO, - F::Extension::ZERO, - ], - &multiplicand_0, - F::Extension::W.into(), - ); - let computed_output = mul_vec(&computed_output, &multiplicand_1, F::Extension::W.into()); - output - .into_iter() - .zip(computed_output) - .map(|(o, co)| o - co) - .collect() + let multiplicand_0 = vars.get_local_ext_algebra(Self::wires_multiplicand_0()); + let multiplicand_1 = vars.get_local_ext_algebra(Self::wires_multiplicand_1()); + let output = vars.get_local_ext_algebra(Self::wires_output()); + let computed_output = multiplicand_0 * multiplicand_1 * const_0.into(); + (output - computed_output).to_basefield_array().to_vec() } fn eval_unfiltered_recursively( @@ -137,18 +51,13 @@ impl, const D: usize> Gate for MulExtensionGate { vars: EvaluationTargets, ) -> Vec> { let const_0 = vars.local_constants[0]; - let multiplicand_0 = vars.local_wires[Self::wires_multiplicand_0()].to_vec(); - let multiplicand_1 = vars.local_wires[Self::wires_multiplicand_1()].to_vec(); - let output = vars.local_wires[Self::wires_output()].to_vec(); - let w = builder.constant_extension(F::Extension::W.into()); - let zero = builder.zero_extension(); - let computed_output = builder.mul_vec(&[const_0, zero, zero, zero], &multiplicand_0, w); - let computed_output = builder.mul_vec(&computed_output, &multiplicand_1, w); - output - .into_iter() - .zip(computed_output) - .map(|(o, co)| builder.sub_extension(o, co)) - .collect() + let multiplicand_0 = vars.get_local_ext_algebra(Self::wires_multiplicand_0()); + let multiplicand_1 = vars.get_local_ext_algebra(Self::wires_multiplicand_1()); + let output = vars.get_local_ext_algebra(Self::wires_output()); + let computed_output = builder.mul_ext_algebra(multiplicand_0, multiplicand_1); + let computed_output = builder.scalar_mul_ext_algebra(const_0, computed_output); + let diff = builder.sub_ext_algebra(output, computed_output); + diff.to_ext_target_array().to_vec() } fn generators( @@ -236,7 +145,6 @@ impl, const D: usize> SimpleGenerator for MulExtensionGenera #[cfg(test)] mod tests { use crate::field::crandall_field::CrandallField; - use crate::gates::arithmetic::ArithmeticGate; use crate::gates::gate_testing::test_low_degree; use crate::gates::mul_extension::MulExtensionGate; diff --git a/src/target.rs b/src/target.rs index b5736564..423865fa 100644 --- a/src/target.rs +++ b/src/target.rs @@ -1,5 +1,6 @@ use crate::circuit_data::CircuitConfig; use crate::wire::Wire; +use std::ops::Range; /// A location in the witness. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -21,4 +22,8 @@ impl Target { Target::VirtualAdviceTarget { .. } => false, } } + + pub fn wires_from_range(gate: usize, range: Range) -> Vec { + range.map(|i| Self::wire(gate, i)).collect() + } } diff --git a/src/wire.rs b/src/wire.rs index 61b7f5be..02d43029 100644 --- a/src/wire.rs +++ b/src/wire.rs @@ -1,4 +1,5 @@ use crate::circuit_data::CircuitConfig; +use std::ops::Range; /// Represents a wire in the circuit. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -13,4 +14,8 @@ impl Wire { pub fn is_routable(&self, config: &CircuitConfig) -> bool { self.input < config.num_routed_wires } + + pub fn from_range(gate: usize, range: Range) -> Vec { + range.map(|i| Wire { gate, input: i }).collect() + } } From 89761ef22a637715267610742a656d612c0c9b08 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 9 Jun 2021 17:39:45 +0200 Subject: [PATCH 014/117] Added in-circuit `reverse_bits` and `exp`. --- src/fri/recursive_verifier.rs | 16 +++++-- src/gadgets/arithmetic.rs | 15 +++++++ src/gadgets/split_base.rs | 24 +++++++---- src/gadgets/split_join.rs | 80 +++++++++++++++++++++++++++++++++++ src/gates/base_sum.rs | 64 +++++++++++++++++----------- src/util/mod.rs | 2 +- 6 files changed, 163 insertions(+), 38 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 2ae287f4..7c84d765 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -21,8 +21,15 @@ use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; impl, const D: usize> CircuitBuilder { /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity /// and P' is the FRI reduced polynomial. - fn compute_evaluation(&mut self) { - todo!(); + fn compute_evaluation( + &mut self, + x: Target, + old_x_index: Target, + arity_bits: usize, + last_evals: &[ExtensionTarget], + beta: ExtensionTarget, + ) -> ExtensionTarget { + todo!() // debug_assert_eq!(last_evals.len(), 1 << arity_bits); // // let g = F::primitive_root_of_unity(arity_bits); @@ -272,10 +279,11 @@ impl, const D: usize> CircuitBuilder { // TODO: The verifier will need to check these constants at some point (out of circuit). let g = self.constant(F::MULTIPLICATIVE_GROUP_GENERATOR); let phi = self.constant(F::primitive_root_of_unity(log_n)); - // TODO: Gate for this. - let reversed_x = self.reverse_bits(x_index, log_n); + + let reversed_x = self.reverse_bits::<2>(x_index, log_n); let phi = self.exp(phi, reversed_x); let mut subgroup_x = self.mul(g, phi); + for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { let arity = 1 << arity_bits; let next_domain_size = domain_size >> arity_bits; diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 98c4d0d0..6767c70d 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -172,6 +172,21 @@ impl, const D: usize> CircuitBuilder { product } + // TODO: Optimize this, maybe with a new gate. + pub fn exp(&mut self, base: Target, exponent: Target) -> Target { + let mut current = base; + let one = self.one(); + let mut product = one; + let exponent_bits = self.split_le(exponent); + + for bit in exponent_bits.into_iter() { + product = self.mul_many(&[bit, current, product]); + current = self.mul(current, current); + } + + product + } + /// Computes `q = x / y` by witnessing `q` and requiring that `q * y = x`. This can be unsafe in /// some cases, as it allows `0 / 0 = `. pub fn div_unsafe(&mut self, x: Target, y: Target) -> Target { diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 810939fb..776c3091 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -4,20 +4,17 @@ use crate::field::field::Field; use crate::gates::base_sum::{BaseSplitGenerator, BaseSumGate}; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; -use crate::util::ceil_div_usize; +use crate::util::{ceil_div_usize, log2_strict}; use crate::wire::Wire; use crate::witness::PartialWitness; impl, const D: usize> CircuitBuilder { - /// Split the given element into a list of 11 targets, where each one represents a - /// base-64 limb of the element, with little-endian ordering. + /// Split the given element into a list of targets, where each one represents a + /// base-B limb of the element, with little-endian ordering. pub(crate) fn split_le_base(&mut self, x: Target) -> Vec { let num_limbs = num_limbs_to_check(64, B); let gate = self.add_gate(BaseSumGate::::new(num_limbs), vec![]); - let sum = Target::Wire(Wire { - gate, - input: BaseSumGate::::WIRE_SUM, - }); + let sum = Target::wire(gate, BaseSumGate::::WIRE_SUM); self.route(x, sum); Target::wires_from_range( @@ -38,12 +35,21 @@ impl, const D: usize> CircuitBuilder { self.assert_zero(limbs[i]); } } + + pub(crate) fn reverse_bits(&mut self, x: Target, num_limbs: usize) -> Target { + let gate = self.add_gate(BaseSumGate::::new(num_limbs), vec![]); + let sum = Target::wire(gate, BaseSumGate::::WIRE_SUM); + self.route(x, sum); + + Target::wire(gate, BaseSumGate::::WIRE_REVERSED_SUM) + } } /// Returns `k` such that any number with `k` trailing zeros in base `base` has at least /// `n` trailing zeros in base 2. -fn num_limbs_to_check(n: u32, base: usize) -> usize { - (n as f64 * (2.0_f64.log(base as f64))).ceil() as usize +const fn num_limbs_to_check(n: u32, base: usize) -> usize { + assert_eq!(base % 2, 0, "Base should be even."); + ceil_div_usize(n as usize, base.trailing_zeros() as usize) } #[cfg(test)] diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index e65198ec..c8f75efa 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -1,8 +1,10 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::Extendable; use crate::field::field::Field; +use crate::gates::base_sum::BaseSumGate; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; +use crate::util::ceil_div_usize; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -21,6 +23,48 @@ impl, const D: usize> CircuitBuilder { }); bit_targets } + + /// Split the given integer into a list of wires, where each one represents a + /// bit of the integer, with little-endian ordering. + /// Verifies that the decomposition is correct by using `k` `BaseSum<2>` gates + /// with `k` such that `k*num_routed_bits>=64`. + pub(crate) fn split_le(&mut self, integer: Target) -> Vec { + let num_limbs = self.config.num_routed_wires - BaseSumGate::WIRE_LIMBS_START; + let k = ceil_div_usize(64, num_limbs); + let gates = (0..k) + .map(|_| self.add_gate_no_constants(BaseSumGate::new(num_limbs))) + .collect::>(); + + let mut bits = Vec::with_capacity(64); + for &gate in &gates { + bits.extend(Target::wires_from_range( + gate, + BaseSumGate::WIRE_LIMBS_START..BaseSumGate::WIRE_LIMBS_START + num_limbs, + )); + } + + let zero = self.zero(); + let mut acc = zero; + for &gate in gates.iter().rev() { + let sum = Target::wire(gate, BaseSumGate::WIRE_SUM); + acc = self.arithmetic( + F::from_canonical_usize(1 << num_limbs), + acc, + zero, + F::ONE, + sum, + ); + } + self.assert_equal(acc, integer); + + self.add_generator(WireSplitGenerator { + integer, + gates, + num_limbs, + }); + + bits + } } /// Generator for a little-endian split. @@ -79,3 +123,39 @@ impl SimpleGenerator for SplitGenerator { result } } + +#[derive(Debug)] +struct WireSplitGenerator { + integer: Target, + gates: Vec, + num_limbs: usize, +} + +impl SimpleGenerator for WireSplitGenerator { + fn dependencies(&self) -> Vec { + vec![self.integer] + } + + fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + let mut integer_value = witness.get_target(self.integer).to_canonical_u64(); + + let mut result = PartialWitness::new(); + for gate in self.gates { + let sum = Target::wire(gate, BaseSumGate::WIRE_SUM); + result.set_target( + sum, + F::from_canonical_u64(integer_value & ((1 << self.num_limbs) - 1)), + ); + integer_value >> self.num_limbs; + } + + debug_assert_eq!( + integer_value, + 0, + "Integer too large to fit in {} many `BaseSumGate`s", + self.gates.len() + ); + + result + } +} diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index f56519e1..fe9068e0 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -12,7 +12,7 @@ use crate::wire::Wire; use crate::witness::PartialWitness; use std::ops::Range; -/// A gate which can sum base W limbs. +/// A gate which can sum base W limbs and the reversed limbs. #[derive(Debug)] pub struct BaseSumGate { num_limbs: usize, @@ -24,7 +24,8 @@ impl BaseSumGate { } pub const WIRE_SUM: usize = 0; - pub const WIRE_LIMBS_START: usize = 1; + pub const WIRE_REVERSED_SUM: usize = 1; + pub const WIRE_LIMBS_START: usize = 2; /// Returns the index of the `i`th limb wire. pub fn limbs(&self) -> Range { @@ -39,9 +40,13 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let sum = vars.local_wires[Self::WIRE_SUM]; - let limbs = vars.local_wires[self.limbs()].to_vec(); + let reversed_sum = vars.local_wires[Self::WIRE_REVERSED_SUM]; + let mut limbs = vars.local_wires[self.limbs()].to_vec(); let computed_sum = reduce_with_powers(&limbs, F::Extension::from_canonical_usize(B)); - let mut constraints = vec![computed_sum - sum]; + limbs.reverse(); + let computed_reversed_sum = + reduce_with_powers(&limbs, F::Extension::from_canonical_usize(B)); + let mut constraints = vec![computed_sum - sum, computed_reversed_sum - reversed_sum]; for limb in limbs { constraints.push( (0..B) @@ -59,10 +64,15 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat ) -> Vec> { let base = builder.constant(F::from_canonical_usize(B)); let sum = vars.local_wires[Self::WIRE_SUM]; - let limbs = vars.local_wires[self.limbs()].to_vec(); - let computed_sum = - reduce_with_powers_recursive(builder, &vars.local_wires[self.limbs()], base); - let mut constraints = vec![builder.sub_extension(computed_sum, sum)]; + let reversed_sum = vars.local_wires[Self::WIRE_REVERSED_SUM]; + let mut limbs = vars.local_wires[self.limbs()].to_vec(); + let computed_sum = reduce_with_powers_recursive(builder, &limbs, base); + limbs.reverse(); + let reversed_computed_sum = reduce_with_powers_recursive(builder, &limbs, base); + let mut constraints = vec![ + builder.sub_extension(computed_sum, sum), + builder.sub_extension(reversed_computed_sum, computed_sum), + ]; for limb in limbs { constraints.push({ let mut acc = builder.one_extension(); @@ -89,20 +99,23 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat vec![Box::new(gen)] } + // 2 for the sum and reversed sum, then `num_limbs` for the limbs. fn num_wires(&self) -> usize { - self.num_limbs + 1 + self.num_limbs + 2 } fn num_constants(&self) -> usize { 0 } + // Bounded by the range-check (x-0)*(x-1)*...*(x-B+1). fn degree(&self) -> usize { B } + // 2 for checking the sum and reversed sum, then `num_limbs` for range-checking the limbs. fn num_constraints(&self) -> usize { - 1 + self.num_limbs + 2 + self.num_limbs } } @@ -121,26 +134,29 @@ impl SimpleGenerator for BaseSplitGenerator { } fn run_once(&self, witness: &PartialWitness) -> PartialWitness { - let mut sum_value = witness - .get_target(Target::Wire(Wire { - gate: self.gate_index, - input: BaseSumGate::::WIRE_SUM, - })) + let sum_value = witness + .get_target(Target::wire(self.gate_index, BaseSumGate::::WIRE_SUM)) .to_canonical_u64() as usize; let limbs = (BaseSumGate::::WIRE_LIMBS_START ..BaseSumGate::::WIRE_LIMBS_START + self.num_limbs) - .map(|i| { - Target::Wire(Wire { - gate: self.gate_index, - input: i, - }) - }); + .map(|i| Target::wire(self.gate_index, i)); + let limbs_value = (0..self.num_limbs) + .scan(sum_value, |acc, _| { + let tmp = *acc % B; + *acc /= B; + Some(tmp) + }) + .collect::>(); + + let reversed_sum = limbs_value.iter().rev().fold(0, |acc, &x| acc * B + x); let mut result = PartialWitness::new(); - for b in limbs { - let b_value = sum_value % B; + result.set_target( + Target::wire(self.gate_index, BaseSumGate::::WIRE_REVERSED_SUM), + F::from_canonical_usize(reversed_sum), + ); + for (b, b_value) in limbs.zip(limbs_value) { result.set_target(b, F::from_canonical_usize(b_value)); - sum_value /= B; } debug_assert_eq!( diff --git a/src/util/mod.rs b/src/util/mod.rs index 7ab9bc5b..09bd4e72 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -7,7 +7,7 @@ pub(crate) fn bits_u64(n: u64) -> usize { (64 - n.leading_zeros()) as usize } -pub(crate) fn ceil_div_usize(a: usize, b: usize) -> usize { +pub(crate) const fn ceil_div_usize(a: usize, b: usize) -> usize { (a + b - 1) / b } From f7e92af963ce9e9df4f6b78dcd14dbfd251a2302 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 9 Jun 2021 17:55:49 +0200 Subject: [PATCH 015/117] Comment out errors --- src/fri/recursive_verifier.rs | 24 +++++++++++------------- src/gadgets/split_base.rs | 9 +++++++-- src/gadgets/split_join.rs | 12 ++++++------ src/proof.rs | 1 + src/verifier.rs | 4 ++-- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 7c84d765..d990977d 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -308,14 +308,14 @@ impl, const D: usize> CircuitBuilder { }; let mut evals = round_proof.steps[i].evals.clone(); // Insert P(y) into the evaluation vector, since it wasn't included by the prover. - evals.insert(x_index & (arity - 1), e_x); - evaluations.push(evals); - self.verify_merkle_proof( - flatten_target(&evaluations[i]), - x_index >> arity_bits, - proof.commit_phase_merkle_roots[i], - &round_proof.steps[i].merkle_proof, - )?; + // evals.insert(x_index & (arity - 1), e_x); + // evaluations.push(evals); + // self.verify_merkle_proof( + // flatten_target(&evaluations[i]), + // x_index >> arity_bits, + // proof.commit_phase_merkle_roots[i], + // &round_proof.steps[i].merkle_proof, + // )?; if i > 0 { // Update the point x to x^arity. @@ -325,7 +325,7 @@ impl, const D: usize> CircuitBuilder { } domain_size = next_domain_size; old_x_index = x_index; - x_index >>= arity_bits; + // x_index >>= arity_bits; } let last_evals = evaluations.last().unwrap(); @@ -343,10 +343,8 @@ impl, const D: usize> CircuitBuilder { // Final check of FRI. After all the reductions, we check that the final polynomial is equal // to the one sent by the prover. - ensure!( - proof.final_poly.eval(subgroup_x.into()) == purported_eval, - "Final polynomial evaluation is invalid." - ); + let eval = proof.final_poly.eval_scalar(self, subgroup_x); + self.assert_equal_extension(eval, purported_eval); Ok(()) } diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 776c3091..a61334bb 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -48,8 +48,13 @@ impl, const D: usize> CircuitBuilder { /// Returns `k` such that any number with `k` trailing zeros in base `base` has at least /// `n` trailing zeros in base 2. const fn num_limbs_to_check(n: u32, base: usize) -> usize { - assert_eq!(base % 2, 0, "Base should be even."); - ceil_div_usize(n as usize, base.trailing_zeros() as usize) + if base % 2 == 1 { + // Dirty trick to panic if the base is odd. + // TODO: replace with `assert_eq!(base % 2, 0, "Base should be even.")` when stable. + [][0] + } else { + ceil_div_usize(n as usize, base.trailing_zeros() as usize) + } } #[cfg(test)] diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index c8f75efa..0524e7d7 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -29,24 +29,24 @@ impl, const D: usize> CircuitBuilder { /// Verifies that the decomposition is correct by using `k` `BaseSum<2>` gates /// with `k` such that `k*num_routed_bits>=64`. pub(crate) fn split_le(&mut self, integer: Target) -> Vec { - let num_limbs = self.config.num_routed_wires - BaseSumGate::WIRE_LIMBS_START; + let num_limbs = self.config.num_routed_wires - BaseSumGate::<2>::WIRE_LIMBS_START; let k = ceil_div_usize(64, num_limbs); let gates = (0..k) - .map(|_| self.add_gate_no_constants(BaseSumGate::new(num_limbs))) + .map(|_| self.add_gate_no_constants(BaseSumGate::<2>::new(num_limbs))) .collect::>(); let mut bits = Vec::with_capacity(64); for &gate in &gates { bits.extend(Target::wires_from_range( gate, - BaseSumGate::WIRE_LIMBS_START..BaseSumGate::WIRE_LIMBS_START + num_limbs, + BaseSumGate::<2>::WIRE_LIMBS_START..BaseSumGate::<2>::WIRE_LIMBS_START + num_limbs, )); } let zero = self.zero(); let mut acc = zero; for &gate in gates.iter().rev() { - let sum = Target::wire(gate, BaseSumGate::WIRE_SUM); + let sum = Target::wire(gate, BaseSumGate::<2>::WIRE_SUM); acc = self.arithmetic( F::from_canonical_usize(1 << num_limbs), acc, @@ -140,8 +140,8 @@ impl SimpleGenerator for WireSplitGenerator { let mut integer_value = witness.get_target(self.integer).to_canonical_u64(); let mut result = PartialWitness::new(); - for gate in self.gates { - let sum = Target::wire(gate, BaseSumGate::WIRE_SUM); + for &gate in &self.gates { + let sum = Target::wire(gate, BaseSumGate::<2>::WIRE_SUM); result.set_target( sum, F::from_canonical_u64(integer_value & ((1 << self.num_limbs) - 1)), diff --git a/src/proof.rs b/src/proof.rs index 59b7bb9c..04ddf06c 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -144,6 +144,7 @@ pub struct FriProofTarget { pub pow_witness: Target, } +#[derive(Clone, Debug)] /// The purported values of each polynomial at a single point. pub struct OpeningSet, const D: usize> { pub constants: Vec, diff --git a/src/verifier.rs b/src/verifier.rs index d2a7bb63..a2750421 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -55,13 +55,13 @@ pub(crate) fn verify, const D: usize>( ); // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. - let quotient_polys_zeta = proof.openings.quotient_polys; + let quotient_polys_zeta = &proof.openings.quotient_polys; let z_h_zeta = eval_zero_poly(common_data.degree(), zeta); for i in 0..num_challenges { ensure!(vanishing_polys_zeta[i] == z_h_zeta * quotient_polys_zeta[i]); } - let evaluations = proof.openings; + let evaluations = proof.openings.clone(); let merkle_roots = &[ verifier_data.constants_root, From 3db6e38d26aca9cde3f623b59a516c635b4f820c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 9 Jun 2021 18:04:58 +0200 Subject: [PATCH 016/117] Convert some `mul_extension_naive` to `mul_extension` --- src/field/extension_field/target.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 17d28172..c5e9f9ab 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -214,7 +214,7 @@ impl, const D: usize> CircuitBuilder { pub fn mul_many_extension(&mut self, terms: &[ExtensionTarget]) -> ExtensionTarget { let mut product = self.one_extension(); for term in terms { - product = self.mul_extension_naive(product, *term); + product = self.mul_extension(product, *term); } product } @@ -227,7 +227,7 @@ impl, const D: usize> CircuitBuilder { b: ExtensionTarget, c: ExtensionTarget, ) -> ExtensionTarget { - let product = self.mul_extension_naive(a, b); + let product = self.mul_extension(a, b); self.add_extension(product, c) } From bb551092a0af24ee0a244e5feb87d808a955f903 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 9 Jun 2021 21:12:15 +0200 Subject: [PATCH 017/117] Remove `mul_extension_naive` --- src/field/extension_field/target.rs | 23 ++--------------------- src/fri/recursive_verifier.rs | 6 +++--- src/gadgets/arithmetic.rs | 2 +- src/gadgets/polynomial.rs | 2 +- src/gates/arithmetic.rs | 2 +- src/gates/base_sum.rs | 2 +- src/gates/gmimc.rs | 6 +++--- 7 files changed, 12 insertions(+), 31 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index c5e9f9ab..712c6103 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -171,25 +171,6 @@ impl, const D: usize> CircuitBuilder { self.mul_extension_with_const(F::ONE, multiplicand_0, multiplicand_1) } - pub fn mul_extension_naive( - &mut self, - a: ExtensionTarget, - b: ExtensionTarget, - ) -> ExtensionTarget { - let mut res = [self.zero(); D]; - for i in 0..D { - for j in 0..D { - res[(i + j) % D] = if i + j < D { - self.mul_add(a.0[i], b.0[j], res[(i + j) % D]) - } else { - // W * a[i] * b[i] + res[(i + j) % D] - self.arithmetic(F::Extension::W, a.0[i], b.0[i], F::ONE, res[(i + j) % D]) - } - } - } - ExtensionTarget(res) - } - pub fn mul_ext_algebra( &mut self, a: ExtensionAlgebraTarget, @@ -199,7 +180,7 @@ impl, const D: usize> CircuitBuilder { let w = self.constant(F::Extension::W); for i in 0..D { for j in 0..D { - let ai_bi = self.mul_extension_naive(a.0[i], b.0[j]); + let ai_bi = self.mul_extension(a.0[i], b.0[j]); res[(i + j) % D] = if i + j < D { self.add_extension(ai_bi, res[(i + j) % D]) } else { @@ -247,7 +228,7 @@ impl, const D: usize> CircuitBuilder { mut b: ExtensionAlgebraTarget, ) -> ExtensionAlgebraTarget { for i in 0..D { - b.0[i] = self.mul_extension_naive(a, b.0[i]); + b.0[i] = self.mul_extension(a, b.0[i]); } b } diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index d990977d..3dbba68e 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -194,7 +194,7 @@ impl, const D: usize> CircuitBuilder { } let g = self.constant_extension(F::Extension::primitive_root_of_unity(degree_log)); - let zeta_right = self.mul_extension_naive(g, zeta); + let zeta_right = self.mul_extension(g, zeta); let mut ev_zeta = self.zero_extension(); for &t in &os.plonk_zs { let a = alpha_powers.next(self); @@ -210,7 +210,7 @@ impl, const D: usize> CircuitBuilder { let numerator = self.sub_extension(ev, interpol_val); let vanish = self.sub_extension(subgroup_x, zeta); let vanish_right = self.sub_extension(subgroup_x, zeta_right); - let denominator = self.mul_extension_naive(vanish, vanish_right); + let denominator = self.mul_extension(vanish, vanish_right); let quotient = self.div_unsafe_extension(numerator, denominator); let sum = self.add_extension(sum, quotient); @@ -244,7 +244,7 @@ impl, const D: usize> CircuitBuilder { let interpol_val = wires_interpol.eval(self, subgroup_x); let numerator = self.sub_extension(ev, interpol_val); let vanish_frob = self.sub_extension(subgroup_x, zeta_frob); - let denominator = self.mul_extension_naive(vanish, vanish_frob); + let denominator = self.mul_extension(vanish, vanish_frob); let quotient = self.div_unsafe_extension(numerator, denominator); let sum = self.add_extension(sum, quotient); diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 6767c70d..2ea7c882 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -335,7 +335,7 @@ impl PowersTarget { builder: &mut CircuitBuilder, ) -> ExtensionTarget { let result = self.current; - self.current = builder.mul_extension_naive(self.base, self.current); + self.current = builder.mul_extension(self.base, self.current); result } } diff --git a/src/gadgets/polynomial.rs b/src/gadgets/polynomial.rs index 9ccfc6a8..543be834 100644 --- a/src/gadgets/polynomial.rs +++ b/src/gadgets/polynomial.rs @@ -26,7 +26,7 @@ impl PolynomialCoeffsExtTarget { ) -> ExtensionTarget { let mut acc = builder.zero_extension(); for &c in self.0.iter().rev() { - let tmp = builder.mul_extension_naive(point, acc); + let tmp = builder.mul_extension(point, acc); acc = builder.add_extension(tmp, c); } acc diff --git a/src/gates/arithmetic.rs b/src/gates/arithmetic.rs index 8208f0f8..0d0fdd7c 100644 --- a/src/gates/arithmetic.rs +++ b/src/gates/arithmetic.rs @@ -57,7 +57,7 @@ impl, const D: usize> Gate for ArithmeticGate { let output = vars.local_wires[Self::WIRE_OUTPUT]; let product_term = builder.mul_many_extension(&[const_0, multiplicand_0, multiplicand_1]); - let addend_term = builder.mul_extension_naive(const_1, addend); + let addend_term = builder.mul_extension(const_1, addend); let computed_output = builder.add_many_extension(&[product_term, addend_term]); vec![builder.sub_extension(computed_output, output)] } diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index fe9068e0..ef69b802 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -79,7 +79,7 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat (0..B).for_each(|i| { let it = builder.constant_extension(F::from_canonical_usize(i).into()); let diff = builder.sub_extension(limb, it); - acc = builder.mul_extension_naive(acc, diff); + acc = builder.mul_extension(acc, diff); }); acc }); diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 7c75951d..19042d57 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -131,7 +131,7 @@ impl, const D: usize, const R: usize> Gate for GMiMCGate< let swap = vars.local_wires[Self::WIRE_SWAP]; let one_ext = builder.one_extension(); let not_swap = builder.sub_extension(swap, one_ext); - constraints.push(builder.mul_extension_naive(swap, not_swap)); + constraints.push(builder.mul_extension(swap, not_swap)); let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD]; let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW]; @@ -168,8 +168,8 @@ impl, const D: usize, const R: usize> Gate for GMiMCGate< let constant = builder.constant_extension(self.constants[r].into()); let cubing_input = builder.add_many_extension(&[state[active], addition_buffer, constant]); - let square = builder.mul_extension_naive(cubing_input, cubing_input); - let f = builder.mul_extension_naive(square, cubing_input); + let square = builder.mul_extension(cubing_input, cubing_input); + let f = builder.mul_extension(square, cubing_input); addition_buffer = builder.add_extension(addition_buffer, f); state[active] = builder.sub_extension(state[active], f); } From 6cce4c1f782c9189937c154a27335338376ffbbd Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 10 Jun 2021 15:55:29 +0200 Subject: [PATCH 018/117] Add low-high split --- src/gadgets/mod.rs | 1 + src/gadgets/range_check.rs | 62 ++++++++++++++++++++++++++++++++++++++ src/gates/base_sum.rs | 9 ++---- 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 src/gadgets/range_check.rs diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index ee331012..636f8afc 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -2,5 +2,6 @@ pub mod arithmetic; pub mod hash; pub mod interpolation; pub mod polynomial; +pub mod range_check; pub mod split_base; pub(crate) mod split_join; diff --git a/src/gadgets/range_check.rs b/src/gadgets/range_check.rs new file mode 100644 index 00000000..4323923d --- /dev/null +++ b/src/gadgets/range_check.rs @@ -0,0 +1,62 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::Extendable; +use crate::field::field::Field; +use crate::gates::base_sum::BaseSumGate; +use crate::generator::SimpleGenerator; +use crate::target::Target; +use crate::witness::PartialWitness; + +impl, const D: usize> CircuitBuilder { + /// Checks that `x < 2^n_log` using a `BaseSumGate`. + pub fn range_check(&mut self, x: Target, n_log: usize) { + let gate = self.add_gate(BaseSumGate::<2>::new(n_log), vec![]); + let sum = Target::wire(gate, BaseSumGate::<2>::WIRE_SUM); + self.route(x, sum); + } + + /// Returns `(a,b)` such that `x = a + 2^n_log * b` with `a < 2^n_log`. + pub fn split_low_high(&mut self, x: Target, n_log: usize) -> (Target, Target) { + let low_gate = self.add_gate(BaseSumGate::<2>::new(n_log), vec![]); + let high_gate = self.add_gate(BaseSumGate::<2>::new(n_log), vec![]); + let low = Target::wire(low_gate, BaseSumGate::<2>::WIRE_SUM); + let high = Target::wire(high_gate, BaseSumGate::<2>::WIRE_SUM); + self.add_generator(LowHighGenerator { + integer: x, + n_log, + low, + high, + }); + + let pow2 = self.constant(F::from_canonical_u64(1 << n_log)); + let comp_x = self.mul_add(high, pow2, low); + self.assert_equal(x, comp_x); + + (low, high) + } +} + +#[derive(Debug)] +struct LowHighGenerator { + integer: Target, + n_log: usize, + low: Target, + high: Target, +} + +impl SimpleGenerator for LowHighGenerator { + fn dependencies(&self) -> Vec { + vec![self.integer] + } + + fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + let mut integer_value = witness.get_target(self.integer).to_canonical_u64(); + let low = integer_value & ((1 << self.n_log) - 1); + let high = integer_value >> self.n_log; + + let mut result = PartialWitness::new(); + result.set_target(self.low, F::from_canonical_u64(low)); + result.set_target(self.high, F::from_canonical_u64(high)); + + result + } +} diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index ef69b802..3efb6533 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -71,7 +71,7 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat let reversed_computed_sum = reduce_with_powers_recursive(builder, &limbs, base); let mut constraints = vec![ builder.sub_extension(computed_sum, sum), - builder.sub_extension(reversed_computed_sum, computed_sum), + builder.sub_extension(reversed_computed_sum, reversed_sum), ]; for limb in limbs { constraints.push({ @@ -127,10 +127,7 @@ pub struct BaseSplitGenerator { impl SimpleGenerator for BaseSplitGenerator { fn dependencies(&self) -> Vec { - vec![Target::Wire(Wire { - gate: self.gate_index, - input: BaseSumGate::::WIRE_SUM, - })] + vec![Target::wire(self.gate_index, BaseSumGate::::WIRE_SUM)] } fn run_once(&self, witness: &PartialWitness) -> PartialWitness { @@ -161,7 +158,7 @@ impl SimpleGenerator for BaseSplitGenerator { debug_assert_eq!( sum_value, 0, - "Integer too large to fit in given number of bits" + "Integer too large to fit in given number of limbs" ); result From cdce82e1f8fcb7d434ae72d282633a56e84ad6cd Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 10 Jun 2021 15:56:17 +0200 Subject: [PATCH 019/117] Small fix --- src/gadgets/range_check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gadgets/range_check.rs b/src/gadgets/range_check.rs index 4323923d..e711eadd 100644 --- a/src/gadgets/range_check.rs +++ b/src/gadgets/range_check.rs @@ -17,7 +17,7 @@ impl, const D: usize> CircuitBuilder { /// Returns `(a,b)` such that `x = a + 2^n_log * b` with `a < 2^n_log`. pub fn split_low_high(&mut self, x: Target, n_log: usize) -> (Target, Target) { let low_gate = self.add_gate(BaseSumGate::<2>::new(n_log), vec![]); - let high_gate = self.add_gate(BaseSumGate::<2>::new(n_log), vec![]); + let high_gate = self.add_gate(BaseSumGate::<2>::new(64 - n_log), vec![]); let low = Target::wire(low_gate, BaseSumGate::<2>::WIRE_SUM); let high = Target::wire(high_gate, BaseSumGate::<2>::WIRE_SUM); self.add_generator(LowHighGenerator { From f8dd35b748e7f0eec3944ad5cc471e9a6b7ba795 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 10 Jun 2021 16:08:57 +0200 Subject: [PATCH 020/117] Use low-high in query round. --- src/fri/recursive_verifier.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 3dbba68e..25fb7687 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -264,9 +264,11 @@ impl, const D: usize> CircuitBuilder { round_proof: &FriQueryRoundTarget, config: &FriConfig, ) -> Result<()> { + let n_log = log2_strict(n); let mut evaluations: Vec>> = Vec::new(); // TODO: Do we need to range check `x_index` to a target smaller than `p`? let mut x_index = challenger.get_challenge(self); + x_index = self.split_low_high(x_index, n_log).0; let mut domain_size = n; self.fri_verify_initial_proof( x_index, @@ -275,12 +277,11 @@ impl, const D: usize> CircuitBuilder { ); let mut old_x_index = self.zero(); // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. - let log_n = log2_strict(n); // TODO: The verifier will need to check these constants at some point (out of circuit). let g = self.constant(F::MULTIPLICATIVE_GROUP_GENERATOR); - let phi = self.constant(F::primitive_root_of_unity(log_n)); + let phi = self.constant(F::primitive_root_of_unity(n_log)); - let reversed_x = self.reverse_bits::<2>(x_index, log_n); + let reversed_x = self.reverse_bits::<2>(x_index, n_log); let phi = self.exp(phi, reversed_x); let mut subgroup_x = self.mul(g, phi); @@ -308,6 +309,7 @@ impl, const D: usize> CircuitBuilder { }; let mut evals = round_proof.steps[i].evals.clone(); // Insert P(y) into the evaluation vector, since it wasn't included by the prover. + let (low_x_index, high_x_index) = self.split_low_high(x_index, arity_bits); // evals.insert(x_index & (arity - 1), e_x); // evaluations.push(evals); // self.verify_merkle_proof( @@ -325,7 +327,7 @@ impl, const D: usize> CircuitBuilder { } domain_size = next_domain_size; old_x_index = x_index; - // x_index >>= arity_bits; + x_index = high_x_index; } let last_evals = evaluations.last().unwrap(); From 81ce0eb7101e313b97fbf07dcbd58540bfe3d7f6 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 10 Jun 2021 16:48:05 +0200 Subject: [PATCH 021/117] Fix bugs --- src/gadgets/split_base.rs | 38 +++++++++++++++++++++++--------------- src/gadgets/split_join.rs | 2 +- src/gates/base_sum.rs | 11 ++++++----- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index a61334bb..0b1d61e8 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -11,8 +11,11 @@ use crate::witness::PartialWitness; impl, const D: usize> CircuitBuilder { /// Split the given element into a list of targets, where each one represents a /// base-B limb of the element, with little-endian ordering. - pub(crate) fn split_le_base(&mut self, x: Target) -> Vec { - let num_limbs = num_limbs_to_check(64, B); + pub(crate) fn split_le_base( + &mut self, + x: Target, + num_limbs: usize, + ) -> Vec { let gate = self.add_gate(BaseSumGate::::new(num_limbs), vec![]); let sum = Target::wire(gate, BaseSumGate::::WIRE_SUM); self.route(x, sum); @@ -25,8 +28,9 @@ impl, const D: usize> CircuitBuilder { /// Asserts that `x`'s bit representation has at least `trailing_zeros` trailing zeros. pub(crate) fn assert_trailing_zeros(&mut self, x: Target, trailing_zeros: u32) { - let limbs = self.split_le_base::(x); + let num_limbs = num_limbs(64, B); let num_limbs_to_check = num_limbs_to_check(trailing_zeros, B); + let limbs = self.split_le_base::(x, num_limbs); assert!( num_limbs_to_check < self.config.num_routed_wires, "Not enough routed wires." @@ -47,6 +51,7 @@ impl, const D: usize> CircuitBuilder { /// Returns `k` such that any number with `k` trailing zeros in base `base` has at least /// `n` trailing zeros in base 2. +#[allow(unconditional_panic)] const fn num_limbs_to_check(n: u32, base: usize) -> usize { if base % 2 == 1 { // Dirty trick to panic if the base is odd. @@ -57,6 +62,10 @@ const fn num_limbs_to_check(n: u32, base: usize) -> usize { } } +fn num_limbs(n_log: u32, base: usize) -> usize { + ((n_log as f64) * 2.0_f64.log(base as f64)).ceil() as usize +} + #[cfg(test)] mod tests { use super::*; @@ -68,7 +77,7 @@ mod tests { use anyhow::Result; #[test] - fn test_split_base() -> Result<()> { + fn test_split_base() { type F = CrandallField; let config = CircuitConfig { num_wires: 134, @@ -85,24 +94,23 @@ mod tests { }, }; let mut builder = CircuitBuilder::::new(config); - let x = F::from_canonical_usize(0b10100000); // 160 =1120 in base 5. + let x = F::from_canonical_usize(0b110100000); // 416 = 1532 in base 6. let xt = builder.constant(x); - let limbs = builder.split_le_base::<5>(xt); - assert_eq!(limbs.len(), 28); // 5^27 < 2^64 <= 5^28 - let zero = builder.zero(); + let limbs = builder.split_le_base::<6>(xt, 24); let one = builder.one(); let two = builder.two(); - builder.assert_equal(limbs[0], zero); - builder.assert_equal(limbs[1], two); - builder.assert_equal(limbs[2], one); + let three = builder.constant(F::from_canonical_u64(3)); + let five = builder.constant(F::from_canonical_u64(5)); + builder.assert_equal(limbs[0], two); + builder.assert_equal(limbs[1], three); + builder.assert_equal(limbs[2], five); builder.assert_equal(limbs[3], one); - builder.assert_trailing_zeros::<3>(xt, 4); - builder.assert_trailing_zeros::<3>(xt, 5); - builder.assert_trailing_zeros::<13>(xt, 5); + builder.assert_trailing_zeros::<6>(xt, 4); + builder.assert_trailing_zeros::<4>(xt, 5); + builder.assert_trailing_zeros::<12>(xt, 5); let data = builder.build(); let proof = data.prove(PartialWitness::new()); - verify(proof, &data.verifier_only, &data.common) } } diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index 0524e7d7..f4118d4e 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -146,7 +146,7 @@ impl SimpleGenerator for WireSplitGenerator { sum, F::from_canonical_u64(integer_value & ((1 << self.num_limbs) - 1)), ); - integer_value >> self.num_limbs; + integer_value >>= self.num_limbs; } debug_assert_eq!( diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index 3efb6533..e3b69618 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -134,6 +134,12 @@ impl SimpleGenerator for BaseSplitGenerator { let sum_value = witness .get_target(Target::wire(self.gate_index, BaseSumGate::::WIRE_SUM)) .to_canonical_u64() as usize; + debug_assert_eq!( + (0..self.num_limbs).fold(sum_value, |acc, _| acc / B), + 0, + "Integer too large to fit in given number of limbs" + ); + let limbs = (BaseSumGate::::WIRE_LIMBS_START ..BaseSumGate::::WIRE_LIMBS_START + self.num_limbs) .map(|i| Target::wire(self.gate_index, i)); @@ -156,11 +162,6 @@ impl SimpleGenerator for BaseSplitGenerator { result.set_target(b, F::from_canonical_usize(b_value)); } - debug_assert_eq!( - sum_value, 0, - "Integer too large to fit in given number of limbs" - ); - result } } From dea6db00ac54e24389870ed953f3ad7a66e4f5e8 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 10 Jun 2021 17:45:45 +0200 Subject: [PATCH 022/117] Rotation gadgets --- src/field/extension_field/algebra.rs | 1 - src/fri/recursive_verifier.rs | 2 +- src/fri/verifier.rs | 2 +- src/gadgets/mod.rs | 1 + src/gadgets/rotate.rs | 66 ++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/gadgets/rotate.rs diff --git a/src/field/extension_field/algebra.rs b/src/field/extension_field/algebra.rs index eac0bbd8..f61f173b 100644 --- a/src/field/extension_field/algebra.rs +++ b/src/field/extension_field/algebra.rs @@ -218,7 +218,6 @@ mod tests { let y = ExtensionAlgebra::from_basefield_array(arr1); let z = x * y; - dbg!(z.0, mul_mle(ts.clone())); assert_eq!(z.0, mul_mle(ts)); } diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 25fb7687..4f8c3864 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -326,7 +326,7 @@ impl, const D: usize> CircuitBuilder { } } domain_size = next_domain_size; - old_x_index = x_index; + old_x_index = low_x_index; x_index = high_x_index; } diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 41baa84e..6275ada6 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -279,7 +279,7 @@ fn fri_verifier_query_round, const D: usize>( } } domain_size = next_domain_size; - old_x_index = x_index; + old_x_index = x_index & (arity - 1); x_index >>= arity_bits; } diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 636f8afc..137b991e 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -3,5 +3,6 @@ pub mod hash; pub mod interpolation; pub mod polynomial; pub mod range_check; +pub mod rotate; pub mod split_base; pub(crate) mod split_join; diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs new file mode 100644 index 00000000..fa5579f7 --- /dev/null +++ b/src/gadgets/rotate.rs @@ -0,0 +1,66 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::Extendable; +use crate::field::field::Field; +use crate::gates::base_sum::BaseSumGate; +use crate::generator::SimpleGenerator; +use crate::target::Target; +use crate::witness::PartialWitness; + +impl, const D: usize> CircuitBuilder { + /// Selects `x` or `y` based on `b`, which is assumed to be binary. + /// In particular, this returns `if b { x } else { y }`. + /// Note: This does not range-check `b`. + pub fn select(&mut self, b: Target, x: Target, y: Target) -> Target { + let b_y_minus_y = self.mul_sub(b, y, y); + self.mul_sub(b, x, b_y_minus_y) + } + + /// Left-rotates an array if `b=1` else return the same array. + pub fn rotate_once(&mut self, b: Target, v: Vec, len: usize) -> Vec { + debug_assert_eq!(v.len(), len); + let mut res = Vec::new(); + + for i in 0..len { + res.push(self.select(b, v[(i + 1) % len], v[i])); + } + + res + } + + pub fn split_unary(&mut self, x: Target, len: usize) -> Vec { + todo!() + } + + /// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be + /// less than `len`. + pub fn rotate(&mut self, num_rotation: Target, v: Vec, len: usize) -> Vec { + // Decompose `num_rotation` into `len` 1-ary limbs, possibly with a new gate. + // Then apply `rotate_once` `len` time using the 1-ary limbs. + todo!() + } +} + +#[derive(Debug)] +struct UnaryBaseGenerator { + integer: Target, + len: usize, + limbs: Vec, +} + +impl SimpleGenerator for UnaryBaseGenerator { + fn dependencies(&self) -> Vec { + vec![self.integer] + } + + fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + let mut integer_value = witness.get_target(self.integer).to_canonical_u64(); + let low = integer_value & ((1 << self.n_log) - 1); + let high = integer_value >> self.n_log; + + let mut result = PartialWitness::new(); + result.set_target(self.low, F::from_canonical_u64(low)); + result.set_target(self.high, F::from_canonical_u64(high)); + + result + } +} From bdf8417c28daaa6609a9a68e6a00f949f9dbce75 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 10 Jun 2021 17:58:41 +0200 Subject: [PATCH 023/117] Modify `rotate` to use binary --- src/gadgets/rotate.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index fa5579f7..29507b94 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -15,28 +15,29 @@ impl, const D: usize> CircuitBuilder { self.mul_sub(b, x, b_y_minus_y) } - /// Left-rotates an array if `b=1` else return the same array. - pub fn rotate_once(&mut self, b: Target, v: Vec, len: usize) -> Vec { + /// Left-rotates an array `k` times if `b=1` else return the same array. + pub fn rotate_fixed(&mut self, b: Target, k: usize, v: Vec, len: usize) -> Vec { debug_assert_eq!(v.len(), len); let mut res = Vec::new(); for i in 0..len { - res.push(self.select(b, v[(i + 1) % len], v[i])); + res.push(self.select(b, v[(i + k) % len], v[i])); } res } - pub fn split_unary(&mut self, x: Target, len: usize) -> Vec { - todo!() - } - /// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be /// less than `len`. - pub fn rotate(&mut self, num_rotation: Target, v: Vec, len: usize) -> Vec { - // Decompose `num_rotation` into `len` 1-ary limbs, possibly with a new gate. - // Then apply `rotate_once` `len` time using the 1-ary limbs. - todo!() + /// Note: We assume `len` is less than 8 since we won't use any arity greater than 8 in FRI (maybe?). + pub fn rotate(&mut self, num_rotation: Target, mut v: Vec, len: usize) -> Vec { + let bits = self.split_le_base::<2>(num_rotation, 3); + + v = self.rotate_fixed(bits[0], 1, v, len); + v = self.rotate_fixed(bits[1], 2, v, len); + v = self.rotate_fixed(bits[2], 4, v, len); + + v } } From 2cd99ff84c88ea20a23eca6f3ee78b03efb75daf Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 09:35:22 +0200 Subject: [PATCH 024/117] Change `ExtensionTarget::frobenius` to use 4 constants instead of 1. --- src/field/extension_field/target.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 712c6103..c00470ac 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -19,15 +19,14 @@ impl ExtensionTarget { pub fn frobenius>(&self, builder: &mut CircuitBuilder) -> Self { let arr = self.to_target_array(); let k = (F::ORDER - 1) / (D as u64); - let z0 = builder.constant(F::Extension::W.exp(k)); - let mut z = builder.one(); - let mut res = [builder.zero(); D]; - for i in 0..D { - res[i] = builder.mul(arr[i], z); - z = builder.mul(z, z0); + let zs = (0..D as u64).map(|i| builder.constant(F::Extension::W.exp(k * i))); + + let mut res = Vec::with_capacity(D); + for (z, a) in zs.zip(arr) { + res.push(builder.mul(z, a)); } - Self(res) + res.try_into().unwrap() } pub fn from_range(gate: usize, range: Range) -> Self { From ccd31d17184592bc99b3e28be74256928cf1500d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 09:39:14 +0200 Subject: [PATCH 025/117] Remove useless Generator --- src/gadgets/rotate.rs | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index 29507b94..acc78734 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -40,28 +40,3 @@ impl, const D: usize> CircuitBuilder { v } } - -#[derive(Debug)] -struct UnaryBaseGenerator { - integer: Target, - len: usize, - limbs: Vec, -} - -impl SimpleGenerator for UnaryBaseGenerator { - fn dependencies(&self) -> Vec { - vec![self.integer] - } - - fn run_once(&self, witness: &PartialWitness) -> PartialWitness { - let mut integer_value = witness.get_target(self.integer).to_canonical_u64(); - let low = integer_value & ((1 << self.n_log) - 1); - let high = integer_value >> self.n_log; - - let mut result = PartialWitness::new(); - result.set_target(self.low, F::from_canonical_u64(low)); - result.set_target(self.high, F::from_canonical_u64(high)); - - result - } -} From 1eb372326afeb4c9533d050d95bf2f8d1afcccd0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 09:44:19 +0200 Subject: [PATCH 026/117] Add `CircuitConfig::large_config()` for tests. --- src/circuit_data.rs | 19 ++++++++++++++++++- src/gadgets/arithmetic.rs | 15 +-------------- src/gadgets/split_base.rs | 15 +-------------- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4d9a7110..466779da 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -7,7 +7,7 @@ use crate::gates::gate::GateRef; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; -use crate::prover::prove; +use crate::prover::{prove, PLONK_BLINDING}; use crate::verifier::verify; use crate::witness::PartialWitness; @@ -48,6 +48,23 @@ impl CircuitConfig { pub fn num_advice_wires(&self) -> usize { self.num_wires - self.num_routed_wires } + + pub fn large_config() -> Self { + Self { + num_wires: 134, + num_routed_wires: 12, + security_bits: 128, + rate_bits: 3, + num_challenges: 3, + fri_config: FriConfig { + proof_of_work_bits: 1, + rate_bits: 3, + reduction_arity_bits: vec![1], + num_query_rounds: 1, + blinding: PLONK_BLINDING.to_vec(), + }, + } + } } /// Circuit data required by the prover or the verifier. diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 2ea7c882..9c35d251 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -366,20 +366,7 @@ mod tests { type FF = QuarticCrandallField; const D: usize = 4; - let config = CircuitConfig { - num_wires: 134, - num_routed_wires: 12, - security_bits: 128, - rate_bits: 0, - num_challenges: 3, - fri_config: FriConfig { - proof_of_work_bits: 1, - rate_bits: 0, - reduction_arity_bits: vec![1], - num_query_rounds: 1, - blinding: PLONK_BLINDING.to_vec(), - }, - }; + let config = CircuitConfig::large_config(); let mut builder = CircuitBuilder::::new(config); diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 0b1d61e8..94d3c3bc 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -79,20 +79,7 @@ mod tests { #[test] fn test_split_base() { type F = CrandallField; - let config = CircuitConfig { - num_wires: 134, - num_routed_wires: 12, - security_bits: 128, - rate_bits: 3, - num_challenges: 3, - fri_config: FriConfig { - proof_of_work_bits: 1, - rate_bits: 3, - reduction_arity_bits: vec![1], - num_query_rounds: 1, - blinding: PLONK_BLINDING.to_vec(), - }, - }; + let config = CircuitConfig::large_config(); let mut builder = CircuitBuilder::::new(config); let x = F::from_canonical_usize(0b110100000); // 416 = 1532 in base 6. let xt = builder.constant(x); From 4106a47dedd4f4faa20e81a3749223dccb6cf471 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 09:59:55 +0200 Subject: [PATCH 027/117] Test `rotate` gadget --- src/field/extension_field/target.rs | 6 ++-- src/gadgets/rotate.rs | 54 +++++++++++++++++++++++++---- src/gadgets/split_base.rs | 4 --- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index c00470ac..7efd0f3b 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -19,10 +19,12 @@ impl ExtensionTarget { pub fn frobenius>(&self, builder: &mut CircuitBuilder) -> Self { let arr = self.to_target_array(); let k = (F::ORDER - 1) / (D as u64); - let zs = (0..D as u64).map(|i| builder.constant(F::Extension::W.exp(k * i))); + let zs = (0..D as u64) + .map(|i| builder.constant(F::Extension::W.exp(k * i))) + .collect::>(); let mut res = Vec::with_capacity(D); - for (z, a) in zs.zip(arr) { + for (z, a) in zs.into_iter().zip(arr) { res.push(builder.mul(z, a)); } diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index acc78734..19230dc0 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -16,8 +16,7 @@ impl, const D: usize> CircuitBuilder { } /// Left-rotates an array `k` times if `b=1` else return the same array. - pub fn rotate_fixed(&mut self, b: Target, k: usize, v: Vec, len: usize) -> Vec { - debug_assert_eq!(v.len(), len); + pub fn rotate_fixed(&mut self, b: Target, k: usize, v: &[Target], len: usize) -> Vec { let mut res = Vec::new(); for i in 0..len { @@ -30,13 +29,56 @@ impl, const D: usize> CircuitBuilder { /// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be /// less than `len`. /// Note: We assume `len` is less than 8 since we won't use any arity greater than 8 in FRI (maybe?). - pub fn rotate(&mut self, num_rotation: Target, mut v: Vec, len: usize) -> Vec { + pub fn rotate(&mut self, num_rotation: Target, v: &[Target], len: usize) -> Vec { + debug_assert_eq!(v.len(), len); let bits = self.split_le_base::<2>(num_rotation, 3); - v = self.rotate_fixed(bits[0], 1, v, len); - v = self.rotate_fixed(bits[1], 2, v, len); - v = self.rotate_fixed(bits[2], 4, v, len); + let v = self.rotate_fixed(bits[0], 1, v, len); + let v = self.rotate_fixed(bits[1], 2, &v, len); + let v = self.rotate_fixed(bits[2], 4, &v, len); v } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::circuit_data::CircuitConfig; + use crate::field::crandall_field::CrandallField; + + fn real_rotate(num_rotation: usize, v: &[Target]) -> Vec { + let mut res = v.to_vec(); + res.rotate_left(num_rotation); + res + } + + fn test_rotate_given_len(len: usize) { + type F = CrandallField; + let config = CircuitConfig::large_config(); + let mut builder = CircuitBuilder::::new(config); + let v = (0..len) + .map(|_| builder.constant(F::rand())) + .collect::>(); // 416 = 1532 in base 6. + + for i in 0..len { + let it = builder.constant(F::from_canonical_usize(i)); + let rotated = real_rotate(i, &v); + let purported_rotated = builder.rotate(it, &v, len); + + for (x, y) in rotated.into_iter().zip(purported_rotated) { + builder.assert_equal(x, y); + } + } + + let data = builder.build(); + let proof = data.prove(PartialWitness::new()); + } + + #[test] + fn test_rotate() { + for i_log in 1..4 { + test_rotate_given_len(1 << i_log); + } + } +} diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 94d3c3bc..f10de655 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -71,10 +71,6 @@ mod tests { use super::*; use crate::circuit_data::CircuitConfig; use crate::field::crandall_field::CrandallField; - use crate::fri::FriConfig; - use crate::prover::PLONK_BLINDING; - use crate::verifier::verify; - use anyhow::Result; #[test] fn test_split_base() { From 1ebeab2c3a1cf83e3d8d1075dca4e3c65666585f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 10:27:03 +0200 Subject: [PATCH 028/117] Implement Frobenius optimization discussed in #61 comments to avoid calling the Frobenius for every wires. --- src/field/extension_field/mod.rs | 8 ++++++++ src/field/extension_field/target.rs | 9 +++++++++ src/field/field.rs | 16 ++++++++++++++++ src/fri/verifier.rs | 9 ++++----- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index d706a341..525d8ecc 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -28,6 +28,14 @@ pub trait OEF: FieldExtension { Self::from_basefield_array(res) } + + /// Repeated Frobenius automorphisms: x -> x^(p^k). + // TODO: Implement this. Is basically the same as `frobenius` above, but using + // `z = W^floor(j*p^k/D)`. I'm not sure there is a closed form for these so + // might require to hardcode them. + fn repeated_frobenius(&self, k: usize) -> Self { + todo!() + } } impl OEF<1> for F { diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 7efd0f3b..aa5bce7f 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -31,6 +31,15 @@ impl ExtensionTarget { res.try_into().unwrap() } + // TODO: Implement this. See comment in `OEF::repeated_frobenius`. + fn repeated_frobenius>( + &self, + k: usize, + builder: &mut CircuitBuilder, + ) -> Self { + todo!() + } + pub fn from_range(gate: usize, range: Range) -> Self { debug_assert_eq!(range.end - range.start, D); Target::wires_from_range(gate, range).try_into().unwrap() diff --git a/src/field/field.rs b/src/field/field.rs index 3c6f0e47..44a20a58 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -7,6 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi use num::Integer; use rand::Rng; +use crate::field::extension_field::OEF; use crate::util::bits_u64; /// A finite field with prime order less than 2^64. @@ -283,3 +284,18 @@ impl Iterator for Powers { Some(result) } } + +impl Powers { + /// Apply the Frobenius automorphism `k` times. + // TODO: Use `OEF::repeated_frobenius` when it is implemented. + pub fn repeated_frobenius(self, k: usize) -> Self + where + F: OEF, + { + let Self { base, current } = self; + Self { + base: (0..k).fold(base, |acc, _| acc.frobenius()), + current: (0..k).fold(current, |acc, _| acc.frobenius()), + } + } +} diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 6275ada6..27fdf53e 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -199,11 +199,10 @@ fn fri_combine_initial, const D: usize>( .map(|(&e, a)| a * e.into()) .sum(); let zeta_frob = zeta.frobenius(); - let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); - let wires_interpol = interpolant(&[ - (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), - (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), - ]); + let ev_zeta = reduce_with_iter(&os.wires, alpha_powers.clone()); + let mut alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1); + let ev_zeta_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius(); + let wires_interpol = interpolant(&[(zeta, ev_zeta), (zeta_frob, ev_zeta_frob)]); let numerator = ev - wires_interpol.eval(subgroup_x); let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); sum += numerator / denominator; From 20741cfb4a83869723d2e1ce03b28022f08e260e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 13:45:51 +0200 Subject: [PATCH 029/117] Implement out-of-circuit `repeated_frobenius` using hardcoded constants --- src/field/crandall_field.rs | 13 ++++++- src/field/extension_field/mod.rs | 53 +++++++++++++++++++------- src/field/extension_field/quadratic.rs | 10 +++-- src/field/extension_field/quartic.rs | 17 ++++++--- src/field/field.rs | 11 +++--- src/fri/verifier.rs | 2 +- src/polynomial/commitment.rs | 2 +- 7 files changed, 76 insertions(+), 32 deletions(-) diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 46dcb172..34d67b65 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -6,7 +6,7 @@ use num::Integer; use crate::field::extension_field::quadratic::QuadraticCrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; -use crate::field::extension_field::Extendable; +use crate::field::extension_field::{Extendable, Frobeniable}; use crate::field::field::Field; use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; @@ -444,6 +444,17 @@ fn split(x: u128) -> (u64, u64) { (x as u64, (x >> 64) as u64) } +impl Frobeniable for CrandallField { + const FROBENIUS_CONSTANTS_2: [u64; 1] = [9223372035646816256]; + /// Placeholder since there's no OEF of degree 3 for `CrandallField`. + const FROBENIUS_CONSTANTS_3: [u64; 2] = [0, 0]; + const FROBENIUS_CONSTANTS_4: [u64; 3] = [ + 4611686017823408128, + 9223372035646816256, + 13835058053470224384, + ]; +} + #[cfg(test)] mod tests { use crate::test_arithmetic; diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 525d8ecc..d9ae3387 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -13,12 +13,32 @@ pub mod target; pub trait OEF: FieldExtension { // Element W of BaseField, such that `X^d - W` is irreducible over BaseField. const W: Self::BaseField; +} - /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. +impl OEF<1> for F { + const W: Self::BaseField = F::ZERO; +} + +pub trait Frobenius: OEF { + /// FrobeniusField automorphisms: x -> x^p, where p is the order of BaseField. fn frobenius(&self) -> Self { + self.repeated_frobenius(1) + } + + /// Repeated Frobenius automorphisms: x -> x^(p^k). + fn repeated_frobenius(&self, k: usize) -> Self { + if k == 0 { + return *self; + } else if k >= D { + return self.repeated_frobenius(k % D); + } let arr = self.to_basefield_array(); - let k = (Self::BaseField::ORDER - 1) / (D as u64); - let z0 = Self::W.exp(k); + let z0 = match D { + 2 => Self::W.exp(BF::FROBENIUS_CONSTANTS_2[k - 1]), + 3 => Self::W.exp(BF::FROBENIUS_CONSTANTS_3[k - 1]), + 4 => Self::W.exp(BF::FROBENIUS_CONSTANTS_4[k - 1]), + _ => unimplemented!("Only extensions of degree 2, 3, or 4 are allowed for now."), + }; let mut z = Self::BaseField::ONE; let mut res = [Self::BaseField::ZERO; D]; for i in 0..D { @@ -28,25 +48,30 @@ pub trait OEF: FieldExtension { Self::from_basefield_array(res) } +} - /// Repeated Frobenius automorphisms: x -> x^(p^k). - // TODO: Implement this. Is basically the same as `frobenius` above, but using - // `z = W^floor(j*p^k/D)`. I'm not sure there is a closed form for these so - // might require to hardcode them. - fn repeated_frobenius(&self, k: usize) -> Self { - todo!() +impl Frobenius for F { + fn frobenius(&self) -> Self { + *self + } + fn repeated_frobenius(&self, _k: usize) -> Self { + *self } } -impl OEF<1> for F { - const W: Self::BaseField = F::ZERO; +/// Trait to hardcode constants used in the Frobenius automorphism. +pub trait Frobeniable: Field { + //! `FROBENIUS_CONSTANTS_D[i-1] = floor( p^i / D) mod p-1` + const FROBENIUS_CONSTANTS_2: [u64; 1]; + const FROBENIUS_CONSTANTS_3: [u64; 2]; + const FROBENIUS_CONSTANTS_4: [u64; 3]; } -pub trait Extendable: Field + Sized { - type Extension: Field + OEF + From; +pub trait Extendable: Frobeniable + Sized { + type Extension: Field + OEF + Frobenius + From; } -impl Extendable<1> for F { +impl> Extendable<1> for F { type Extension = F; } diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index 27fc33a1..f0fd2ac9 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -1,5 +1,5 @@ use crate::field::crandall_field::CrandallField; -use crate::field::extension_field::{FieldExtension, OEF}; +use crate::field::extension_field::{FieldExtension, Frobenius, OEF}; use crate::field::field::Field; use rand::Rng; use std::fmt::{Debug, Display, Formatter}; @@ -16,6 +16,8 @@ impl OEF<2> for QuadraticCrandallField { const W: CrandallField = CrandallField(3); } +impl Frobenius for QuadraticCrandallField {} + impl FieldExtension<2> for QuadraticCrandallField { type BaseField = CrandallField; @@ -63,7 +65,7 @@ impl Field for QuadraticCrandallField { return None; } - let a_pow_r_minus_1 = OEF::<2>::frobenius(self); + let a_pow_r_minus_1 = self.frobenius(); let a_pow_r = a_pow_r_minus_1 * *self; debug_assert!(FieldExtension::<2>::is_in_basefield(&a_pow_r)); @@ -190,7 +192,7 @@ impl DivAssign for QuadraticCrandallField { #[cfg(test)] mod tests { use crate::field::extension_field::quadratic::QuadraticCrandallField; - use crate::field::extension_field::{FieldExtension, OEF}; + use crate::field::extension_field::{FieldExtension, Frobenius, OEF}; use crate::field::field::Field; #[test] @@ -231,7 +233,7 @@ mod tests { let x = F::rand(); assert_eq!( x.exp(>::BaseField::ORDER), - OEF::<2>::frobenius(&x) + x.frobenius() ); } diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 2acac183..f0e8bed4 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -6,7 +6,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi use rand::Rng; use crate::field::crandall_field::CrandallField; -use crate::field::extension_field::{FieldExtension, OEF}; +use crate::field::extension_field::{FieldExtension, Frobenius, OEF}; use crate::field::field::Field; /// A quartic extension of `CrandallField`. @@ -20,6 +20,8 @@ impl OEF<4> for QuarticCrandallField { const W: CrandallField = CrandallField(3); } +impl Frobenius for QuarticCrandallField {} + impl FieldExtension<4> for QuarticCrandallField { type BaseField = CrandallField; @@ -93,9 +95,9 @@ impl Field for QuarticCrandallField { return None; } - let a_pow_p = OEF::<4>::frobenius(self); + let a_pow_p = self.frobenius(); let a_pow_p_plus_1 = a_pow_p * *self; - let a_pow_p3_plus_p2 = OEF::<4>::frobenius(&OEF::<4>::frobenius(&a_pow_p_plus_1)); + let a_pow_p3_plus_p2 = a_pow_p_plus_1.repeated_frobenius(2); let a_pow_r_minus_1 = a_pow_p3_plus_p2 * a_pow_p; let a_pow_r = a_pow_r_minus_1 * *self; debug_assert!(FieldExtension::<4>::is_in_basefield(&a_pow_r)); @@ -241,7 +243,7 @@ impl DivAssign for QuarticCrandallField { #[cfg(test)] mod tests { use crate::field::extension_field::quartic::QuarticCrandallField; - use crate::field::extension_field::{FieldExtension, OEF}; + use crate::field::extension_field::{FieldExtension, Frobenius, OEF}; use crate::field::field::Field; fn exp_naive(x: F, power: u128) -> F { @@ -295,7 +297,12 @@ mod tests { let x = F::rand(); assert_eq!( exp_naive(x, >::BaseField::ORDER as u128), - OEF::<4>::frobenius(&x) + x.frobenius() + ); + assert_eq!(x.repeated_frobenius(2), x.frobenius().frobenius()); + assert_eq!( + x.repeated_frobenius(3), + x.frobenius().frobenius().frobenius() ); } diff --git a/src/field/field.rs b/src/field/field.rs index 44a20a58..8a652a0a 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi use num::Integer; use rand::Rng; -use crate::field::extension_field::OEF; +use crate::field::extension_field::{Extendable, FieldExtension, Frobeniable, Frobenius, OEF}; use crate::util::bits_u64; /// A finite field with prime order less than 2^64. @@ -287,15 +287,14 @@ impl Iterator for Powers { impl Powers { /// Apply the Frobenius automorphism `k` times. - // TODO: Use `OEF::repeated_frobenius` when it is implemented. - pub fn repeated_frobenius(self, k: usize) -> Self + pub fn repeated_frobenius(self, k: usize) -> Self where - F: OEF, + F: Frobenius, { let Self { base, current } = self; Self { - base: (0..k).fold(base, |acc, _| acc.frobenius()), - current: (0..k).fold(current, |acc, _| acc.frobenius()), + base: base.repeated_frobenius(k), + current: base.repeated_frobenius(k), } } } diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 27fdf53e..a3f3864a 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -1,7 +1,7 @@ use anyhow::{ensure, Result}; use itertools::izip; -use crate::field::extension_field::{flatten, Extendable, FieldExtension, OEF}; +use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius, OEF}; use crate::field::field::Field; use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; use crate::fri::FriConfig; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 22fc4114..3607d794 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -1,8 +1,8 @@ use anyhow::Result; use rayon::prelude::*; -use crate::field::extension_field::FieldExtension; use crate::field::extension_field::{Extendable, OEF}; +use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; use crate::field::lagrange::interpolant; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; From bc7f67c3162152cf1c0556bbc4d1f957ce16eca3 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 13:58:05 +0200 Subject: [PATCH 030/117] In-circuit repeated frobenius --- src/field/extension_field/target.rs | 21 ++++++++++++++++++++- src/field/field.rs | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index aa5bce7f..2123079d 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -4,6 +4,7 @@ use crate::field::extension_field::{Extendable, FieldExtension, OEF}; use crate::field::field::Field; use crate::gates::mul_extension::MulExtensionGate; use crate::target::Target; +use num::traits::real::Real; use std::convert::{TryFrom, TryInto}; use std::ops::Range; @@ -37,7 +38,25 @@ impl ExtensionTarget { k: usize, builder: &mut CircuitBuilder, ) -> Self { - todo!() + let arr = self.to_target_array(); + let z0 = match D { + 2 => F::Extension::W.exp(F::FROBENIUS_CONSTANTS_2[k - 1]), + 3 => F::Extension::W.exp(F::FROBENIUS_CONSTANTS_3[k - 1]), + 4 => F::Extension::W.exp(F::FROBENIUS_CONSTANTS_4[k - 1]), + _ => unimplemented!("Only extensions of degree 2, 3, or 4 are allowed for now."), + }; + let zs = z0 + .powers() + .take(D) + .map(|z| builder.constant(z)) + .collect::>(); + + let mut res = Vec::with_capacity(D); + for (z, a) in zs.into_iter().zip(arr) { + res.push(builder.mul(z, a)); + } + + res.try_into().unwrap() } pub fn from_range(gate: usize, range: Range) -> Self { diff --git a/src/field/field.rs b/src/field/field.rs index 8a652a0a..88c12dae 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -294,7 +294,7 @@ impl Powers { let Self { base, current } = self; Self { base: base.repeated_frobenius(k), - current: base.repeated_frobenius(k), + current: current.repeated_frobenius(k), } } } From 4b1f368e890df5165ee77fbcd7a50accb26f6197 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 14:16:40 +0200 Subject: [PATCH 031/117] Use Frobenius optimization in the circuit --- src/field/extension_field/target.rs | 3 +-- src/fri/recursive_verifier.rs | 26 ++++++++++++-------------- src/fri/verifier.rs | 6 +++--- src/gadgets/arithmetic.rs | 12 ++++++++++++ 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 2123079d..2e743098 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -32,8 +32,7 @@ impl ExtensionTarget { res.try_into().unwrap() } - // TODO: Implement this. See comment in `OEF::repeated_frobenius`. - fn repeated_frobenius>( + pub fn repeated_frobenius>( &self, k: usize, builder: &mut CircuitBuilder, diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 4f8c3864..3a4332cf 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -225,22 +225,20 @@ impl, const D: usize> CircuitBuilder { ev = self.mul_add_extension(a, e, ev); } let zeta_frob = zeta.frobenius(self); - let wire_evals_frob = os + let wire_eval = os.wires.iter().fold(self.zero_extension(), |acc, &w| { + let a = alpha_powers.next(self); + self.mul_add_extension(a, w, acc) + }); + let mut alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1, self); + let wire_eval_frob = os .wires .iter() - .map(|e| e.frobenius(self)) - .collect::>(); - let mut ev_zeta = self.zero_extension(); - for &t in &os.wires { - let a = alpha_powers.next(self); - ev_zeta = self.mul_add_extension(a, t, ev_zeta); - } - let mut ev_zeta_frob = self.zero_extension(); - for &t in &wire_evals_frob { - let a = alpha_powers.next(self); - ev_zeta_right = self.mul_add_extension(a, t, ev_zeta); - } - let wires_interpol = self.interpolate2([(zeta, ev_zeta), (zeta_frob, ev_zeta_frob)]); + .fold(self.zero_extension(), |acc, &w| { + let a = alpha_powers_frob.next(self); + self.mul_add_extension(a, w, acc) + }) + .frobenius(self); + let wires_interpol = self.interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); let interpol_val = wires_interpol.eval(self, subgroup_x); let numerator = self.sub_extension(ev, interpol_val); let vanish_frob = self.sub_extension(subgroup_x, zeta_frob); diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index a3f3864a..104deaa3 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -199,10 +199,10 @@ fn fri_combine_initial, const D: usize>( .map(|(&e, a)| a * e.into()) .sum(); let zeta_frob = zeta.frobenius(); - let ev_zeta = reduce_with_iter(&os.wires, alpha_powers.clone()); + let wire_eval = reduce_with_iter(&os.wires, alpha_powers.clone()); let mut alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1); - let ev_zeta_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius(); - let wires_interpol = interpolant(&[(zeta, ev_zeta), (zeta_frob, ev_zeta_frob)]); + let wire_eval_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius(); + let wires_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); let numerator = ev - wires_interpol.eval(subgroup_x); let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); sum += numerator / denominator; diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 9c35d251..c6c3b2b7 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -338,6 +338,18 @@ impl PowersTarget { self.current = builder.mul_extension(self.base, self.current); result } + + pub fn repeated_frobenius>( + self, + k: usize, + builder: &mut CircuitBuilder, + ) -> Self { + let Self { base, current } = self; + Self { + base: base.repeated_frobenius(k, builder), + current: current.repeated_frobenius(k, builder), + } + } } impl, const D: usize> CircuitBuilder { From 5200d70cf0832d251962e0e0c967a04183052d63 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 16:22:29 +0200 Subject: [PATCH 032/117] Add interpolation gadgets --- src/field/extension_field/target.rs | 12 ++++ src/fri/recursive_verifier.rs | 48 ++++++++------- src/gadgets/arithmetic.rs | 2 +- src/gadgets/interpolation.rs | 96 ++++++++++++++++++++++++++++- src/gadgets/rotate.rs | 76 ++++++++++++++++++----- src/gadgets/split_join.rs | 9 +-- src/gates/interpolation.rs | 4 +- src/gates/mod.rs | 2 +- 8 files changed, 199 insertions(+), 50 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 2e743098..5a3cd403 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -240,6 +240,18 @@ impl, const D: usize> CircuitBuilder { self.add_extension(product, c) } + /// Like `mul_sub`, but for `ExtensionTarget`s. Note that, unlike `mul_sub`, this has no + /// performance benefit over separate muls and subs. + pub fn scalar_mul_sub_extension( + &mut self, + a: Target, + b: ExtensionTarget, + c: ExtensionTarget, + ) -> ExtensionTarget { + let product = self.scalar_mul_ext(a, b); + self.sub_extension(product, c) + } + /// Returns `a * b`, where `b` is in the extension field and `a` is in the base field. pub fn scalar_mul_ext(&mut self, a: Target, mut b: ExtensionTarget) -> ExtensionTarget { for i in 0..D { diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 3a4332cf..9eb1a1c0 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -29,24 +29,28 @@ impl, const D: usize> CircuitBuilder { last_evals: &[ExtensionTarget], beta: ExtensionTarget, ) -> ExtensionTarget { - todo!() - // debug_assert_eq!(last_evals.len(), 1 << arity_bits); - // - // let g = F::primitive_root_of_unity(arity_bits); - // - // // The evaluation vector needs to be reordered first. - // let mut evals = last_evals.to_vec(); - // reverse_index_bits_in_place(&mut evals); - // evals.rotate_left(reverse_bits(old_x_index, arity_bits)); - // - // // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. - // let points = g - // .powers() - // .zip(evals) - // .map(|(y, e)| ((x * y).into(), e)) - // .collect::>(); - // let barycentric_weights = barycentric_weights(&points); - // interpolate(&points, beta, &barycentric_weights) + debug_assert_eq!(last_evals.len(), 1 << arity_bits); + + let g = F::primitive_root_of_unity(arity_bits); + + // The evaluation vector needs to be reordered first. + let mut evals = last_evals.to_vec(); + reverse_index_bits_in_place(&mut evals); + let mut old_x_index_bits = self.split_le(old_x_index, arity_bits); + old_x_index_bits.reverse(); + self.rotate_left_from_bits(&old_x_index_bits, &evals, arity_bits); + + // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. + let points = g + .powers() + .zip(evals) + .map(|(y, e)| { + let yt = self.constant(y); + (self.mul(x, yt), e) + }) + .collect::>(); + + self.interpolate(&points, beta) } fn fri_verify_proof_of_work( @@ -205,8 +209,8 @@ impl, const D: usize> CircuitBuilder { let a = alpha_powers.next(self); ev_zeta_right = self.mul_add_extension(a, t, ev_zeta); } - let zs_interpol = self.interpolate2([(zeta, ev_zeta), (zeta_right, ev_zeta_right)]); - let interpol_val = zs_interpol.eval(self, subgroup_x); + let interpol_val = + self.interpolate2([(zeta, ev_zeta), (zeta_right, ev_zeta_right)], subgroup_x); let numerator = self.sub_extension(ev, interpol_val); let vanish = self.sub_extension(subgroup_x, zeta); let vanish_right = self.sub_extension(subgroup_x, zeta_right); @@ -238,8 +242,8 @@ impl, const D: usize> CircuitBuilder { self.mul_add_extension(a, w, acc) }) .frobenius(self); - let wires_interpol = self.interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); - let interpol_val = wires_interpol.eval(self, subgroup_x); + let interpol_val = + self.interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)], subgroup_x); let numerator = self.sub_extension(ev, interpol_val); let vanish_frob = self.sub_extension(subgroup_x, zeta_frob); let denominator = self.mul_extension(vanish, vanish_frob); diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index c6c3b2b7..8bf3a797 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -177,7 +177,7 @@ impl, const D: usize> CircuitBuilder { let mut current = base; let one = self.one(); let mut product = one; - let exponent_bits = self.split_le(exponent); + let exponent_bits = self.split_le(exponent, 64); for bit in exponent_bits.into_iter() { product = self.mul_many(&[bit, current, product]); diff --git a/src/gadgets/interpolation.rs b/src/gadgets/interpolation.rs index 6d44cd76..e867620c 100644 --- a/src/gadgets/interpolation.rs +++ b/src/gadgets/interpolation.rs @@ -2,14 +2,104 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; +use crate::gates::interpolation::InterpolationGate; +use crate::target::Target; +use std::marker::PhantomData; impl, const D: usize> CircuitBuilder { /// Interpolate two points. No need for an `InterpolationGate` since the coefficients /// of the linear interpolation polynomial can be easily computed with arithmetic operations. pub fn interpolate2( &mut self, - points: [(ExtensionTarget, ExtensionTarget); 2], - ) -> PolynomialCoeffsExtTarget { - todo!() + interpolation_points: [(ExtensionTarget, ExtensionTarget); 2], + evaluation_point: ExtensionTarget, + ) -> ExtensionTarget { + // a0 -> a1 + // b0 -> b1 + // x -> a1 + (x-a0)*(b1-a1)/(b0-a0) + + let x_m_a0 = self.sub_extension(evaluation_point, interpolation_points[0].0); + let b1_m_a1 = self.sub_extension(interpolation_points[1].1, interpolation_points[0].1); + let b0_m_a0 = self.sub_extension(interpolation_points[1].0, interpolation_points[0].0); + let quotient = self.div_unsafe_extension(b1_m_a1, b0_m_a0); + + self.mul_add_extension(x_m_a0, quotient, interpolation_points[0].1) + } + + /// Interpolate a list of point/evaluation pairs at a given point. + /// Returns the evaluation of the interpolated polynomial at `evaluation_point`. + pub fn interpolate( + &mut self, + interpolation_points: &[(Target, ExtensionTarget)], + evaluation_point: ExtensionTarget, + ) -> ExtensionTarget { + let gate = InterpolationGate:: { + num_points: interpolation_points.len(), + _phantom: PhantomData, + }; + let gate_index = + self.add_gate_no_constants(InterpolationGate::new(interpolation_points.len())); + for (i, &(p, v)) in interpolation_points.iter().enumerate() { + self.route(p, Target::wire(gate_index, gate.wire_point(i))); + self.route_extension( + v, + ExtensionTarget::from_range(gate_index, gate.wires_value(i)), + ); + } + self.route_extension( + evaluation_point, + ExtensionTarget::from_range(gate_index, gate.wires_evaluation_point()), + ); + + ExtensionTarget::from_range(gate_index, gate.wires_evaluation_value()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::circuit_data::CircuitConfig; + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::field::extension_field::FieldExtension; + use crate::field::field::Field; + use crate::field::lagrange::{interpolant, interpolate}; + use crate::witness::PartialWitness; + + #[test] + fn test_interpolate() { + type F = CrandallField; + type FF = QuarticCrandallField; + let config = CircuitConfig::large_config(); + let mut builder = CircuitBuilder::::new(config); + + let len = 2; + let points = (0..len) + .map(|_| (F::rand(), FF::rand())) + .collect::>(); + + let homogeneous_points = points + .iter() + .map(|&(a, b)| (>::from_basefield(a), b)) + .collect::>(); + + let true_interpolant = interpolant(&homogeneous_points); + + let z = FF::rand(); + let true_eval = true_interpolant.eval(z); + + let points_target = points + .iter() + .map(|&(p, v)| (builder.constant(p), builder.constant_extension(v))) + .collect::>(); + + let zt = builder.constant_extension(z); + + let eval = builder.interpolate(&points_target, zt); + let true_eval_target = builder.constant_extension(true_eval); + builder.assert_equal_extension(eval, true_eval_target); + + let data = builder.build(); + let proof = data.prove(PartialWitness::new()); } } diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index 19230dc0..7afbdfa3 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -1,4 +1,5 @@ use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::gates::base_sum::BaseSumGate; @@ -10,13 +11,24 @@ impl, const D: usize> CircuitBuilder { /// Selects `x` or `y` based on `b`, which is assumed to be binary. /// In particular, this returns `if b { x } else { y }`. /// Note: This does not range-check `b`. - pub fn select(&mut self, b: Target, x: Target, y: Target) -> Target { - let b_y_minus_y = self.mul_sub(b, y, y); - self.mul_sub(b, x, b_y_minus_y) + pub fn select( + &mut self, + b: Target, + x: ExtensionTarget, + y: ExtensionTarget, + ) -> ExtensionTarget { + let b_y_minus_y = self.scalar_mul_sub_extension(b, y, y); + self.scalar_mul_sub_extension(b, x, b_y_minus_y) } /// Left-rotates an array `k` times if `b=1` else return the same array. - pub fn rotate_fixed(&mut self, b: Target, k: usize, v: &[Target], len: usize) -> Vec { + pub fn rotate_left_fixed( + &mut self, + b: Target, + k: usize, + v: &[ExtensionTarget], + len: usize, + ) -> Vec> { let mut res = Vec::new(); for i in 0..len { @@ -29,16 +41,40 @@ impl, const D: usize> CircuitBuilder { /// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be /// less than `len`. /// Note: We assume `len` is less than 8 since we won't use any arity greater than 8 in FRI (maybe?). - pub fn rotate(&mut self, num_rotation: Target, v: &[Target], len: usize) -> Vec { + pub fn rotate_left_from_bits( + &mut self, + num_rotation_bits: &[Target], + v: &[ExtensionTarget], + len_log: usize, + ) -> Vec> { + debug_assert_eq!(num_rotation_bits.len(), len_log); + let len = 1 << len_log; debug_assert_eq!(v.len(), len); - let bits = self.split_le_base::<2>(num_rotation, 3); + let mut v = v.to_vec(); - let v = self.rotate_fixed(bits[0], 1, v, len); - let v = self.rotate_fixed(bits[1], 2, &v, len); - let v = self.rotate_fixed(bits[2], 4, &v, len); + for i in 0..len_log { + v = self.rotate_left_fixed(num_rotation_bits[i], 1 << i, &v, len); + } v } + + /// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be + /// less than `len`. + /// Note: We assume `len` is a power of two less than or equal to 8, since we won't use any + /// arity greater than 8 in FRI (maybe?). + pub fn rotate_left( + &mut self, + num_rotation: Target, + v: &[ExtensionTarget], + len_log: usize, + ) -> Vec> { + let len = 1 << len_log; + debug_assert_eq!(v.len(), len); + let bits = self.split_le(num_rotation, len_log); + + self.rotate_left_from_bits(&bits, v, len_log) + } } #[cfg(test)] @@ -46,28 +82,34 @@ mod tests { use super::*; use crate::circuit_data::CircuitConfig; use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; - fn real_rotate(num_rotation: usize, v: &[Target]) -> Vec { + fn real_rotate( + num_rotation: usize, + v: &[ExtensionTarget], + ) -> Vec> { let mut res = v.to_vec(); res.rotate_left(num_rotation); res } - fn test_rotate_given_len(len: usize) { + fn test_rotate_given_len(len_log: usize) { type F = CrandallField; + type FF = QuarticCrandallField; + let len = 1 << len_log; let config = CircuitConfig::large_config(); let mut builder = CircuitBuilder::::new(config); let v = (0..len) - .map(|_| builder.constant(F::rand())) - .collect::>(); // 416 = 1532 in base 6. + .map(|_| builder.constant_extension(FF::rand())) + .collect::>(); for i in 0..len { let it = builder.constant(F::from_canonical_usize(i)); let rotated = real_rotate(i, &v); - let purported_rotated = builder.rotate(it, &v, len); + let purported_rotated = builder.rotate_left(it, &v, len_log); for (x, y) in rotated.into_iter().zip(purported_rotated) { - builder.assert_equal(x, y); + builder.assert_equal_extension(x, y); } } @@ -77,8 +119,8 @@ mod tests { #[test] fn test_rotate() { - for i_log in 1..4 { - test_rotate_given_len(1 << i_log); + for len_log in 1..4 { + test_rotate_given_len(len_log); } } } diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index f4118d4e..647b0ef5 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -27,21 +27,22 @@ impl, const D: usize> CircuitBuilder { /// Split the given integer into a list of wires, where each one represents a /// bit of the integer, with little-endian ordering. /// Verifies that the decomposition is correct by using `k` `BaseSum<2>` gates - /// with `k` such that `k*num_routed_bits>=64`. - pub(crate) fn split_le(&mut self, integer: Target) -> Vec { + /// with `k` such that `k*num_routed_wires>=num_bits`. + pub(crate) fn split_le(&mut self, integer: Target, num_bits: usize) -> Vec { let num_limbs = self.config.num_routed_wires - BaseSumGate::<2>::WIRE_LIMBS_START; - let k = ceil_div_usize(64, num_limbs); + let k = ceil_div_usize(num_bits, num_limbs); let gates = (0..k) .map(|_| self.add_gate_no_constants(BaseSumGate::<2>::new(num_limbs))) .collect::>(); - let mut bits = Vec::with_capacity(64); + let mut bits = Vec::with_capacity(num_bits); for &gate in &gates { bits.extend(Target::wires_from_range( gate, BaseSumGate::<2>::WIRE_LIMBS_START..BaseSumGate::<2>::WIRE_LIMBS_START + num_limbs, )); } + bits.drain(num_bits..); let zero = self.zero(); let mut acc = zero; diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index b3fd35d0..8cea3328 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -22,8 +22,8 @@ use crate::witness::PartialWitness; /// given point. #[derive(Clone, Debug)] pub(crate) struct InterpolationGate, const D: usize> { - num_points: usize, - _phantom: PhantomData, + pub num_points: usize, + pub _phantom: PhantomData, } impl, const D: usize> InterpolationGate { diff --git a/src/gates/mod.rs b/src/gates/mod.rs index 3ac7bd74..d013c7cd 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -3,7 +3,7 @@ pub mod base_sum; pub mod constant; pub(crate) mod gate; pub mod gmimc; -mod interpolation; +pub mod interpolation; pub mod mul_extension; pub(crate) mod noop; From 133c75d68e8d880ef03d8ea532d7b7fce2843419 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 11 Jun 2021 18:05:59 +0200 Subject: [PATCH 033/117] Interpolation gadgets tests --- src/gadgets/interpolation.rs | 38 +++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/gadgets/interpolation.rs b/src/gadgets/interpolation.rs index e867620c..7705025d 100644 --- a/src/gadgets/interpolation.rs +++ b/src/gadgets/interpolation.rs @@ -65,12 +65,16 @@ mod tests { use crate::field::field::Field; use crate::field::lagrange::{interpolant, interpolate}; use crate::witness::PartialWitness; + use std::convert::TryInto; #[test] fn test_interpolate() { type F = CrandallField; type FF = QuarticCrandallField; - let config = CircuitConfig::large_config(); + let config = CircuitConfig { + num_routed_wires: 18, + ..CircuitConfig::large_config() + }; let mut builder = CircuitBuilder::::new(config); let len = 2; @@ -102,4 +106,36 @@ mod tests { let data = builder.build(); let proof = data.prove(PartialWitness::new()); } + + #[test] + fn test_interpolate2() { + type F = CrandallField; + type FF = QuarticCrandallField; + let config = CircuitConfig::large_config(); + let mut builder = CircuitBuilder::::new(config); + + let len = 2; + let points = (0..len) + .map(|_| (FF::rand(), FF::rand())) + .collect::>(); + + let true_interpolant = interpolant(&points); + + let z = FF::rand(); + let true_eval = true_interpolant.eval(z); + + let points_target = points + .iter() + .map(|&(p, v)| (builder.constant_extension(p), builder.constant_extension(v))) + .collect::>(); + + let zt = builder.constant_extension(z); + + let eval = builder.interpolate2(points_target.try_into().unwrap(), zt); + let true_eval_target = builder.constant_extension(true_eval); + builder.assert_equal_extension(eval, true_eval_target); + + let data = builder.build(); + let proof = data.prove(PartialWitness::new()); + } } From 445ea3771da8e791dc3efd1594d04ec73030bf75 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 10:28:15 +0200 Subject: [PATCH 034/117] Remove `Frobeniable trait` --- src/field/crandall_field.rs | 13 ++------ src/field/extension_field/mod.rs | 45 +++++++------------------- src/field/extension_field/quadratic.rs | 2 +- src/field/extension_field/quartic.rs | 16 +++++---- src/field/extension_field/target.rs | 15 +++++---- src/field/field.rs | 6 ++-- src/gadgets/rotate.rs | 2 +- 7 files changed, 35 insertions(+), 64 deletions(-) diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 34d67b65..81a2c9e6 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -6,7 +6,7 @@ use num::Integer; use crate::field::extension_field::quadratic::QuadraticCrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; -use crate::field::extension_field::{Extendable, Frobeniable}; +use crate::field::extension_field::{Extendable, Frobenius}; use crate::field::field::Field; use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; @@ -444,16 +444,7 @@ fn split(x: u128) -> (u64, u64) { (x as u64, (x >> 64) as u64) } -impl Frobeniable for CrandallField { - const FROBENIUS_CONSTANTS_2: [u64; 1] = [9223372035646816256]; - /// Placeholder since there's no OEF of degree 3 for `CrandallField`. - const FROBENIUS_CONSTANTS_3: [u64; 2] = [0, 0]; - const FROBENIUS_CONSTANTS_4: [u64; 3] = [ - 4611686017823408128, - 9223372035646816256, - 13835058053470224384, - ]; -} +impl Frobenius<1> for CrandallField {} #[cfg(test)] mod tests { diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index d9ae3387..9caa7dc8 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -19,59 +19,36 @@ impl OEF<1> for F { const W: Self::BaseField = F::ZERO; } -pub trait Frobenius: OEF { +pub trait Frobenius: OEF { /// FrobeniusField automorphisms: x -> x^p, where p is the order of BaseField. fn frobenius(&self) -> Self { self.repeated_frobenius(1) } /// Repeated Frobenius automorphisms: x -> x^(p^k). - fn repeated_frobenius(&self, k: usize) -> Self { - if k == 0 { + fn repeated_frobenius(&self, count: usize) -> Self { + if count == 0 { return *self; - } else if k >= D { - return self.repeated_frobenius(k % D); + } else if count >= D { + return self.repeated_frobenius(count % D); } let arr = self.to_basefield_array(); - let z0 = match D { - 2 => Self::W.exp(BF::FROBENIUS_CONSTANTS_2[k - 1]), - 3 => Self::W.exp(BF::FROBENIUS_CONSTANTS_3[k - 1]), - 4 => Self::W.exp(BF::FROBENIUS_CONSTANTS_4[k - 1]), - _ => unimplemented!("Only extensions of degree 2, 3, or 4 are allowed for now."), - }; - let mut z = Self::BaseField::ONE; + let k = (Self::BaseField::ORDER - 1) / (D as u64); + let z0 = Self::W.exp(k * count as u64); let mut res = [Self::BaseField::ZERO; D]; - for i in 0..D { + for (i, z) in z0.powers().take(D).enumerate() { res[i] = arr[i] * z; - z *= z0; } Self::from_basefield_array(res) } } -impl Frobenius for F { - fn frobenius(&self) -> Self { - *self - } - fn repeated_frobenius(&self, _k: usize) -> Self { - *self - } +pub trait Extendable: Field + Sized { + type Extension: Field + OEF + Frobenius + From; } -/// Trait to hardcode constants used in the Frobenius automorphism. -pub trait Frobeniable: Field { - //! `FROBENIUS_CONSTANTS_D[i-1] = floor( p^i / D) mod p-1` - const FROBENIUS_CONSTANTS_2: [u64; 1]; - const FROBENIUS_CONSTANTS_3: [u64; 2]; - const FROBENIUS_CONSTANTS_4: [u64; 3]; -} - -pub trait Extendable: Frobeniable + Sized { - type Extension: Field + OEF + Frobenius + From; -} - -impl> Extendable<1> for F { +impl + FieldExtension<1, BaseField = F>> Extendable<1> for F { type Extension = F; } diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index f0fd2ac9..4bf0c08d 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -16,7 +16,7 @@ impl OEF<2> for QuadraticCrandallField { const W: CrandallField = CrandallField(3); } -impl Frobenius for QuadraticCrandallField {} +impl Frobenius<2> for QuadraticCrandallField {} impl FieldExtension<2> for QuadraticCrandallField { type BaseField = CrandallField; diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index f0e8bed4..b93cbb56 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -20,7 +20,7 @@ impl OEF<4> for QuarticCrandallField { const W: CrandallField = CrandallField(3); } -impl Frobenius for QuarticCrandallField {} +impl Frobenius<4> for QuarticCrandallField {} impl FieldExtension<4> for QuarticCrandallField { type BaseField = CrandallField; @@ -294,16 +294,18 @@ mod tests { #[test] fn test_frobenius() { type F = QuarticCrandallField; + const D: usize = 4; let x = F::rand(); assert_eq!( - exp_naive(x, >::BaseField::ORDER as u128), + exp_naive(x, >::BaseField::ORDER as u128), x.frobenius() ); - assert_eq!(x.repeated_frobenius(2), x.frobenius().frobenius()); - assert_eq!( - x.repeated_frobenius(3), - x.frobenius().frobenius().frobenius() - ); + for count in 2..D { + assert_eq!( + x.repeated_frobenius(count), + (0..count).fold(x, |acc, _| acc.frobenius()) + ); + } } #[test] diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 5a3cd403..20bcd382 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -34,16 +34,17 @@ impl ExtensionTarget { pub fn repeated_frobenius>( &self, - k: usize, + count: usize, builder: &mut CircuitBuilder, ) -> Self { + if count == 0 { + return *self; + } else if count >= D { + return self.repeated_frobenius(count % D, builder); + } let arr = self.to_target_array(); - let z0 = match D { - 2 => F::Extension::W.exp(F::FROBENIUS_CONSTANTS_2[k - 1]), - 3 => F::Extension::W.exp(F::FROBENIUS_CONSTANTS_3[k - 1]), - 4 => F::Extension::W.exp(F::FROBENIUS_CONSTANTS_4[k - 1]), - _ => unimplemented!("Only extensions of degree 2, 3, or 4 are allowed for now."), - }; + let k = (F::ORDER - 1) / (D as u64); + let z0 = F::W.exp(k * count as u64); let zs = z0 .powers() .take(D) diff --git a/src/field/field.rs b/src/field/field.rs index 88c12dae..f28dc004 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi use num::Integer; use rand::Rng; -use crate::field::extension_field::{Extendable, FieldExtension, Frobeniable, Frobenius, OEF}; +use crate::field::extension_field::{Extendable, FieldExtension, Frobenius, OEF}; use crate::util::bits_u64; /// A finite field with prime order less than 2^64. @@ -287,9 +287,9 @@ impl Iterator for Powers { impl Powers { /// Apply the Frobenius automorphism `k` times. - pub fn repeated_frobenius(self, k: usize) -> Self + pub fn repeated_frobenius(self, k: usize) -> Self where - F: Frobenius, + F: Frobenius, { let Self { base, current } = self; Self { diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index 7afbdfa3..6e5d6dfb 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -119,7 +119,7 @@ mod tests { #[test] fn test_rotate() { - for len_log in 1..4 { + for len_log in 1..3 { test_rotate_given_len(len_log); } } From 19b47b5251261f2bcc9be39e35d6e800ea1fd6ba Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 10:33:38 +0200 Subject: [PATCH 035/117] Clippy --- src/field/field.rs | 2 +- src/fri/verifier.rs | 4 ++-- src/gadgets/interpolation.rs | 1 - src/gadgets/rotate.rs | 4 ---- src/gadgets/split_base.rs | 8 ++------ src/polynomial/commitment.rs | 2 +- 6 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/field/field.rs b/src/field/field.rs index f28dc004..95d2f6e0 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi use num::Integer; use rand::Rng; -use crate::field::extension_field::{Extendable, FieldExtension, Frobenius, OEF}; +use crate::field::extension_field::Frobenius; use crate::util::bits_u64; /// A finite field with prime order less than 2^64. diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 104deaa3..f3d868ff 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -1,7 +1,7 @@ use anyhow::{ensure, Result}; use itertools::izip; -use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius, OEF}; +use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius}; use crate::field::field::Field; use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; use crate::fri::FriConfig; @@ -200,7 +200,7 @@ fn fri_combine_initial, const D: usize>( .sum(); let zeta_frob = zeta.frobenius(); let wire_eval = reduce_with_iter(&os.wires, alpha_powers.clone()); - let mut alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1); + let alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1); let wire_eval_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius(); let wires_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); let numerator = ev - wires_interpol.eval(subgroup_x); diff --git a/src/gadgets/interpolation.rs b/src/gadgets/interpolation.rs index 7705025d..a94eb582 100644 --- a/src/gadgets/interpolation.rs +++ b/src/gadgets/interpolation.rs @@ -1,7 +1,6 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; -use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; use crate::gates::interpolation::InterpolationGate; use crate::target::Target; use std::marker::PhantomData; diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index 6e5d6dfb..c7b05ebc 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -1,11 +1,7 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; -use crate::field::field::Field; -use crate::gates::base_sum::BaseSumGate; -use crate::generator::SimpleGenerator; use crate::target::Target; -use crate::witness::PartialWitness; impl, const D: usize> CircuitBuilder { /// Selects `x` or `y` based on `b`, which is assumed to be binary. diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index f10de655..c05ddc8e 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -1,12 +1,8 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::Extendable; -use crate::field::field::Field; -use crate::gates::base_sum::{BaseSplitGenerator, BaseSumGate}; -use crate::generator::{SimpleGenerator, WitnessGenerator}; +use crate::gates::base_sum::BaseSumGate; use crate::target::Target; -use crate::util::{ceil_div_usize, log2_strict}; -use crate::wire::Wire; -use crate::witness::PartialWitness; +use crate::util::ceil_div_usize; impl, const D: usize> CircuitBuilder { /// Split the given element into a list of targets, where each one represents a diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 3607d794..fe81f3c7 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -1,7 +1,7 @@ use anyhow::Result; use rayon::prelude::*; -use crate::field::extension_field::{Extendable, OEF}; +use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; use crate::field::lagrange::interpolant; From 88c58c3227d6a2e5cab9d269262dcb260d3a4196 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 10:37:02 +0200 Subject: [PATCH 036/117] Fix imports --- src/gadgets/rotate.rs | 2 ++ src/gadgets/split_base.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index c7b05ebc..b7041510 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -79,6 +79,8 @@ mod tests { use crate::circuit_data::CircuitConfig; use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::field::field::Field; + use crate::witness::PartialWitness; fn real_rotate( num_rotation: usize, diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index c05ddc8e..50a5e579 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -67,6 +67,8 @@ mod tests { use super::*; use crate::circuit_data::CircuitConfig; use crate::field::crandall_field::CrandallField; + use crate::field::field::Field; + use crate::witness::PartialWitness; #[test] fn test_split_base() { From 670e48380a4ed1a1f4360a27cdda1a7e2431fa1f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 13:26:22 +0200 Subject: [PATCH 037/117] Finish recursive verifier --- src/fri/recursive_verifier.rs | 154 +++++++++++++++++----------------- src/gadgets/polynomial.rs | 4 + src/plonk_challenger.rs | 25 +++++- 3 files changed, 100 insertions(+), 83 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 9eb1a1c0..a424dee0 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -58,81 +58,78 @@ impl, const D: usize> CircuitBuilder { proof: &FriProofTarget, challenger: &mut RecursiveChallenger, config: &FriConfig, - ) -> Result<()> { + ) { let mut inputs = challenger.get_hash(self).elements.to_vec(); inputs.push(proof.pow_witness); let hash = self.hash_n_to_m(inputs, 1, false)[0]; - self.assert_trailing_zeros::<64>(hash, config.proof_of_work_bits); - - Ok(()) + self.assert_trailing_zeros::<2>(hash, config.proof_of_work_bits); } - // pub fn verify_fri_proof( - // purported_degree_log: usize, - // // Openings of the PLONK polynomials. - // os: &OpeningSet, - // // Point at which the PLONK polynomials are opened. - // zeta: F::Extension, - // // Scaling factor to combine polynomials. - // alpha: F::Extension, - // initial_merkle_roots: &[Hash], - // proof: &FriProof, - // challenger: &mut Challenger, - // config: &FriConfig, - // ) -> Result<()> { - // let total_arities = config.reduction_arity_bits.iter().sum::(); - // ensure!( - // purported_degree_log - // == log2_strict(proof.final_poly.len()) + total_arities - config.rate_bits, - // "Final polynomial has wrong degree." - // ); - // - // // Size of the LDE domain. - // let n = proof.final_poly.len() << total_arities; - // - // // Recover the random betas used in the FRI reductions. - // let betas = proof - // .commit_phase_merkle_roots - // .iter() - // .map(|root| { - // challenger.observe_hash(root); - // challenger.get_extension_challenge() - // }) - // .collect::>(); - // challenger.observe_extension_elements(&proof.final_poly.coeffs); - // - // // Check PoW. - // fri_verify_proof_of_work(proof, challenger, config)?; - // - // // Check that parameters are coherent. - // ensure!( - // config.num_query_rounds == proof.query_round_proofs.len(), - // "Number of query rounds does not match config." - // ); - // ensure!( - // !config.reduction_arity_bits.is_empty(), - // "Number of reductions should be non-zero." - // ); - // - // for round_proof in &proof.query_round_proofs { - // fri_verifier_query_round( - // os, - // zeta, - // alpha, - // initial_merkle_roots, - // &proof, - // challenger, - // n, - // &betas, - // round_proof, - // config, - // )?; - // } - // - // Ok(()) - // } - // + pub fn verify_fri_proof( + &mut self, + purported_degree_log: usize, + // Openings of the PLONK polynomials. + os: &OpeningSetTarget, + // Point at which the PLONK polynomials are opened. + zeta: ExtensionTarget, + // Scaling factor to combine polynomials. + alpha: ExtensionTarget, + initial_merkle_roots: &[HashTarget], + proof: &FriProofTarget, + challenger: &mut RecursiveChallenger, + config: &FriConfig, + ) { + let total_arities = config.reduction_arity_bits.iter().sum::(); + debug_assert_eq!( + purported_degree_log, + log2_strict(proof.final_poly.len()) + total_arities - config.rate_bits, + "Final polynomial has wrong degree." + ); + + // Size of the LDE domain. + let n = proof.final_poly.len() << total_arities; + + // Recover the random betas used in the FRI reductions. + let betas = proof + .commit_phase_merkle_roots + .iter() + .map(|root| { + challenger.observe_hash(root); + challenger.get_extension_challenge(self) + }) + .collect::>(); + challenger.observe_extension_elements(&proof.final_poly.0); + + // Check PoW. + self.fri_verify_proof_of_work(proof, challenger, config); + + // Check that parameters are coherent. + debug_assert_eq!( + config.num_query_rounds, + proof.query_round_proofs.len(), + "Number of query rounds does not match config." + ); + debug_assert!( + !config.reduction_arity_bits.is_empty(), + "Number of reductions should be non-zero." + ); + + for round_proof in &proof.query_round_proofs { + self.fri_verifier_query_round( + os, + zeta, + alpha, + initial_merkle_roots, + &proof, + challenger, + n, + &betas, + round_proof, + config, + ); + } + } fn fri_verify_initial_proof( &mut self, @@ -265,7 +262,7 @@ impl, const D: usize> CircuitBuilder { betas: &[ExtensionTarget], round_proof: &FriQueryRoundTarget, config: &FriConfig, - ) -> Result<()> { + ) { let n_log = log2_strict(n); let mut evaluations: Vec>> = Vec::new(); // TODO: Do we need to range check `x_index` to a target smaller than `p`? @@ -312,14 +309,15 @@ impl, const D: usize> CircuitBuilder { let mut evals = round_proof.steps[i].evals.clone(); // Insert P(y) into the evaluation vector, since it wasn't included by the prover. let (low_x_index, high_x_index) = self.split_low_high(x_index, arity_bits); + // TODO: Uncomment this. // evals.insert(x_index & (arity - 1), e_x); - // evaluations.push(evals); - // self.verify_merkle_proof( - // flatten_target(&evaluations[i]), - // x_index >> arity_bits, - // proof.commit_phase_merkle_roots[i], - // &round_proof.steps[i].merkle_proof, - // )?; + evaluations.push(evals); + self.verify_merkle_proof( + flatten_target(&evaluations[i]), + high_x_index, + proof.commit_phase_merkle_roots[i], + &round_proof.steps[i].merkle_proof, + ); if i > 0 { // Update the point x to x^arity. @@ -349,7 +347,5 @@ impl, const D: usize> CircuitBuilder { // to the one sent by the prover. let eval = proof.final_poly.eval_scalar(self, subgroup_x); self.assert_equal_extension(eval, purported_eval); - - Ok(()) } } diff --git a/src/gadgets/polynomial.rs b/src/gadgets/polynomial.rs index 543be834..07bc1952 100644 --- a/src/gadgets/polynomial.rs +++ b/src/gadgets/polynomial.rs @@ -6,6 +6,10 @@ use crate::target::Target; pub struct PolynomialCoeffsExtTarget(pub Vec>); impl PolynomialCoeffsExtTarget { + pub fn len(&self) -> usize { + self.0.len() + } + pub fn eval_scalar>( &self, builder: &mut CircuitBuilder, diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index bcb2c8cd..b24bbde8 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -1,9 +1,11 @@ use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH}; use crate::proof::{Hash, HashTarget, OpeningSet}; use crate::target::Target; +use std::convert::TryInto; /// Observes prover messages, and generates challenges by hashing the transcript. #[derive(Clone)] @@ -41,9 +43,7 @@ impl Challenger { where F: Extendable, { - for &e in &element.to_basefield_array() { - self.observe_element(e); - } + self.observe_elements(&element.to_basefield_array()); } pub fn observe_elements(&mut self, elements: &[F]) { @@ -177,7 +177,7 @@ impl Default for Challenger { } /// A recursive version of `Challenger`. -pub(crate) struct RecursiveChallenger { +pub struct RecursiveChallenger { sponge_state: [Target; SPONGE_WIDTH], input_buffer: Vec, output_buffer: Vec, @@ -212,6 +212,16 @@ impl RecursiveChallenger { self.observe_elements(&hash.elements) } + pub fn observe_extension_element(&mut self, element: ExtensionTarget) { + self.observe_elements(&element.0); + } + + pub fn observe_extension_elements(&mut self, elements: &[ExtensionTarget]) { + for &element in elements { + self.observe_extension_element(element); + } + } + pub(crate) fn get_challenge, const D: usize>( &mut self, builder: &mut CircuitBuilder, @@ -269,6 +279,13 @@ impl RecursiveChallenger { } } + pub fn get_extension_challenge, const D: usize>( + &mut self, + builder: &mut CircuitBuilder, + ) -> ExtensionTarget { + self.get_n_challenges(builder, D).try_into().unwrap() + } + /// Absorb any buffered inputs. After calling this, the input buffer will be empty. fn absorb_buffered_inputs, const D: usize>( &mut self, From 6132b2ad7370ff615145b6deb275d708bc8c7579 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 13:30:14 +0200 Subject: [PATCH 038/117] Use `mul_extension` in `scalar_mul_ext` since `mul_extension` now uses a single gate. --- src/field/extension_field/target.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 20bcd382..1691fba2 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -255,10 +255,8 @@ impl, const D: usize> CircuitBuilder { /// Returns `a * b`, where `b` is in the extension field and `a` is in the base field. pub fn scalar_mul_ext(&mut self, a: Target, mut b: ExtensionTarget) -> ExtensionTarget { - for i in 0..D { - b.0[i] = self.mul(a, b.0[i]); - } - b + let a_ext = self.convert_to_ext(a); + self.mul_extension(a_ext, b) } /// Returns `a * b`, where `b` is in the extension of the extension field, and `a` is in the From fa229d9a27ac6a80f2183b438a77f830cb143a6d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 13:41:44 +0200 Subject: [PATCH 039/117] Add comments on possible optimizations --- src/field/extension_field/target.rs | 3 +++ src/gadgets/rotate.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 1691fba2..9d078e15 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -151,6 +151,7 @@ impl, const D: usize> CircuitBuilder { sum } + /// TODO: Change this to using an `arithmetic_extension` function once `MulExtensionGate` supports addend. pub fn sub_extension( &mut self, mut a: ExtensionTarget, @@ -231,6 +232,7 @@ impl, const D: usize> CircuitBuilder { /// Like `mul_add`, but for `ExtensionTarget`s. Note that, unlike `mul_add`, this has no /// performance benefit over separate muls and adds. + /// TODO: Change this to using an `arithmetic_extension` function once `MulExtensionGate` supports addend. pub fn mul_add_extension( &mut self, a: ExtensionTarget, @@ -243,6 +245,7 @@ impl, const D: usize> CircuitBuilder { /// Like `mul_sub`, but for `ExtensionTarget`s. Note that, unlike `mul_sub`, this has no /// performance benefit over separate muls and subs. + /// TODO: Change this to using an `arithmetic_extension` function once `MulExtensionGate` supports addend. pub fn scalar_mul_sub_extension( &mut self, a: Target, diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index b7041510..12bf3bc0 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -7,6 +7,9 @@ impl, const D: usize> CircuitBuilder { /// Selects `x` or `y` based on `b`, which is assumed to be binary. /// In particular, this returns `if b { x } else { y }`. /// Note: This does not range-check `b`. + // TODO: This uses 10 gates per call. If addends are added to `MulExtensionGate`, this will be + // reduced to 2 gates. We could also use a new degree 2 `SelectGate` for this. + // If `num_routed_wire` is larger than 26, we could batch two `select` in one gate. pub fn select( &mut self, b: Target, From cfa58075561e29f7664db6d28aa186a33033af28 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 15:15:22 +0200 Subject: [PATCH 040/117] Add `insert` gadget to insert inferred leaf in FRI query rounds --- src/fri/recursive_verifier.rs | 24 ++++++------------ src/gadgets/insert.rs | 29 +++++++++++++++++++++ src/gadgets/mod.rs | 1 + src/gadgets/rotate.rs | 47 +++++++++++++++++++++++++++++++++++ src/gates/base_sum.rs | 2 -- src/gates/noop.rs | 2 +- 6 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 src/gadgets/insert.rs diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index a424dee0..4219aefc 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -1,22 +1,16 @@ -use anyhow::{ensure, Result}; use itertools::izip; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::{flatten_target, ExtensionTarget}; -use crate::field::extension_field::{flatten, Extendable, FieldExtension, OEF}; +use crate::field::extension_field::Extendable; use crate::field::field::Field; -use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; use crate::fri::FriConfig; -use crate::hash::hash_n_to_1; -use crate::merkle_proofs::verify_merkle_proof; -use crate::plonk_challenger::{Challenger, RecursiveChallenger}; -use crate::plonk_common::reduce_with_iter; +use crate::plonk_challenger::RecursiveChallenger; use crate::proof::{ - FriInitialTreeProof, FriInitialTreeProofTarget, FriProof, FriProofTarget, FriQueryRound, - FriQueryRoundTarget, Hash, HashTarget, OpeningSet, OpeningSetTarget, + FriInitialTreeProofTarget, FriProofTarget, FriQueryRoundTarget, HashTarget, OpeningSetTarget, }; use crate::target::Target; -use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; +use crate::util::{log2_strict, reverse_index_bits_in_place}; impl, const D: usize> CircuitBuilder { /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity @@ -180,7 +174,7 @@ impl, const D: usize> CircuitBuilder { } let denominator = self.sub_extension(subgroup_x, zeta); let quotient = self.div_unsafe_extension(numerator, denominator); - let sum = self.add_extension(sum, quotient); + sum = self.add_extension(sum, quotient); let evs = proof .unsalted_evals(3, config) @@ -213,7 +207,7 @@ impl, const D: usize> CircuitBuilder { let vanish_right = self.sub_extension(subgroup_x, zeta_right); let denominator = self.mul_extension(vanish, vanish_right); let quotient = self.div_unsafe_extension(numerator, denominator); - let sum = self.add_extension(sum, quotient); + sum = self.add_extension(sum, quotient); let evs = proof .unsalted_evals(2, config) @@ -245,7 +239,7 @@ impl, const D: usize> CircuitBuilder { let vanish_frob = self.sub_extension(subgroup_x, zeta_frob); let denominator = self.mul_extension(vanish, vanish_frob); let quotient = self.div_unsafe_extension(numerator, denominator); - let sum = self.add_extension(sum, quotient); + sum = self.add_extension(sum, quotient); sum } @@ -285,7 +279,6 @@ impl, const D: usize> CircuitBuilder { let mut subgroup_x = self.mul(g, phi); for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { - let arity = 1 << arity_bits; let next_domain_size = domain_size >> arity_bits; let e_x = if i == 0 { self.fri_combine_initial( @@ -309,8 +302,7 @@ impl, const D: usize> CircuitBuilder { let mut evals = round_proof.steps[i].evals.clone(); // Insert P(y) into the evaluation vector, since it wasn't included by the prover. let (low_x_index, high_x_index) = self.split_low_high(x_index, arity_bits); - // TODO: Uncomment this. - // evals.insert(x_index & (arity - 1), e_x); + evals = self.insert(low_x_index, e_x, evals); evaluations.push(evals); self.verify_merkle_proof( flatten_target(&evaluations[i]), diff --git a/src/gadgets/insert.rs b/src/gadgets/insert.rs new file mode 100644 index 00000000..e8a1f6a4 --- /dev/null +++ b/src/gadgets/insert.rs @@ -0,0 +1,29 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; +use crate::field::extension_field::Extendable; +use crate::target::Target; +use crate::util::log2_strict; + +impl, const D: usize> CircuitBuilder { + /// Inserts a `Target` in a vector at a non-deterministic index. This is done by rotating to the + /// left, inserting at 0 and then rotating to the right. + /// Note: `index` is not range-checked. + pub fn insert( + &mut self, + index: Target, + element: ExtensionTarget, + mut v: Vec>, + ) -> Vec> { + let n = v.len(); + debug_assert!(n.is_power_of_two()); + let n_log = log2_strict(n); + + v.push(self.zero_extension()); + let mut v = self.rotate_left(index, &v, n_log); + + v.insert(0, element); + v.pop().unwrap(); + + self.rotate_right(index, &v, n_log) + } +} diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 137b991e..a1e041fc 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -1,5 +1,6 @@ pub mod arithmetic; pub mod hash; +pub mod insert; pub mod interpolation; pub mod polynomial; pub mod range_check; diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index 12bf3bc0..cc3c7fce 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -37,6 +37,22 @@ impl, const D: usize> CircuitBuilder { res } + pub fn rotate_right_fixed( + &mut self, + b: Target, + k: usize, + v: &[ExtensionTarget], + len: usize, + ) -> Vec> { + let mut res = Vec::new(); + + for i in 0..len { + res.push(self.select(b, v[(i - k) % len], v[i])); + } + + res + } + /// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be /// less than `len`. /// Note: We assume `len` is less than 8 since we won't use any arity greater than 8 in FRI (maybe?). @@ -58,6 +74,24 @@ impl, const D: usize> CircuitBuilder { v } + pub fn rotate_right_from_bits( + &mut self, + num_rotation_bits: &[Target], + v: &[ExtensionTarget], + len_log: usize, + ) -> Vec> { + debug_assert_eq!(num_rotation_bits.len(), len_log); + let len = 1 << len_log; + debug_assert_eq!(v.len(), len); + let mut v = v.to_vec(); + + for i in 0..len_log { + v = self.rotate_right_fixed(num_rotation_bits[i], 1 << i, &v, len); + } + + v + } + /// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be /// less than `len`. /// Note: We assume `len` is a power of two less than or equal to 8, since we won't use any @@ -74,6 +108,19 @@ impl, const D: usize> CircuitBuilder { self.rotate_left_from_bits(&bits, v, len_log) } + + pub fn rotate_right( + &mut self, + num_rotation: Target, + v: &[ExtensionTarget], + len_log: usize, + ) -> Vec> { + let len = 1 << len_log; + debug_assert_eq!(v.len(), len); + let bits = self.split_le(num_rotation, len_log); + + self.rotate_right_from_bits(&bits, v, len_log) + } } #[cfg(test)] diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index e3b69618..150a492e 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -1,5 +1,4 @@ use crate::circuit_builder::CircuitBuilder; -use crate::circuit_data::CircuitConfig; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; @@ -8,7 +7,6 @@ use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::plonk_common::{reduce_with_powers, reduce_with_powers_recursive}; use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars}; -use crate::wire::Wire; use crate::witness::PartialWitness; use std::ops::Range; diff --git a/src/gates/noop.rs b/src/gates/noop.rs index eddd0361..a12df932 100644 --- a/src/gates/noop.rs +++ b/src/gates/noop.rs @@ -5,7 +5,7 @@ use crate::gates::gate::{Gate, GateRef}; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars}; -/// A gate which takes a single constant parameter and outputs that value. +/// A gate which does nothing. pub struct NoopGate; impl NoopGate { From 322556260c250bd165187efa19be540279104507 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 16:06:41 +0200 Subject: [PATCH 041/117] Cosmetic changes --- src/generator.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 443809b6..b6534921 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -17,17 +17,13 @@ pub(crate) fn generate_partial_witness( for watch in generator.watch_list() { generator_indices_by_watches .entry(watch) - .or_insert_with(Vec::new) - .push(i); + .or_insert_with(|| vec![i]); } } // Build a list of "pending" generators which are queued to be run. Initially, all generators // are queued. - let mut pending_generator_indices = HashSet::new(); - for i in 0..generators.len() { - pending_generator_indices.insert(i); - } + let mut pending_generator_indices: HashSet<_> = (0..generators.len()).collect(); // We also track a list of "expired" generators which have already returned false. let mut expired_generator_indices = HashSet::new(); From ea6a724560392b2422be85cce1988093c6debfbf Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 17:02:52 +0200 Subject: [PATCH 042/117] Enforce copy constraints in partial witness generation. --- src/circuit_builder.rs | 11 ++++++++--- src/circuit_data.rs | 3 +++ src/gates/gmimc.rs | 10 +++++++++- src/generator.rs | 9 ++++++++- src/permutation_argument.rs | 21 +++++++++++++++++++++ src/plonk_challenger.rs | 9 +++++++-- src/prover.rs | 6 +++++- src/witness.rs | 4 ++++ 8 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 4150bb68..b9b2e533 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -236,7 +236,7 @@ impl, const D: usize> CircuitBuilder { .collect() } - fn sigma_vecs(&self, k_is: &[F]) -> Vec> { + fn sigma_vecs(&self, k_is: &[F]) -> (Vec>, TargetPartitions) { let degree = self.gate_instances.len(); let degree_log = log2_strict(degree); let mut target_partitions = TargetPartitions::new(); @@ -256,7 +256,11 @@ impl, const D: usize> CircuitBuilder { } let wire_partitions = target_partitions.to_wire_partitions(); - wire_partitions.get_sigma_polys(degree_log, k_is) + + ( + wire_partitions.get_sigma_polys(degree_log, k_is), + target_partitions, + ) } /// Builds a "full circuit", with both prover and verifier data. @@ -278,7 +282,7 @@ impl, const D: usize> CircuitBuilder { ); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); - let sigma_vecs = self.sigma_vecs(&k_is); + let (sigma_vecs, targets_partition) = self.sigma_vecs(&k_is); let sigmas_commitment = ListPolynomialCommitment::new( sigma_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -297,6 +301,7 @@ impl, const D: usize> CircuitBuilder { generators, constants_commitment, sigmas_commitment, + targets_partition, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4d9a7110..6b6d614a 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -5,6 +5,7 @@ use crate::field::field::Field; use crate::fri::FriConfig; use crate::gates::gate::GateRef; use crate::generator::WitnessGenerator; +use crate::permutation_argument::TargetPartitions; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; @@ -104,6 +105,8 @@ pub(crate) struct ProverOnlyCircuitData { pub constants_commitment: ListPolynomialCommitment, /// Commitments to the sigma polynomial. pub sigmas_commitment: ListPolynomialCommitment, + /// Partition of the targets into copy-constrained sets. + pub targets_partition: TargetPartitions, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 19042d57..ac5741e4 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -323,6 +323,8 @@ mod tests { use crate::gates::gmimc::{GMiMCGate, W}; use crate::generator::generate_partial_witness; use crate::gmimc::gmimc_permute_naive; + use crate::permutation_argument::TargetPartitions; + use crate::target::Target; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -368,7 +370,13 @@ mod tests { } let generators = gate.0.generators(0, &[]); - generate_partial_witness(&mut witness, &generators); + let mut tp = TargetPartitions::new(); + for g in 0..10 { + for i in 0..config.num_routed_wires { + tp.add_partition(Target::wire(g, i)); + } + } + generate_partial_witness(&mut witness, &generators, &tp); let expected_outputs: [F; W] = gmimc_permute_naive(permutation_inputs.try_into().unwrap(), constants); diff --git a/src/generator.rs b/src/generator.rs index b6534921..b04442b5 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use crate::field::field::Field; +use crate::permutation_argument::TargetPartitions; use crate::target::Target; use crate::witness::PartialWitness; @@ -10,6 +11,7 @@ use crate::witness::PartialWitness; pub(crate) fn generate_partial_witness( witness: &mut PartialWitness, generators: &[Box>], + target_partition: &TargetPartitions, ) { // Index generator indices by their watched targets. let mut generator_indices_by_watches = HashMap::new(); @@ -21,6 +23,8 @@ pub(crate) fn generate_partial_witness( } } + target_partition.generate_copies(witness, &witness.all_populated_targets()); + // Build a list of "pending" generators which are queued to be run. Initially, all generators // are queued. let mut pending_generator_indices: HashSet<_> = (0..generators.len()).collect(); @@ -33,11 +37,14 @@ pub(crate) fn generate_partial_witness( let mut next_pending_generator_indices = HashSet::new(); for &generator_idx in &pending_generator_indices { - let (result, finished) = generators[generator_idx].run(&witness); + let (mut result, finished) = generators[generator_idx].run(&witness); if finished { expired_generator_indices.insert(generator_idx); } + let new_targets = result.all_populated_targets(); + target_partition.generate_copies(&mut result, &new_targets); + // Enqueue unfinished generators that were watching one of the newly populated targets. for watch in result.target_values.keys() { if let Some(watching_generator_indices) = generator_indices_by_watches.get(watch) { diff --git a/src/permutation_argument.rs b/src/permutation_argument.rs index 62ee63d4..f2739756 100644 --- a/src/permutation_argument.rs +++ b/src/permutation_argument.rs @@ -6,6 +6,7 @@ use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; use crate::wire::Wire; +use crate::witness::PartialWitness; #[derive(Debug, Clone)] pub struct TargetPartitions { @@ -82,6 +83,26 @@ impl TargetPartitions { indices, } } + /// For the given set of targets, find any copy constraints involving those targets and populate + /// the witness with copies as needed. + pub fn generate_copies(&self, witness: &mut PartialWitness, targets: &[Target]) { + let mut result = PartialWitness::new(); + + for &target in targets { + let value = witness.get_target(target); + let partition = self.get_partition(target); + + for &sibling in partition { + if witness.contains(sibling) { + // This sibling's value was already set; make sure it has the same value. + assert_eq!(witness.get_target(sibling), value); + } else { + result.set_target(sibling, value); + } + } + } + witness.extend(result); + } } pub struct WirePartitions { diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index fd21ee0d..30d4cf70 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -289,6 +289,7 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::field::field::Field; use crate::generator::generate_partial_witness; + use crate::permutation_argument::TargetPartitions; use crate::plonk_challenger::{Challenger, RecursiveChallenger}; use crate::target::Target; use crate::witness::PartialWitness; @@ -337,7 +338,7 @@ mod tests { let config = CircuitConfig { num_wires: 12 + 12 + 3 + 101, - num_routed_wires: 27, + num_routed_wires: 200, ..CircuitConfig::default() }; let mut builder = CircuitBuilder::::new(config); @@ -351,7 +352,11 @@ mod tests { } let circuit = builder.build(); let mut witness = PartialWitness::new(); - generate_partial_witness(&mut witness, &circuit.prover_only.generators); + generate_partial_witness( + &mut witness, + &circuit.prover_only.generators, + &circuit.prover_only.targets_partition, + ); let recursive_output_values_per_round: Vec> = recursive_outputs_per_round .iter() .map(|outputs| witness.get_targets(outputs)) diff --git a/src/prover.rs b/src/prover.rs index 7a492eaa..fc4e6f38 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -34,7 +34,11 @@ pub(crate) fn prove, const D: usize>( let mut witness = inputs; info!("Running {} generators", prover_data.generators.len()); timed!( - generate_partial_witness(&mut witness, &prover_data.generators), + generate_partial_witness( + &mut witness, + &prover_data.generators, + &prover_data.targets_partition + ), "to generate witness" ); diff --git a/src/witness.rs b/src/witness.rs index a0b4b2a4..e71eebc9 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -59,6 +59,10 @@ impl PartialWitness { targets.iter().all(|&t| self.contains(t)) } + pub fn all_populated_targets(&self) -> Vec { + self.target_values.keys().cloned().collect() + } + pub fn set_target(&mut self, target: Target, value: F) { let opt_old_value = self.target_values.insert(target, value); if let Some(old_value) = opt_old_value { From 86b4b0ab41295df4d133c5803ccca8125dd25e98 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 17:06:53 +0200 Subject: [PATCH 043/117] Add check that all generators were run. --- src/generator.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/generator.rs b/src/generator.rs index b04442b5..a5ba4eaf 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -61,6 +61,11 @@ pub(crate) fn generate_partial_witness( pending_generator_indices = next_pending_generator_indices; } + assert_eq!( + expired_generator_indices.len(), + generators.len(), + "Some generators weren't run." + ); } /// A generator participates in the generation of the witness. From c22bc6261ed9c36288300acbf833fdaca0cefb84 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 21:03:50 +0200 Subject: [PATCH 044/117] Revert watch insertion --- src/generator.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/generator.rs b/src/generator.rs index a5ba4eaf..5b6a258a 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -19,7 +19,8 @@ pub(crate) fn generate_partial_witness( for watch in generator.watch_list() { generator_indices_by_watches .entry(watch) - .or_insert_with(|| vec![i]); + .or_insert_with(Vec::new) + .push(i) } } From 39c793a380759c3d9c82b16195d004143f4516f7 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 15 Jun 2021 08:35:06 +0200 Subject: [PATCH 045/117] Fixes from PR feedback --- src/circuit_data.rs | 2 +- src/witness.rs | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 466779da..7429e62a 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -49,7 +49,7 @@ impl CircuitConfig { self.num_wires - self.num_routed_wires } - pub fn large_config() -> Self { + pub(crate) fn large_config() -> Self { Self { num_wires: 134, num_routed_wires: 12, diff --git a/src/witness.rs b/src/witness.rs index c5748c3f..e71b1cfb 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -89,16 +89,9 @@ impl PartialWitness { F: Extendable, { let limbs = value.to_basefield_array(); - for i in 0..D { - let opt_old_value = self.target_values.insert(et.0[i], limbs[i]); - if let Some(old_value) = opt_old_value { - assert_eq!( - old_value, limbs[i], - "Target was set twice with different values: {:?}", - et.0[i] - ); - } - } + (0..D).for_each(|i| { + self.set_target(et.0[i], limbs[i]); + }); } pub fn set_wire(&mut self, wire: Wire, value: F) { From 89c6a6b4e61d09d3d613b404dca58f3ff51ece2a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 15 Jun 2021 17:34:37 +0200 Subject: [PATCH 046/117] Use `repeated_frobenius` in `ExtensionTarget::frobenius`. --- src/field/extension_field/target.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index 0f9a7e24..e4c2320e 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -18,18 +18,7 @@ impl ExtensionTarget { } pub fn frobenius>(&self, builder: &mut CircuitBuilder) -> Self { - let arr = self.to_target_array(); - let k = (F::ORDER - 1) / (D as u64); - let zs = (0..D as u64) - .map(|i| builder.constant(F::Extension::W.exp(k * i))) - .collect::>(); - - let mut res = Vec::with_capacity(D); - for (z, a) in zs.into_iter().zip(arr) { - res.push(builder.mul(z, a)); - } - - res.try_into().unwrap() + self.repeated_frobenius(1, builder) } pub fn repeated_frobenius>( From 89b25c528c69213fac06512420d2cf908b1697b0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 15 Jun 2021 19:13:15 +0200 Subject: [PATCH 047/117] PR feedback fixes --- src/fri/verifier.rs | 5 +++++ src/gadgets/split_base.rs | 2 +- src/gadgets/split_join.rs | 4 ++-- src/gates/base_sum.rs | 16 ++++++++-------- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index a135f5f0..45c94173 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -196,6 +196,11 @@ fn fri_combine_initial, const D: usize>( let wire_composition_eval = reduce_with_iter(wire_evals, alpha_powers.clone()); let zeta_frob = zeta.frobenius(); let wire_eval = reduce_with_iter(&os.wires, alpha_powers.clone()); + // We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism. + // Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities: + // `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)` + // So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha, + // and one call at the end of the sum. let alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1); let wire_eval_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius(); let wires_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 50a5e579..10189841 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -18,7 +18,7 @@ impl, const D: usize> CircuitBuilder { Target::wires_from_range( gate, - BaseSumGate::::WIRE_LIMBS_START..BaseSumGate::::WIRE_LIMBS_START + num_limbs, + BaseSumGate::::START_LIMBS..BaseSumGate::::START_LIMBS + num_limbs, ) } diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index 647b0ef5..2a388e35 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -29,7 +29,7 @@ impl, const D: usize> CircuitBuilder { /// Verifies that the decomposition is correct by using `k` `BaseSum<2>` gates /// with `k` such that `k*num_routed_wires>=num_bits`. pub(crate) fn split_le(&mut self, integer: Target, num_bits: usize) -> Vec { - let num_limbs = self.config.num_routed_wires - BaseSumGate::<2>::WIRE_LIMBS_START; + let num_limbs = self.config.num_routed_wires - BaseSumGate::<2>::START_LIMBS; let k = ceil_div_usize(num_bits, num_limbs); let gates = (0..k) .map(|_| self.add_gate_no_constants(BaseSumGate::<2>::new(num_limbs))) @@ -39,7 +39,7 @@ impl, const D: usize> CircuitBuilder { for &gate in &gates { bits.extend(Target::wires_from_range( gate, - BaseSumGate::<2>::WIRE_LIMBS_START..BaseSumGate::<2>::WIRE_LIMBS_START + num_limbs, + BaseSumGate::<2>::START_LIMBS..BaseSumGate::<2>::START_LIMBS + num_limbs, )); } bits.drain(num_bits..); diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index 150a492e..0792ea7a 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; @@ -8,9 +10,8 @@ use crate::plonk_common::{reduce_with_powers, reduce_with_powers_recursive}; use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::witness::PartialWitness; -use std::ops::Range; -/// A gate which can sum base W limbs and the reversed limbs. +/// A gate which can sum base B limbs and the reversed limbs. #[derive(Debug)] pub struct BaseSumGate { num_limbs: usize, @@ -23,11 +24,11 @@ impl BaseSumGate { pub const WIRE_SUM: usize = 0; pub const WIRE_REVERSED_SUM: usize = 1; - pub const WIRE_LIMBS_START: usize = 2; + pub const START_LIMBS: usize = 2; /// Returns the index of the `i`th limb wire. pub fn limbs(&self) -> Range { - Self::WIRE_LIMBS_START..Self::WIRE_LIMBS_START + self.num_limbs + Self::START_LIMBS..Self::START_LIMBS + self.num_limbs } } @@ -66,10 +67,10 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat let mut limbs = vars.local_wires[self.limbs()].to_vec(); let computed_sum = reduce_with_powers_recursive(builder, &limbs, base); limbs.reverse(); - let reversed_computed_sum = reduce_with_powers_recursive(builder, &limbs, base); + let computed_reversed_sum = reduce_with_powers_recursive(builder, &limbs, base); let mut constraints = vec![ builder.sub_extension(computed_sum, sum), - builder.sub_extension(reversed_computed_sum, reversed_sum), + builder.sub_extension(computed_reversed_sum, reversed_sum), ]; for limb in limbs { constraints.push({ @@ -138,8 +139,7 @@ impl SimpleGenerator for BaseSplitGenerator { "Integer too large to fit in given number of limbs" ); - let limbs = (BaseSumGate::::WIRE_LIMBS_START - ..BaseSumGate::::WIRE_LIMBS_START + self.num_limbs) + let limbs = (BaseSumGate::::START_LIMBS..BaseSumGate::::START_LIMBS + self.num_limbs) .map(|i| Target::wire(self.gate_index, i)); let limbs_value = (0..self.num_limbs) .scan(sum_value, |acc, _| { From ab7e2381a2e4a2ff392c394542aaddb192aaeed9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 16 Jun 2021 08:15:44 +0200 Subject: [PATCH 048/117] Remove `rev` in computation of the reversed sum in `BaseSplitGenerator`. --- src/gadgets/split_base.rs | 11 +++++++---- src/gates/base_sum.rs | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 10189841..1d5e1fcd 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -82,10 +82,13 @@ mod tests { let two = builder.two(); let three = builder.constant(F::from_canonical_u64(3)); let five = builder.constant(F::from_canonical_u64(5)); - builder.assert_equal(limbs[0], two); - builder.assert_equal(limbs[1], three); - builder.assert_equal(limbs[2], five); - builder.assert_equal(limbs[3], one); + builder.route(limbs[0], two); + builder.route(limbs[1], three); + builder.route(limbs[2], five); + builder.route(limbs[3], one); + let rev = builder.constant(F::from_canonical_u64(11)); + let revt = builder.reverse_bits::<2>(xt, 9); + builder.route(revt, rev); builder.assert_trailing_zeros::<6>(xt, 4); builder.assert_trailing_zeros::<4>(xt, 5); diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index 0792ea7a..09f448e2 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -145,19 +145,22 @@ impl SimpleGenerator for BaseSplitGenerator { .scan(sum_value, |acc, _| { let tmp = *acc % B; *acc /= B; - Some(tmp) + Some(F::from_canonical_usize(tmp)) }) .collect::>(); - let reversed_sum = limbs_value.iter().rev().fold(0, |acc, &x| acc * B + x); + let b_field = F::from_canonical_usize(B); + let reversed_sum = limbs_value + .iter() + .fold(F::ZERO, |acc, &x| acc * b_field + x); let mut result = PartialWitness::new(); result.set_target( Target::wire(self.gate_index, BaseSumGate::::WIRE_REVERSED_SUM), - F::from_canonical_usize(reversed_sum), + reversed_sum, ); for (b, b_value) in limbs.zip(limbs_value) { - result.set_target(b, F::from_canonical_usize(b_value)); + result.set_target(b, b_value); } result From 8200bdcedcd5a0fd8193ff50ca581d3cc2ba10e0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 16 Jun 2021 08:40:28 +0200 Subject: [PATCH 049/117] Add `num_bits` in `split_low_high`. --- src/fri/recursive_verifier.rs | 7 +++++-- src/gadgets/range_check.rs | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 4219aefc..d0795520 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -261,7 +261,8 @@ impl, const D: usize> CircuitBuilder { let mut evaluations: Vec>> = Vec::new(); // TODO: Do we need to range check `x_index` to a target smaller than `p`? let mut x_index = challenger.get_challenge(self); - x_index = self.split_low_high(x_index, n_log).0; + x_index = self.split_low_high(x_index, n_log, 64).0; + let mut x_index_num_bits = n_log; let mut domain_size = n; self.fri_verify_initial_proof( x_index, @@ -301,7 +302,8 @@ impl, const D: usize> CircuitBuilder { }; let mut evals = round_proof.steps[i].evals.clone(); // Insert P(y) into the evaluation vector, since it wasn't included by the prover. - let (low_x_index, high_x_index) = self.split_low_high(x_index, arity_bits); + let (low_x_index, high_x_index) = + self.split_low_high(x_index, arity_bits, x_index_num_bits); evals = self.insert(low_x_index, e_x, evals); evaluations.push(evals); self.verify_merkle_proof( @@ -320,6 +322,7 @@ impl, const D: usize> CircuitBuilder { domain_size = next_domain_size; old_x_index = low_x_index; x_index = high_x_index; + x_index_num_bits -= arity_bits; } let last_evals = evaluations.last().unwrap(); diff --git a/src/gadgets/range_check.rs b/src/gadgets/range_check.rs index e711eadd..34f34496 100644 --- a/src/gadgets/range_check.rs +++ b/src/gadgets/range_check.rs @@ -15,9 +15,10 @@ impl, const D: usize> CircuitBuilder { } /// Returns `(a,b)` such that `x = a + 2^n_log * b` with `a < 2^n_log`. - pub fn split_low_high(&mut self, x: Target, n_log: usize) -> (Target, Target) { + /// `x` is assumed to be range-checked for having `num_bits` bits. + pub fn split_low_high(&mut self, x: Target, n_log: usize, num_bits: usize) -> (Target, Target) { let low_gate = self.add_gate(BaseSumGate::<2>::new(n_log), vec![]); - let high_gate = self.add_gate(BaseSumGate::<2>::new(64 - n_log), vec![]); + let high_gate = self.add_gate(BaseSumGate::<2>::new(num_bits - n_log), vec![]); let low = Target::wire(low_gate, BaseSumGate::<2>::WIRE_SUM); let high = Target::wire(high_gate, BaseSumGate::<2>::WIRE_SUM); self.add_generator(LowHighGenerator { From 6203eb009711926d0f937811a287237e1784bb27 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 16 Jun 2021 08:52:25 +0200 Subject: [PATCH 050/117] Rewrite `MulExtensionGenerator::run_once`. --- src/gates/mul_extension.rs | 45 +++++++++++++++----------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/gates/mul_extension.rs b/src/gates/mul_extension.rs index 00a0ad8d..e8fd65f7 100644 --- a/src/gates/mul_extension.rs +++ b/src/gates/mul_extension.rs @@ -1,3 +1,6 @@ +use std::convert::TryInto; +use std::ops::Range; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; @@ -7,8 +10,6 @@ use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::wire::Wire; use crate::witness::PartialWitness; -use std::convert::TryInto; -use std::ops::Range; /// A gate which can multiply two field extension elements. /// TODO: Add an addend if `NUM_ROUTED_WIRES` is large enough. @@ -108,36 +109,26 @@ impl, const D: usize> SimpleGenerator for MulExtensionGenera } fn run_once(&self, witness: &PartialWitness) -> PartialWitness { - let multiplicand_0 = MulExtensionGate::::wires_multiplicand_0() - .map(|i| { - witness.get_wire(Wire { - gate: self.gate_index, - input: i, - }) - }) - .collect::>(); - let multiplicand_0 = F::Extension::from_basefield_array(multiplicand_0.try_into().unwrap()); - let multiplicand_1 = MulExtensionGate::::wires_multiplicand_1() - .map(|i| { - witness.get_wire(Wire { - gate: self.gate_index, - input: i, - }) - }) - .collect::>(); - let multiplicand_1 = F::Extension::from_basefield_array(multiplicand_1.try_into().unwrap()); - let output = MulExtensionGate::::wires_output() - .map(|i| Wire { - gate: self.gate_index, - input: i, - }) - .collect::>(); + let multiplicand_0_target = ExtensionTarget::from_range( + self.gate_index, + MulExtensionGate::::wires_multiplicand_0(), + ); + let multiplicand_0 = witness.get_extension_target(multiplicand_0_target); + + let multiplicand_1_target = ExtensionTarget::from_range( + self.gate_index, + MulExtensionGate::::wires_multiplicand_1(), + ); + let multiplicand_1 = witness.get_extension_target(multiplicand_1_target); + + let output_target = + ExtensionTarget::from_range(self.gate_index, MulExtensionGate::::wires_output()); let computed_output = F::Extension::from_basefield(self.const_0) * multiplicand_0 * multiplicand_1; let mut pw = PartialWitness::new(); - pw.set_ext_wires(output, computed_output); + pw.set_extension_target(output_target, computed_output); pw } } From 4437012d2af8b90ba152a53be628091276c2a172 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 16 Jun 2021 08:56:58 +0200 Subject: [PATCH 051/117] Add `num_bits` to `exp`. --- src/fri/recursive_verifier.rs | 2 +- src/gadgets/arithmetic.rs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index d0795520..2d8cabff 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -276,7 +276,7 @@ impl, const D: usize> CircuitBuilder { let phi = self.constant(F::primitive_root_of_unity(n_log)); let reversed_x = self.reverse_bits::<2>(x_index, n_log); - let phi = self.exp(phi, reversed_x); + let phi = self.exp(phi, reversed_x, n_log); let mut subgroup_x = self.mul(g, phi); for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 8bf3a797..6250ea81 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; @@ -8,7 +10,6 @@ use crate::generator::SimpleGenerator; use crate::target::Target; use crate::wire::Wire; use crate::witness::PartialWitness; -use std::convert::TryInto; impl, const D: usize> CircuitBuilder { /// Computes `-x`. @@ -173,11 +174,12 @@ impl, const D: usize> CircuitBuilder { } // TODO: Optimize this, maybe with a new gate. - pub fn exp(&mut self, base: Target, exponent: Target) -> Target { + /// Exponentiate `base` to the power of `exponent`, where `exponent < 2^num_bits`. + pub fn exp(&mut self, base: Target, exponent: Target, num_bits: usize) -> Target { let mut current = base; let one = self.one(); let mut product = one; - let exponent_bits = self.split_le(exponent, 64); + let exponent_bits = self.split_le(exponent, num_bits); for bit in exponent_bits.into_iter() { product = self.mul_many(&[bit, current, product]); From 8d999ab2993a2a7f6d7fdde225bb295730e8c766 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 16 Jun 2021 10:43:18 +0200 Subject: [PATCH 052/117] Rewrite `insert` gadget. --- src/fri/recursive_verifier.rs | 2 +- src/gadgets/insert.rs | 65 ++++++++++++++++++++++++++++++----- src/gadgets/rotate.rs | 59 +++++++++++++------------------ 3 files changed, 81 insertions(+), 45 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 2d8cabff..aa9e1d0c 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -32,7 +32,7 @@ impl, const D: usize> CircuitBuilder { reverse_index_bits_in_place(&mut evals); let mut old_x_index_bits = self.split_le(old_x_index, arity_bits); old_x_index_bits.reverse(); - self.rotate_left_from_bits(&old_x_index_bits, &evals, arity_bits); + self.rotate_left_from_bits(&old_x_index_bits, &evals); // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. let points = g diff --git a/src/gadgets/insert.rs b/src/gadgets/insert.rs index e8a1f6a4..45fe663b 100644 --- a/src/gadgets/insert.rs +++ b/src/gadgets/insert.rs @@ -2,7 +2,7 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::target::Target; -use crate::util::log2_strict; +use crate::util::bits_u64; impl, const D: usize> CircuitBuilder { /// Inserts a `Target` in a vector at a non-deterministic index. This is done by rotating to the @@ -14,16 +14,65 @@ impl, const D: usize> CircuitBuilder { element: ExtensionTarget, mut v: Vec>, ) -> Vec> { - let n = v.len(); - debug_assert!(n.is_power_of_two()); - let n_log = log2_strict(n); + let len = v.len(); + let len_bits = bits_u64(len as u64); - v.push(self.zero_extension()); - let mut v = self.rotate_left(index, &v, n_log); + let mut v = self.rotate_left(index, &v, len_bits); v.insert(0, element); - v.pop().unwrap(); - self.rotate_right(index, &v, n_log) + let len_bits = bits_u64(len as u64 + 1); + self.rotate_right(index, &v, len_bits) + } +} +#[cfg(test)] +mod tests { + use super::*; + use crate::circuit_data::CircuitConfig; + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::field::field::Field; + use crate::witness::PartialWitness; + + fn real_insert( + index: usize, + element: ExtensionTarget, + v: &[ExtensionTarget], + ) -> Vec> { + let mut res = v.to_vec(); + res.insert(index, element); + res + } + + fn test_insert_given_len(len_log: usize) { + type F = CrandallField; + type FF = QuarticCrandallField; + let len = 1 << len_log; + let config = CircuitConfig::large_config(); + let mut builder = CircuitBuilder::::new(config); + let v = (0..len - 1) + .map(|_| builder.constant_extension(FF::rand())) + .collect::>(); + + for i in 0..len { + let it = builder.constant(F::from_canonical_usize(i)); + let elem = builder.constant_extension(FF::rand()); + let inserted = real_insert(i, elem, &v); + let purported_inserted = builder.insert(it, elem, v.clone()); + + for (x, y) in inserted.into_iter().zip(purported_inserted) { + builder.route_extension(x, y); + } + } + + let data = builder.build(); + let proof = data.prove(PartialWitness::new()); + } + + #[test] + fn test_insert() { + for len_log in 1..3 { + test_insert_given_len(len_log); + } } } diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index cc3c7fce..914197f1 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -2,6 +2,7 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::target::Target; +use crate::util::bits_u64; impl, const D: usize> CircuitBuilder { /// Selects `x` or `y` based on `b`, which is assumed to be binary. @@ -26,8 +27,8 @@ impl, const D: usize> CircuitBuilder { b: Target, k: usize, v: &[ExtensionTarget], - len: usize, ) -> Vec> { + let len = v.len(); let mut res = Vec::new(); for i in 0..len { @@ -37,38 +38,33 @@ impl, const D: usize> CircuitBuilder { res } + /// Left-rotates an array `k` times if `b=1` else return the same array. pub fn rotate_right_fixed( &mut self, b: Target, k: usize, v: &[ExtensionTarget], - len: usize, ) -> Vec> { + let len = v.len(); let mut res = Vec::new(); for i in 0..len { - res.push(self.select(b, v[(i - k) % len], v[i])); + res.push(self.select(b, v[(len + i - k) % len], v[i])); } res } - /// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be - /// less than `len`. - /// Note: We assume `len` is less than 8 since we won't use any arity greater than 8 in FRI (maybe?). + /// Left-rotates an vector by the `Target` having bits given in little-endian by `num_rotation_bits`. pub fn rotate_left_from_bits( &mut self, num_rotation_bits: &[Target], v: &[ExtensionTarget], - len_log: usize, ) -> Vec> { - debug_assert_eq!(num_rotation_bits.len(), len_log); - let len = 1 << len_log; - debug_assert_eq!(v.len(), len); let mut v = v.to_vec(); - for i in 0..len_log { - v = self.rotate_left_fixed(num_rotation_bits[i], 1 << i, &v, len); + for i in 0..num_rotation_bits.len() { + v = self.rotate_left_fixed(num_rotation_bits[i], 1 << i, &v); } v @@ -78,48 +74,40 @@ impl, const D: usize> CircuitBuilder { &mut self, num_rotation_bits: &[Target], v: &[ExtensionTarget], - len_log: usize, ) -> Vec> { - debug_assert_eq!(num_rotation_bits.len(), len_log); - let len = 1 << len_log; - debug_assert_eq!(v.len(), len); let mut v = v.to_vec(); - for i in 0..len_log { - v = self.rotate_right_fixed(num_rotation_bits[i], 1 << i, &v, len); + for i in 0..num_rotation_bits.len() { + v = self.rotate_right_fixed(num_rotation_bits[i], 1 << i, &v); } v } /// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be - /// less than `len`. - /// Note: We assume `len` is a power of two less than or equal to 8, since we won't use any - /// arity greater than 8 in FRI (maybe?). + /// less than `2^len_bits`. pub fn rotate_left( &mut self, num_rotation: Target, v: &[ExtensionTarget], - len_log: usize, + len_bits: usize, ) -> Vec> { - let len = 1 << len_log; - debug_assert_eq!(v.len(), len); - let bits = self.split_le(num_rotation, len_log); + debug_assert_eq!(bits_u64(v.len() as u64), len_bits); + let bits = self.split_le(num_rotation, len_bits); - self.rotate_left_from_bits(&bits, v, len_log) + self.rotate_left_from_bits(&bits, v) } pub fn rotate_right( &mut self, num_rotation: Target, v: &[ExtensionTarget], - len_log: usize, + len_bits: usize, ) -> Vec> { - let len = 1 << len_log; - debug_assert_eq!(v.len(), len); - let bits = self.split_le(num_rotation, len_log); + debug_assert_eq!(bits_u64(v.len() as u64), len_bits); + let bits = self.split_le(num_rotation, len_bits); - self.rotate_right_from_bits(&bits, v, len_log) + self.rotate_right_from_bits(&bits, v) } } @@ -141,10 +129,9 @@ mod tests { res } - fn test_rotate_given_len(len_log: usize) { + fn test_rotate_given_len(len: usize) { type F = CrandallField; type FF = QuarticCrandallField; - let len = 1 << len_log; let config = CircuitConfig::large_config(); let mut builder = CircuitBuilder::::new(config); let v = (0..len) @@ -154,7 +141,7 @@ mod tests { for i in 0..len { let it = builder.constant(F::from_canonical_usize(i)); let rotated = real_rotate(i, &v); - let purported_rotated = builder.rotate_left(it, &v, len_log); + let purported_rotated = builder.rotate_left(it, &v, bits_u64(len as u64)); for (x, y) in rotated.into_iter().zip(purported_rotated) { builder.assert_equal_extension(x, y); @@ -167,8 +154,8 @@ mod tests { #[test] fn test_rotate() { - for len_log in 1..3 { - test_rotate_given_len(len_log); + for len in 1..6 { + test_rotate_given_len(len); } } } From e647e17720ca18fdf23c4cbb63fcb0cfd9ba1a1c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 16 Jun 2021 11:17:45 +0200 Subject: [PATCH 053/117] Simplify `insert` and `rotate` gadgets, and check that we don't overrotate. --- src/gadgets/insert.rs | 10 ++-------- src/gadgets/rotate.rs | 14 +++++++------- src/gadgets/split_join.rs | 3 +++ 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/gadgets/insert.rs b/src/gadgets/insert.rs index 45fe663b..dc5d1245 100644 --- a/src/gadgets/insert.rs +++ b/src/gadgets/insert.rs @@ -14,15 +14,9 @@ impl, const D: usize> CircuitBuilder { element: ExtensionTarget, mut v: Vec>, ) -> Vec> { - let len = v.len(); - let len_bits = bits_u64(len as u64); - - let mut v = self.rotate_left(index, &v, len_bits); - + let mut v = self.rotate_left(index, &v); v.insert(0, element); - - let len_bits = bits_u64(len as u64 + 1); - self.rotate_right(index, &v, len_bits) + self.rotate_right(index, &v) } } #[cfg(test)] diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index 914197f1..bd39a36b 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -2,7 +2,7 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::target::Target; -use crate::util::bits_u64; +use crate::util::log2_ceil; impl, const D: usize> CircuitBuilder { /// Selects `x` or `y` based on `b`, which is assumed to be binary. @@ -29,6 +29,7 @@ impl, const D: usize> CircuitBuilder { v: &[ExtensionTarget], ) -> Vec> { let len = v.len(); + debug_assert!(k < len, "Trying to rotate by more than the vector length."); let mut res = Vec::new(); for i in 0..len { @@ -46,6 +47,7 @@ impl, const D: usize> CircuitBuilder { v: &[ExtensionTarget], ) -> Vec> { let len = v.len(); + debug_assert!(k < len, "Trying to rotate by more than the vector length."); let mut res = Vec::new(); for i in 0..len { @@ -90,9 +92,8 @@ impl, const D: usize> CircuitBuilder { &mut self, num_rotation: Target, v: &[ExtensionTarget], - len_bits: usize, ) -> Vec> { - debug_assert_eq!(bits_u64(v.len() as u64), len_bits); + let len_bits = log2_ceil(v.len()); let bits = self.split_le(num_rotation, len_bits); self.rotate_left_from_bits(&bits, v) @@ -102,9 +103,8 @@ impl, const D: usize> CircuitBuilder { &mut self, num_rotation: Target, v: &[ExtensionTarget], - len_bits: usize, ) -> Vec> { - debug_assert_eq!(bits_u64(v.len() as u64), len_bits); + let len_bits = log2_ceil(v.len()); let bits = self.split_le(num_rotation, len_bits); self.rotate_right_from_bits(&bits, v) @@ -141,7 +141,7 @@ mod tests { for i in 0..len { let it = builder.constant(F::from_canonical_usize(i)); let rotated = real_rotate(i, &v); - let purported_rotated = builder.rotate_left(it, &v, bits_u64(len as u64)); + let purported_rotated = builder.rotate_left(it, &v); for (x, y) in rotated.into_iter().zip(purported_rotated) { builder.assert_equal_extension(x, y); @@ -154,7 +154,7 @@ mod tests { #[test] fn test_rotate() { - for len in 1..6 { + for len in 1..5 { test_rotate_given_len(len); } } diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index 2a388e35..f8aa12e6 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -29,6 +29,9 @@ impl, const D: usize> CircuitBuilder { /// Verifies that the decomposition is correct by using `k` `BaseSum<2>` gates /// with `k` such that `k*num_routed_wires>=num_bits`. pub(crate) fn split_le(&mut self, integer: Target, num_bits: usize) -> Vec { + if num_bits == 0 { + return Vec::new(); + } let num_limbs = self.config.num_routed_wires - BaseSumGate::<2>::START_LIMBS; let k = ceil_div_usize(num_bits, num_limbs); let gates = (0..k) From 5edaab59e66ef6c69ac92fe51f98a10db938abb5 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 16 Jun 2021 11:37:07 +0200 Subject: [PATCH 054/117] Renaming + Clippy --- src/field/extension_field/target.rs | 2 +- src/gadgets/insert.rs | 3 +-- src/gadgets/range_check.rs | 2 +- src/gadgets/split_base.rs | 4 ++-- src/gadgets/split_join.rs | 15 ++++++++------- src/gates/base_sum.rs | 3 ++- src/gates/mul_extension.rs | 1 - 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index e4c2320e..0316b04f 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -246,7 +246,7 @@ impl, const D: usize> CircuitBuilder { } /// Returns `a * b`, where `b` is in the extension field and `a` is in the base field. - pub fn scalar_mul_ext(&mut self, a: Target, mut b: ExtensionTarget) -> ExtensionTarget { + pub fn scalar_mul_ext(&mut self, a: Target, b: ExtensionTarget) -> ExtensionTarget { let a_ext = self.convert_to_ext(a); self.mul_extension(a_ext, b) } diff --git a/src/gadgets/insert.rs b/src/gadgets/insert.rs index dc5d1245..64cf7299 100644 --- a/src/gadgets/insert.rs +++ b/src/gadgets/insert.rs @@ -2,7 +2,6 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::target::Target; -use crate::util::bits_u64; impl, const D: usize> CircuitBuilder { /// Inserts a `Target` in a vector at a non-deterministic index. This is done by rotating to the @@ -12,7 +11,7 @@ impl, const D: usize> CircuitBuilder { &mut self, index: Target, element: ExtensionTarget, - mut v: Vec>, + v: Vec>, ) -> Vec> { let mut v = self.rotate_left(index, &v); v.insert(0, element); diff --git a/src/gadgets/range_check.rs b/src/gadgets/range_check.rs index 34f34496..7fd35efc 100644 --- a/src/gadgets/range_check.rs +++ b/src/gadgets/range_check.rs @@ -50,7 +50,7 @@ impl SimpleGenerator for LowHighGenerator { } fn run_once(&self, witness: &PartialWitness) -> PartialWitness { - let mut integer_value = witness.get_target(self.integer).to_canonical_u64(); + let integer_value = witness.get_target(self.integer).to_canonical_u64(); let low = integer_value & ((1 << self.n_log) - 1); let high = integer_value >> self.n_log; diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 1d5e1fcd..bf2c158c 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -22,13 +22,13 @@ impl, const D: usize> CircuitBuilder { ) } - /// Asserts that `x`'s bit representation has at least `trailing_zeros` trailing zeros. + /// Asserts that `x`'s big-endian bit representation has at least `trailing_zeros` trailing zeros. pub(crate) fn assert_trailing_zeros(&mut self, x: Target, trailing_zeros: u32) { let num_limbs = num_limbs(64, B); let num_limbs_to_check = num_limbs_to_check(trailing_zeros, B); let limbs = self.split_le_base::(x, num_limbs); assert!( - num_limbs_to_check < self.config.num_routed_wires, + num_limbs_to_check <= self.config.num_routed_wires, "Not enough routed wires." ); for i in 0..num_limbs_to_check { diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index f8aa12e6..5eb60148 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -32,29 +32,30 @@ impl, const D: usize> CircuitBuilder { if num_bits == 0 { return Vec::new(); } - let num_limbs = self.config.num_routed_wires - BaseSumGate::<2>::START_LIMBS; - let k = ceil_div_usize(num_bits, num_limbs); + let bits_per_gate = self.config.num_routed_wires - BaseSumGate::<2>::START_LIMBS; + let k = ceil_div_usize(num_bits, bits_per_gate); let gates = (0..k) - .map(|_| self.add_gate_no_constants(BaseSumGate::<2>::new(num_limbs))) + .map(|_| self.add_gate_no_constants(BaseSumGate::<2>::new(bits_per_gate))) .collect::>(); let mut bits = Vec::with_capacity(num_bits); for &gate in &gates { bits.extend(Target::wires_from_range( gate, - BaseSumGate::<2>::START_LIMBS..BaseSumGate::<2>::START_LIMBS + num_limbs, + BaseSumGate::<2>::START_LIMBS..BaseSumGate::<2>::START_LIMBS + bits_per_gate, )); } bits.drain(num_bits..); let zero = self.zero(); + let one = self.one(); let mut acc = zero; for &gate in gates.iter().rev() { let sum = Target::wire(gate, BaseSumGate::<2>::WIRE_SUM); acc = self.arithmetic( - F::from_canonical_usize(1 << num_limbs), + F::from_canonical_usize(1 << bits_per_gate), acc, - zero, + one, F::ONE, sum, ); @@ -64,7 +65,7 @@ impl, const D: usize> CircuitBuilder { self.add_generator(WireSplitGenerator { integer, gates, - num_limbs, + num_limbs: bits_per_gate, }); bits diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index 09f448e2..e34aa6c1 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -11,7 +11,8 @@ use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::witness::PartialWitness; -/// A gate which can sum base B limbs and the reversed limbs. +/// A gate which can decompose a number into base B little-endian limbs, +/// and compute the limb-reversed (i.e. big-endian) sum. #[derive(Debug)] pub struct BaseSumGate { num_limbs: usize, diff --git a/src/gates/mul_extension.rs b/src/gates/mul_extension.rs index e8fd65f7..e378e2b1 100644 --- a/src/gates/mul_extension.rs +++ b/src/gates/mul_extension.rs @@ -1,4 +1,3 @@ -use std::convert::TryInto; use std::ops::Range; use crate::circuit_builder::CircuitBuilder; From a6acd14dfaadbb3a742ff8ee8a1d199fd04ffdc0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 16 Jun 2021 17:43:41 +0200 Subject: [PATCH 055/117] Minor rewrites and optimizations --- src/circuit_builder.rs | 15 ++++++++------- src/circuit_data.rs | 2 ++ src/field/fft.rs | 13 +++++++------ src/field/field.rs | 14 +++++++------- src/field/lagrange.rs | 7 ++----- src/permutation_argument.rs | 4 ++-- src/prover.rs | 21 ++++++++++++++++++++- 7 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 4150bb68..1916eba6 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -236,7 +236,7 @@ impl, const D: usize> CircuitBuilder { .collect() } - fn sigma_vecs(&self, k_is: &[F]) -> Vec> { + fn sigma_vecs(&self, k_is: &[F], subgroup: &[F]) -> Vec> { let degree = self.gate_instances.len(); let degree_log = log2_strict(degree); let mut target_partitions = TargetPartitions::new(); @@ -256,7 +256,7 @@ impl, const D: usize> CircuitBuilder { } let wire_partitions = target_partitions.to_wire_partitions(); - wire_partitions.get_sigma_polys(degree_log, k_is) + wire_partitions.get_sigma_polys(degree_log, k_is, subgroup) } /// Builds a "full circuit", with both prover and verifier data. @@ -270,6 +270,9 @@ impl, const D: usize> CircuitBuilder { let degree = self.gate_instances.len(); info!("degree after blinding & padding: {}", degree); + let degree_bits = log2_strict(degree); + let subgroup = F::two_adic_subgroup(degree_bits); + let constant_vecs = self.constant_polys(); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), @@ -278,7 +281,7 @@ impl, const D: usize> CircuitBuilder { ); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); - let sigma_vecs = self.sigma_vecs(&k_is); + let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); let sigmas_commitment = ListPolynomialCommitment::new( sigma_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -292,11 +295,11 @@ impl, const D: usize> CircuitBuilder { sigmas_root, }; - let generators = self.generators; let prover_only = ProverOnlyCircuitData { - generators, + generators: self.generators, constants_commitment, sigmas_commitment, + subgroup, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we @@ -310,8 +313,6 @@ impl, const D: usize> CircuitBuilder { .max() .expect("No gates?"); - let degree_bits = log2_strict(degree); - // TODO: This should also include an encoding of gate constraints. let circuit_digest_parts = [constants_root.elements, sigmas_root.elements]; let circuit_digest = hash_n_to_hash(circuit_digest_parts.concat(), false); diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4d9a7110..6cc38522 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -104,6 +104,8 @@ pub(crate) struct ProverOnlyCircuitData { pub constants_commitment: ListPolynomialCommitment, /// Commitments to the sigma polynomial. pub sigmas_commitment: ListPolynomialCommitment, + /// Subgroup of order `degree`. + pub subgroup: Vec, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/field/fft.rs b/src/field/fft.rs index 8bcde967..abce58d6 100644 --- a/src/field/fft.rs +++ b/src/field/fft.rs @@ -44,12 +44,14 @@ pub(crate) fn fft_precompute(degree: usize) -> FftPrecomputation { let degree_log = log2_ceil(degree); let mut subgroups_rev = Vec::new(); - for i in 0..=degree_log { - let g_i = F::primitive_root_of_unity(i); - let subgroup = F::cyclic_subgroup_known_order(g_i, 1 << i); + let mut subgroup = F::two_adic_subgroup(degree_log); + for _i in 0..=degree_log { + let subsubgroup = subgroup.iter().step_by(2).copied().collect(); let subgroup_rev = reverse_index_bits(subgroup); subgroups_rev.push(subgroup_rev); + subgroup = subsubgroup; } + subgroups_rev.reverse(); FftPrecomputation { subgroups_rev } } @@ -200,10 +202,9 @@ mod tests { let degree = coefficients.len(); let degree_log = log2_strict(degree); - let g = F::primitive_root_of_unity(degree_log); - let powers_of_g = F::cyclic_subgroup_known_order(g, degree); + let subgroup = F::two_adic_subgroup(degree_log); - let values = powers_of_g + let values = subgroup .into_iter() .map(|x| evaluate_at_naive(&coefficients, x)) .collect(); diff --git a/src/field/field.rs b/src/field/field.rs index 3c6f0e47..b637c72b 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -111,13 +111,13 @@ pub trait Field: /// Computes a multiplicative subgroup whose order is known in advance. fn cyclic_subgroup_known_order(generator: Self, order: usize) -> Vec { - let mut subgroup = Vec::with_capacity(order); - let mut current = Self::ONE; - for _i in 0..order { - subgroup.push(current); - current *= generator; - } - subgroup + generator.powers().take(order).collect() + } + + /// Computes the subgroup generated by the root of unity of a given order generated by `Self::primitive_root_of_unity`. + fn two_adic_subgroup(n_log: usize) -> Vec { + let generator = Self::primitive_root_of_unity(n_log); + generator.powers().take(1 << n_log).collect() } fn cyclic_subgroup_unknown_order(generator: Self) -> Vec { diff --git a/src/field/lagrange.rs b/src/field/lagrange.rs index 8911feb0..4b75651b 100644 --- a/src/field/lagrange.rs +++ b/src/field/lagrange.rs @@ -10,10 +10,8 @@ use crate::util::log2_ceil; pub(crate) fn interpolant(points: &[(F, F)]) -> PolynomialCoeffs { let n = points.len(); let n_log = log2_ceil(n); - let n_padded = 1 << n_log; - let g = F::primitive_root_of_unity(n_log); - let subgroup = F::cyclic_subgroup_known_order(g, n_padded); + let subgroup = F::two_adic_subgroup(n_log); let barycentric_weights = barycentric_weights(points); let subgroup_evals = subgroup .into_iter() @@ -92,8 +90,7 @@ mod tests { for deg_log in 0..4 { let deg = 1 << deg_log; - let g = F::primitive_root_of_unity(deg_log); - let domain = F::cyclic_subgroup_known_order(g, deg); + let domain = F::two_adic_subgroup(deg_log); let coeffs = F::rand_vec(deg); let coeffs = PolynomialCoeffs { coeffs }; diff --git a/src/permutation_argument.rs b/src/permutation_argument.rs index 62ee63d4..54436ecb 100644 --- a/src/permutation_argument.rs +++ b/src/permutation_argument.rs @@ -110,9 +110,9 @@ impl WirePartitions { &self, degree_log: usize, k_is: &[F], + subgroup: &[F], ) -> Vec> { let degree = 1 << degree_log; - let subgroup_generator = F::primitive_root_of_unity(degree_log); let sigma = self.get_sigma_map(degree); sigma @@ -120,7 +120,7 @@ impl WirePartitions { .map(|chunk| { let values = chunk .par_iter() - .map(|&x| k_is[x / degree] * subgroup_generator.exp((x % degree) as u64)) + .map(|&x| k_is[x / degree] * subgroup[x % degree]) .collect::>(); PolynomialValues::new(values) }) diff --git a/src/prover.rs b/src/prover.rs index 7a492eaa..0731d2b4 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -157,7 +157,26 @@ fn compute_z, const D: usize>( common_data: &CommonCircuitData, _i: usize, ) -> PolynomialCoeffs { - PolynomialCoeffs::zero(common_data.degree()) // TODO + todo!() + // let subgroup = + // let mut plonk_z_points = vec![F::ONE]; + // let k_is = common_data.k_is; + // for i in 1..common_data.degree() { + // let x = subgroup[i - 1]; + // let mut numerator = F::ONE; + // let mut denominator = F::ONE; + // for j in 0..NUM_ROUTED_WIRES { + // let wire_value = witness.get_indices(i - 1, j); + // let k_i = k_is[j]; + // let s_id = k_i * x; + // let s_sigma = sigma_values[j][8 * (i - 1)]; + // numerator = numerator * (wire_value + beta * s_id + gamma); + // denominator = denominator * (wire_value + beta * s_sigma + gamma); + // } + // let last = *plonk_z_points.last().unwrap(); + // plonk_z_points.push(last * numerator / denominator); + // } + // plonk_z_points } fn compute_vanishing_polys, const D: usize>( From eaba5238a6400448e3aacb65c969e76b0519c006 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 09:49:41 +0200 Subject: [PATCH 056/117] Change PoW to checking leading zeros --- src/fri/prover.rs | 4 ++-- src/fri/recursive_verifier.rs | 2 +- src/fri/verifier.rs | 3 ++- src/gadgets/split_base.rs | 36 ++++------------------------------- 4 files changed, 9 insertions(+), 36 deletions(-) diff --git a/src/fri/prover.rs b/src/fri/prover.rs index a919b559..f937ee55 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -106,8 +106,8 @@ fn fri_proof_of_work(current_hash: Hash, config: &FriConfig) -> F { false, ) .to_canonical_u64() - .trailing_zeros() - >= config.proof_of_work_bits + .leading_zeros() + >= config.proof_of_work_bits + F::ORDER.leading_zeros() }) .map(F::from_canonical_u64) .expect("Proof of work failed.") diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index aa9e1d0c..4a92172d 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -57,7 +57,7 @@ impl, const D: usize> CircuitBuilder { inputs.push(proof.pow_witness); let hash = self.hash_n_to_m(inputs, 1, false)[0]; - self.assert_trailing_zeros::<2>(hash, config.proof_of_work_bits); + self.assert_leading_zeros(hash, config.proof_of_work_bits + F::ORDER.leading_zeros()); } pub fn verify_fri_proof( diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 45c94173..04432ed4 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -55,7 +55,8 @@ fn fri_verify_proof_of_work, const D: usize>( false, ); ensure!( - hash.to_canonical_u64().trailing_zeros() >= config.proof_of_work_bits, + hash.to_canonical_u64().leading_zeros() + >= config.proof_of_work_bits + F::ORDER.leading_zeros(), "Invalid proof of work witness." ); diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index bf2c158c..9aefe6da 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -22,18 +22,9 @@ impl, const D: usize> CircuitBuilder { ) } - /// Asserts that `x`'s big-endian bit representation has at least `trailing_zeros` trailing zeros. - pub(crate) fn assert_trailing_zeros(&mut self, x: Target, trailing_zeros: u32) { - let num_limbs = num_limbs(64, B); - let num_limbs_to_check = num_limbs_to_check(trailing_zeros, B); - let limbs = self.split_le_base::(x, num_limbs); - assert!( - num_limbs_to_check <= self.config.num_routed_wires, - "Not enough routed wires." - ); - for i in 0..num_limbs_to_check { - self.assert_zero(limbs[i]); - } + /// Asserts that `x`'s big-endian bit representation has at least `leading_zeros` leading zeros. + pub(crate) fn assert_leading_zeros(&mut self, x: Target, leading_zeros: u32) { + self.range_check(x, (64 - leading_zeros) as usize); } pub(crate) fn reverse_bits(&mut self, x: Target, num_limbs: usize) -> Target { @@ -45,23 +36,6 @@ impl, const D: usize> CircuitBuilder { } } -/// Returns `k` such that any number with `k` trailing zeros in base `base` has at least -/// `n` trailing zeros in base 2. -#[allow(unconditional_panic)] -const fn num_limbs_to_check(n: u32, base: usize) -> usize { - if base % 2 == 1 { - // Dirty trick to panic if the base is odd. - // TODO: replace with `assert_eq!(base % 2, 0, "Base should be even.")` when stable. - [][0] - } else { - ceil_div_usize(n as usize, base.trailing_zeros() as usize) - } -} - -fn num_limbs(n_log: u32, base: usize) -> usize { - ((n_log as f64) * 2.0_f64.log(base as f64)).ceil() as usize -} - #[cfg(test)] mod tests { use super::*; @@ -90,9 +64,7 @@ mod tests { let revt = builder.reverse_bits::<2>(xt, 9); builder.route(revt, rev); - builder.assert_trailing_zeros::<6>(xt, 4); - builder.assert_trailing_zeros::<4>(xt, 5); - builder.assert_trailing_zeros::<12>(xt, 5); + builder.assert_leading_zeros(xt, 64 - 9); let data = builder.build(); let proof = data.prove(PartialWitness::new()); From 5bebc746f67a80ed3aa32c8c4405421c27f3a9b6 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 11:31:14 +0200 Subject: [PATCH 057/117] PR feedback --- src/fri/recursive_verifier.rs | 97 +++++++++++++++++++---------------- src/fri/verifier.rs | 10 ++-- src/gadgets/split_base.rs | 4 +- src/prover.rs | 2 +- 4 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 4a92172d..1f6d8a1a 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -32,16 +32,16 @@ impl, const D: usize> CircuitBuilder { reverse_index_bits_in_place(&mut evals); let mut old_x_index_bits = self.split_le(old_x_index, arity_bits); old_x_index_bits.reverse(); - self.rotate_left_from_bits(&old_x_index_bits, &evals); + let evals = self.rotate_left_from_bits(&old_x_index_bits, &evals); // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. let points = g .powers() - .zip(evals) - .map(|(y, e)| { + .map(|y| { let yt = self.constant(y); - (self.mul(x, yt), e) + self.mul(x, yt) }) + .zip(evals) .collect::>(); self.interpolate(&points, beta) @@ -154,7 +154,7 @@ impl, const D: usize> CircuitBuilder { // We will add three terms to `sum`: // - one for polynomials opened at `x` only // - one for polynomials opened at `x` and `g x` - // - one for polynomials opened at `x` and its conjugate + // - one for polynomials opened at `x` and `x.frobenius()` let evals = [0, 1, 4] .iter() @@ -166,62 +166,69 @@ impl, const D: usize> CircuitBuilder { .iter() .chain(&os.plonk_sigmas) .chain(&os.quotient_polys); - let mut numerator = self.zero_extension(); - for (e, &o) in izip!(evals, openings) { + let mut single_numerator = self.zero_extension(); + for (e, &o) in izip!(single_evals, single_openings) { let a = alpha_powers.next(self); let diff = self.sub_extension(e, o); - numerator = self.mul_add_extension(a, diff, numerator); + single_numerator = self.mul_add_extension(a, diff, single_numerator); } - let denominator = self.sub_extension(subgroup_x, zeta); - let quotient = self.div_unsafe_extension(numerator, denominator); + let single_denominator = self.sub_extension(subgroup_x, zeta); + let quotient = self.div_unsafe_extension(single_numerator, single_denominator); sum = self.add_extension(sum, quotient); - let evs = proof + // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. + let zs_evals = proof .unsalted_evals(3, config) .iter() .map(|&e| self.convert_to_ext(e)) .collect::>(); // TODO: Would probably be more efficient using `CircuitBuilder::reduce_with_powers_recursive` - let mut ev = self.zero_extension(); - for &e in &evs { - let a = alpha_powers.next(self); - ev = self.mul_add_extension(a, e, ev); + let mut zs_composition_eval = self.zero_extension(); + let mut alpha_powers_cloned = alpha_powers.clone(); + for &e in &zs_evals { + let a = alpha_powers_cloned.next(self); + zs_composition_eval = self.mul_add_extension(a, e, zs_composition_eval); } let g = self.constant_extension(F::Extension::primitive_root_of_unity(degree_log)); let zeta_right = self.mul_extension(g, zeta); - let mut ev_zeta = self.zero_extension(); + let mut zs_ev_zeta = self.zero_extension(); + let mut alpha_powers_cloned = alpha_powers.clone(); for &t in &os.plonk_zs { - let a = alpha_powers.next(self); - ev_zeta = self.mul_add_extension(a, t, ev_zeta); + let a = alpha_powers_cloned.next(self); + zs_ev_zeta = self.mul_add_extension(a, t, zs_ev_zeta); } - let mut ev_zeta_right = self.zero_extension(); + let mut zs_ev_zeta_right = self.zero_extension(); for &t in &os.plonk_zs_right { let a = alpha_powers.next(self); - ev_zeta_right = self.mul_add_extension(a, t, ev_zeta); + zs_ev_zeta_right = self.mul_add_extension(a, t, zs_ev_zeta); } - let interpol_val = - self.interpolate2([(zeta, ev_zeta), (zeta_right, ev_zeta_right)], subgroup_x); - let numerator = self.sub_extension(ev, interpol_val); - let vanish = self.sub_extension(subgroup_x, zeta); - let vanish_right = self.sub_extension(subgroup_x, zeta_right); - let denominator = self.mul_extension(vanish, vanish_right); - let quotient = self.div_unsafe_extension(numerator, denominator); - sum = self.add_extension(sum, quotient); + let interpol_val = self.interpolate2( + [(zeta, zs_ev_zeta), (zeta_right, zs_ev_zeta_right)], + subgroup_x, + ); + let zs_numerator = self.sub_extension(zs_composition_eval, interpol_val); + let vanish_zeta = self.sub_extension(subgroup_x, zeta); + let vanish_zeta_right = self.sub_extension(subgroup_x, zeta_right); + let zs_denominator = self.mul_extension(vanish_zeta, vanish_zeta_right); + let zs_quotient = self.div_unsafe_extension(zs_numerator, zs_denominator); + sum = self.add_extension(sum, zs_quotient); - let evs = proof + // Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials. + let wire_evals = proof .unsalted_evals(2, config) .iter() .map(|&e| self.convert_to_ext(e)) .collect::>(); - let mut ev = self.zero_extension(); - for &e in &evs { - let a = alpha_powers.next(self); - ev = self.mul_add_extension(a, e, ev); + let mut wire_composition_eval = self.zero_extension(); + let mut alpha_powers_cloned = alpha_powers.clone(); + for &e in &wire_evals { + let a = alpha_powers_cloned.next(self); + wire_composition_eval = self.mul_add_extension(a, e, wire_composition_eval); } - let zeta_frob = zeta.frobenius(self); + let mut alpha_powers_cloned = alpha_powers.clone(); let wire_eval = os.wires.iter().fold(self.zero_extension(), |acc, &w| { - let a = alpha_powers.next(self); + let a = alpha_powers_cloned.next(self); self.mul_add_extension(a, w, acc) }); let mut alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1, self); @@ -233,13 +240,14 @@ impl, const D: usize> CircuitBuilder { self.mul_add_extension(a, w, acc) }) .frobenius(self); - let interpol_val = + let zeta_frob = zeta.frobenius(self); + let wire_interpol_val = self.interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)], subgroup_x); - let numerator = self.sub_extension(ev, interpol_val); - let vanish_frob = self.sub_extension(subgroup_x, zeta_frob); - let denominator = self.mul_extension(vanish, vanish_frob); - let quotient = self.div_unsafe_extension(numerator, denominator); - sum = self.add_extension(sum, quotient); + let wire_numerator = self.sub_extension(wire_composition_eval, wire_interpol_val); + let vanish_zeta_frob = self.sub_extension(subgroup_x, zeta_frob); + let wire_denominator = self.mul_extension(vanish_zeta, vanish_zeta_frob); + let wire_quotient = self.div_unsafe_extension(wire_numerator, wire_denominator); + sum = self.add_extension(sum, wire_quotient); sum } @@ -271,11 +279,10 @@ impl, const D: usize> CircuitBuilder { ); let mut old_x_index = self.zero(); // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. - // TODO: The verifier will need to check these constants at some point (out of circuit). let g = self.constant(F::MULTIPLICATIVE_GROUP_GENERATOR); let phi = self.constant(F::primitive_root_of_unity(n_log)); - let reversed_x = self.reverse_bits::<2>(x_index, n_log); + let reversed_x = self.reverse_limbs::<2>(x_index, n_log); let phi = self.exp(phi, reversed_x, n_log); let mut subgroup_x = self.mul(g, phi); @@ -316,7 +323,7 @@ impl, const D: usize> CircuitBuilder { if i > 0 { // Update the point x to x^arity. for _ in 0..config.reduction_arity_bits[i - 1] { - subgroup_x = self.mul(subgroup_x, subgroup_x); + subgroup_x = self.square(subgroup_x); } } domain_size = next_domain_size; @@ -335,7 +342,7 @@ impl, const D: usize> CircuitBuilder { *betas.last().unwrap(), ); for _ in 0..final_arity_bits { - subgroup_x = self.mul(subgroup_x, subgroup_x); + subgroup_x = self.square(subgroup_x); } // Final check of FRI. After all the reductions, we check that the final polynomial is equal diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 04432ed4..c6cde5c7 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -173,6 +173,7 @@ fn fri_combine_initial, const D: usize>( let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; + // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. let zs_evals = proof .unsalted_evals(3, config) .iter() @@ -190,6 +191,7 @@ fn fri_combine_initial, const D: usize>( let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); sum += zs_numerator / zs_denominator; + // Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials. let wire_evals = proof .unsalted_evals(2, config) .iter() @@ -204,10 +206,10 @@ fn fri_combine_initial, const D: usize>( // and one call at the end of the sum. let alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1); let wire_eval_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius(); - let wires_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); - let numerator = wire_composition_eval - wires_interpol.eval(subgroup_x); - let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); - sum += numerator / denominator; + let wire_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); + let wire_numerator = wire_composition_eval - wire_interpol.eval(subgroup_x); + let wire_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); + sum += wire_numerator / wire_denominator; sum } diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 9aefe6da..6c8eebb0 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -27,7 +27,7 @@ impl, const D: usize> CircuitBuilder { self.range_check(x, (64 - leading_zeros) as usize); } - pub(crate) fn reverse_bits(&mut self, x: Target, num_limbs: usize) -> Target { + pub(crate) fn reverse_limbs(&mut self, x: Target, num_limbs: usize) -> Target { let gate = self.add_gate(BaseSumGate::::new(num_limbs), vec![]); let sum = Target::wire(gate, BaseSumGate::::WIRE_SUM); self.route(x, sum); @@ -61,7 +61,7 @@ mod tests { builder.route(limbs[2], five); builder.route(limbs[3], one); let rev = builder.constant(F::from_canonical_u64(11)); - let revt = builder.reverse_bits::<2>(xt, 9); + let revt = builder.reverse_limbs::<2>(xt, 9); builder.route(revt, rev); builder.assert_leading_zeros(xt, 64 - 9); diff --git a/src/prover.rs b/src/prover.rs index 7a492eaa..df4d912a 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -115,7 +115,7 @@ pub(crate) fn prove, const D: usize>( let zeta = challenger.get_extension_challenge(); - let (opening_proof, mut openings) = timed!( + let (opening_proof, openings) = timed!( ListPolynomialCommitment::open_plonk( &[ &prover_data.constants_commitment, From 930e1171850ad763455474f51dba209110faeb5e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 11:35:10 +0200 Subject: [PATCH 058/117] Minor --- src/fri/recursive_verifier.rs | 5 +++-- src/fri/verifier.rs | 1 + src/gadgets/split_base.rs | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 1f6d8a1a..b35098e1 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -156,12 +156,13 @@ impl, const D: usize> CircuitBuilder { // - one for polynomials opened at `x` and `g x` // - one for polynomials opened at `x` and `x.frobenius()` - let evals = [0, 1, 4] + // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. + let single_evals = [0, 1, 4] .iter() .flat_map(|&i| proof.unsalted_evals(i, config)) .map(|&e| self.convert_to_ext(e)) .collect::>(); - let openings = os + let single_openings = os .constants .iter() .chain(&os.plonk_sigmas) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index c6cde5c7..b5a650bb 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -159,6 +159,7 @@ fn fri_combine_initial, const D: usize>( // - one for Zs, which are opened at `x` and `g x` // - one for wire polynomials, which are opened at `x` and its conjugate + // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. let single_evals = [0, 1, 4] .iter() .flat_map(|&i| proof.unsalted_evals(i, config)) diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 6c8eebb0..37f88409 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -2,7 +2,6 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::Extendable; use crate::gates::base_sum::BaseSumGate; use crate::target::Target; -use crate::util::ceil_div_usize; impl, const D: usize> CircuitBuilder { /// Split the given element into a list of targets, where each one represents a From bfd5f063842018594b2576802e72bb18522342ea Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 11:54:31 +0200 Subject: [PATCH 059/117] Hardcode Plonk polynomials indices and blinding flags. --- src/bin/bench_recursion.rs | 2 -- src/circuit_data.rs | 4 +-- src/fri/mod.rs | 16 ----------- src/fri/recursive_verifier.rs | 19 ++++++++----- src/fri/verifier.rs | 20 ++++++++------ src/gadgets/arithmetic.rs | 1 - src/plonk_common.rs | 52 +++++++++++++++++++++++++++++++++++ src/polynomial/commitment.rs | 16 ++--------- src/proof.rs | 14 +++++----- src/prover.rs | 3 -- 10 files changed, 86 insertions(+), 61 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 19f7716c..9fab2456 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -8,7 +8,6 @@ use plonky2::fri::FriConfig; use plonky2::gates::constant::ConstantGate; use plonky2::gates::gmimc::GMiMCGate; use plonky2::hash::GMIMC_ROUNDS; -use plonky2::prover::PLONK_BLINDING; use plonky2::witness::PartialWitness; fn main() { @@ -41,7 +40,6 @@ fn bench_prove, const D: usize>() { rate_bits: 3, reduction_arity_bits: vec![1], num_query_rounds: 1, - blinding: PLONK_BLINDING.to_vec(), }, }; diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 7429e62a..c8477464 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -7,7 +7,7 @@ use crate::gates::gate::GateRef; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; -use crate::prover::{prove, PLONK_BLINDING}; +use crate::prover::prove; use crate::verifier::verify; use crate::witness::PartialWitness; @@ -38,7 +38,6 @@ impl Default for CircuitConfig { rate_bits: 1, reduction_arity_bits: vec![1], num_query_rounds: 1, - blinding: vec![true], }, } } @@ -61,7 +60,6 @@ impl CircuitConfig { rate_bits: 3, reduction_arity_bits: vec![1], num_query_rounds: 1, - blinding: PLONK_BLINDING.to_vec(), }, } } diff --git a/src/fri/mod.rs b/src/fri/mod.rs index 6351a2af..87fe3db5 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -1,5 +1,3 @@ -use crate::polynomial::commitment::SALT_SIZE; - pub mod prover; mod recursive_verifier; pub mod verifier; @@ -22,20 +20,6 @@ pub struct FriConfig { /// Number of query rounds to perform. pub num_query_rounds: usize, - - /// Vector of the same length as the number of initial Merkle trees. - /// `blinding[i]==true` iff the i-th tree is salted. - pub blinding: Vec, -} - -impl FriConfig { - pub(crate) fn salt_size(&self, i: usize) -> usize { - if self.blinding[i] { - SALT_SIZE - } else { - 0 - } - } } fn fri_delta(rate_log: usize, conjecture: bool) -> f64 { diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index b35098e1..7c04d4a2 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -6,6 +6,7 @@ use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; use crate::plonk_challenger::RecursiveChallenger; +use crate::plonk_common::PlonkPolynomials; use crate::proof::{ FriInitialTreeProofTarget, FriProofTarget, FriQueryRoundTarget, HashTarget, OpeningSetTarget, }; @@ -157,11 +158,15 @@ impl, const D: usize> CircuitBuilder { // - one for polynomials opened at `x` and `x.frobenius()` // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. - let single_evals = [0, 1, 4] - .iter() - .flat_map(|&i| proof.unsalted_evals(i, config)) - .map(|&e| self.convert_to_ext(e)) - .collect::>(); + let single_evals = [ + PlonkPolynomials::CONSTANTS, + PlonkPolynomials::SIGMAS, + PlonkPolynomials::QUOTIENT, + ] + .iter() + .flat_map(|&p| proof.unsalted_evals(p)) + .map(|&e| self.convert_to_ext(e)) + .collect::>(); let single_openings = os .constants .iter() @@ -179,7 +184,7 @@ impl, const D: usize> CircuitBuilder { // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. let zs_evals = proof - .unsalted_evals(3, config) + .unsalted_evals(PlonkPolynomials::ZS) .iter() .map(|&e| self.convert_to_ext(e)) .collect::>(); @@ -217,7 +222,7 @@ impl, const D: usize> CircuitBuilder { // Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials. let wire_evals = proof - .unsalted_evals(2, config) + .unsalted_evals(PlonkPolynomials::WIRES) .iter() .map(|&e| self.convert_to_ext(e)) .collect::>(); diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index b5a650bb..d100797e 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -7,7 +7,7 @@ use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; -use crate::plonk_common::reduce_with_iter; +use crate::plonk_common::{reduce_with_iter, PlonkPolynomials}; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet}; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; @@ -157,13 +157,17 @@ fn fri_combine_initial, const D: usize>( // We will add three terms to `sum`: // - one for various polynomials which are opened at a single point `x` // - one for Zs, which are opened at `x` and `g x` - // - one for wire polynomials, which are opened at `x` and its conjugate + // - one for wire polynomials, which are opened at `x` and `x.frobenius()` // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. - let single_evals = [0, 1, 4] - .iter() - .flat_map(|&i| proof.unsalted_evals(i, config)) - .map(|&e| F::Extension::from_basefield(e)); + let single_evals = [ + PlonkPolynomials::CONSTANTS, + PlonkPolynomials::SIGMAS, + PlonkPolynomials::QUOTIENT, + ] + .iter() + .flat_map(|&p| proof.unsalted_evals(p)) + .map(|&e| F::Extension::from_basefield(e)); let single_openings = os .constants .iter() @@ -176,7 +180,7 @@ fn fri_combine_initial, const D: usize>( // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. let zs_evals = proof - .unsalted_evals(3, config) + .unsalted_evals(PlonkPolynomials::ZS) .iter() .map(|&e| F::Extension::from_basefield(e)); let zs_composition_eval = reduce_with_iter(zs_evals, alpha_powers.clone()); @@ -194,7 +198,7 @@ fn fri_combine_initial, const D: usize>( // Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials. let wire_evals = proof - .unsalted_evals(2, config) + .unsalted_evals(PlonkPolynomials::WIRES) .iter() .map(|&e| F::Extension::from_basefield(e)); let wire_composition_eval = reduce_with_iter(wire_evals, alpha_powers.clone()); diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 6250ea81..520f8cd9 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -371,7 +371,6 @@ mod tests { use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; use crate::fri::FriConfig; - use crate::prover::PLONK_BLINDING; use crate::witness::PartialWitness; #[test] diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 5c881fb1..c9f11b74 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -6,10 +6,62 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::gates::gate::GateRef; +use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +/// Holds the Merkle tree index and blinding flag of a set of polynomials used in FRI. +#[derive(Debug, Copy, Clone)] +pub struct PolynomialsIndexBlinding { + pub(crate) index: usize, + pub(crate) blinding: bool, +} +impl PolynomialsIndexBlinding { + pub fn salt_size(&self) -> usize { + if self.blinding { + SALT_SIZE + } else { + 0 + } + } +} +/// Holds the indices and blinding flags of the Plonk polynomials. +pub struct PlonkPolynomials; +impl PlonkPolynomials { + pub const CONSTANTS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { + index: 0, + blinding: false, + }; + pub const SIGMAS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { + index: 1, + blinding: false, + }; + pub const WIRES: PolynomialsIndexBlinding = PolynomialsIndexBlinding { + index: 2, + blinding: true, + }; + pub const ZS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { + index: 3, + blinding: true, + }; + pub const QUOTIENT: PolynomialsIndexBlinding = PolynomialsIndexBlinding { + index: 4, + blinding: true, + }; + + pub fn polynomials(i: usize) -> PolynomialsIndexBlinding { + match i { + 0 => Self::CONSTANTS, + 1 => Self::SIGMAS, + 2 => Self::WIRES, + 3 => Self::ZS, + 4 => Self::QUOTIENT, + _ => panic!("There are only 5 sets of polynomials in Plonk."), + } + } +} + /// Evaluate the vanishing polynomial at `x`. In this context, the vanishing polynomial is a random /// linear combination of gate constraints, plus some other terms relating to the permutation /// argument. All such terms should vanish on `H`. diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index c1fd08eb..caa61553 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -260,9 +260,9 @@ pub struct OpeningProofTarget { #[cfg(test)] mod tests { use anyhow::Result; - use rand::Rng; use super::*; + use crate::plonk_common::PlonkPolynomials; fn gen_random_test_case, const D: usize>( k: usize, @@ -288,17 +288,6 @@ mod tests { point } - fn gen_random_blindings() -> Vec { - let mut rng = rand::thread_rng(); - vec![ - rng.gen_bool(0.5), - rng.gen_bool(0.5), - rng.gen_bool(0.5), - rng.gen_bool(0.5), - rng.gen_bool(0.5), - ] - } - fn check_batch_polynomial_commitment, const D: usize>() -> Result<()> { let ks = [1, 2, 3, 5, 8]; let degree_log = 11; @@ -307,7 +296,6 @@ mod tests { rate_bits: 2, reduction_arity_bits: vec![2, 3, 1, 2], num_query_rounds: 3, - blinding: gen_random_blindings(), }; let lpcs = (0..5) @@ -315,7 +303,7 @@ mod tests { ListPolynomialCommitment::::new( gen_random_test_case(ks[i], degree_log), fri_config.rate_bits, - fri_config.blinding[i], + PlonkPolynomials::polynomials(i).blinding, ) }) .collect::>(); diff --git a/src/proof.rs b/src/proof.rs index e05e4093..7536f3ee 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -3,9 +3,9 @@ use std::convert::TryInto; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; -use crate::fri::FriConfig; use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; use crate::merkle_proofs::{MerkleProof, MerkleProofTarget}; +use crate::plonk_common::PolynomialsIndexBlinding; use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof, OpeningProofTarget}; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; @@ -99,9 +99,9 @@ pub struct FriInitialTreeProof { } impl FriInitialTreeProof { - pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> &[F] { - let evals = &self.evals_proofs[i].0; - &evals[..evals.len() - config.salt_size(i)] + pub(crate) fn unsalted_evals(&self, polynomials: PolynomialsIndexBlinding) -> &[F] { + let evals = &self.evals_proofs[polynomials.index].0; + &evals[..evals.len() - polynomials.salt_size()] } } @@ -110,9 +110,9 @@ pub struct FriInitialTreeProofTarget { } impl FriInitialTreeProofTarget { - pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> &[Target] { - let evals = &self.evals_proofs[i].0; - &evals[..evals.len() - config.salt_size(i)] + pub(crate) fn unsalted_evals(&self, polynomials: PolynomialsIndexBlinding) -> &[Target] { + let evals = &self.evals_proofs[polynomials.index].0; + &evals[..evals.len() - polynomials.salt_size()] } } diff --git a/src/prover.rs b/src/prover.rs index df4d912a..5ea6ddfd 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -19,9 +19,6 @@ use crate::vars::EvaluationVarsBase; use crate::wire::Wire; use crate::witness::PartialWitness; -/// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. -pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; - pub(crate) fn prove, const D: usize>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, From a71909ba1555005f953753678359789f02039d5c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 15:49:21 +0200 Subject: [PATCH 060/117] Implement `compute_z` and rewrite of `compute_vanishing_polys` --- src/circuit_builder.rs | 15 ++-- src/circuit_data.rs | 5 +- src/polynomial/commitment.rs | 55 +++++++++++--- src/prover.rs | 139 +++++++++++++++++++---------------- src/witness.rs | 24 +++++- 5 files changed, 154 insertions(+), 84 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 02d34fba..6f10c726 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -292,19 +292,13 @@ impl, const D: usize> CircuitBuilder { let subgroup = F::two_adic_subgroup(degree_bits); let constant_vecs = self.constant_polys(); - let constants_commitment = ListPolynomialCommitment::new( - constant_vecs.into_iter().map(|v| v.ifft()).collect(), - self.config.fri_config.rate_bits, - false, - ); + let constants_commitment = + ListPolynomialCommitment::new(constant_vecs, self.config.fri_config.rate_bits, false); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); - let sigmas_commitment = ListPolynomialCommitment::new( - sigma_vecs.into_iter().map(|v| v.ifft()).collect(), - self.config.fri_config.rate_bits, - false, - ); + let sigmas_commitment = + ListPolynomialCommitment::new(sigma_vecs, self.config.fri_config.rate_bits, false); let constants_root = constants_commitment.merkle_tree.root; let sigmas_root = sigmas_commitment.merkle_tree.root; @@ -339,6 +333,7 @@ impl, const D: usize> CircuitBuilder { config: self.config, degree_bits, gates, + max_filtered_constraint_degree_bits: 3, // TODO: compute this correctly once filters land. num_gate_constraints, k_is, circuit_digest, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 28dfbfac..3dc2d64d 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -143,6 +143,9 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The types of gates used in this circuit. pub(crate) gates: Vec>, + /// The largest number of constraints imposed by any gate. + pub(crate) max_filtered_constraint_degree_bits: usize, + /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, @@ -176,7 +179,7 @@ impl, const D: usize> CommonCircuitData { } pub fn quotient_degree(&self) -> usize { - self.constraint_degree() - 1 + 1 << self.max_filtered_constraint_degree_bits - 1 } pub fn total_constraints(&self) -> usize { diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index c1fd08eb..b11e1ca2 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -9,37 +9,73 @@ use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter}; -use crate::polynomial::polynomial::PolynomialCoeffs; +use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; -use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; +use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; pub struct ListPolynomialCommitment { pub polynomials: Vec>, + pub values: Vec>, pub merkle_tree: MerkleTree, pub degree: usize, + pub degree_log: usize, pub rate_bits: usize, pub blinding: bool, } impl ListPolynomialCommitment { - pub fn new(polynomials: Vec>, rate_bits: usize, blinding: bool) -> Self { - let degree = polynomials[0].len(); + /// Creates a list polynomial commitment for the polynomials interpolating the values in `values`. + pub fn new(values: Vec>, rate_bits: usize, blinding: bool) -> Self { + let degree = values[0].len(); + let polynomials = values.iter().map(|v| v.clone().ifft()).collect::>(); let lde_values = timed!( Self::lde_values(&polynomials, rate_bits, blinding), "to compute LDE" ); + Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + } + + /// Creates a list polynomial commitment for the polynomials `polynomials`. + pub fn new_from_polys( + polynomials: Vec>, + rate_bits: usize, + blinding: bool, + ) -> Self { + let degree = polynomials[0].len(); + let values = polynomials + .iter() + .map(|v| v.clone().fft()) + .collect::>(); + let lde_values = timed!( + Self::lde_values(&polynomials, rate_bits, blinding), + "to compute LDE" + ); + + Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + } + + fn new_from_data( + polynomials: Vec>, + values: Vec>, + lde_values: Vec>, + degree: usize, + rate_bits: usize, + blinding: bool, + ) -> Self { let mut leaves = timed!(transpose(&lde_values), "to transpose LDEs"); reverse_index_bits_in_place(&mut leaves); let merkle_tree = timed!(MerkleTree::new(leaves, false), "to build Merkle tree"); Self { polynomials, + values, merkle_tree, degree, + degree_log: log2_strict(degree), rate_bits, blinding, } @@ -71,9 +107,8 @@ impl ListPolynomialCommitment { .collect() } - pub fn leaf(&self, index: usize) -> &[F] { - let leaf = &self.merkle_tree.leaves[index]; - &leaf[0..leaf.len() - if self.blinding { SALT_SIZE } else { 0 }] + pub fn original_value(&self, index: usize) -> Vec { + self.values.iter().map(|v| v.values[index]).collect() } /// Takes the commitments to the constants - sigmas - wires - zs - quotient — polynomials, @@ -88,7 +123,7 @@ impl ListPolynomialCommitment { F: Extendable, { assert!(D > 1, "Not implemented for D=1."); - let degree_log = log2_strict(commitments[0].degree); + let degree_log = commitments[0].degree_log; let g = F::Extension::primitive_root_of_unity(degree_log); for p in &[zeta, g * zeta] { assert_ne!( @@ -267,11 +302,11 @@ mod tests { fn gen_random_test_case, const D: usize>( k: usize, degree_log: usize, - ) -> Vec> { + ) -> Vec> { let degree = 1 << degree_log; (0..k) - .map(|_| PolynomialCoeffs::new(F::rand_vec(degree))) + .map(|_| PolynomialValues::new(F::rand_vec(degree))) .collect() } diff --git a/src/prover.rs b/src/prover.rs index 116a6b24..08362ac3 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -17,7 +17,7 @@ use crate::timed; use crate::util::transpose; use crate::vars::EvaluationVarsBase; use crate::wire::Wire; -use crate::witness::PartialWitness; +use crate::witness::{PartialWitness, Witness}; /// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; @@ -31,10 +31,10 @@ pub(crate) fn prove, const D: usize>( let start_proof_gen = Instant::now(); - let mut witness = inputs; + let mut partial_witness = inputs; info!("Running {} generators", prover_data.generators.len()); timed!( - generate_partial_witness(&mut witness, &prover_data.generators), + generate_partial_witness(&mut partial_witness, &prover_data.generators), "to generate witness" ); @@ -42,12 +42,15 @@ pub(crate) fn prove, const D: usize>( let num_wires = config.num_wires; let num_challenges = config.num_challenges; let quotient_degree = common_data.quotient_degree(); - let degree = common_data.degree(); - let wires_polynomials: Vec> = timed!( - (0..num_wires) - .into_par_iter() - .map(|i| compute_wire_polynomial(i, &witness, degree)) + + let witness = partial_witness.full_witness(degree, num_wires); + + let wires_values: Vec> = timed!( + witness + .wire_values + .iter() + .map(|column| PolynomialValues::new(column.clone())) .collect(), "to compute wire polynomials" ); @@ -55,7 +58,7 @@ pub(crate) fn prove, const D: usize>( // TODO: Could try parallelizing the transpose, or not doing it explicitly, instead having // merkle_root_bit_rev_order do it implicitly. let wires_commitment = timed!( - ListPolynomialCommitment::new(wires_polynomials, fri_config.rate_bits, true), + ListPolynomialCommitment::new(wires_values, fri_config.rate_bits, true), "to compute wires commitment" ); @@ -68,7 +71,10 @@ pub(crate) fn prove, const D: usize>( let betas = challenger.get_n_challenges(num_challenges); let gammas = challenger.get_n_challenges(num_challenges); - let plonk_z_vecs = timed!(compute_zs(&common_data), "to compute Z's"); + let plonk_z_vecs = timed!( + compute_zs(&witness, &betas, &gammas, &prover_data, &common_data), + "to compute Z's" + ); let plonk_zs_commitment = timed!( ListPolynomialCommitment::new(plonk_z_vecs, fri_config.rate_bits, true), @@ -107,7 +113,11 @@ pub(crate) fn prove, const D: usize>( ); let quotient_polys_commitment = timed!( - ListPolynomialCommitment::new(all_quotient_poly_chunks, fri_config.rate_bits, true), + ListPolynomialCommitment::new_from_polys( + all_quotient_poly_chunks, + fri_config.rate_bits, + true + ), "to commit to quotient polys" ); @@ -146,37 +156,44 @@ pub(crate) fn prove, const D: usize>( } fn compute_zs, const D: usize>( + witness: &Witness, + betas: &[F], + gammas: &[F], + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, -) -> Vec> { +) -> Vec> { (0..common_data.config.num_challenges) - .map(|i| compute_z(common_data, i)) + .map(|i| compute_z(witness, betas[i], gammas[i], prover_data, common_data)) .collect() } fn compute_z, const D: usize>( + witness: &Witness, + beta: F, + gamma: F, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, - _i: usize, -) -> PolynomialCoeffs { - todo!() - // let subgroup = - // let mut plonk_z_points = vec![F::ONE]; - // let k_is = common_data.k_is; - // for i in 1..common_data.degree() { - // let x = subgroup[i - 1]; - // let mut numerator = F::ONE; - // let mut denominator = F::ONE; - // for j in 0..NUM_ROUTED_WIRES { - // let wire_value = witness.get_indices(i - 1, j); - // let k_i = k_is[j]; - // let s_id = k_i * x; - // let s_sigma = sigma_values[j][8 * (i - 1)]; - // numerator = numerator * (wire_value + beta * s_id + gamma); - // denominator = denominator * (wire_value + beta * s_sigma + gamma); - // } - // let last = *plonk_z_points.last().unwrap(); - // plonk_z_points.push(last * numerator / denominator); - // } - // plonk_z_points +) -> PolynomialValues { + let subgroup = &prover_data.subgroup; + let mut plonk_z_points = vec![F::ONE]; + let k_is = &common_data.k_is; + for i in 1..common_data.degree() { + let x = subgroup[i - 1]; + let mut numerator = F::ONE; + let mut denominator = F::ONE; + let s_sigmas = prover_data.sigmas_commitment.original_value(i - 1); + for j in 0..common_data.config.num_routed_wires { + let wire_value = witness.get_wire(i - 1, j); + let k_i = k_is[j]; + let s_id = k_i * x; + let s_sigma = s_sigmas[j]; + numerator = numerator * (wire_value + beta * s_id + gamma); + denominator = denominator * (wire_value + beta * s_sigma + gamma); + } + let last = *plonk_z_points.last().unwrap(); + plonk_z_points.push(last * numerator / denominator); + } + plonk_z_points.into() } fn compute_vanishing_polys, const D: usize>( @@ -188,21 +205,38 @@ fn compute_vanishing_polys, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec> { - let lde_size = common_data.lde_size(); - let lde_gen = common_data.lde_generator(); let num_challenges = common_data.config.num_challenges; + let points = F::two_adic_subgroup( + common_data.degree_bits + common_data.max_filtered_constraint_degree_bits, + ); + let lde_size = points.len(); + + let commitment_to_lde = |comm: &ListPolynomialCommitment| -> Vec> { + comm.polynomials + .iter() + .map(|p| p.lde(common_data.max_filtered_constraint_degree_bits).fft()) + .collect() + }; + + let constants_lde = commitment_to_lde(&prover_data.constants_commitment); + let sigmas_lde = commitment_to_lde(&prover_data.sigmas_commitment); + let wires_lde = commitment_to_lde(wires_commitment); + let zs_lde = commitment_to_lde(plonk_zs_commitment); + + let get_at_index = |ldes: &[PolynomialValues], i: usize| { + ldes.iter().map(|l| l.values[i]).collect::>() + }; - let points = F::cyclic_subgroup_known_order(lde_gen, lde_size); let values: Vec> = points .into_par_iter() .enumerate() .map(|(i, x)| { let i_next = (i + 1) % lde_size; - let local_wires = wires_commitment.leaf(i); - let local_constants = prover_data.constants_commitment.leaf(i); - let local_plonk_zs = plonk_zs_commitment.leaf(i); - let next_plonk_zs = plonk_zs_commitment.leaf(i_next); - let s_sigmas = prover_data.sigmas_commitment.leaf(i); + let local_constants = &get_at_index(&constants_lde, i); + let s_sigmas = &get_at_index(&sigmas_lde, i); + let local_wires = &get_at_index(&wires_lde, i); + let local_plonk_zs = &get_at_index(&zs_lde, i); + let next_plonk_zs = &get_at_index(&zs_lde, i_next); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_challenges); @@ -230,22 +264,3 @@ fn compute_vanishing_polys, const D: usize>( .map(PolynomialValues::new) .collect() } - -fn compute_wire_polynomial( - input: usize, - witness: &PartialWitness, - degree: usize, -) -> PolynomialCoeffs { - let wire_values = (0..degree) - // Some gates do not use all wires, and we do not require that generators populate unused - // wires, so some wire values will not be set. We can set these to any value; here we - // arbitrary pick zero. Ideally we would verify that no constraints operate on these unset - // wires, but that isn't trivial. - .map(|gate| { - witness - .try_get_wire(Wire { gate, input }) - .unwrap_or(F::ZERO) - }) - .collect(); - PolynomialValues::new(wire_values).ifft() -} diff --git a/src/witness.rs b/src/witness.rs index e71b1cfb..a870059b 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,11 +1,23 @@ use std::collections::HashMap; +use std::convert::TryInto; +use crate::circuit_data::{CircuitConfig, CommonCircuitData}; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::target::Target; use crate::wire::Wire; -use std::convert::TryInto; + +#[derive(Clone, Debug)] +pub struct Witness { + pub(crate) wire_values: Vec>, +} + +impl Witness { + pub fn get_wire(&self, gate: usize, input: usize) -> F { + self.wire_values[input][gate] + } +} #[derive(Clone, Debug)] pub struct PartialWitness { @@ -121,6 +133,16 @@ impl PartialWitness { self.set_target(target, value); } } + + pub fn full_witness(self, degree: usize, num_wires: usize) -> Witness { + let mut wire_values = vec![vec![F::ZERO; degree]; num_wires]; + self.target_values.into_iter().for_each(|(t, v)| { + if let Target::Wire(Wire { gate, input }) = t { + wire_values[input][gate] = v; + } + }); + Witness { wire_values } + } } impl Default for PartialWitness { From ad5c18b4997451f27cca9647a2f0eafb548013ef Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 16:23:15 +0200 Subject: [PATCH 061/117] Comments and Clippy --- src/circuit_data.rs | 2 +- src/field/extension_field/quadratic.rs | 2 +- src/polynomial/commitment.rs | 4 ++-- src/polynomial/polynomial.rs | 7 ++++++- src/prover.rs | 20 ++++++++++++++------ src/witness.rs | 1 - 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 3dc2d64d..aa734650 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -179,7 +179,7 @@ impl, const D: usize> CommonCircuitData { } pub fn quotient_degree(&self) -> usize { - 1 << self.max_filtered_constraint_degree_bits - 1 + ((1 << self.max_filtered_constraint_degree_bits) - 1) * self.degree() } pub fn total_constraints(&self) -> usize { diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index 4dc712af..af21ad60 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -194,7 +194,7 @@ impl DivAssign for QuadraticCrandallField { #[cfg(test)] mod tests { use crate::field::extension_field::quadratic::QuadraticCrandallField; - use crate::field::extension_field::{FieldExtension, Frobenius, OEF}; + use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; #[test] diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index b11e1ca2..5cd1f219 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -12,7 +12,7 @@ use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter}; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; -use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transpose}; +use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; @@ -107,7 +107,7 @@ impl ListPolynomialCommitment { .collect() } - pub fn original_value(&self, index: usize) -> Vec { + pub fn original_values(&self, index: usize) -> Vec { self.values.iter().map(|v| v.values[index]).collect() } diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 9f605051..0660be1a 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -128,7 +128,12 @@ impl PolynomialCoeffs { } pub(crate) fn padded(&self, new_len: usize) -> Self { - assert!(new_len >= self.len()); + assert!( + new_len >= self.len(), + "Trying to pad a polynomial of length {} to a length of {}.", + self.len(), + new_len + ); let mut coeffs = self.coeffs.clone(); coeffs.resize(new_len, F::ZERO); Self { coeffs } diff --git a/src/prover.rs b/src/prover.rs index 08362ac3..6371d1fd 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -6,17 +6,15 @@ use rayon::prelude::*; use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; use crate::field::extension_field::Extendable; use crate::field::fft::ifft; -use crate::field::field::Field; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; use crate::plonk_common::eval_vanishing_poly_base; use crate::polynomial::commitment::ListPolynomialCommitment; -use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::polynomial::polynomial::PolynomialValues; use crate::proof::Proof; use crate::timed; use crate::util::transpose; use crate::vars::EvaluationVarsBase; -use crate::wire::Wire; use crate::witness::{PartialWitness, Witness}; /// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. @@ -104,7 +102,15 @@ pub(crate) fn prove, const D: usize>( .into_par_iter() .flat_map(|vanishing_poly| { let vanishing_poly_coeff = ifft(vanishing_poly); + // TODO: run `padded` when the division works. let quotient_poly_coeff = vanishing_poly_coeff.divide_by_z_h(degree); + let x = F::rand(); + assert!( + quotient_poly_coeff.eval(x) * (x.exp(degree as u64) - F::ONE) + != vanishing_poly_coeff.eval(x), + "That's good news, this should fail! The division by z_h doesn't work yet,\ + most likely because compute_vanishing_polys isn't complete (doesn't use filters for example)." + ); // Split t into degree-n chunks. quotient_poly_coeff.chunks(degree) }) @@ -181,14 +187,14 @@ fn compute_z, const D: usize>( let x = subgroup[i - 1]; let mut numerator = F::ONE; let mut denominator = F::ONE; - let s_sigmas = prover_data.sigmas_commitment.original_value(i - 1); + let s_sigmas = prover_data.sigmas_commitment.original_values(i - 1); for j in 0..common_data.config.num_routed_wires { let wire_value = witness.get_wire(i - 1, j); let k_i = k_is[j]; let s_id = k_i * x; let s_sigma = s_sigmas[j]; - numerator = numerator * (wire_value + beta * s_id + gamma); - denominator = denominator * (wire_value + beta * s_sigma + gamma); + numerator *= wire_value + beta * s_id + gamma; + denominator *= wire_value + beta * s_sigma + gamma; } let last = *plonk_z_points.last().unwrap(); plonk_z_points.push(last * numerator / denominator); @@ -211,6 +217,7 @@ fn compute_vanishing_polys, const D: usize>( ); let lde_size = points.len(); + // Low-degree extend the polynomials commited in `comm` to the subgroup of size `lde_size`. let commitment_to_lde = |comm: &ListPolynomialCommitment| -> Vec> { comm.polynomials .iter() @@ -223,6 +230,7 @@ fn compute_vanishing_polys, const D: usize>( let wires_lde = commitment_to_lde(wires_commitment); let zs_lde = commitment_to_lde(plonk_zs_commitment); + // Retrieve the polynomial values at index `i`. let get_at_index = |ldes: &[PolynomialValues], i: usize| { ldes.iter().map(|l| l.values[i]).collect::>() }; diff --git a/src/witness.rs b/src/witness.rs index a870059b..ad810af8 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use std::convert::TryInto; -use crate::circuit_data::{CircuitConfig, CommonCircuitData}; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; From 79e99148ef4882e548d695f0b139fccaad79fc93 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 16:31:24 +0200 Subject: [PATCH 062/117] Minor --- src/circuit_data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index aa734650..736eb933 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -143,7 +143,7 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The types of gates used in this circuit. pub(crate) gates: Vec>, - /// The largest number of constraints imposed by any gate. + /// The maximum degree of a filter times a constraint by any gate. pub(crate) max_filtered_constraint_degree_bits: usize, /// The largest number of constraints imposed by any gate. From f27620ca901c55f4b5ec71bb8e36476d998802d7 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 19:40:41 +0200 Subject: [PATCH 063/117] First impl --- src/fri/verifier.rs | 44 ++++++++++++++++++++++++-------------------- src/proof.rs | 4 ++-- src/util/mod.rs | 1 + src/util/scaling.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 src/util/scaling.rs diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index b5a650bb..2ce33bd0 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -9,6 +9,7 @@ use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_iter; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet}; +use crate::util::scaling::ScalingFactor; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity @@ -151,7 +152,7 @@ fn fri_combine_initial, const D: usize>( assert!(D > 1, "Not implemented for D=1."); let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; let subgroup_x = F::Extension::from_basefield(subgroup_x); - let mut alpha_powers = alpha.powers(); + let mut alpha = ScalingFactor::new(alpha); let mut sum = F::Extension::ZERO; // We will add three terms to `sum`: @@ -160,53 +161,56 @@ fn fri_combine_initial, const D: usize>( // - one for wire polynomials, which are opened at `x` and its conjugate // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. - let single_evals = [0, 1, 4] - .iter() - .flat_map(|&i| proof.unsalted_evals(i, config)) - .map(|&e| F::Extension::from_basefield(e)); + let single_evals = &proof + .unsalted_evals(0, config) + .into_iter() + .chain(proof.unsalted_evals(1, config)) + .chain(proof.unsalted_evals(4, config)) + .map(|e| F::Extension::from_basefield(e)); let single_openings = os .constants - .iter() - .chain(&os.plonk_s_sigmas) - .chain(&os.quotient_polys); - let single_diffs = single_evals.zip(single_openings).map(|(e, &o)| e - o); - let single_numerator = reduce_with_iter(single_diffs, &mut alpha_powers); + .clone() + .into_iter() + .chain(os.plonk_s_sigmas.clone()) + .chain(os.quotient_polys.clone()); + let single_diffs = single_evals.zip(single_openings).map(|(e, o)| e - o); + dbg!(single_diffs.rev()); + let single_numerator = alpha.scale(single_diffs); let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; + alpha.shift(sum); // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. let zs_evals = proof .unsalted_evals(3, config) .iter() .map(|&e| F::Extension::from_basefield(e)); - let zs_composition_eval = reduce_with_iter(zs_evals, alpha_powers.clone()); + let zs_composition_eval = alpha.clone().scale(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; let zs_interpol = interpolant(&[ - (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), - ( - zeta_right, - reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - ), + (zeta, alpha.clone().scale(&os.plonk_zs)), + (zeta_right, alpha.scale(&os.plonk_zs_right)), ]); let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); sum += zs_numerator / zs_denominator; + alpha.shift(sum); // Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials. let wire_evals = proof .unsalted_evals(2, config) .iter() .map(|&e| F::Extension::from_basefield(e)); - let wire_composition_eval = reduce_with_iter(wire_evals, alpha_powers.clone()); + let wire_composition_eval = alpha.clone().scale(wire_evals); let zeta_frob = zeta.frobenius(); - let wire_eval = reduce_with_iter(&os.wires, alpha_powers.clone()); + let wire_eval = alpha.clone().scale(&os.wires); // We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism. // Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities: // `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)` // So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha, // and one call at the end of the sum. - let alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1); - let wire_eval_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius(); + let mut alpha_frob = alpha.repeated_frobenius(D - 1); + let wire_eval_frob = alpha_frob.scale(&os.wires).frobenius(); let wire_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); let wire_numerator = wire_composition_eval - wire_interpol.eval(subgroup_x); let wire_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); diff --git a/src/proof.rs b/src/proof.rs index e05e4093..a029114a 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -99,9 +99,9 @@ pub struct FriInitialTreeProof { } impl FriInitialTreeProof { - pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> &[F] { + pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> Vec { let evals = &self.evals_proofs[i].0; - &evals[..evals.len() - config.salt_size(i)] + evals[..evals.len() - config.salt_size(i)].to_vec() } } diff --git a/src/util/mod.rs b/src/util/mod.rs index 09bd4e72..f901b0af 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,3 +1,4 @@ +pub mod scaling; pub(crate) mod timing; use crate::field::field::Field; diff --git a/src/util/scaling.rs b/src/util/scaling.rs new file mode 100644 index 00000000..36148d85 --- /dev/null +++ b/src/util/scaling.rs @@ -0,0 +1,41 @@ +use std::borrow::Borrow; + +use crate::field::extension_field::Frobenius; +use crate::field::field::Field; + +#[derive(Copy, Clone)] +pub struct ScalingFactor { + base: F, + count: u64, +} + +impl ScalingFactor { + pub fn new(base: F) -> Self { + Self { base, count: 0 } + } + + pub fn mul(&mut self, x: F) -> F { + self.count += 1; + self.base * x + } + + pub fn scale(&mut self, iter: impl DoubleEndedIterator>) -> F { + iter.rev().fold(F::ZERO, |acc, x| self.mul(acc) + x) + } + + pub fn shift(&mut self, x: F) -> F { + let tmp = self.base.exp(self.count) * x; + self.count = 0; + tmp + } + + pub fn repeated_frobenius(&self, count: usize) -> Self + where + F: Frobenius, + { + Self { + base: self.base.repeated_frobenius(count), + count: self.count, + } + } +} From fe9cd3f76b2da36fd4a8bfd84d830fe91f72d618 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 21:34:04 +0200 Subject: [PATCH 064/117] Working commitments and verifier --- src/fri/verifier.rs | 26 +++++++++++-------- src/polynomial/commitment.rs | 48 +++++++++++++++++++++++------------- src/util/scaling.rs | 32 +++++++++++++++++++++--- 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index f27070b2..50537a9e 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -168,17 +168,22 @@ fn fri_combine_initial, const D: usize>( ] .iter() .flat_map(|&p| proof.unsalted_evals(p)) - .map(|&e| F::Extension::from_basefield(e)); + .map(|&e| F::Extension::from_basefield(e)) + .collect::>(); let single_openings = os .constants .iter() .chain(&os.plonk_s_sigmas) - .chain(&os.quotient_polys); - let single_diffs = single_evals.zip(single_openings).map(|(e, &o)| e - o); - let single_numerator = reduce_with_iter(single_diffs, &mut alpha_powers); + .chain(&os.quotient_polys) + .collect::>(); + let single_diffs = single_evals + .into_iter() + .zip(single_openings) + .map(|(e, &o)| e - o); + let single_numerator = alpha.scale(single_diffs); let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; - alpha.shift(sum); + alpha.reset(); // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. let zs_evals = proof @@ -188,13 +193,13 @@ fn fri_combine_initial, const D: usize>( let zs_composition_eval = alpha.clone().scale(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; let zs_interpol = interpolant(&[ - (zeta, alpha.clone().scale(&os.plonk_zs)), - (zeta_right, alpha.scale(&os.plonk_zs_right)), + (zeta, alpha.clone().scale(os.plonk_zs.iter())), + (zeta_right, alpha.scale(os.plonk_zs_right.iter())), ]); let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); + sum = alpha.shift(sum); sum += zs_numerator / zs_denominator; - alpha.shift(sum); // Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials. let wire_evals = proof @@ -203,17 +208,18 @@ fn fri_combine_initial, const D: usize>( .map(|&e| F::Extension::from_basefield(e)); let wire_composition_eval = alpha.clone().scale(wire_evals); let zeta_frob = zeta.frobenius(); - let wire_eval = alpha.clone().scale(&os.wires); + let wire_eval = alpha.clone().scale(os.wires.iter()); // We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism. // Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities: // `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)` // So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha, // and one call at the end of the sum. let mut alpha_frob = alpha.repeated_frobenius(D - 1); - let wire_eval_frob = alpha_frob.scale(&os.wires).frobenius(); + let wire_eval_frob = alpha_frob.scale(os.wires.iter()).frobenius(); let wire_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); let wire_numerator = wire_composition_eval - wire_interpol.eval(subgroup_x); let wire_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); + sum = alpha_frob.shift(sum); sum += wire_numerator / wire_denominator; sum diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index caa61553..9fa7bc01 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -8,10 +8,11 @@ use crate::field::lagrange::interpolant; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter}; +use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter, PlonkPolynomials}; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; +use crate::util::scaling::ScalingFactor; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; @@ -110,20 +111,24 @@ impl ListPolynomialCommitment { challenger.observe_opening_set(&os); let alpha = challenger.get_extension_challenge(); - let mut alpha_powers = alpha.powers(); + let mut alpha = ScalingFactor::new(alpha); // Final low-degree polynomial that goes into FRI. let mut final_poly = PolynomialCoeffs::empty(); // Polynomials opened at a single point. - let single_polys = [0, 1, 4] - .iter() - .flat_map(|&i| &commitments[i].polynomials) - .map(|p| p.to_extension()); + let single_polys = [ + PlonkPolynomials::CONSTANTS, + PlonkPolynomials::SIGMAS, + PlonkPolynomials::QUOTIENT, + ] + .iter() + .flat_map(|&p| &commitments[p.index].polynomials) + .map(|p| p.to_extension()); let single_os = [&os.constants, &os.plonk_s_sigmas, &os.quotient_polys]; let single_evals = single_os.iter().flat_map(|v| v.iter()); - let single_composition_poly = reduce_polys_with_iter(single_polys, alpha_powers.clone()); - let single_composition_eval = reduce_with_iter(single_evals, &mut alpha_powers); + let single_composition_poly = alpha.clone().scale_polys(single_polys); + let single_composition_eval = alpha.scale(single_evals); let single_quotient = Self::compute_quotient( &[zeta], @@ -131,13 +136,17 @@ impl ListPolynomialCommitment { &single_composition_poly, ); final_poly = &final_poly + &single_quotient; + alpha.reset(); // Zs polynomials are opened at `zeta` and `g*zeta`. - let zs_polys = commitments[3].polynomials.iter().map(|p| p.to_extension()); - let zs_composition_poly = reduce_polys_with_iter(zs_polys, alpha_powers.clone()); + let zs_polys = commitments[PlonkPolynomials::ZS.index] + .polynomials + .iter() + .map(|p| p.to_extension()); + let zs_composition_poly = alpha.clone().scale_polys(zs_polys); let zs_composition_evals = [ - reduce_with_iter(&os.plonk_zs, alpha_powers.clone()), - reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + alpha.clone().scale(os.plonk_zs.iter()), + alpha.scale(os.plonk_zs_right.iter()), ]; let zs_quotient = Self::compute_quotient( @@ -145,17 +154,21 @@ impl ListPolynomialCommitment { &zs_composition_evals, &zs_composition_poly, ); + final_poly = alpha.shift_poly(final_poly); final_poly = &final_poly + &zs_quotient; // When working in an extension field, need to check that wires are in the base field. // Check this by opening the wires polynomials at `zeta` and `zeta.frobenius()` and using the fact that // a polynomial `f` is over the base field iff `f(z).frobenius()=f(z.frobenius())` with high probability. - let wire_polys = commitments[2].polynomials.iter().map(|p| p.to_extension()); - let wire_composition_poly = reduce_polys_with_iter(wire_polys, alpha_powers.clone()); - let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); + let wire_polys = commitments[PlonkPolynomials::WIRES.index] + .polynomials + .iter() + .map(|p| p.to_extension()); + let wire_composition_poly = alpha.clone().scale_polys(wire_polys); + let mut alpha_frob = alpha.repeated_frobenius(D - 1); let wire_composition_evals = [ - reduce_with_iter(&os.wires, alpha_powers.clone()), - reduce_with_iter(&wire_evals_frob, alpha_powers), + alpha.clone().scale(os.wires.iter()), + alpha_frob.scale(os.wires.iter()).frobenius(), ]; let wires_quotient = Self::compute_quotient( @@ -163,6 +176,7 @@ impl ListPolynomialCommitment { &wire_composition_evals, &wire_composition_poly, ); + final_poly = alpha_frob.shift_poly(final_poly); final_poly = &final_poly + &wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); diff --git a/src/util/scaling.rs b/src/util/scaling.rs index 36148d85..e29edf0e 100644 --- a/src/util/scaling.rs +++ b/src/util/scaling.rs @@ -2,8 +2,9 @@ use std::borrow::Borrow; use crate::field::extension_field::Frobenius; use crate::field::field::Field; +use crate::polynomial::polynomial::PolynomialCoeffs; -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct ScalingFactor { base: F, count: u64, @@ -14,13 +15,28 @@ impl ScalingFactor { Self { base, count: 0 } } - pub fn mul(&mut self, x: F) -> F { + fn mul(&mut self, x: F) -> F { self.count += 1; self.base * x } + fn mul_poly(&mut self, p: PolynomialCoeffs) -> PolynomialCoeffs { + self.count += 1; + &p * self.base + } + pub fn scale(&mut self, iter: impl DoubleEndedIterator>) -> F { - iter.rev().fold(F::ZERO, |acc, x| self.mul(acc) + x) + iter.rev() + .fold(F::ZERO, |acc, x| self.mul(acc) + *x.borrow()) + } + + pub fn scale_polys( + &mut self, + polys: impl DoubleEndedIterator>>, + ) -> PolynomialCoeffs { + polys.rev().fold(PolynomialCoeffs::empty(), |acc, x| { + &self.mul_poly(acc) + x.borrow() + }) } pub fn shift(&mut self, x: F) -> F { @@ -29,6 +45,16 @@ impl ScalingFactor { tmp } + pub fn shift_poly(&mut self, p: PolynomialCoeffs) -> PolynomialCoeffs { + let tmp = &p * self.base.exp(self.count); + self.count = 0; + tmp + } + + pub fn reset(&mut self) { + self.count = 0; + } + pub fn repeated_frobenius(&self, count: usize) -> Self where F: Frobenius, From 92e0f60c2318c4f7f92ce750229e540b5845604c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 21:57:31 +0200 Subject: [PATCH 065/117] Clippy --- src/fri/verifier.rs | 2 +- src/polynomial/commitment.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 50537a9e..b94807f1 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -7,7 +7,7 @@ use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{reduce_with_iter, PlonkPolynomials}; +use crate::plonk_common::PlonkPolynomials; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet}; use crate::util::scaling::ScalingFactor; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 9fa7bc01..c09e1e20 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -8,7 +8,7 @@ use crate::field::lagrange::interpolant; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter, PlonkPolynomials}; +use crate::plonk_common::PlonkPolynomials; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; From 9db7dce738ded406ba23d99356c3a70be4e67bd9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 22:06:53 +0200 Subject: [PATCH 066/117] scale -> reduce --- src/fri/verifier.rs | 18 +++++++++--------- src/polynomial/commitment.rs | 20 ++++++++++---------- src/util/scaling.rs | 8 ++++---- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index b94807f1..e2c8568f 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -9,7 +9,7 @@ use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; use crate::plonk_common::PlonkPolynomials; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet}; -use crate::util::scaling::ScalingFactor; +use crate::util::scaling::ReducingFactor; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity @@ -152,7 +152,7 @@ fn fri_combine_initial, const D: usize>( assert!(D > 1, "Not implemented for D=1."); let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; let subgroup_x = F::Extension::from_basefield(subgroup_x); - let mut alpha = ScalingFactor::new(alpha); + let mut alpha = ReducingFactor::new(alpha); let mut sum = F::Extension::ZERO; // We will add three terms to `sum`: @@ -180,7 +180,7 @@ fn fri_combine_initial, const D: usize>( .into_iter() .zip(single_openings) .map(|(e, &o)| e - o); - let single_numerator = alpha.scale(single_diffs); + let single_numerator = alpha.reduce(single_diffs); let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; alpha.reset(); @@ -190,11 +190,11 @@ fn fri_combine_initial, const D: usize>( .unsalted_evals(PlonkPolynomials::ZS) .iter() .map(|&e| F::Extension::from_basefield(e)); - let zs_composition_eval = alpha.clone().scale(zs_evals); + let zs_composition_eval = alpha.clone().reduce(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; let zs_interpol = interpolant(&[ - (zeta, alpha.clone().scale(os.plonk_zs.iter())), - (zeta_right, alpha.scale(os.plonk_zs_right.iter())), + (zeta, alpha.clone().reduce(os.plonk_zs.iter())), + (zeta_right, alpha.reduce(os.plonk_zs_right.iter())), ]); let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); @@ -206,16 +206,16 @@ fn fri_combine_initial, const D: usize>( .unsalted_evals(PlonkPolynomials::WIRES) .iter() .map(|&e| F::Extension::from_basefield(e)); - let wire_composition_eval = alpha.clone().scale(wire_evals); + let wire_composition_eval = alpha.clone().reduce(wire_evals); let zeta_frob = zeta.frobenius(); - let wire_eval = alpha.clone().scale(os.wires.iter()); + let wire_eval = alpha.clone().reduce(os.wires.iter()); // We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism. // Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities: // `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)` // So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha, // and one call at the end of the sum. let mut alpha_frob = alpha.repeated_frobenius(D - 1); - let wire_eval_frob = alpha_frob.scale(os.wires.iter()).frobenius(); + let wire_eval_frob = alpha_frob.reduce(os.wires.iter()).frobenius(); let wire_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); let wire_numerator = wire_composition_eval - wire_interpol.eval(subgroup_x); let wire_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index c09e1e20..9d07975e 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -12,7 +12,7 @@ use crate::plonk_common::PlonkPolynomials; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; -use crate::util::scaling::ScalingFactor; +use crate::util::scaling::ReducingFactor; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; @@ -111,7 +111,7 @@ impl ListPolynomialCommitment { challenger.observe_opening_set(&os); let alpha = challenger.get_extension_challenge(); - let mut alpha = ScalingFactor::new(alpha); + let mut alpha = ReducingFactor::new(alpha); // Final low-degree polynomial that goes into FRI. let mut final_poly = PolynomialCoeffs::empty(); @@ -127,8 +127,8 @@ impl ListPolynomialCommitment { .map(|p| p.to_extension()); let single_os = [&os.constants, &os.plonk_s_sigmas, &os.quotient_polys]; let single_evals = single_os.iter().flat_map(|v| v.iter()); - let single_composition_poly = alpha.clone().scale_polys(single_polys); - let single_composition_eval = alpha.scale(single_evals); + let single_composition_poly = alpha.clone().reduce_polys(single_polys); + let single_composition_eval = alpha.reduce(single_evals); let single_quotient = Self::compute_quotient( &[zeta], @@ -143,10 +143,10 @@ impl ListPolynomialCommitment { .polynomials .iter() .map(|p| p.to_extension()); - let zs_composition_poly = alpha.clone().scale_polys(zs_polys); + let zs_composition_poly = alpha.clone().reduce_polys(zs_polys); let zs_composition_evals = [ - alpha.clone().scale(os.plonk_zs.iter()), - alpha.scale(os.plonk_zs_right.iter()), + alpha.clone().reduce(os.plonk_zs.iter()), + alpha.reduce(os.plonk_zs_right.iter()), ]; let zs_quotient = Self::compute_quotient( @@ -164,11 +164,11 @@ impl ListPolynomialCommitment { .polynomials .iter() .map(|p| p.to_extension()); - let wire_composition_poly = alpha.clone().scale_polys(wire_polys); + let wire_composition_poly = alpha.clone().reduce_polys(wire_polys); let mut alpha_frob = alpha.repeated_frobenius(D - 1); let wire_composition_evals = [ - alpha.clone().scale(os.wires.iter()), - alpha_frob.scale(os.wires.iter()).frobenius(), + alpha.clone().reduce(os.wires.iter()), + alpha_frob.reduce(os.wires.iter()).frobenius(), ]; let wires_quotient = Self::compute_quotient( diff --git a/src/util/scaling.rs b/src/util/scaling.rs index e29edf0e..7155cb37 100644 --- a/src/util/scaling.rs +++ b/src/util/scaling.rs @@ -5,12 +5,12 @@ use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialCoeffs; #[derive(Debug, Copy, Clone)] -pub struct ScalingFactor { +pub struct ReducingFactor { base: F, count: u64, } -impl ScalingFactor { +impl ReducingFactor { pub fn new(base: F) -> Self { Self { base, count: 0 } } @@ -25,12 +25,12 @@ impl ScalingFactor { &p * self.base } - pub fn scale(&mut self, iter: impl DoubleEndedIterator>) -> F { + pub fn reduce(&mut self, iter: impl DoubleEndedIterator>) -> F { iter.rev() .fold(F::ZERO, |acc, x| self.mul(acc) + *x.borrow()) } - pub fn scale_polys( + pub fn reduce_polys( &mut self, polys: impl DoubleEndedIterator>>, ) -> PolynomialCoeffs { From 4f8ef2e17820ca5a1ce75c505fcf3e7859ee33e5 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:10:33 +0200 Subject: [PATCH 067/117] Optimize some polynomial operations --- src/field/lagrange.rs | 41 ++++++++++++++++++- src/polynomial/commitment.rs | 77 +++++++++++++++++++----------------- src/polynomial/division.rs | 67 ++++++++++++++++++++++++++++++- src/polynomial/polynomial.rs | 22 ++++++++++- 4 files changed, 168 insertions(+), 39 deletions(-) diff --git a/src/field/lagrange.rs b/src/field/lagrange.rs index 8911feb0..97d6fb27 100644 --- a/src/field/lagrange.rs +++ b/src/field/lagrange.rs @@ -65,11 +65,35 @@ pub fn barycentric_weights(points: &[(F, F)]) -> Vec { ) } +/// Interpolate the linear polynomial passing through `points` on `x`. +pub fn interpolate2(points: [(F, F); 2], x: F) -> F { + // a0 -> a1 + // b0 -> b1 + // x -> a1 + (x-a0)*(b1-a1)/(b0-a0) + let (a0, a1) = points[0]; + let (b0, b1) = points[1]; + assert_ne!(a0, b0); + a1 + (x - a0) * (b1 - a1) / (b0 - a0) +} + +/// Returns the linear polynomial passing through `points`. +pub fn interpolant2(points: [(F, F); 2]) -> PolynomialCoeffs { + // a0 -> a1 + // b0 -> b1 + // x -> a1 + (x-a0)*(b1-a1)/(b0-a0) + let (a0, a1) = points[0]; + let (b0, b1) = points[1]; + assert_ne!(a0, b0); + let mult = (b1 - a1) / (b0 - a0); + vec![a1 - a0 * mult, mult].into() +} + #[cfg(test)] mod tests { + use super::*; use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; - use crate::field::lagrange::interpolant; use crate::polynomial::polynomial::PolynomialCoeffs; #[test] @@ -120,4 +144,19 @@ mod tests { fn eval_naive(coeffs: &PolynomialCoeffs, domain: &[F]) -> Vec<(F, F)> { domain.iter().map(|&x| (x, coeffs.eval(x))).collect() } + + #[test] + fn test_interpolant2() { + type F = QuarticCrandallField; + let points = [(F::rand(), F::rand()), (F::rand(), F::rand())]; + let x = F::rand(); + + let intepol0 = interpolant(&points); + let intepol1 = interpolant2(points); + assert_eq!(intepol0.trimmed(), intepol1.trimmed()); + + let ev0 = interpolate(&points, x, &barycentric_weights(&points)); + let ev1 = interpolate2(points, x); + assert_eq!(ev0, ev1); + } } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index caa61553..45251e77 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,7 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::interpolant; +use crate::field::lagrange::{interpolant, interpolant2}; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; @@ -122,15 +122,10 @@ impl ListPolynomialCommitment { .map(|p| p.to_extension()); let single_os = [&os.constants, &os.plonk_s_sigmas, &os.quotient_polys]; let single_evals = single_os.iter().flat_map(|v| v.iter()); - let single_composition_poly = reduce_polys_with_iter(single_polys, alpha_powers.clone()); - let single_composition_eval = reduce_with_iter(single_evals, &mut alpha_powers); + let single_composition_poly = reduce_polys_with_iter(single_polys, &mut alpha_powers); - let single_quotient = Self::compute_quotient( - &[zeta], - &[single_composition_eval], - &single_composition_poly, - ); - final_poly = &final_poly + &single_quotient; + let single_quotient = Self::compute_quotient1(zeta, single_composition_poly); + final_poly += single_quotient; // Zs polynomials are opened at `zeta` and `g*zeta`. let zs_polys = commitments[3].polynomials.iter().map(|p| p.to_extension()); @@ -140,12 +135,9 @@ impl ListPolynomialCommitment { reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), ]; - let zs_quotient = Self::compute_quotient( - &[zeta, g * zeta], - &zs_composition_evals, - &zs_composition_poly, - ); - final_poly = &final_poly + &zs_quotient; + let zs_quotient = + Self::compute_quotient2([zeta, g * zeta], zs_composition_evals, zs_composition_poly); + final_poly += zs_quotient; // When working in an extension field, need to check that wires are in the base field. // Check this by opening the wires polynomials at `zeta` and `zeta.frobenius()` and using the fact that @@ -158,12 +150,12 @@ impl ListPolynomialCommitment { reduce_with_iter(&wire_evals_frob, alpha_powers), ]; - let wires_quotient = Self::compute_quotient( - &[zeta, zeta.frobenius()], - &wire_composition_evals, - &wire_composition_poly, + let wires_quotient = Self::compute_quotient2( + [zeta, zeta.frobenius()], + wire_composition_evals, + wire_composition_poly, ); - final_poly = &final_poly + &wires_quotient; + final_poly += wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); let lde_final_values = lde_final_poly @@ -192,28 +184,41 @@ impl ListPolynomialCommitment { ) } - /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial - /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. - fn compute_quotient( - points: &[F::Extension], - evals: &[F::Extension], - poly: &PolynomialCoeffs, + /// Given `x` and `poly=P(X)`, computes the polynomial `Q=(P-P(x))/(X-x)`. + fn compute_quotient1( + point: F::Extension, + poly: PolynomialCoeffs, ) -> PolynomialCoeffs where F: Extendable, { - let pairs = points - .iter() - .zip(evals) - .map(|(&x, &e)| (x, e)) - .collect::>(); + let (quotient, _ev) = poly.divide_by_linear(point); + quotient.padded(quotient.degree_plus_one().next_power_of_two()) + } + + /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial + /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. + fn compute_quotient2( + points: [F::Extension; 2], + evals: [F::Extension; 2], + poly: PolynomialCoeffs, + ) -> PolynomialCoeffs + where + F: Extendable, + { + let pairs = [(points[0], evals[0]), (points[1], evals[1])]; debug_assert!(pairs.iter().all(|&(x, e)| poly.eval(x) == e)); - let interpolant = interpolant(&pairs); - let denominator = points.iter().fold(PolynomialCoeffs::one(), |acc, &x| { - &acc * &PolynomialCoeffs::new(vec![-x, F::Extension::ONE]) - }); - let numerator = poly - &interpolant; + let interpolant = interpolant2(pairs); + let denominator = vec![ + points[0] * points[1], + -points[0] - points[1], + F::Extension::ONE, + ] + .into(); + + let mut numerator = poly; + numerator -= interpolant; let (quotient, rem) = numerator.div_rem(&denominator); debug_assert!(rem.is_zero()); diff --git a/src/polynomial/division.rs b/src/polynomial/division.rs index b74fd00f..db1606af 100644 --- a/src/polynomial/division.rs +++ b/src/polynomial/division.rs @@ -125,8 +125,25 @@ impl PolynomialCoeffs { p } + /// Let `self=p(X)`, this returns `(p(X)-p(z))/(X-z)` and `p(z)`. + /// See https://en.wikipedia.org/wiki/Horner%27s_method + pub(crate) fn divide_by_linear(&self, z: F) -> (PolynomialCoeffs, F) { + let mut bs = self + .coeffs + .iter() + .rev() + .scan(F::ZERO, |acc, &c| { + *acc = *acc * z + c; + Some(*acc) + }) + .collect::>(); + let ev = bs.pop().unwrap_or(F::ZERO); + bs.reverse(); + (Self { coeffs: bs }, ev) + } + /// Computes the inverse of `self` modulo `x^n`. - pub(crate) fn inv_mod_xn(&self, n: usize) -> Self { + pub fn inv_mod_xn(&self, n: usize) -> Self { assert!(self.coeffs[0].is_nonzero(), "Inverse doesn't exist."); let h = if self.len() < n { @@ -166,7 +183,10 @@ impl PolynomialCoeffs { #[cfg(test)] mod tests { + use std::time::Instant; + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialCoeffs; @@ -199,4 +219,49 @@ mod tests { let computed_q = a.divide_by_z_h(4); assert_eq!(computed_q, q); } + + #[test] + #[ignore] + fn test_division_by_linear() { + type F = QuarticCrandallField; + let n = 1_000_000; + let poly = PolynomialCoeffs::new(F::rand_vec(n)); + let z = F::rand(); + let ev = poly.eval(z); + + let timer = Instant::now(); + let (quotient, ev2) = poly.div_rem(&PolynomialCoeffs::new(vec![-z, F::ONE])); + println!("{:.3}s for usual", timer.elapsed().as_secs_f32()); + assert_eq!(ev2.trimmed().coeffs, vec![ev]); + + let timer = Instant::now(); + let (quotient, ev3) = poly.div_rem_long_division(&PolynomialCoeffs::new(vec![-z, F::ONE])); + println!("{:.3}s for long division", timer.elapsed().as_secs_f32()); + assert_eq!(ev3.trimmed().coeffs, vec![ev]); + + let timer = Instant::now(); + let horn = poly.divide_by_linear(z); + println!("{:.3}s for Horner", timer.elapsed().as_secs_f32()); + assert_eq!((quotient, ev), horn); + } + + #[test] + #[ignore] + fn test_division_by_quadratic() { + type F = QuarticCrandallField; + let n = 1_000_000; + let poly = PolynomialCoeffs::new(F::rand_vec(n)); + let quad = PolynomialCoeffs::new(F::rand_vec(2)); + + let timer = Instant::now(); + let (quotient0, rem0) = poly.div_rem(&quad); + println!("{:.3}s for usual", timer.elapsed().as_secs_f32()); + + let timer = Instant::now(); + let (quotient1, rem1) = poly.div_rem_long_division(&quad); + println!("{:.3}s for long division", timer.elapsed().as_secs_f32()); + + assert_eq!(quotient0.trimmed(), quotient1.trimmed()); + assert_eq!(rem0.trimmed(), rem1.trimmed()); + } } diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 9f605051..aefcc0c6 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -1,6 +1,6 @@ use std::cmp::max; use std::iter::Sum; -use std::ops::{Add, Mul, Sub}; +use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; use crate::field::extension_field::Extendable; use crate::field::fft::{fft, ifft}; @@ -243,6 +243,26 @@ impl Sub for &PolynomialCoeffs { } } +impl AddAssign for PolynomialCoeffs { + fn add_assign(&mut self, rhs: Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, r) in self.coeffs.iter_mut().zip(rhs.coeffs) { + *l += r; + } + } +} + +impl SubAssign for PolynomialCoeffs { + fn sub_assign(&mut self, rhs: Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, r) in self.coeffs.iter_mut().zip(rhs.coeffs) { + *l -= r; + } + } +} + impl Mul for &PolynomialCoeffs { type Output = PolynomialCoeffs; From 5a8d951590072207c8fc5420ab4232a03a416558 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:16:22 +0200 Subject: [PATCH 068/117] Use `interpolate2` in the FRI verifier --- src/fri/verifier.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index d100797e..486e7174 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -2,7 +2,7 @@ use anyhow::{ensure, Result}; use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; +use crate::field::lagrange::{barycentric_weights, interpolant, interpolate, interpolate2}; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; @@ -185,14 +185,17 @@ fn fri_combine_initial, const D: usize>( .map(|&e| F::Extension::from_basefield(e)); let zs_composition_eval = reduce_with_iter(zs_evals, alpha_powers.clone()); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; - let zs_interpol = interpolant(&[ - (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), - ( - zeta_right, - reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - ), - ]); - let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); + let zs_interpol = interpolate2( + [ + (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), + ( + zeta_right, + reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + ), + ], + subgroup_x, + ); + let zs_numerator = zs_composition_eval - zs_interpol; let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); sum += zs_numerator / zs_denominator; @@ -211,8 +214,8 @@ fn fri_combine_initial, const D: usize>( // and one call at the end of the sum. let alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1); let wire_eval_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius(); - let wire_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); - let wire_numerator = wire_composition_eval - wire_interpol.eval(subgroup_x); + let wire_interpol = interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)], subgroup_x); + let wire_numerator = wire_composition_eval - wire_interpol; let wire_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); sum += wire_numerator / wire_denominator; From 621c046fe43d23c2f02d10636b8327718d11145d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:17:15 +0200 Subject: [PATCH 069/117] Use long division when dividing by quadratic polynomial --- src/polynomial/commitment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 45251e77..1b7d611b 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -219,7 +219,7 @@ impl ListPolynomialCommitment { let mut numerator = poly; numerator -= interpolant; - let (quotient, rem) = numerator.div_rem(&denominator); + let (quotient, rem) = numerator.div_rem_long_division(&denominator); debug_assert!(rem.is_zero()); quotient.padded(quotient.degree_plus_one().next_power_of_two()) From 7d4e79f069aa5e043042b1f73bfb6f8d3717867d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:22:38 +0200 Subject: [PATCH 070/117] Clippy --- src/fri/verifier.rs | 2 +- src/polynomial/commitment.rs | 4 +--- src/polynomial/division.rs | 7 +++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 486e7174..6320505e 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -2,7 +2,7 @@ use anyhow::{ensure, Result}; use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::{barycentric_weights, interpolant, interpolate, interpolate2}; +use crate::field::lagrange::{barycentric_weights, interpolate, interpolate2}; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 1b7d611b..ce978d96 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,7 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::{interpolant, interpolant2}; +use crate::field::lagrange::interpolant2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; @@ -120,8 +120,6 @@ impl ListPolynomialCommitment { .iter() .flat_map(|&i| &commitments[i].polynomials) .map(|p| p.to_extension()); - let single_os = [&os.constants, &os.plonk_s_sigmas, &os.quotient_polys]; - let single_evals = single_os.iter().flat_map(|v| v.iter()); let single_composition_poly = reduce_polys_with_iter(single_polys, &mut alpha_powers); let single_quotient = Self::compute_quotient1(zeta, single_composition_poly); diff --git a/src/polynomial/division.rs b/src/polynomial/division.rs index db1606af..50e1f8a6 100644 --- a/src/polynomial/division.rs +++ b/src/polynomial/division.rs @@ -26,7 +26,7 @@ impl PolynomialCoeffs { .to_vec() .into(); let mut q = rev_q.rev(); - let mut qb = &q * b; + let qb = &q * b; let mut r = self - &qb; q.trim(); r.trim(); @@ -59,8 +59,7 @@ impl PolynomialCoeffs { quotient.coeffs[cur_q_degree] = cur_q_coeff; for (i, &div_coeff) in b.coeffs.iter().enumerate() { - remainder.coeffs[cur_q_degree + i] = - remainder.coeffs[cur_q_degree + i] - (cur_q_coeff * div_coeff); + remainder.coeffs[cur_q_degree + i] -= cur_q_coeff * div_coeff; } remainder.trim(); } @@ -97,7 +96,7 @@ impl PolynomialCoeffs { let denominators = (0..a_eval.len()) .map(|i| { if i != 0 { - root_pow = root_pow * root_n; + root_pow *= root_n; } denominator_g * root_pow - F::ONE }) From a4c86a6b081dfb540579fa16217d78d9d16e6448 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:44:06 +0200 Subject: [PATCH 071/117] `lagrange.rs` -> `interpolation.rs` --- src/field/{lagrange.rs => interpolation.rs} | 0 src/field/mod.rs | 2 +- src/fri/verifier.rs | 2 +- src/gadgets/interpolation.rs | 8 +++++--- src/gates/interpolation.rs | 2 +- src/polynomial/commitment.rs | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) rename src/field/{lagrange.rs => interpolation.rs} (100%) diff --git a/src/field/lagrange.rs b/src/field/interpolation.rs similarity index 100% rename from src/field/lagrange.rs rename to src/field/interpolation.rs diff --git a/src/field/mod.rs b/src/field/mod.rs index 179fb10d..15efe280 100644 --- a/src/field/mod.rs +++ b/src/field/mod.rs @@ -3,7 +3,7 @@ pub mod crandall_field; pub mod extension_field; pub mod fft; pub mod field; -pub(crate) mod lagrange; +pub(crate) mod interpolation; #[cfg(test)] mod field_testing; diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 6320505e..4c8f4d8c 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -2,7 +2,7 @@ use anyhow::{ensure, Result}; use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::{barycentric_weights, interpolate, interpolate2}; +use crate::field::interpolation::{barycentric_weights, interpolate, interpolate2}; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; diff --git a/src/gadgets/interpolation.rs b/src/gadgets/interpolation.rs index a94eb582..40b91c1e 100644 --- a/src/gadgets/interpolation.rs +++ b/src/gadgets/interpolation.rs @@ -1,9 +1,10 @@ +use std::marker::PhantomData; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::interpolation::InterpolationGate; use crate::target::Target; -use std::marker::PhantomData; impl, const D: usize> CircuitBuilder { /// Interpolate two points. No need for an `InterpolationGate` since the coefficients @@ -56,15 +57,16 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { + use std::convert::TryInto; + use super::*; use crate::circuit_data::CircuitConfig; use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::extension_field::FieldExtension; use crate::field::field::Field; - use crate::field::lagrange::{interpolant, interpolate}; + use crate::field::interpolation::{interpolant, interpolate}; use crate::witness::PartialWitness; - use std::convert::TryInto; #[test] fn test_interpolate() { diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index ac2ca49f..ccf8d57d 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -6,7 +6,7 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::algebra::PolynomialCoeffsAlgebra; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; -use crate::field::lagrange::interpolant; +use crate::field::interpolation::interpolant; use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index ce978d96..c67bed2f 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,7 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::interpolant2; +use crate::field::interpolation::interpolant2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; From 37171505c74a8db76991ead8fef7a7650bc0bd15 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 12:49:40 +0200 Subject: [PATCH 072/117] Remove useless interpolation from `open_plonk` --- src/field/interpolation.rs | 23 +++--------- src/polynomial/commitment.rs | 73 +++++++++++------------------------- 2 files changed, 27 insertions(+), 69 deletions(-) diff --git a/src/field/interpolation.rs b/src/field/interpolation.rs index 97d6fb27..968eeca2 100644 --- a/src/field/interpolation.rs +++ b/src/field/interpolation.rs @@ -76,18 +76,6 @@ pub fn interpolate2(points: [(F, F); 2], x: F) -> F { a1 + (x - a0) * (b1 - a1) / (b0 - a0) } -/// Returns the linear polynomial passing through `points`. -pub fn interpolant2(points: [(F, F); 2]) -> PolynomialCoeffs { - // a0 -> a1 - // b0 -> b1 - // x -> a1 + (x-a0)*(b1-a1)/(b0-a0) - let (a0, a1) = points[0]; - let (b0, b1) = points[1]; - assert_ne!(a0, b0); - let mult = (b1 - a1) / (b0 - a0); - vec![a1 - a0 * mult, mult].into() -} - #[cfg(test)] mod tests { use super::*; @@ -146,17 +134,16 @@ mod tests { } #[test] - fn test_interpolant2() { + fn test_interpolate2() { type F = QuarticCrandallField; let points = [(F::rand(), F::rand()), (F::rand(), F::rand())]; let x = F::rand(); - let intepol0 = interpolant(&points); - let intepol1 = interpolant2(points); - assert_eq!(intepol0.trimmed(), intepol1.trimmed()); + let ev0 = interpolant(&points).eval(x); + let ev1 = interpolate(&points, x, &barycentric_weights(&points)); + let ev2 = interpolate2(points, x); - let ev0 = interpolate(&points, x, &barycentric_weights(&points)); - let ev1 = interpolate2(points, x); assert_eq!(ev0, ev1); + assert_eq!(ev0, ev2); } } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index c67bed2f..1e4ba1b4 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,11 +4,10 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::interpolation::interpolant2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter}; +use crate::plonk_common::reduce_polys_with_iter; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; @@ -122,37 +121,24 @@ impl ListPolynomialCommitment { .map(|p| p.to_extension()); let single_composition_poly = reduce_polys_with_iter(single_polys, &mut alpha_powers); - let single_quotient = Self::compute_quotient1(zeta, single_composition_poly); + let single_quotient = Self::compute_quotient([zeta], single_composition_poly); final_poly += single_quotient; // Zs polynomials are opened at `zeta` and `g*zeta`. let zs_polys = commitments[3].polynomials.iter().map(|p| p.to_extension()); - let zs_composition_poly = reduce_polys_with_iter(zs_polys, alpha_powers.clone()); - let zs_composition_evals = [ - reduce_with_iter(&os.plonk_zs, alpha_powers.clone()), - reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - ]; + let zs_composition_poly = reduce_polys_with_iter(zs_polys, &mut alpha_powers); - let zs_quotient = - Self::compute_quotient2([zeta, g * zeta], zs_composition_evals, zs_composition_poly); + let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly); final_poly += zs_quotient; // When working in an extension field, need to check that wires are in the base field. // Check this by opening the wires polynomials at `zeta` and `zeta.frobenius()` and using the fact that // a polynomial `f` is over the base field iff `f(z).frobenius()=f(z.frobenius())` with high probability. let wire_polys = commitments[2].polynomials.iter().map(|p| p.to_extension()); - let wire_composition_poly = reduce_polys_with_iter(wire_polys, alpha_powers.clone()); - let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); - let wire_composition_evals = [ - reduce_with_iter(&os.wires, alpha_powers.clone()), - reduce_with_iter(&wire_evals_frob, alpha_powers), - ]; + let wire_composition_poly = reduce_polys_with_iter(wire_polys, &mut alpha_powers); - let wires_quotient = Self::compute_quotient2( - [zeta, zeta.frobenius()], - wire_composition_evals, - wire_composition_poly, - ); + let wires_quotient = + Self::compute_quotient([zeta, zeta.frobenius()], wire_composition_poly); final_poly += wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); @@ -182,43 +168,28 @@ impl ListPolynomialCommitment { ) } - /// Given `x` and `poly=P(X)`, computes the polynomial `Q=(P-P(x))/(X-x)`. - fn compute_quotient1( - point: F::Extension, - poly: PolynomialCoeffs, - ) -> PolynomialCoeffs - where - F: Extendable, - { - let (quotient, _ev) = poly.divide_by_linear(point); - quotient.padded(quotient.degree_plus_one().next_power_of_two()) - } - /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. - fn compute_quotient2( - points: [F::Extension; 2], - evals: [F::Extension; 2], + fn compute_quotient( + points: [F::Extension; N], poly: PolynomialCoeffs, ) -> PolynomialCoeffs where F: Extendable, { - let pairs = [(points[0], evals[0]), (points[1], evals[1])]; - debug_assert!(pairs.iter().all(|&(x, e)| poly.eval(x) == e)); - - let interpolant = interpolant2(pairs); - let denominator = vec![ - points[0] * points[1], - -points[0] - points[1], - F::Extension::ONE, - ] - .into(); - - let mut numerator = poly; - numerator -= interpolant; - let (quotient, rem) = numerator.div_rem_long_division(&denominator); - debug_assert!(rem.is_zero()); + let quotient = if N == 1 { + poly.divide_by_linear(points[0]).0 + } else if N == 2 { + let denominator = vec![ + points[0] * points[1], + -points[0] - points[1], + F::Extension::ONE, + ] + .into(); + poly.div_rem_long_division(&denominator).0 // Could also use `divide_by_linear` twice. + } else { + unreachable!("This shouldn't happen. Plonk should open polynomials at 1 or 2 points.") + }; quotient.padded(quotient.degree_plus_one().next_power_of_two()) } From 15922d25189ff6af519a24777b8457bb68275d36 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 21 Jun 2021 10:32:32 +0200 Subject: [PATCH 073/117] Add comment for denominator polynomial --- src/polynomial/commitment.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 1e4ba1b4..bc17f3f3 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -180,6 +180,7 @@ impl ListPolynomialCommitment { let quotient = if N == 1 { poly.divide_by_linear(points[0]).0 } else if N == 2 { + // The denominator is `(X - p0)(X - p1) = p0 p1 - (p0 + p1) X + X^2`. let denominator = vec![ points[0] * points[1], -points[0] - points[1], From da3d34a0d4ecb83fb3a430250710609c2ac25c90 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 14:31:46 +0200 Subject: [PATCH 074/117] Working gate tree generation --- src/circuit_builder.rs | 7 ++ src/gates/gate.rs | 7 ++ src/gates/gate_tree.rs | 156 +++++++++++++++++++++++++++++++++++++++++ src/gates/mod.rs | 1 + 4 files changed, 171 insertions(+) create mode 100644 src/gates/gate_tree.rs diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 3a26497e..232c9c20 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -12,6 +12,7 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; use crate::gates::gate::{GateInstance, GateRef}; +use crate::gates::gate_tree::{GatePrefixes, Tree}; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; @@ -279,6 +280,12 @@ impl, const D: usize> CircuitBuilder { /// Builds a "full circuit", with both prover and verifier data. pub fn build(mut self) -> CircuitData { + let gates = self.gates.iter().cloned().collect(); + let tree = Tree::from_gates(gates); + let prefixes = GatePrefixes::from(tree); + for (g, p) in &prefixes.prefixes { + println!("{}: {:?}", g.0.id(), p); + } let start = Instant::now(); info!( "degree before blinding & padding: {}", diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 1765191e..d27c8f8b 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,3 +1,4 @@ +use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::Arc; @@ -113,6 +114,12 @@ impl, const D: usize> Hash for GateRef { impl, const D: usize> Eq for GateRef {} +impl, const D: usize> Debug for GateRef { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + write!(f, "{}", self.0.id()) + } +} + /// A gate along with any constants used to configure it. pub struct GateInstance, const D: usize> { pub gate_type: GateRef, diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs new file mode 100644 index 00000000..39b3b9c2 --- /dev/null +++ b/src/gates/gate_tree.rs @@ -0,0 +1,156 @@ +use std::collections::HashMap; +use std::iter::FromIterator; + +use crate::field::extension_field::Extendable; +use crate::field::field::Field; +use crate::gates::gate::GateRef; + +#[derive(Debug, Clone)] +enum Node { + Terminus(T), + Bifurcation, +} + +#[derive(Debug, Clone)] +pub struct Tree { + node: Node, + left: Option>>, + right: Option>>, +} + +impl Default for Tree { + fn default() -> Self { + Self { + node: Node::Bifurcation, + left: None, + right: None, + } + } +} + +impl Tree { + pub fn preorder_traversal(&self) -> Vec<(T, Vec)> { + let mut res = Vec::new(); + let prefix = []; + self.traverse(&prefix, &mut res); + res + } + + fn traverse(&self, prefix: &[bool], current: &mut Vec<(T, Vec)>) { + if let Node::Terminus(t) = &self.node { + current.push((t.clone(), prefix.to_vec())); + } else { + if let Some(l) = &self.left { + let mut left_prefix = prefix.to_vec(); + left_prefix.push(false); + l.traverse(&left_prefix, current); + } + if let Some(r) = &self.right { + let mut right_prefix = prefix.to_vec(); + right_prefix.push(true); + r.traverse(&right_prefix, current); + } + } + } +} + +#[derive(Clone)] +pub struct GatePrefixes, const D: usize> { + pub prefixes: HashMap, Vec>, +} + +impl, const D: usize> From>> for GatePrefixes { + fn from(tree: Tree>) -> Self { + GatePrefixes { + prefixes: HashMap::from_iter(tree.preorder_traversal()), + } + } +} + +impl, const D: usize> Tree> { + pub fn from_gates(mut gates: Vec>) -> Self { + let timer = std::time::Instant::now(); + gates.sort_unstable_by_key(|g| -((g.0.degree() + g.0.num_constants()) as isize)); + + for max_degree in 1..100 { + if let Some(mut tree) = Self::find_tree(&gates, max_degree) { + tree.prune(); + println!( + "Found tree with max degree {} in {}s.", + max_degree, + timer.elapsed().as_secs_f32() + ); + return tree; + } + } + + panic!("Can't find a tree.") + } + + fn find_tree(gates: &[GateRef], max_degree: usize) -> Option { + let mut tree = Tree::default(); + + for g in gates { + tree.try_add_gate(g, max_degree)?; + } + Some(tree) + } + + fn try_add_gate(&mut self, g: &GateRef, max_degree: usize) -> Option<()> { + let depth = max_degree.checked_sub(g.0.num_constants() + g.0.degree())?; + self.try_add_gate_at_depth(g, depth).is_err().then(|| ()) + } + + fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Result<(), GateAdded> { + if depth == 0 { + return if let Node::Bifurcation = self.node { + self.node = Node::Terminus(g.clone()); + Err(GateAdded) + } else { + Ok(()) + }; + } + + if let Node::Terminus(_) = self.node { + return Ok(()); + } + + if let Some(left) = &mut self.left { + left.try_add_gate_at_depth(g, depth - 1)?; + } else { + let mut left = Tree::default(); + if left.try_add_gate_at_depth(g, depth - 1).is_err() { + self.left = Some(Box::new(left)); + return Err(GateAdded); + } + } + if let Some(right) = &mut self.right { + right.try_add_gate_at_depth(g, depth - 1)?; + } else { + let mut right = Tree::default(); + if right.try_add_gate_at_depth(g, depth - 1).is_err() { + self.right = Some(Box::new(right)); + return Err(GateAdded); + } + } + + Ok(()) + } + + fn prune(&mut self) { + if let (Some(left), None) = (&self.left, &self.right) { + debug_assert!(matches!(self.node, Node::Bifurcation)); + let mut new = *left.clone(); + new.prune(); + *self = new; + } + if let Some(left) = &mut self.left { + left.prune(); + } + if let Some(right) = &mut self.right { + right.prune(); + } + } +} + +struct GateAdded; diff --git a/src/gates/mod.rs b/src/gates/mod.rs index d013c7cd..bb8b178b 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -2,6 +2,7 @@ pub(crate) mod arithmetic; pub mod base_sum; pub mod constant; pub(crate) mod gate; +pub mod gate_tree; pub mod gmimc; pub mod interpolation; pub mod mul_extension; From 1983600169bb728fea8abaf26df48299052807b3 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 14:50:08 +0200 Subject: [PATCH 075/117] Change tree from struct to enum --- src/gates/gate_tree.rs | 115 ++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 54 deletions(-) diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index 39b3b9c2..fd3d8c33 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -12,19 +12,14 @@ enum Node { } #[derive(Debug, Clone)] -pub struct Tree { - node: Node, - left: Option>>, - right: Option>>, +pub enum Tree { + Node(T), + Bifurcation(Option>>, Option>>), } impl Default for Tree { fn default() -> Self { - Self { - node: Node::Bifurcation, - left: None, - right: None, - } + Self::Bifurcation(None, None) } } @@ -37,18 +32,21 @@ impl Tree { } fn traverse(&self, prefix: &[bool], current: &mut Vec<(T, Vec)>) { - if let Node::Terminus(t) = &self.node { - current.push((t.clone(), prefix.to_vec())); - } else { - if let Some(l) = &self.left { - let mut left_prefix = prefix.to_vec(); - left_prefix.push(false); - l.traverse(&left_prefix, current); + match &self { + Tree::Node(t) => { + current.push((t.clone(), prefix.to_vec())); } - if let Some(r) = &self.right { - let mut right_prefix = prefix.to_vec(); - right_prefix.push(true); - r.traverse(&right_prefix, current); + Tree::Bifurcation(left, right) => { + if let Some(l) = left { + let mut left_prefix = prefix.to_vec(); + left_prefix.push(false); + l.traverse(&left_prefix, current); + } + if let Some(r) = right { + let mut right_prefix = prefix.to_vec(); + right_prefix.push(true); + r.traverse(&right_prefix, current); + } } } } @@ -98,57 +96,66 @@ impl, const D: usize> Tree> { fn try_add_gate(&mut self, g: &GateRef, max_degree: usize) -> Option<()> { let depth = max_degree.checked_sub(g.0.num_constants() + g.0.degree())?; - self.try_add_gate_at_depth(g, depth).is_err().then(|| ()) + self.try_add_gate_at_depth(g, depth) } - fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Result<(), GateAdded> { + fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Option<()> { if depth == 0 { - return if let Node::Bifurcation = self.node { - self.node = Node::Terminus(g.clone()); - Err(GateAdded) + return if let Tree::Bifurcation(_, _) = self { + *self = Tree::Node(g.clone()); + Some(()) } else { - Ok(()) + None }; } - if let Node::Terminus(_) = self.node { - return Ok(()); + if let Tree::Node(_) = self { + return None; } - if let Some(left) = &mut self.left { - left.try_add_gate_at_depth(g, depth - 1)?; - } else { - let mut left = Tree::default(); - if left.try_add_gate_at_depth(g, depth - 1).is_err() { - self.left = Some(Box::new(left)); - return Err(GateAdded); + if let Tree::Bifurcation(left, right) = self { + if let Some(left) = left { + if left.try_add_gate_at_depth(g, depth - 1).is_some() { + return Some(()); + } + } else { + let mut new_left = Tree::default(); + if new_left.try_add_gate_at_depth(g, depth - 1).is_some() { + *left = Some(Box::new(new_left)); + return Some(()); + } } - } - if let Some(right) = &mut self.right { - right.try_add_gate_at_depth(g, depth - 1)?; - } else { - let mut right = Tree::default(); - if right.try_add_gate_at_depth(g, depth - 1).is_err() { - self.right = Some(Box::new(right)); - return Err(GateAdded); + if let Some(right) = right { + if right.try_add_gate_at_depth(g, depth - 1).is_some() { + return Some(()); + } + } else { + let mut new_right = Tree::default(); + if new_right.try_add_gate_at_depth(g, depth - 1).is_some() { + *right = Some(Box::new(new_right)); + return Some(()); + } } } - Ok(()) + None } fn prune(&mut self) { - if let (Some(left), None) = (&self.left, &self.right) { - debug_assert!(matches!(self.node, Node::Bifurcation)); - let mut new = *left.clone(); - new.prune(); - *self = new; + if let Tree::Bifurcation(left, right) = self { + if let (Some(left), None) = (left, right) { + let mut new = *left.clone(); + new.prune(); + *self = new; + } } - if let Some(left) = &mut self.left { - left.prune(); - } - if let Some(right) = &mut self.right { - right.prune(); + if let Tree::Bifurcation(left, right) = self { + if let Some(left) = left { + left.prune(); + } + if let Some(right) = right { + right.prune(); + } } } } From cfa3d3a6609401d52d1acefd5541bceeee5dd862 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 15:34:50 +0200 Subject: [PATCH 076/117] Added comments --- src/circuit_builder.rs | 4 +- src/field/extension_field/mod.rs | 3 +- src/gates/gate.rs | 17 +++++++ src/gates/gate_tree.rs | 76 +++++++++++++++++--------------- src/plonk_challenger.rs | 3 +- src/target.rs | 3 +- src/wire.rs | 3 +- src/witness.rs | 2 +- 8 files changed, 69 insertions(+), 42 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 232c9c20..c29fda79 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -11,8 +11,8 @@ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; -use crate::gates::gate::{GateInstance, GateRef}; -use crate::gates::gate_tree::{GatePrefixes, Tree}; +use crate::gates::gate::{GateInstance, GatePrefixes, GateRef}; +use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 9caa7dc8..2a176fe9 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -1,6 +1,7 @@ -use crate::field::field::Field; use std::convert::TryInto; +use crate::field::field::Field; + pub mod algebra; pub mod quadratic; pub mod quartic; diff --git a/src/gates/gate.rs b/src/gates/gate.rs index d27c8f8b..9219d5dc 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,10 +1,13 @@ +use std::collections::HashMap; use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::gates::gate_tree::Tree; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; @@ -125,3 +128,17 @@ pub struct GateInstance, const D: usize> { pub gate_type: GateRef, pub constants: Vec, } + +/// Map each gate to a boolean prefix used to construct the gate's selector polynomial. +#[derive(Debug, Clone)] +pub struct GatePrefixes, const D: usize> { + pub prefixes: HashMap, Vec>, +} + +impl, const D: usize> From>> for GatePrefixes { + fn from(tree: Tree>) -> Self { + GatePrefixes { + prefixes: HashMap::from_iter(tree.traversal()), + } + } +} diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index fd3d8c33..7ece765e 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -1,19 +1,10 @@ -use std::collections::HashMap; -use std::iter::FromIterator; - use crate::field::extension_field::Extendable; -use crate::field::field::Field; use crate::gates::gate::GateRef; -#[derive(Debug, Clone)] -enum Node { - Terminus(T), - Bifurcation, -} - +/// A binary tree where leaves hold some type `T` and other nodes are empty. #[derive(Debug, Clone)] pub enum Tree { - Node(T), + Leaf(T), Bifurcation(Option>>, Option>>), } @@ -24,18 +15,23 @@ impl Default for Tree { } impl Tree { - pub fn preorder_traversal(&self) -> Vec<(T, Vec)> { + /// Traverse a tree using a depth-first traversal and collect data and position for each leaf. + /// A leaf's position is represented by its left/right path, where `false` means left and `true` means right. + pub fn traversal(&self) -> Vec<(T, Vec)> { let mut res = Vec::new(); let prefix = []; self.traverse(&prefix, &mut res); res } + /// Utility function to traverse the tree. fn traverse(&self, prefix: &[bool], current: &mut Vec<(T, Vec)>) { match &self { - Tree::Node(t) => { + // If node is a leaf, collect the data and position. + Tree::Leaf(t) => { current.push((t.clone(), prefix.to_vec())); } + // Otherwise, traverse the left subtree and then the right subtree. Tree::Bifurcation(left, right) => { if let Some(l) = left { let mut left_prefix = prefix.to_vec(); @@ -52,27 +48,22 @@ impl Tree { } } -#[derive(Clone)] -pub struct GatePrefixes, const D: usize> { - pub prefixes: HashMap, Vec>, -} - -impl, const D: usize> From>> for GatePrefixes { - fn from(tree: Tree>) -> Self { - GatePrefixes { - prefixes: HashMap::from_iter(tree.preorder_traversal()), - } - } -} - impl, const D: usize> Tree> { + /// Construct a binary tree of gates using the following greedy algorithm: + /// We want a tree where the maximum `M` of + /// `F(gate) = gate.degree() + gate.num_constants() + tree.depth(gate)` + /// over all gates is minimized. Such a tree is constructed by iterating over possible values of `M` + /// (from 1 to 99, then we give up) and then looking for a tree with this value of `M` + /// using `Self::find_tree`. This latter function greedily adds gates at the depth where + /// `F(gate)=M` to ensure no space is wasted. We return the first tree found in this manner, + /// i.e., the one with minimal `M` value. pub fn from_gates(mut gates: Vec>) -> Self { let timer = std::time::Instant::now(); gates.sort_unstable_by_key(|g| -((g.0.degree() + g.0.num_constants()) as isize)); for max_degree in 1..100 { if let Some(mut tree) = Self::find_tree(&gates, max_degree) { - tree.prune(); + tree.shorten(); println!( "Found tree with max degree {} in {}s.", max_degree, @@ -85,6 +76,7 @@ impl, const D: usize> Tree> { panic!("Can't find a tree.") } + /// Greedily add gates wherever possible. Returns `None` if this fails. fn find_tree(gates: &[GateRef], max_degree: usize) -> Option { let mut tree = Tree::default(); @@ -94,31 +86,39 @@ impl, const D: usize> Tree> { Some(tree) } + /// Try to add a gate in the tree. Returns `None` if this fails. fn try_add_gate(&mut self, g: &GateRef, max_degree: usize) -> Option<()> { let depth = max_degree.checked_sub(g.0.num_constants() + g.0.degree())?; self.try_add_gate_at_depth(g, depth) } + /// Try to add a gate in the tree at a specified depth. Returns `None` if this fails. fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Option<()> { + // If depth is 0, we have to insert the gate here. if depth == 0 { return if let Tree::Bifurcation(_, _) = self { - *self = Tree::Node(g.clone()); + // Insert the gate as a new leaf. + *self = Tree::Leaf(g.clone()); Some(()) } else { + // A leaf is already here. None }; } - if let Tree::Node(_) = self { + // A leaf is already here so we cannot go deeper. + if let Tree::Leaf(_) = self { return None; } if let Tree::Bifurcation(left, right) = self { if let Some(left) = left { + // Try to add the gate to the left if there's already a left subtree. if left.try_add_gate_at_depth(g, depth - 1).is_some() { return Some(()); } } else { + // Add a new left subtree and try to add the gate to it. let mut new_left = Tree::default(); if new_left.try_add_gate_at_depth(g, depth - 1).is_some() { *left = Some(Box::new(new_left)); @@ -126,10 +126,12 @@ impl, const D: usize> Tree> { } } if let Some(right) = right { + // Try to add the gate to the right if there's already a right subtree. if right.try_add_gate_at_depth(g, depth - 1).is_some() { return Some(()); } } else { + // Add a new right subtree and try to add the gate to it. let mut new_right = Tree::default(); if new_right.try_add_gate_at_depth(g, depth - 1).is_some() { *right = Some(Box::new(new_right)); @@ -141,23 +143,27 @@ impl, const D: usize> Tree> { None } - fn prune(&mut self) { + /// `Self::find_tree` returns a tree where each gate has `F(gate)=M` (see `Self::from_gates` comment). + /// This can produce subtrees with more nodes than necessary. This function removes useless nodes, + /// i.e., nodes that have a left but no right subtree. + fn shorten(&mut self) { if let Tree::Bifurcation(left, right) = self { if let (Some(left), None) = (left, right) { + // If the node has a left but no right subtree, set the node to its (shortened) left subtree. let mut new = *left.clone(); - new.prune(); + new.shorten(); *self = new; } } if let Tree::Bifurcation(left, right) = self { if let Some(left) = left { - left.prune(); + // Shorten the left subtree if there is one. + left.shorten(); } if let Some(right) = right { - right.prune(); + // Shorten the right subtree if there is one. + right.shorten(); } } } } - -struct GateAdded; diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index b24bbde8..b66b422b 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; @@ -5,7 +7,6 @@ use crate::field::field::Field; use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH}; use crate::proof::{Hash, HashTarget, OpeningSet}; use crate::target::Target; -use std::convert::TryInto; /// Observes prover messages, and generates challenges by hashing the transcript. #[derive(Clone)] diff --git a/src/target.rs b/src/target.rs index 423865fa..8aec0b5a 100644 --- a/src/target.rs +++ b/src/target.rs @@ -1,6 +1,7 @@ +use std::ops::Range; + use crate::circuit_data::CircuitConfig; use crate::wire::Wire; -use std::ops::Range; /// A location in the witness. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] diff --git a/src/wire.rs b/src/wire.rs index 02d43029..f63a19c1 100644 --- a/src/wire.rs +++ b/src/wire.rs @@ -1,6 +1,7 @@ -use crate::circuit_data::CircuitConfig; use std::ops::Range; +use crate::circuit_data::CircuitConfig; + /// Represents a wire in the circuit. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct Wire { diff --git a/src/witness.rs b/src/witness.rs index e71b1cfb..5df8b238 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,11 +1,11 @@ use std::collections::HashMap; +use std::convert::TryInto; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::target::Target; use crate::wire::Wire; -use std::convert::TryInto; #[derive(Clone, Debug)] pub struct PartialWitness { From aec6f217922a81ad79a46e74aade7c1c255fd0d7 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 16:07:35 +0200 Subject: [PATCH 077/117] Test with tree from all gates. --- src/circuit_builder.rs | 9 +----- src/gates/gate_tree.rs | 68 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index c29fda79..3a26497e 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -11,8 +11,7 @@ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; -use crate::gates::gate::{GateInstance, GatePrefixes, GateRef}; -use crate::gates::gate_tree::Tree; +use crate::gates::gate::{GateInstance, GateRef}; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; @@ -280,12 +279,6 @@ impl, const D: usize> CircuitBuilder { /// Builds a "full circuit", with both prover and verifier data. pub fn build(mut self) -> CircuitData { - let gates = self.gates.iter().cloned().collect(); - let tree = Tree::from_gates(gates); - let prefixes = GatePrefixes::from(tree); - for (g, p) in &prefixes.prefixes { - println!("{}: {:?}", g.0.id(), p); - } let start = Instant::now(); info!( "degree before blinding & padding: {}", diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index 7ece765e..8081ab25 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -167,3 +167,71 @@ impl, const D: usize> Tree> { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::field::crandall_field::CrandallField; + use crate::gates::arithmetic::ArithmeticGate; + use crate::gates::base_sum::BaseSumGate; + use crate::gates::constant::ConstantGate; + use crate::gates::gmimc::GMiMCGate; + use crate::gates::interpolation::InterpolationGate; + use crate::gates::mul_extension::MulExtensionGate; + use crate::gates::noop::NoopGate; + use crate::hash::GMIMC_ROUNDS; + + #[test] + fn test_prefix_generation() { + type F = CrandallField; + const D: usize = 4; + + let gates = vec![ + NoopGate::get::(), + ConstantGate::get(), + ArithmeticGate::new(), + BaseSumGate::<4>::new(4), + GMiMCGate::::with_automatic_constants(), + InterpolationGate::new(4), + MulExtensionGate::new(), + ]; + let len = gates.len(); + + let tree = Tree::from_gates(gates.clone()); + let mut gates_with_prefix = tree.traversal(); + for (g, p) in &gates_with_prefix { + println!("{}: {:?}", g.0.id(), p); + } + + assert_eq!( + gates_with_prefix.len(), + gates.len(), + "The tree has too much or too little gates." + ); + assert!( + gates.iter().all(|g| gates_with_prefix + .iter() + .map(|(gg, _)| gg) + .find(|gg| *gg == g) + .is_some()), + "Some gates are not in the tree." + ); + assert!( + gates_with_prefix + .iter() + .all(|(g, p)| g.0.degree() + g.0.num_constants() + p.len() <= 8), + "Total degree is larger than 8." + ); + + gates_with_prefix.sort_unstable_by_key(|(g, p)| p.len()); + for i in 0..gates_with_prefix.len() { + for j in i + 1..gates_with_prefix.len() { + assert_ne!( + &gates_with_prefix[i].1, + &gates_with_prefix[j].1[0..gates_with_prefix[i].1.len()], + "Some gates share the same prefix" + ); + } + } + } +} From 5acbb674ad1ad13e6baf28a2c19180f7c43a1550 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 16:54:20 +0200 Subject: [PATCH 078/117] Add prefix to constant polys --- src/circuit_builder.rs | 28 +++++++++++++++++++--------- src/gates/gate.rs | 14 ++++++++++++++ src/gates/gate_tree.rs | 7 +++---- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 3a26497e..c91ef470 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -11,7 +11,8 @@ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; -use crate::gates::gate::{GateInstance, GateRef}; +use crate::gates::gate::{GateInstance, GatePrefixes, GateRef}; +use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; @@ -229,22 +230,28 @@ impl, const D: usize> CircuitBuilder { } } - fn constant_polys(&self) -> Vec> { + fn constant_polys(&self, prefixes: &GatePrefixes) -> Vec> { let num_constants = self .gate_instances .iter() - .map(|gate_inst| gate_inst.constants.len()) + .map(|gate_inst| gate_inst.constants.len() + prefixes[&gate_inst.gate_type].len()) .max() .unwrap(); let constants_per_gate = self .gate_instances .iter() .map(|gate_inst| { - let mut padded_constants = gate_inst.constants.clone(); - for _ in padded_constants.len()..num_constants { - padded_constants.push(F::ZERO); - } - padded_constants + let mut prefixed_constants = Vec::new(); + prefixed_constants.extend(prefixes[&gate_inst.gate_type].iter().map(|&b| { + if b { + F::ONE + } else { + F::ZERO + } + })); + prefixed_constants.extend_from_slice(&gate_inst.constants); + prefixed_constants.resize(num_constants, F::ZERO); + prefixed_constants }) .collect::>(); @@ -288,7 +295,10 @@ impl, const D: usize> CircuitBuilder { let degree = self.gate_instances.len(); info!("degree after blinding & padding: {}", degree); - let constant_vecs = self.constant_polys(); + let gates = self.gates.iter().cloned().collect(); + let gate_tree = Tree::from_gates(gates); + + let constant_vecs = self.constant_polys(&gate_tree.into()); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 9219d5dc..cf939d9d 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,7 +1,9 @@ +use std::borrow::Borrow; use std::collections::HashMap; use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; +use std::ops::Index; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; @@ -142,3 +144,15 @@ impl, const D: usize> From>> for GatePrefixe } } } + +impl, T: Borrow>, const D: usize> Index for GatePrefixes { + type Output = Vec; + + fn index(&self, index: T) -> &Self::Output { + &self.prefixes[index.borrow()] + } +} + +// impl, const D: usize> GatePrefixes { +// pub fn prefix_len() +// } diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index 8081ab25..9195e30c 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -1,3 +1,5 @@ +use log::info; + use crate::field::extension_field::Extendable; use crate::gates::gate::GateRef; @@ -64,7 +66,7 @@ impl, const D: usize> Tree> { for max_degree in 1..100 { if let Some(mut tree) = Self::find_tree(&gates, max_degree) { tree.shorten(); - println!( + info!( "Found tree with max degree {} in {}s.", max_degree, timer.elapsed().as_secs_f32() @@ -199,9 +201,6 @@ mod tests { let tree = Tree::from_gates(gates.clone()); let mut gates_with_prefix = tree.traversal(); - for (g, p) in &gates_with_prefix { - println!("{}: {:?}", g.0.id(), p); - } assert_eq!( gates_with_prefix.len(), From 680d7a6389a7e6553c0433a44e60973ec32c5751 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 17:10:36 +0200 Subject: [PATCH 079/117] Add `eval_filtered` methods --- src/circuit_builder.rs | 4 +++- src/circuit_data.rs | 5 ++++- src/gates/gate.rs | 35 +++++++++++++++++++++++++++++++---- src/plonk_common.rs | 24 +++++++++++++++++------- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index c91ef470..e6a1e296 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -297,8 +297,9 @@ impl, const D: usize> CircuitBuilder { let gates = self.gates.iter().cloned().collect(); let gate_tree = Tree::from_gates(gates); + let gate_prefixes = gate_tree.into(); - let constant_vecs = self.constant_polys(&gate_tree.into()); + let constant_vecs = self.constant_polys(&gate_prefixes); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -348,6 +349,7 @@ impl, const D: usize> CircuitBuilder { config: self.config, degree_bits, gates, + gate_prefixes, num_gate_constraints, k_is, circuit_digest, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index c8477464..3b314281 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -3,7 +3,7 @@ use anyhow::Result; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; -use crate::gates::gate::GateRef; +use crate::gates::gate::{GatePrefixes, GateRef}; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; @@ -139,6 +139,9 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The types of gates used in this circuit. pub(crate) gates: Vec>, + /// The gate prefixes used to construct the selector polynomials. + pub(crate) gate_prefixes: GatePrefixes, + /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, diff --git a/src/gates/gate.rs b/src/gates/gate.rs index cf939d9d..2ab410d4 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -9,6 +9,7 @@ use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::field::Field; use crate::gates::gate_tree::Tree; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; @@ -57,15 +58,41 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { vars: EvaluationTargets, ) -> Vec>; - fn eval_filtered(&self, vars: EvaluationVars) -> Vec { - // TODO: Filter + fn eval_filtered(&self, vars: EvaluationVars, prefix: &[bool]) -> Vec { + let filter: F::Extension = prefix + .iter() + .enumerate() + .map(|(i, &b)| { + if b { + vars.local_constants[i] + } else { + F::Extension::ONE - vars.local_constants[i] + } + }) + .product(); self.eval_unfiltered(vars) + .into_iter() + .map(|c| filter * c) + .collect() } /// Like `eval_filtered`, but specialized for points in the base field. - fn eval_filtered_base(&self, vars: EvaluationVarsBase) -> Vec { - // TODO: Filter + fn eval_filtered_base(&self, vars: EvaluationVarsBase, prefix: &[bool]) -> Vec { + let filter = prefix + .iter() + .enumerate() + .map(|(i, &b)| { + if b { + vars.local_constants[i] + } else { + F::ONE - vars.local_constants[i] + } + }) + .product(); self.eval_unfiltered_base(vars) + .into_iter() + .map(|c| c * filter) + .collect() } fn eval_filtered_recursively( diff --git a/src/plonk_common.rs b/src/plonk_common.rs index c9f11b74..2edd2add 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -5,7 +5,7 @@ use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; -use crate::gates::gate::GateRef; +use crate::gates::gate::{GatePrefixes, GateRef}; use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; @@ -76,8 +76,12 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let constraint_terms = - evaluate_gate_constraints(&common_data.gates, common_data.num_gate_constraints, vars); + let constraint_terms = evaluate_gate_constraints( + &common_data.gates, + common_data.num_gate_constraints, + vars, + &common_data.gate_prefixes, + ); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -125,8 +129,12 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let constraint_terms = - evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); + let constraint_terms = evaluate_gate_constraints_base( + &common_data.gates, + common_data.num_gate_constraints, + vars, + &common_data.gate_prefixes, + ); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -170,10 +178,11 @@ pub fn evaluate_gate_constraints, const D: usize>( gates: &[GateRef], num_gate_constraints: usize, vars: EvaluationVars, + prefixes: &GatePrefixes, ) -> Vec { let mut constraints = vec![F::Extension::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered(vars); + let gate_constraints = gate.0.eval_filtered(vars, &prefixes[gate]); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, @@ -189,10 +198,11 @@ pub fn evaluate_gate_constraints_base, const D: usize>( gates: &[GateRef], num_gate_constraints: usize, vars: EvaluationVarsBase, + prefixes: &GatePrefixes, ) -> Vec { let mut constraints = vec![F::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered_base(vars); + let gate_constraints = gate.0.eval_filtered_base(vars, &prefixes[gate]); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, From 01053ab96ae09293bc9b0a024d59181550727f88 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 11:30:57 +0200 Subject: [PATCH 080/117] Fix bug --- src/fri/verifier.rs | 25 ++++++----- src/polynomial/commitment.rs | 82 +++++++++++++----------------------- 2 files changed, 43 insertions(+), 64 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index e2c8568f..0e7c7c4d 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -2,7 +2,7 @@ use anyhow::{ensure, Result}; use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; +use crate::field::interpolation::{barycentric_weights, interpolate, interpolate2}; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; @@ -192,11 +192,14 @@ fn fri_combine_initial, const D: usize>( .map(|&e| F::Extension::from_basefield(e)); let zs_composition_eval = alpha.clone().reduce(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; - let zs_interpol = interpolant(&[ - (zeta, alpha.clone().reduce(os.plonk_zs.iter())), - (zeta_right, alpha.reduce(os.plonk_zs_right.iter())), - ]); - let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); + let zs_interpol = interpolate2( + [ + (zeta, alpha.clone().reduce(os.plonk_zs.iter())), + (zeta_right, alpha.reduce(os.plonk_zs_right.iter())), + ], + subgroup_x, + ); + let zs_numerator = zs_composition_eval - zs_interpol; let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); sum = alpha.shift(sum); sum += zs_numerator / zs_denominator; @@ -208,18 +211,18 @@ fn fri_combine_initial, const D: usize>( .map(|&e| F::Extension::from_basefield(e)); let wire_composition_eval = alpha.clone().reduce(wire_evals); let zeta_frob = zeta.frobenius(); - let wire_eval = alpha.clone().reduce(os.wires.iter()); + let mut alpha_frob = alpha.repeated_frobenius(D - 1); + let wire_eval = alpha.reduce(os.wires.iter()); // We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism. // Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities: // `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)` // So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha, // and one call at the end of the sum. - let mut alpha_frob = alpha.repeated_frobenius(D - 1); let wire_eval_frob = alpha_frob.reduce(os.wires.iter()).frobenius(); - let wire_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); - let wire_numerator = wire_composition_eval - wire_interpol.eval(subgroup_x); + let wire_interpol = interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)], subgroup_x); + let wire_numerator = wire_composition_eval - wire_interpol; let wire_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); - sum = alpha_frob.shift(sum); + sum = alpha.shift(sum); sum += wire_numerator / wire_denominator; sum diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 9d07975e..df92a31b 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,7 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::interpolant; +use crate::field::interpolation::interpolate2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; @@ -125,17 +125,10 @@ impl ListPolynomialCommitment { .iter() .flat_map(|&p| &commitments[p.index].polynomials) .map(|p| p.to_extension()); - let single_os = [&os.constants, &os.plonk_s_sigmas, &os.quotient_polys]; - let single_evals = single_os.iter().flat_map(|v| v.iter()); - let single_composition_poly = alpha.clone().reduce_polys(single_polys); - let single_composition_eval = alpha.reduce(single_evals); + let single_composition_poly = alpha.reduce_polys(single_polys); - let single_quotient = Self::compute_quotient( - &[zeta], - &[single_composition_eval], - &single_composition_poly, - ); - final_poly = &final_poly + &single_quotient; + let single_quotient = Self::compute_quotient([zeta], single_composition_poly); + final_poly += single_quotient; alpha.reset(); // Zs polynomials are opened at `zeta` and `g*zeta`. @@ -143,19 +136,11 @@ impl ListPolynomialCommitment { .polynomials .iter() .map(|p| p.to_extension()); - let zs_composition_poly = alpha.clone().reduce_polys(zs_polys); - let zs_composition_evals = [ - alpha.clone().reduce(os.plonk_zs.iter()), - alpha.reduce(os.plonk_zs_right.iter()), - ]; + let zs_composition_poly = alpha.reduce_polys(zs_polys); - let zs_quotient = Self::compute_quotient( - &[zeta, g * zeta], - &zs_composition_evals, - &zs_composition_poly, - ); + let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly); final_poly = alpha.shift_poly(final_poly); - final_poly = &final_poly + &zs_quotient; + final_poly += zs_quotient; // When working in an extension field, need to check that wires are in the base field. // Check this by opening the wires polynomials at `zeta` and `zeta.frobenius()` and using the fact that @@ -164,20 +149,12 @@ impl ListPolynomialCommitment { .polynomials .iter() .map(|p| p.to_extension()); - let wire_composition_poly = alpha.clone().reduce_polys(wire_polys); - let mut alpha_frob = alpha.repeated_frobenius(D - 1); - let wire_composition_evals = [ - alpha.clone().reduce(os.wires.iter()), - alpha_frob.reduce(os.wires.iter()).frobenius(), - ]; + let wire_composition_poly = alpha.reduce_polys(wire_polys); - let wires_quotient = Self::compute_quotient( - &[zeta, zeta.frobenius()], - &wire_composition_evals, - &wire_composition_poly, - ); - final_poly = alpha_frob.shift_poly(final_poly); - final_poly = &final_poly + &wires_quotient; + let wires_quotient = + Self::compute_quotient([zeta, zeta.frobenius()], wire_composition_poly); + final_poly = alpha.shift_poly(final_poly); + final_poly += wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); let lde_final_values = lde_final_poly @@ -208,28 +185,27 @@ impl ListPolynomialCommitment { /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. - fn compute_quotient( - points: &[F::Extension], - evals: &[F::Extension], - poly: &PolynomialCoeffs, + fn compute_quotient( + points: [F::Extension; N], + poly: PolynomialCoeffs, ) -> PolynomialCoeffs where F: Extendable, { - let pairs = points - .iter() - .zip(evals) - .map(|(&x, &e)| (x, e)) - .collect::>(); - debug_assert!(pairs.iter().all(|&(x, e)| poly.eval(x) == e)); - - let interpolant = interpolant(&pairs); - let denominator = points.iter().fold(PolynomialCoeffs::one(), |acc, &x| { - &acc * &PolynomialCoeffs::new(vec![-x, F::Extension::ONE]) - }); - let numerator = poly - &interpolant; - let (quotient, rem) = numerator.div_rem(&denominator); - debug_assert!(rem.is_zero()); + let quotient = if N == 1 { + poly.divide_by_linear(points[0]).0 + } else if N == 2 { + // The denominator is `(X - p0)(X - p1) = p0 p1 - (p0 + p1) X + X^2`. + let denominator = vec![ + points[0] * points[1], + -points[0] - points[1], + F::Extension::ONE, + ] + .into(); + poly.div_rem_long_division(&denominator).0 // Could also use `divide_by_linear` twice. + } else { + unreachable!("This shouldn't happen. Plonk should open polynomials at 1 or 2 points.") + }; quotient.padded(quotient.degree_plus_one().next_power_of_two()) } From 26e669ddeca3a440bbee65f3a85249a1478cd55e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 11:33:50 +0200 Subject: [PATCH 081/117] 2 `collect`s -> 1 `collect` --- src/fri/verifier.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 0e7c7c4d..d88e8e0b 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -168,19 +168,18 @@ fn fri_combine_initial, const D: usize>( ] .iter() .flat_map(|&p| proof.unsalted_evals(p)) - .map(|&e| F::Extension::from_basefield(e)) - .collect::>(); + .map(|&e| F::Extension::from_basefield(e)); let single_openings = os .constants .iter() .chain(&os.plonk_s_sigmas) - .chain(&os.quotient_polys) - .collect::>(); + .chain(&os.quotient_polys); let single_diffs = single_evals .into_iter() .zip(single_openings) - .map(|(e, &o)| e - o); - let single_numerator = alpha.reduce(single_diffs); + .map(|(e, &o)| e - o) + .collect::>(); + let single_numerator = alpha.reduce(single_diffs.iter()); let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; alpha.reset(); From 492b04843e026ed8a8d1bae048ebf50b72431025 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 11:41:32 +0200 Subject: [PATCH 082/117] Optimize some polynomial operations to avoid cloning. --- src/polynomial/commitment.rs | 5 ++--- src/polynomial/polynomial.rs | 28 +++++++++++++++++++++++++++- src/util/scaling.rs | 15 ++++++++------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index df92a31b..4b2bf872 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,6 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::interpolation::interpolate2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; @@ -139,7 +138,7 @@ impl ListPolynomialCommitment { let zs_composition_poly = alpha.reduce_polys(zs_polys); let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly); - final_poly = alpha.shift_poly(final_poly); + alpha.shift_poly(&mut final_poly); final_poly += zs_quotient; // When working in an extension field, need to check that wires are in the base field. @@ -153,7 +152,7 @@ impl ListPolynomialCommitment { let wires_quotient = Self::compute_quotient([zeta, zeta.frobenius()], wire_composition_poly); - final_poly = alpha.shift_poly(final_poly); + alpha.shift_poly(&mut final_poly); final_poly += wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index aefcc0c6..02f66684 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -1,6 +1,6 @@ use std::cmp::max; use std::iter::Sum; -use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; +use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; use crate::field::extension_field::Extendable; use crate::field::fft::{fft, ifft}; @@ -253,6 +253,16 @@ impl AddAssign for PolynomialCoeffs { } } +impl AddAssign<&Self> for PolynomialCoeffs { + fn add_assign(&mut self, rhs: &Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, &r) in self.coeffs.iter_mut().zip(&rhs.coeffs) { + *l += r; + } + } +} + impl SubAssign for PolynomialCoeffs { fn sub_assign(&mut self, rhs: Self) { let len = max(self.len(), rhs.len()); @@ -263,6 +273,16 @@ impl SubAssign for PolynomialCoeffs { } } +impl SubAssign<&Self> for PolynomialCoeffs { + fn sub_assign(&mut self, rhs: &Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, &r) in self.coeffs.iter_mut().zip(&rhs.coeffs) { + *l -= r; + } + } +} + impl Mul for &PolynomialCoeffs { type Output = PolynomialCoeffs; @@ -272,6 +292,12 @@ impl Mul for &PolynomialCoeffs { } } +impl MulAssign for PolynomialCoeffs { + fn mul_assign(&mut self, rhs: F) { + self.coeffs.iter_mut().for_each(|x| *x *= rhs); + } +} + impl Mul for &PolynomialCoeffs { type Output = PolynomialCoeffs; diff --git a/src/util/scaling.rs b/src/util/scaling.rs index 7155cb37..25da24c7 100644 --- a/src/util/scaling.rs +++ b/src/util/scaling.rs @@ -20,9 +20,9 @@ impl ReducingFactor { self.base * x } - fn mul_poly(&mut self, p: PolynomialCoeffs) -> PolynomialCoeffs { + fn mul_poly(&mut self, p: &mut PolynomialCoeffs) { self.count += 1; - &p * self.base + *p *= self.base; } pub fn reduce(&mut self, iter: impl DoubleEndedIterator>) -> F { @@ -34,8 +34,10 @@ impl ReducingFactor { &mut self, polys: impl DoubleEndedIterator>>, ) -> PolynomialCoeffs { - polys.rev().fold(PolynomialCoeffs::empty(), |acc, x| { - &self.mul_poly(acc) + x.borrow() + polys.rev().fold(PolynomialCoeffs::empty(), |mut acc, x| { + self.mul_poly(&mut acc); + acc += x.borrow(); + acc }) } @@ -45,10 +47,9 @@ impl ReducingFactor { tmp } - pub fn shift_poly(&mut self, p: PolynomialCoeffs) -> PolynomialCoeffs { - let tmp = &p * self.base.exp(self.count); + pub fn shift_poly(&mut self, p: &mut PolynomialCoeffs) { + *p *= self.base.exp(self.count); self.count = 0; - tmp } pub fn reset(&mut self) { From 517c75abe2f442613d6d260ea21b294e8f0d8bcc Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 11:51:16 +0200 Subject: [PATCH 083/117] Add comment for `ReducingFactor` --- src/util/scaling.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/util/scaling.rs b/src/util/scaling.rs index 25da24c7..cea86195 100644 --- a/src/util/scaling.rs +++ b/src/util/scaling.rs @@ -4,6 +4,13 @@ use crate::field::extension_field::Frobenius; use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialCoeffs; +/// When verifying the composition polynomial in FRI we have to compute sums of the form +/// `(sum_0^k a^i * x_i)/d_0 + (sum_k^r a^i * y_i)/d_1` +/// The most efficient way to do this is to compute both quotient separately using Horner's method, +/// scale the second one by `a^(r-1-k)`, and add them up. +/// This struct abstract away these operations by implementing Horner's method and keeping track +/// of the number of multiplications by `a` to compute the scaling factor. +/// See https://github.com/mir-protocol/plonky2/pull/69 for more details and discussions. #[derive(Debug, Copy, Clone)] pub struct ReducingFactor { base: F, From 3bc27c65ef49a149cdfb4e68dfe7d4dff8d13823 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 13:46:19 +0200 Subject: [PATCH 084/117] Rollback to previous semantics --- src/circuit_builder.rs | 10 +++------- src/circuit_data.rs | 3 --- src/gates/gmimc.rs | 8 +------- src/generator.rs | 10 ++-------- src/permutation_argument.rs | 21 --------------------- src/plonk_challenger.rs | 6 +----- src/prover.rs | 6 +----- src/witness.rs | 4 ---- 8 files changed, 8 insertions(+), 60 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index b9b2e533..8c7d0a72 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -236,7 +236,7 @@ impl, const D: usize> CircuitBuilder { .collect() } - fn sigma_vecs(&self, k_is: &[F]) -> (Vec>, TargetPartitions) { + fn sigma_vecs(&self, k_is: &[F]) -> Vec> { let degree = self.gate_instances.len(); let degree_log = log2_strict(degree); let mut target_partitions = TargetPartitions::new(); @@ -257,10 +257,7 @@ impl, const D: usize> CircuitBuilder { let wire_partitions = target_partitions.to_wire_partitions(); - ( - wire_partitions.get_sigma_polys(degree_log, k_is), - target_partitions, - ) + wire_partitions.get_sigma_polys(degree_log, k_is) } /// Builds a "full circuit", with both prover and verifier data. @@ -282,7 +279,7 @@ impl, const D: usize> CircuitBuilder { ); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); - let (sigma_vecs, targets_partition) = self.sigma_vecs(&k_is); + let sigma_vecs = self.sigma_vecs(&k_is); let sigmas_commitment = ListPolynomialCommitment::new( sigma_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -301,7 +298,6 @@ impl, const D: usize> CircuitBuilder { generators, constants_commitment, sigmas_commitment, - targets_partition, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 6b6d614a..4d9a7110 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -5,7 +5,6 @@ use crate::field::field::Field; use crate::fri::FriConfig; use crate::gates::gate::GateRef; use crate::generator::WitnessGenerator; -use crate::permutation_argument::TargetPartitions; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; @@ -105,8 +104,6 @@ pub(crate) struct ProverOnlyCircuitData { pub constants_commitment: ListPolynomialCommitment, /// Commitments to the sigma polynomial. pub sigmas_commitment: ListPolynomialCommitment, - /// Partition of the targets into copy-constrained sets. - pub targets_partition: TargetPartitions, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index ac5741e4..bdfade7c 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -370,13 +370,7 @@ mod tests { } let generators = gate.0.generators(0, &[]); - let mut tp = TargetPartitions::new(); - for g in 0..10 { - for i in 0..config.num_routed_wires { - tp.add_partition(Target::wire(g, i)); - } - } - generate_partial_witness(&mut witness, &generators, &tp); + generate_partial_witness(&mut witness, &generators); let expected_outputs: [F; W] = gmimc_permute_naive(permutation_inputs.try_into().unwrap(), constants); diff --git a/src/generator.rs b/src/generator.rs index 5b6a258a..db81172f 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -11,7 +11,6 @@ use crate::witness::PartialWitness; pub(crate) fn generate_partial_witness( witness: &mut PartialWitness, generators: &[Box>], - target_partition: &TargetPartitions, ) { // Index generator indices by their watched targets. let mut generator_indices_by_watches = HashMap::new(); @@ -20,12 +19,10 @@ pub(crate) fn generate_partial_witness( generator_indices_by_watches .entry(watch) .or_insert_with(Vec::new) - .push(i) + .push(i); } } - target_partition.generate_copies(witness, &witness.all_populated_targets()); - // Build a list of "pending" generators which are queued to be run. Initially, all generators // are queued. let mut pending_generator_indices: HashSet<_> = (0..generators.len()).collect(); @@ -38,14 +35,11 @@ pub(crate) fn generate_partial_witness( let mut next_pending_generator_indices = HashSet::new(); for &generator_idx in &pending_generator_indices { - let (mut result, finished) = generators[generator_idx].run(&witness); + let (result, finished) = generators[generator_idx].run(&witness); if finished { expired_generator_indices.insert(generator_idx); } - let new_targets = result.all_populated_targets(); - target_partition.generate_copies(&mut result, &new_targets); - // Enqueue unfinished generators that were watching one of the newly populated targets. for watch in result.target_values.keys() { if let Some(watching_generator_indices) = generator_indices_by_watches.get(watch) { diff --git a/src/permutation_argument.rs b/src/permutation_argument.rs index f2739756..62ee63d4 100644 --- a/src/permutation_argument.rs +++ b/src/permutation_argument.rs @@ -6,7 +6,6 @@ use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; use crate::wire::Wire; -use crate::witness::PartialWitness; #[derive(Debug, Clone)] pub struct TargetPartitions { @@ -83,26 +82,6 @@ impl TargetPartitions { indices, } } - /// For the given set of targets, find any copy constraints involving those targets and populate - /// the witness with copies as needed. - pub fn generate_copies(&self, witness: &mut PartialWitness, targets: &[Target]) { - let mut result = PartialWitness::new(); - - for &target in targets { - let value = witness.get_target(target); - let partition = self.get_partition(target); - - for &sibling in partition { - if witness.contains(sibling) { - // This sibling's value was already set; make sure it has the same value. - assert_eq!(witness.get_target(sibling), value); - } else { - result.set_target(sibling, value); - } - } - } - witness.extend(result); - } } pub struct WirePartitions { diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 30d4cf70..290b5231 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -352,11 +352,7 @@ mod tests { } let circuit = builder.build(); let mut witness = PartialWitness::new(); - generate_partial_witness( - &mut witness, - &circuit.prover_only.generators, - &circuit.prover_only.targets_partition, - ); + generate_partial_witness(&mut witness, &circuit.prover_only.generators); let recursive_output_values_per_round: Vec> = recursive_outputs_per_round .iter() .map(|outputs| witness.get_targets(outputs)) diff --git a/src/prover.rs b/src/prover.rs index fc4e6f38..81b02674 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -34,11 +34,7 @@ pub(crate) fn prove, const D: usize>( let mut witness = inputs; info!("Running {} generators", prover_data.generators.len()); timed!( - generate_partial_witness( - &mut witness, - &prover_data.generators, - &prover_data.targets_partition - ), + generate_partial_witness(&mut witness, &prover_data.generators,), "to generate witness" ); diff --git a/src/witness.rs b/src/witness.rs index e71eebc9..a0b4b2a4 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -59,10 +59,6 @@ impl PartialWitness { targets.iter().all(|&t| self.contains(t)) } - pub fn all_populated_targets(&self) -> Vec { - self.target_values.keys().cloned().collect() - } - pub fn set_target(&mut self, target: Target, value: F) { let opt_old_value = self.target_values.insert(target, value); if let Some(old_value) = opt_old_value { From bc90909fa34ca0c81b73d42545edecddc53ec01f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 14:16:05 +0200 Subject: [PATCH 085/117] Add check of copy constraints after witness generation --- src/circuit_builder.rs | 5 +++-- src/circuit_data.rs | 13 +++++++++---- src/prover.rs | 11 +++++++++-- src/witness.rs | 27 +++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 8c7d0a72..9be203bb 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -293,11 +293,12 @@ impl, const D: usize> CircuitBuilder { sigmas_root, }; - let generators = self.generators; let prover_only = ProverOnlyCircuitData { - generators, + generators: self.generators, constants_commitment, sigmas_commitment, + copy_constraints: self.copy_constraints, + gate_instances: self.gate_instances, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4d9a7110..37925c0c 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -3,11 +3,12 @@ use anyhow::Result; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; -use crate::gates::gate::GateRef; +use crate::gates::gate::{GateInstance, GateRef}; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; +use crate::target::Target; use crate::verifier::verify; use crate::witness::PartialWitness; @@ -52,7 +53,7 @@ impl CircuitConfig { /// Circuit data required by the prover or the verifier. pub struct CircuitData, const D: usize> { - pub(crate) prover_only: ProverOnlyCircuitData, + pub(crate) prover_only: ProverOnlyCircuitData, pub(crate) verifier_only: VerifierOnlyCircuitData, pub(crate) common: CommonCircuitData, } @@ -75,7 +76,7 @@ impl, const D: usize> CircuitData { /// required, like LDEs of preprocessed polynomials. If more succinctness was desired, we could /// construct a more minimal prover structure and convert back and forth. pub struct ProverCircuitData, const D: usize> { - pub(crate) prover_only: ProverOnlyCircuitData, + pub(crate) prover_only: ProverOnlyCircuitData, pub(crate) common: CommonCircuitData, } @@ -98,12 +99,16 @@ impl, const D: usize> VerifierCircuitData { } /// Circuit data required by the prover, but not the verifier. -pub(crate) struct ProverOnlyCircuitData { +pub(crate) struct ProverOnlyCircuitData, const D: usize> { pub generators: Vec>>, /// Commitments to the constants polynomial. pub constants_commitment: ListPolynomialCommitment, /// Commitments to the sigma polynomial. pub sigmas_commitment: ListPolynomialCommitment, + /// The circuit's copy constraints. + pub copy_constraints: Vec<(Target, Target)>, + /// The concrete placement of each gate in the circuit. + pub gate_instances: Vec>, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/prover.rs b/src/prover.rs index 81b02674..0215aa13 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -23,7 +23,7 @@ use crate::witness::PartialWitness; pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; pub(crate) fn prove, const D: usize>( - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, inputs: PartialWitness, ) -> Proof { @@ -38,6 +38,13 @@ pub(crate) fn prove, const D: usize>( "to generate witness" ); + timed!( + witness + .check_copy_constraints(&prover_data.copy_constraints, &prover_data.gate_instances) + .unwrap(), // TODO: Change return value to `Result` and use `?` here. + "to check copy constraints" + ); + let config = &common_data.config; let num_wires = config.num_wires; let num_challenges = config.num_challenges; @@ -162,7 +169,7 @@ fn compute_z, const D: usize>( fn compute_vanishing_polys, const D: usize>( common_data: &CommonCircuitData, - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, wires_commitment: &ListPolynomialCommitment, plonk_zs_commitment: &ListPolynomialCommitment, betas: &[F], diff --git a/src/witness.rs b/src/witness.rs index a0b4b2a4..ff6a8a50 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,7 +1,10 @@ use std::collections::HashMap; +use anyhow::{ensure, Result}; + use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; +use crate::gates::gate::GateInstance; use crate::target::Target; use crate::wire::Wire; @@ -97,6 +100,30 @@ impl PartialWitness { self.set_target(target, value); } } + + /// Checks that the copy constraints are satisfied in the witness. + pub fn check_copy_constraints( + &self, + copy_constraints: &[(Target, Target)], + gate_instances: &[GateInstance], + ) -> Result<()> + where + F: Extendable, + { + for &(a, b) in copy_constraints { + if let (Target::Wire(wa), Target::Wire(wb)) = (a, b) { + let va = self.target_values.get(&a).copied().unwrap_or(F::ZERO); + let vb = self.target_values.get(&b).copied().unwrap_or(F::ZERO); + ensure!( + va == vb, + "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied.\ + Got values of {} and {} respectively.", + wa.input, wa.gate, gate_instances[wa.gate].gate_type.0.id(), wb.input, wb.gate, + gate_instances[wb.gate].gate_type.0.id(), va, vb); + } + } + Ok(()) + } } impl Default for PartialWitness { From 8ae664d94fba60ea38d96dc55ca5353c3a40e72c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 14:22:42 +0200 Subject: [PATCH 086/117] Minor --- src/gadgets/arithmetic.rs | 2 -- src/witness.rs | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 520f8cd9..7a9fd441 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -385,8 +385,6 @@ mod tests { let x = FF::rand(); let y = FF::rand(); - let x = FF::TWO; - let y = FF::ONE; let z = x / y; let xt = builder.constant_extension(x); let yt = builder.constant_extension(y); diff --git a/src/witness.rs b/src/witness.rs index f55d4297..aff0192a 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::convert::TryInto; use anyhow::{ensure, Result}; @@ -139,8 +140,8 @@ impl PartialWitness { let vb = self.target_values.get(&b).copied().unwrap_or(F::ZERO); ensure!( va == vb, - "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied.\ - Got values of {} and {} respectively.", + "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied. \ + Got values of {} and {} respectively.", wa.input, wa.gate, gate_instances[wa.gate].gate_type.0.id(), wb.input, wb.gate, gate_instances[wb.gate].gate_type.0.id(), va, vb); } From 747f1875af0e76b3b44ed6c91dc19430bff7ac4a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 14:26:05 +0200 Subject: [PATCH 087/117] Add todo for public inputs --- src/circuit_builder.rs | 1 - src/witness.rs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 640e17a5..0a7041e5 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -274,7 +274,6 @@ impl, const D: usize> CircuitBuilder { } let wire_partitions = target_partitions.to_wire_partitions(); - wire_partitions.get_sigma_polys(degree_log, k_is) } diff --git a/src/witness.rs b/src/witness.rs index aff0192a..989ec2bc 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -135,6 +135,7 @@ impl PartialWitness { F: Extendable, { for &(a, b) in copy_constraints { + // TODO: Take care of public inputs once they land. if let (Target::Wire(wa), Target::Wire(wb)) = (a, b) { let va = self.target_values.get(&a).copied().unwrap_or(F::ZERO); let vb = self.target_values.get(&b).copied().unwrap_or(F::ZERO); From c01e772fd80526de8a4e803971498b5668fbee0b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 15:41:39 +0200 Subject: [PATCH 088/117] Simplify filter computation --- src/gates/gate.rs | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 2ab410d4..1e73e37d 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -59,17 +59,7 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { ) -> Vec>; fn eval_filtered(&self, vars: EvaluationVars, prefix: &[bool]) -> Vec { - let filter: F::Extension = prefix - .iter() - .enumerate() - .map(|(i, &b)| { - if b { - vars.local_constants[i] - } else { - F::Extension::ONE - vars.local_constants[i] - } - }) - .product(); + let filter = compute_filter(prefix, vars.local_constants); self.eval_unfiltered(vars) .into_iter() .map(|c| filter * c) @@ -78,17 +68,7 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { /// Like `eval_filtered`, but specialized for points in the base field. fn eval_filtered_base(&self, vars: EvaluationVarsBase, prefix: &[bool]) -> Vec { - let filter = prefix - .iter() - .enumerate() - .map(|(i, &b)| { - if b { - vars.local_constants[i] - } else { - F::ONE - vars.local_constants[i] - } - }) - .product(); + let filter = compute_filter(prefix, vars.local_constants); self.eval_unfiltered_base(vars) .into_iter() .map(|c| c * filter) @@ -180,6 +160,16 @@ impl, T: Borrow>, const D: usize> Index for Ga } } -// impl, const D: usize> GatePrefixes { -// pub fn prefix_len() -// } +fn compute_filter(prefix: &[bool], constants: &[K]) -> K { + prefix + .iter() + .enumerate() + .map(|(i, &b)| { + if b { + constants[i] + } else { + K::ONE - constants[i] + } + }) + .product() +} From 0a75dcdb95fee4d00f14fbfd24a3be4e8ff58375 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 16:06:30 +0200 Subject: [PATCH 089/117] Remove prefix before calling `eval_unfiltered_*` --- src/gates/gate.rs | 6 ++++-- src/vars.rs | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 1e73e37d..24d0c062 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -58,8 +58,9 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { vars: EvaluationTargets, ) -> Vec>; - fn eval_filtered(&self, vars: EvaluationVars, prefix: &[bool]) -> Vec { + fn eval_filtered(&self, mut vars: EvaluationVars, prefix: &[bool]) -> Vec { let filter = compute_filter(prefix, vars.local_constants); + vars.remove_prefix(prefix); self.eval_unfiltered(vars) .into_iter() .map(|c| filter * c) @@ -67,8 +68,9 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { } /// Like `eval_filtered`, but specialized for points in the base field. - fn eval_filtered_base(&self, vars: EvaluationVarsBase, prefix: &[bool]) -> Vec { + fn eval_filtered_base(&self, mut vars: EvaluationVarsBase, prefix: &[bool]) -> Vec { let filter = compute_filter(prefix, vars.local_constants); + vars.remove_prefix(prefix); self.eval_unfiltered_base(vars) .into_iter() .map(|c| c * filter) diff --git a/src/vars.rs b/src/vars.rs index 74f15f23..a8fa6527 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -27,6 +27,16 @@ impl<'a, F: Extendable, const D: usize> EvaluationVars<'a, F, D> { let arr = self.local_wires[wire_range].try_into().unwrap(); ExtensionAlgebra::from_basefield_array(arr) } + + pub fn remove_prefix(&mut self, prefix: &[bool]) { + self.local_constants = &self.local_constants[prefix.len()..]; + } +} + +impl<'a, F: Field> EvaluationVarsBase<'a, F> { + pub fn remove_prefix(&mut self, prefix: &[bool]) { + self.local_constants = &self.local_constants[prefix.len()..]; + } } #[derive(Copy, Clone)] From 1cfffcc919734300767457ead43a540ed5a9569b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 16:45:30 +0200 Subject: [PATCH 090/117] Add comment on `compute_filter` --- src/gates/gate.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 24d0c062..831927e1 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -162,6 +162,8 @@ impl, T: Borrow>, const D: usize> Index for Ga } } +/// A gate's filter is computed as `prod b_i*c_i + (1-b_i)*(1-c_i)`, with `(b_i)` the prefix and +/// `(c_i)` the local constants, which is one if the prefix of `constants` matches `prefix`. fn compute_filter(prefix: &[bool], constants: &[K]) -> K { prefix .iter() From d69f11794e45034964a95d6f5069d88aaa4b9bc6 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 19:15:52 +0200 Subject: [PATCH 091/117] Revert `num_routed_wires` to 27 --- src/plonk_challenger.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index d1141672..9af5e590 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; @@ -5,7 +7,6 @@ use crate::field::field::Field; use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH}; use crate::proof::{Hash, HashTarget, OpeningSet}; use crate::target::Target; -use std::convert::TryInto; /// Observes prover messages, and generates challenges by hashing the transcript. #[derive(Clone)] @@ -369,7 +370,7 @@ mod tests { let config = CircuitConfig { num_wires: 12 + 12 + 3 + 101, - num_routed_wires: 200, + num_routed_wires: 27, ..CircuitConfig::default() }; let mut builder = CircuitBuilder::::new(config); From ac1179255a95aefd0dfacd3cd2c8e5d6403148ee Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 23 Jun 2021 15:45:48 -0700 Subject: [PATCH 092/117] Delete coset [I]FFT methods (#72) I think they had a mistake, and in any case we have a similar method in `polynomial.rs` now which has tests. --- src/field/fft.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/field/fft.rs b/src/field/fft.rs index 8bcde967..e1c1a097 100644 --- a/src/field/fft.rs +++ b/src/field/fft.rs @@ -126,32 +126,11 @@ pub(crate) fn fft_with_precomputation_power_of_2( PolynomialValues { values } } -pub(crate) fn coset_fft(poly: PolynomialCoeffs, shift: F) -> PolynomialValues { - let mut points = fft(poly); - let mut shift_exp_i = F::ONE; - for p in points.values.iter_mut() { - *p *= shift_exp_i; - shift_exp_i *= shift; - } - points -} - pub(crate) fn ifft(poly: PolynomialValues) -> PolynomialCoeffs { let precomputation = fft_precompute(poly.len()); ifft_with_precomputation_power_of_2(poly, &precomputation) } -pub(crate) fn coset_ifft(poly: PolynomialValues, shift: F) -> PolynomialCoeffs { - let shift_inv = shift.inverse(); - let mut shift_inv_exp_i = F::ONE; - let mut coeffs = ifft(poly); - for c in coeffs.coeffs.iter_mut() { - *c *= shift_inv_exp_i; - shift_inv_exp_i *= shift_inv; - } - coeffs -} - #[cfg(test)] mod tests { use crate::field::crandall_field::CrandallField; From 4ee70e449b705b2b56bfd9946f28a43a6f515782 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 11:00:47 +0200 Subject: [PATCH 093/117] Fix type errors and move copy constraints check to `Witness` --- src/prover.rs | 22 +++++++++--------- src/witness.rs | 60 +++++++++++++++++++++++++++++--------------------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/prover.rs b/src/prover.rs index 7f0e5399..16f63fdb 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -23,6 +23,11 @@ pub(crate) fn prove, const D: usize>( inputs: PartialWitness, ) -> Proof { let fri_config = &common_data.config.fri_config; + let config = &common_data.config; + let num_wires = config.num_wires; + let num_challenges = config.num_challenges; + let quotient_degree = common_data.quotient_degree(); + let degree = common_data.degree(); let start_proof_gen = Instant::now(); @@ -33,6 +38,11 @@ pub(crate) fn prove, const D: usize>( "to generate witness" ); + let witness = timed!( + partial_witness.full_witness(degree, num_wires), + "to compute full witness" + ); + timed!( witness .check_copy_constraints(&prover_data.copy_constraints, &prover_data.gate_instances) @@ -40,14 +50,6 @@ pub(crate) fn prove, const D: usize>( "to check copy constraints" ); - let config = &common_data.config; - let num_wires = config.num_wires; - let num_challenges = config.num_challenges; - let quotient_degree = common_data.quotient_degree(); - let degree = common_data.degree(); - - let witness = partial_witness.full_witness(degree, num_wires); - let wires_values: Vec> = timed!( witness .wire_values @@ -169,7 +171,7 @@ fn compute_zs, const D: usize>( witness: &Witness, betas: &[F], gammas: &[F], - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> Vec> { (0..common_data.config.num_challenges) @@ -181,7 +183,7 @@ fn compute_z, const D: usize>( witness: &Witness, beta: F, gamma: F, - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> PolynomialValues { let subgroup = &prover_data.subgroup; diff --git a/src/witness.rs b/src/witness.rs index a0e29268..7294f6e4 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -19,6 +19,41 @@ impl Witness { pub fn get_wire(&self, gate: usize, input: usize) -> F { self.wire_values[input][gate] } + + /// Checks that the copy constraints are satisfied in the witness. + pub fn check_copy_constraints( + &self, + copy_constraints: &[(Target, Target)], + gate_instances: &[GateInstance], + ) -> Result<()> + where + F: Extendable, + { + for &(a, b) in copy_constraints { + // TODO: Take care of public inputs once they land. + if let ( + Target::Wire(Wire { + gate: a_gate, + input: a_input, + }), + Target::Wire(Wire { + gate: b_gate, + input: b_input, + }), + ) = (a, b) + { + let va = self.get_wire(a_gate, a_input); + let vb = self.get_wire(b_gate, b_input); + ensure!( + va == vb, + "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied. \ + Got values of {} and {} respectively.", + a_input, a_gate, gate_instances[a_gate].gate_type.0.id(), b_input, b_gate, + gate_instances[b_gate].gate_type.0.id(), va, vb); + } + } + Ok(()) + } } #[derive(Clone, Debug)] @@ -145,31 +180,6 @@ impl PartialWitness { }); Witness { wire_values } } - - /// Checks that the copy constraints are satisfied in the witness. - pub fn check_copy_constraints( - &self, - copy_constraints: &[(Target, Target)], - gate_instances: &[GateInstance], - ) -> Result<()> - where - F: Extendable, - { - for &(a, b) in copy_constraints { - // TODO: Take care of public inputs once they land. - if let (Target::Wire(wa), Target::Wire(wb)) = (a, b) { - let va = self.target_values.get(&a).copied().unwrap_or(F::ZERO); - let vb = self.target_values.get(&b).copied().unwrap_or(F::ZERO); - ensure!( - va == vb, - "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied. \ - Got values of {} and {} respectively.", - wa.input, wa.gate, gate_instances[wa.gate].gate_type.0.id(), wb.input, wb.gate, - gate_instances[wb.gate].gate_type.0.id(), va, vb); - } - } - Ok(()) - } } impl Default for PartialWitness { From f215dffa9d357c4c9b64ed468ad321582a7e0909 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 11:45:16 +0200 Subject: [PATCH 094/117] Compute quotient directly --- src/polynomial/commitment.rs | 7 ++- src/polynomial/polynomial.rs | 24 +++++++-- src/prover.rs | 94 ++++++++++++++++++------------------ 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 19b3f4b9..e403cd36 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -13,7 +13,7 @@ use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; use crate::util::scaling::ReducingFactor; -use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; +use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; @@ -111,6 +111,11 @@ impl ListPolynomialCommitment { pub fn original_values(&self, index: usize) -> Vec { self.values.iter().map(|v| v.values[index]).collect() } + pub fn get_lde_values(&self, mut index: usize) -> &[F] { + reverse_bits(index, self.degree_log + self.rate_bits); + let slice = &self.merkle_tree.leaves[index]; + &slice[..slice.len() - if self.blinding { SALT_SIZE } else { 0 }] + } /// Takes the commitments to the constants - sigmas - wires - zs - quotient — polynomials, /// and an opening point `zeta` and produces a batched opening proof + opening set. diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 54c96c7e..e176aaf8 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -34,6 +34,18 @@ impl PolynomialValues { ifft(self) } + pub fn coset_ifft(self, shift: F) -> PolynomialCoeffs { + let mut shifted_coeffs = self.ifft(); + shifted_coeffs + .coeffs + .iter_mut() + .zip(shift.inverse().powers()) + .for_each(|(c, r)| { + *c *= r; + }); + shifted_coeffs + } + pub fn lde_multiple(polys: Vec, rate_bits: usize) -> Vec { polys.into_iter().map(|p| p.lde(rate_bits)).collect() } @@ -127,16 +139,20 @@ impl PolynomialCoeffs { self.padded(self.len() << rate_bits) } - pub(crate) fn padded(&self, new_len: usize) -> Self { + pub(crate) fn pad(&mut self, new_len: usize) { assert!( new_len >= self.len(), "Trying to pad a polynomial of length {} to a length of {}.", self.len(), new_len ); - let mut coeffs = self.coeffs.clone(); - coeffs.resize(new_len, F::ZERO); - Self { coeffs } + self.coeffs.resize(new_len, F::ZERO); + } + + pub(crate) fn padded(&self, new_len: usize) -> Self { + let mut poly = self.clone(); + poly.pad(new_len); + poly } /// Removes leading zero coefficients. diff --git a/src/prover.rs b/src/prover.rs index 16f63fdb..cd03f6e7 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -10,7 +10,7 @@ use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; use crate::plonk_common::eval_vanishing_poly_base; use crate::polynomial::commitment::ListPolynomialCommitment; -use crate::polynomial::polynomial::PolynomialValues; +use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; use crate::timed; use crate::util::transpose; @@ -89,8 +89,8 @@ pub(crate) fn prove, const D: usize>( let alphas = challenger.get_n_challenges(num_challenges); - let vanishing_polys = timed!( - compute_vanishing_polys( + let quotient_polys = timed!( + compute_quotient_polys( common_data, prover_data, &wires_commitment, @@ -104,21 +104,13 @@ pub(crate) fn prove, const D: usize>( // Compute the quotient polynomials, aka `t` in the Plonk paper. let all_quotient_poly_chunks = timed!( - vanishing_polys + quotient_polys .into_par_iter() - .flat_map(|vanishing_poly| { - let vanishing_poly_coeff = ifft(vanishing_poly); - // TODO: run `padded` when the division works. - let quotient_poly_coeff = vanishing_poly_coeff.divide_by_z_h(degree); - let x = F::rand(); - assert!( - quotient_poly_coeff.eval(x) * (x.exp(degree as u64) - F::ONE) - != vanishing_poly_coeff.eval(x), - "That's good news, this should fail! The division by z_h doesn't work yet,\ - most likely because compute_vanishing_polys isn't complete (doesn't use filters for example)." - ); + .flat_map(|mut quotient_poly| { + quotient_poly.trim(); + quotient_poly.pad(quotient_degree); // Split t into degree-n chunks. - quotient_poly_coeff.chunks(degree) + quotient_poly.chunks(degree) }) .collect(), "to compute quotient polys" @@ -208,49 +200,50 @@ fn compute_z, const D: usize>( plonk_z_points.into() } -fn compute_vanishing_polys, const D: usize>( +fn compute_quotient_polys<'a, F: Extendable, const D: usize>( common_data: &CommonCircuitData, - prover_data: &ProverOnlyCircuitData, - wires_commitment: &ListPolynomialCommitment, - plonk_zs_commitment: &ListPolynomialCommitment, + prover_data: &'a ProverOnlyCircuitData, + wires_commitment: &'a ListPolynomialCommitment, + plonk_zs_commitment: &'a ListPolynomialCommitment, betas: &[F], gammas: &[F], alphas: &[F], -) -> Vec> { +) -> Vec> { let num_challenges = common_data.config.num_challenges; + assert!( + common_data.max_filtered_constraint_degree_bits <= common_data.config.rate_bits, + "Having constraints of degree higher than the rate is not supported yet. \ + If we need this in the future, we can precompute the larger LDE before computing the `ListPolynomialCommitment`s." + ); + + // We reuse the LDE computed in `ListPolynomialCommitment` and extract every `step` points to get + // an LDE matching `max_filtered_constraint_degree`. + let step = + 1 << (common_data.config.rate_bits - common_data.max_filtered_constraint_degree_bits); + // When opening the `Z`s polys at the "next" point in Plonk, need to look at the point `next_step` + // steps away since we work on an LDE of degree `max_filtered_constraint_degree`. + let next_step = 1 << common_data.max_filtered_constraint_degree_bits; + let points = F::two_adic_subgroup( common_data.degree_bits + common_data.max_filtered_constraint_degree_bits, ); let lde_size = points.len(); - // Low-degree extend the polynomials commited in `comm` to the subgroup of size `lde_size`. - let commitment_to_lde = |comm: &ListPolynomialCommitment| -> Vec> { - comm.polynomials - .iter() - .map(|p| p.lde(common_data.max_filtered_constraint_degree_bits).fft()) - .collect() + // Retrieve the LDE values at index `i`. + let get_at_index = |comm: &'a ListPolynomialCommitment, i: usize| -> &'a [F] { + comm.get_lde_values(i * step) }; - let constants_lde = commitment_to_lde(&prover_data.constants_commitment); - let sigmas_lde = commitment_to_lde(&prover_data.sigmas_commitment); - let wires_lde = commitment_to_lde(wires_commitment); - let zs_lde = commitment_to_lde(plonk_zs_commitment); - - // Retrieve the polynomial values at index `i`. - let get_at_index = |ldes: &[PolynomialValues], i: usize| { - ldes.iter().map(|l| l.values[i]).collect::>() - }; - - let values: Vec> = points + let quotient_values: Vec> = points .into_par_iter() .enumerate() .map(|(i, x)| { - let i_next = (i + 1) % lde_size; - let local_constants = &get_at_index(&constants_lde, i); - let s_sigmas = &get_at_index(&sigmas_lde, i); - let local_wires = &get_at_index(&wires_lde, i); - let local_plonk_zs = &get_at_index(&zs_lde, i); - let next_plonk_zs = &get_at_index(&zs_lde, i_next); + let i_next = (i + next_step) % lde_size; + let local_constants = get_at_index(&prover_data.constants_commitment, i); + let s_sigmas = get_at_index(&prover_data.sigmas_commitment, i); + let local_wires = get_at_index(&wires_commitment, i); + let local_plonk_zs = get_at_index(&plonk_zs_commitment, i); + let next_plonk_zs = get_at_index(&plonk_zs_commitment, i_next); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_challenges); @@ -259,7 +252,7 @@ fn compute_vanishing_polys, const D: usize>( local_constants, local_wires, }; - eval_vanishing_poly_base( + let mut quotient_values = eval_vanishing_poly_base( common_data, x, vars, @@ -269,12 +262,19 @@ fn compute_vanishing_polys, const D: usize>( betas, gammas, alphas, - ) + ); + // TODO: We can avoid computing the exp. + let denominator_inv = x.exp(common_data.degree() as u64).inverse(); + quotient_values + .iter_mut() + .for_each(|v| *v *= denominator_inv); + quotient_values }) .collect(); - transpose(&values) + transpose("ient_values) .into_iter() .map(PolynomialValues::new) + .map(|values| values.coset_ifft(F::MULTIPLICATIVE_GROUP_GENERATOR)) .collect() } From 31f4eee367fc8f6aa3210385aa52c22bceccda61 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 14:11:47 +0200 Subject: [PATCH 095/117] Fix bug with shifted `x` --- src/field/extension_field/mod.rs | 3 ++- src/field/field.rs | 4 ++++ src/gates/gate.rs | 7 +++++++ src/polynomial/commitment.rs | 9 +++------ src/prover.rs | 7 ++++--- src/vars.rs | 4 ++-- 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 9caa7dc8..2a176fe9 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -1,6 +1,7 @@ -use crate::field::field::Field; use std::convert::TryInto; +use crate::field::field::Field; + pub mod algebra; pub mod quadratic; pub mod quartic; diff --git a/src/field/field.rs b/src/field/field.rs index b19f175e..3156d2c3 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -266,6 +266,10 @@ pub trait Field: fn rand_vec(n: usize) -> Vec { (0..n).map(|_| Self::rand()).collect() } + + fn coset_shift() -> Self { + Self::MULTIPLICATIVE_GROUP_GENERATOR + } } /// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 1765191e..cb8decef 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,9 +1,16 @@ +use std::borrow::Borrow; +use std::collections::HashMap; +use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; +use std::ops::Index; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::field::Field; +use crate::gates::gate_tree::Tree; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index e403cd36..aa70fd68 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -92,10 +92,7 @@ impl ListPolynomialCommitment { .par_iter() .map(|p| { assert_eq!(p.len(), degree, "Polynomial degree invalid."); - p.clone() - .lde(rate_bits) - .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR) - .values + p.clone().lde(rate_bits).coset_fft(F::coset_shift()).values }) .chain(if blinding { // If blinding, salt with two random elements to each leaf vector. @@ -111,8 +108,8 @@ impl ListPolynomialCommitment { pub fn original_values(&self, index: usize) -> Vec { self.values.iter().map(|v| v.values[index]).collect() } - pub fn get_lde_values(&self, mut index: usize) -> &[F] { - reverse_bits(index, self.degree_log + self.rate_bits); + pub fn get_lde_values(&self, index: usize) -> &[F] { + let index = reverse_bits(index, self.degree_log + self.rate_bits); let slice = &self.merkle_tree.leaves[index]; &slice[..slice.len() - if self.blinding { SALT_SIZE } else { 0 }] } diff --git a/src/prover.rs b/src/prover.rs index cd03f6e7..c0cc3d75 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -238,6 +238,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( .into_par_iter() .enumerate() .map(|(i, x)| { + let shifted_x = F::coset_shift() * x; let i_next = (i + next_step) % lde_size; let local_constants = get_at_index(&prover_data.constants_commitment, i); let s_sigmas = get_at_index(&prover_data.sigmas_commitment, i); @@ -254,7 +255,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( }; let mut quotient_values = eval_vanishing_poly_base( common_data, - x, + shifted_x, vars, local_plonk_zs, next_plonk_zs, @@ -264,7 +265,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( alphas, ); // TODO: We can avoid computing the exp. - let denominator_inv = x.exp(common_data.degree() as u64).inverse(); + let denominator_inv = (shifted_x.exp(common_data.degree() as u64) - F::ONE).inverse(); quotient_values .iter_mut() .for_each(|v| *v *= denominator_inv); @@ -275,6 +276,6 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( transpose("ient_values) .into_iter() .map(PolynomialValues::new) - .map(|values| values.coset_ifft(F::MULTIPLICATIVE_GROUP_GENERATOR)) + .map(|values| values.coset_ifft(F::coset_shift())) .collect() } diff --git a/src/vars.rs b/src/vars.rs index 74f15f23..9815dcce 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -6,13 +6,13 @@ use crate::field::extension_field::target::{ExtensionAlgebraTarget, ExtensionTar use crate::field::extension_field::Extendable; use crate::field::field::Field; -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct EvaluationVars<'a, F: Extendable, const D: usize> { pub(crate) local_constants: &'a [F::Extension], pub(crate) local_wires: &'a [F::Extension], } -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct EvaluationVarsBase<'a, F: Field> { pub(crate) local_constants: &'a [F], pub(crate) local_wires: &'a [F], From b0550979a62697b5199212d08eea444a073ecd9d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 15:42:29 +0200 Subject: [PATCH 096/117] Optimize evaluation of `Z_H` on coset. --- src/field/field.rs | 13 ++++++++---- src/gates/gate.rs | 4 ---- src/plonk_common.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++- src/prover.rs | 12 ++++++++--- 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/field/field.rs b/src/field/field.rs index 3156d2c3..081423c8 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -104,10 +104,7 @@ pub trait Field: fn primitive_root_of_unity(n_log: usize) -> Self { assert!(n_log <= Self::TWO_ADICITY); let mut base = Self::POWER_OF_TWO_GENERATOR; - for _ in n_log..Self::TWO_ADICITY { - base = base.square(); - } - base + base.exp_power_of_2(Self::TWO_ADICITY - n_log) } /// Computes a multiplicative subgroup whose order is known in advance. @@ -158,6 +155,14 @@ pub trait Field: bits_u64(self.to_canonical_u64()) } + fn exp_power_of_2(&self, power_log: usize) -> Self { + let mut res = *self; + for _ in 0..power_log { + res = res.square(); + } + res + } + fn exp(&self, power: u64) -> Self { let mut current = *self; let mut product = Self::ONE; diff --git a/src/gates/gate.rs b/src/gates/gate.rs index cb8decef..83b799be 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,6 +1,3 @@ -use std::borrow::Borrow; -use std::collections::HashMap; -use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; use std::ops::Index; @@ -10,7 +7,6 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; -use crate::gates::gate_tree::Tree; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; diff --git a/src/plonk_common.rs b/src/plonk_common.rs index c9f11b74..b92a4c57 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -116,6 +116,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( /// Like `eval_vanishing_poly`, but specialized for base field points. pub(crate) fn eval_vanishing_poly_base, const D: usize>( common_data: &CommonCircuitData, + i: usize, x: F, vars: EvaluationVarsBase, local_plonk_zs: &[F], @@ -124,6 +125,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( betas: &[F], gammas: &[F], alphas: &[F], + z_h_on_coset: &ZeroPolyOnCoset, ) -> Vec { let constraint_terms = evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); @@ -136,7 +138,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( for i in 0..common_data.config.num_challenges { let z_x = local_plonk_zs[i]; let z_gz = next_plonk_zs[i]; - vanishing_z_1_terms.push(eval_l_1(common_data.degree(), x) * (z_x - F::ONE)); + vanishing_z_1_terms.push(z_h_on_coset.eval_l1(i, x) * (z_x - F::ONE)); let mut f_prime = F::ONE; let mut g_prime = F::ONE; @@ -226,6 +228,51 @@ pub(crate) fn eval_zero_poly(n: usize, x: F) -> F { x.exp(n as u64) - F::ONE } +/// Precomputations of the evaluation of `Z_H(X) = X^n - 1` on a coset `gK` with `H <= K`. +pub(crate) struct ZeroPolyOnCoset { + /// `n = |H|`. + n: F, + /// `rate = |K|/|H|`. + rate: usize, + /// Holds `g^n * (w^n)^i - 1 = g^n * v^i - 1` for `i in 0..rate`, with `w` a generator of `K` and `v` a + /// `rate`-primitive root of unity. + evals: Vec, + /// Holds the multiplicative inverses of `evals`. + inverses: Vec, +} +impl ZeroPolyOnCoset { + pub fn new(n_log: usize, rate_bits: usize) -> Self { + let g_pow_n = F::coset_shift().exp_power_of_2(n_log); + let evals = F::two_adic_subgroup(rate_bits) + .into_iter() + .map(|x| g_pow_n * x - F::ONE) + .collect::>(); + let inverses = F::batch_multiplicative_inverse(&evals); + Self { + n: F::from_canonical_usize(1 << n_log), + rate: 1 << rate_bits, + evals, + inverses, + } + } + + /// Returns `Z_H(g * w^i)`. + pub fn eval(&self, i: usize) -> F { + self.evals[i % self.rate] + } + + /// Returns `Z_H(g * w^i)`. + pub fn eval_inverse(&self, i: usize) -> F { + self.inverses[i % self.rate] + } + + /// Returns `L_1(x) = Z_H(x)/(n * (x - 1))` with `x = w^i`. + pub fn eval_l1(&self, i: usize, x: F) -> F { + // Could also precompute the inverses using Montgomery. + self.eval(i) * (self.n * (x - F::ONE)).inverse() + } +} + /// Evaluate the Lagrange basis `L_1` with `L_1(1) = 1`, and `L_1(x) = 0` for other members of an /// order `n` multiplicative subgroup. pub(crate) fn eval_l_1(n: usize, x: F) -> F { diff --git a/src/prover.rs b/src/prover.rs index c0cc3d75..06a215e5 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -8,7 +8,7 @@ use crate::field::extension_field::Extendable; use crate::field::fft::ifft; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; -use crate::plonk_common::eval_vanishing_poly_base; +use crate::plonk_common::{eval_vanishing_poly_base, ZeroPolyOnCoset}; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; @@ -234,6 +234,11 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( comm.get_lde_values(i * step) }; + let z_h_on_coset = ZeroPolyOnCoset::new( + common_data.degree_bits, + common_data.max_filtered_constraint_degree_bits, + ); + let quotient_values: Vec> = points .into_par_iter() .enumerate() @@ -255,6 +260,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( }; let mut quotient_values = eval_vanishing_poly_base( common_data, + i, shifted_x, vars, local_plonk_zs, @@ -263,9 +269,9 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( betas, gammas, alphas, + &z_h_on_coset, ); - // TODO: We can avoid computing the exp. - let denominator_inv = (shifted_x.exp(common_data.degree() as u64) - F::ONE).inverse(); + let denominator_inv = z_h_on_coset.eval_inverse(i); quotient_values .iter_mut() .for_each(|v| *v *= denominator_inv); From 35f73a505f77ac3df36bf2d68ed3c2e485a63d7a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 15:49:36 +0200 Subject: [PATCH 097/117] Clippy --- src/gates/gate.rs | 3 --- src/generator.rs | 1 - src/plonk_common.rs | 4 ++-- src/polynomial/commitment.rs | 1 - src/prover.rs | 9 ++++----- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 83b799be..1765191e 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,12 +1,9 @@ use std::hash::{Hash, Hasher}; -use std::iter::FromIterator; -use std::ops::Index; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; -use crate::field::field::Field; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; diff --git a/src/generator.rs b/src/generator.rs index db81172f..d760df05 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -2,7 +2,6 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use crate::field::field::Field; -use crate::permutation_argument::TargetPartitions; use crate::target::Target; use crate::witness::PartialWitness; diff --git a/src/plonk_common.rs b/src/plonk_common.rs index b92a4c57..731d5741 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -116,7 +116,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( /// Like `eval_vanishing_poly`, but specialized for base field points. pub(crate) fn eval_vanishing_poly_base, const D: usize>( common_data: &CommonCircuitData, - i: usize, + index: usize, x: F, vars: EvaluationVarsBase, local_plonk_zs: &[F], @@ -138,7 +138,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( for i in 0..common_data.config.num_challenges { let z_x = local_plonk_zs[i]; let z_gz = next_plonk_zs[i]; - vanishing_z_1_terms.push(z_h_on_coset.eval_l1(i, x) * (z_x - F::ONE)); + vanishing_z_1_terms.push(z_h_on_coset.eval_l1(index, x) * (z_x - F::ONE)); let mut f_prime = F::ONE; let mut g_prime = F::ONE; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index aa70fd68..5ace678c 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -8,7 +8,6 @@ use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::PlonkPolynomials; -use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter}; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; diff --git a/src/prover.rs b/src/prover.rs index 06a215e5..6ea0b8fc 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -5,7 +5,6 @@ use rayon::prelude::*; use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; use crate::field::extension_field::Extendable; -use crate::field::fft::ifft; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; use crate::plonk_common::{eval_vanishing_poly_base, ZeroPolyOnCoset}; @@ -76,7 +75,7 @@ pub(crate) fn prove, const D: usize>( let gammas = challenger.get_n_challenges(num_challenges); let plonk_z_vecs = timed!( - compute_zs(&witness, &betas, &gammas, &prover_data, &common_data), + compute_zs(&witness, &betas, &gammas, prover_data, common_data), "to compute Z's" ); @@ -247,9 +246,9 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( let i_next = (i + next_step) % lde_size; let local_constants = get_at_index(&prover_data.constants_commitment, i); let s_sigmas = get_at_index(&prover_data.sigmas_commitment, i); - let local_wires = get_at_index(&wires_commitment, i); - let local_plonk_zs = get_at_index(&plonk_zs_commitment, i); - let next_plonk_zs = get_at_index(&plonk_zs_commitment, i_next); + let local_wires = get_at_index(wires_commitment, i); + let local_plonk_zs = get_at_index(plonk_zs_commitment, i); + let next_plonk_zs = get_at_index(plonk_zs_commitment, i_next); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_challenges); From 24af1f8c0ad2441f38d87d4b0e8f009c44d03f12 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 17:03:07 +0200 Subject: [PATCH 098/117] New heuristic --- src/gates/gate_tree.rs | 49 +++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index 9195e30c..ac1c7d37 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -61,17 +61,20 @@ impl, const D: usize> Tree> { /// i.e., the one with minimal `M` value. pub fn from_gates(mut gates: Vec>) -> Self { let timer = std::time::Instant::now(); - gates.sort_unstable_by_key(|g| -((g.0.degree() + g.0.num_constants()) as isize)); + gates.sort_unstable_by_key(|g| (-(g.0.degree() as isize), -(g.0.num_constants() as isize))); - for max_degree in 1..100 { - if let Some(mut tree) = Self::find_tree(&gates, max_degree) { - tree.shorten(); - info!( - "Found tree with max degree {} in {}s.", - max_degree, - timer.elapsed().as_secs_f32() - ); - return tree; + for max_degree_bits in 1..10 { + let max_degree = 1 << max_degree_bits; + for max_wires in 1..100 { + if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_wires) { + tree.shorten(); + info!( + "Found tree with max degree {} in {}s.", + max_degree, + timer.elapsed().as_secs_f32() + ); + return tree; + } } } @@ -79,18 +82,25 @@ impl, const D: usize> Tree> { } /// Greedily add gates wherever possible. Returns `None` if this fails. - fn find_tree(gates: &[GateRef], max_degree: usize) -> Option { + fn find_tree(gates: &[GateRef], max_degree: usize, max_wires: usize) -> Option { let mut tree = Tree::default(); for g in gates { - tree.try_add_gate(g, max_degree)?; + tree.try_add_gate(g, max_degree, max_wires)?; } Some(tree) } /// Try to add a gate in the tree. Returns `None` if this fails. - fn try_add_gate(&mut self, g: &GateRef, max_degree: usize) -> Option<()> { - let depth = max_degree.checked_sub(g.0.num_constants() + g.0.degree())?; + fn try_add_gate( + &mut self, + g: &GateRef, + max_degree: usize, + max_wires: usize, + ) -> Option<()> { + let depth = max_degree + .checked_sub(g.0.degree())? + .min(max_wires.checked_sub(g.0.num_constants())?); self.try_add_gate_at_depth(g, depth) } @@ -98,7 +108,7 @@ impl, const D: usize> Tree> { fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Option<()> { // If depth is 0, we have to insert the gate here. if depth == 0 { - return if let Tree::Bifurcation(_, _) = self { + return if let Tree::Bifurcation(None, None) = self { // Insert the gate as a new leaf. *self = Tree::Leaf(g.clone()); Some(()) @@ -185,6 +195,7 @@ mod tests { #[test] fn test_prefix_generation() { + env_logger::init(); type F = CrandallField; const D: usize = 4; @@ -201,6 +212,14 @@ mod tests { let tree = Tree::from_gates(gates.clone()); let mut gates_with_prefix = tree.traversal(); + for (g, p) in &gates_with_prefix { + println!("{} {:?}", &g.0.id()[..20.min(g.0.id().len())], p); + println!( + "{} {}", + g.0.degree() + p.len(), + g.0.num_constants() + p.len() + ); + } assert_eq!( gates_with_prefix.len(), From 8c008ce4fb99915110656059246daa06d0612349 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 17:43:53 +0200 Subject: [PATCH 099/117] Add comments --- src/gates/gate_tree.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index ac1c7d37..f0fb0414 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -51,19 +51,20 @@ impl Tree { } impl, const D: usize> Tree> { - /// Construct a binary tree of gates using the following greedy algorithm: - /// We want a tree where the maximum `M` of - /// `F(gate) = gate.degree() + gate.num_constants() + tree.depth(gate)` - /// over all gates is minimized. Such a tree is constructed by iterating over possible values of `M` - /// (from 1 to 99, then we give up) and then looking for a tree with this value of `M` - /// using `Self::find_tree`. This latter function greedily adds gates at the depth where - /// `F(gate)=M` to ensure no space is wasted. We return the first tree found in this manner, - /// i.e., the one with minimal `M` value. + /// The binary gate tree influences the degree `D` of the constraint polynomial and the number `C` + /// of constant wires in the circuit. We want to construct a tree minimizing both values. To do so + /// we iterate over possible values of `(D, C)` and try to construct a tree with these values. + /// For this construction, we use the greedy algorithm in `Self::find_tree`. + /// This latter function greedily adds gates at the depth where + /// `filtered_deg(gate)=D, constant_wires(gate)=C` to ensure no space is wasted. + /// We return the first tree found in this manner. pub fn from_gates(mut gates: Vec>) -> Self { let timer = std::time::Instant::now(); gates.sort_unstable_by_key(|g| (-(g.0.degree() as isize), -(g.0.num_constants() as isize))); for max_degree_bits in 1..10 { + // The constraint polynomials are padded to the next power in `compute_vanishig_polys`. + // So we can restrict our search space by setting `max_degree` to a power of 2. let max_degree = 1 << max_degree_bits; for max_wires in 1..100 { if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_wires) { @@ -98,6 +99,7 @@ impl, const D: usize> Tree> { max_degree: usize, max_wires: usize, ) -> Option<()> { + // We want `gate.degree + depth <= max_degree` and `gate.num_constants + depth <= max_wires`. let depth = max_degree .checked_sub(g.0.degree())? .min(max_wires.checked_sub(g.0.num_constants())?); @@ -213,9 +215,11 @@ mod tests { let tree = Tree::from_gates(gates.clone()); let mut gates_with_prefix = tree.traversal(); for (g, p) in &gates_with_prefix { - println!("{} {:?}", &g.0.id()[..20.min(g.0.id().len())], p); - println!( - "{} {}", + info!( + "\nGate: {}, prefix: {:?}.\n\ + Filtered constraint degree: {}, Num constant wires: {}", + &g.0.id()[..20.min(g.0.id().len())], + p, g.0.degree() + p.len(), g.0.num_constants() + p.len() ); From b4258976b10129e2bc5e41f90bdf9eb868a3e1cf Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 17:49:30 +0200 Subject: [PATCH 100/117] PR fixes --- src/gates/gate_tree.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index f0fb0414..f99ae076 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -231,11 +231,9 @@ mod tests { "The tree has too much or too little gates." ); assert!( - gates.iter().all(|g| gates_with_prefix + gates .iter() - .map(|(gg, _)| gg) - .find(|gg| *gg == g) - .is_some()), + .all(|g| gates_with_prefix.iter().map(|(gg, _)| gg).any(|gg| gg == g)), "Some gates are not in the tree." ); assert!( From 54315d173535f7128c9c8c12615b5f48b51807b3 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 18:06:48 +0200 Subject: [PATCH 101/117] Remove `GatePrefixes` to avoid using a `HashMap` --- src/circuit_builder.rs | 33 +++++++++++++++------------------ src/circuit_data.rs | 11 ++++------- src/gates/gate.rs | 24 +++++++++--------------- src/plonk_common.rs | 28 +++++++++------------------- 4 files changed, 37 insertions(+), 59 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index e6a1e296..221aa27b 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -11,7 +11,7 @@ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; -use crate::gates::gate::{GateInstance, GatePrefixes, GateRef}; +use crate::gates::gate::{GateInstance, GateRef, PrefixedGate}; use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; @@ -230,26 +230,24 @@ impl, const D: usize> CircuitBuilder { } } - fn constant_polys(&self, prefixes: &GatePrefixes) -> Vec> { - let num_constants = self - .gate_instances + fn constant_polys(&self, gates: &[PrefixedGate]) -> Vec> { + let num_constants = gates .iter() - .map(|gate_inst| gate_inst.constants.len() + prefixes[&gate_inst.gate_type].len()) + .map(|gate| gate.gate.0.num_constants() + gate.prefix.len()) .max() .unwrap(); let constants_per_gate = self .gate_instances .iter() - .map(|gate_inst| { + .map(|gate| { + let prefix = &gates + .iter() + .find(|g| g.gate.0.id() == gate.gate_type.0.id()) + .unwrap() + .prefix; let mut prefixed_constants = Vec::new(); - prefixed_constants.extend(prefixes[&gate_inst.gate_type].iter().map(|&b| { - if b { - F::ONE - } else { - F::ZERO - } - })); - prefixed_constants.extend_from_slice(&gate_inst.constants); + prefixed_constants.extend(prefix.iter().map(|&b| if b { F::ONE } else { F::ZERO })); + prefixed_constants.extend_from_slice(&gate.constants); prefixed_constants.resize(num_constants, F::ZERO); prefixed_constants }) @@ -297,9 +295,9 @@ impl, const D: usize> CircuitBuilder { let gates = self.gates.iter().cloned().collect(); let gate_tree = Tree::from_gates(gates); - let gate_prefixes = gate_tree.into(); + let prefixed_gates = PrefixedGate::from_tree(gate_tree); - let constant_vecs = self.constant_polys(&gate_prefixes); + let constant_vecs = self.constant_polys(&prefixed_gates); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -348,8 +346,7 @@ impl, const D: usize> CircuitBuilder { let common = CommonCircuitData { config: self.config, degree_bits, - gates, - gate_prefixes, + gates: prefixed_gates, num_gate_constraints, k_is, circuit_digest, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 3b314281..e54acd4f 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -3,7 +3,7 @@ use anyhow::Result; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; -use crate::gates::gate::{GatePrefixes, GateRef}; +use crate::gates::gate::{GateRef, PrefixedGate}; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; @@ -136,11 +136,8 @@ pub(crate) struct CommonCircuitData, const D: usize> { pub(crate) degree_bits: usize, - /// The types of gates used in this circuit. - pub(crate) gates: Vec>, - - /// The gate prefixes used to construct the selector polynomials. - pub(crate) gate_prefixes: GatePrefixes, + /// The types of gates used in this circuit, along with their prefixes. + pub(crate) gates: Vec>, /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, @@ -169,7 +166,7 @@ impl, const D: usize> CommonCircuitData { pub fn constraint_degree(&self) -> usize { self.gates .iter() - .map(|g| g.0.degree()) + .map(|g| g.gate.0.degree()) .max() .expect("No gates?") } diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 831927e1..5882ce00 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -142,23 +142,17 @@ pub struct GateInstance, const D: usize> { /// Map each gate to a boolean prefix used to construct the gate's selector polynomial. #[derive(Debug, Clone)] -pub struct GatePrefixes, const D: usize> { - pub prefixes: HashMap, Vec>, +pub struct PrefixedGate, const D: usize> { + pub gate: GateRef, + pub prefix: Vec, } -impl, const D: usize> From>> for GatePrefixes { - fn from(tree: Tree>) -> Self { - GatePrefixes { - prefixes: HashMap::from_iter(tree.traversal()), - } - } -} - -impl, T: Borrow>, const D: usize> Index for GatePrefixes { - type Output = Vec; - - fn index(&self, index: T) -> &Self::Output { - &self.prefixes[index.borrow()] +impl, const D: usize> PrefixedGate { + pub fn from_tree(tree: Tree>) -> Vec { + tree.traversal() + .into_iter() + .map(|(gate, prefix)| PrefixedGate { gate, prefix }) + .collect() } } diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 2edd2add..617ba530 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -5,7 +5,7 @@ use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; -use crate::gates::gate::{GatePrefixes, GateRef}; +use crate::gates::gate::{GateRef, PrefixedGate}; use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; @@ -76,12 +76,8 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let constraint_terms = evaluate_gate_constraints( - &common_data.gates, - common_data.num_gate_constraints, - vars, - &common_data.gate_prefixes, - ); + let constraint_terms = + evaluate_gate_constraints(&common_data.gates, common_data.num_gate_constraints, vars); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -129,12 +125,8 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let constraint_terms = evaluate_gate_constraints_base( - &common_data.gates, - common_data.num_gate_constraints, - vars, - &common_data.gate_prefixes, - ); + let constraint_terms = + evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -175,14 +167,13 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( /// strictly necessary, but it helps performance by ensuring that we allocate a vector with exactly /// the capacity that we need. pub fn evaluate_gate_constraints, const D: usize>( - gates: &[GateRef], + gates: &[PrefixedGate], num_gate_constraints: usize, vars: EvaluationVars, - prefixes: &GatePrefixes, ) -> Vec { let mut constraints = vec![F::Extension::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered(vars, &prefixes[gate]); + let gate_constraints = gate.gate.0.eval_filtered(vars, &gate.prefix); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, @@ -195,14 +186,13 @@ pub fn evaluate_gate_constraints, const D: usize>( } pub fn evaluate_gate_constraints_base, const D: usize>( - gates: &[GateRef], + gates: &[PrefixedGate], num_gate_constraints: usize, vars: EvaluationVarsBase, - prefixes: &GatePrefixes, ) -> Vec { let mut constraints = vec![F::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered_base(vars, &prefixes[gate]); + let gate_constraints = gate.gate.0.eval_filtered_base(vars, &gate.prefix); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, From c7753186b85f3e8eeaaecf6664e20ed51edc2a95 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 18:11:37 +0200 Subject: [PATCH 102/117] Clippy --- src/circuit_data.rs | 2 +- src/gates/gate.rs | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index e54acd4f..fe4fd0e0 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -3,7 +3,7 @@ use anyhow::Result; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; -use crate::gates::gate::{GateRef, PrefixedGate}; +use crate::gates::gate::PrefixedGate; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 5882ce00..4b37892c 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,9 +1,5 @@ -use std::borrow::Borrow; -use std::collections::HashMap; use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; -use std::iter::FromIterator; -use std::ops::Index; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; From aa78d02c016f8c0f3708adf20a9045940e66db84 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 24 Jun 2021 10:43:44 -0700 Subject: [PATCH 103/117] Delete outdated comment --- src/bin/bench_recursion.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 9fab2456..0682578d 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -51,12 +51,6 @@ fn bench_prove, const D: usize>() { builder.add_gate(ConstantGate::get(), vec![F::NEG_ONE]); - // for _ in 0..(40 * 5) { - // builder.add_gate( - // FriConsistencyGate::new(2, 3, 13), - // vec![F::primitive_root_of_unity(13)]); - // } - let prover = builder.build_prover(); let inputs = PartialWitness::new(); prover.prove(inputs); From e50eeb6cf49c2fa23455b3c406a5246a1d4d69c0 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 24 Jun 2021 10:44:46 -0700 Subject: [PATCH 104/117] Delete more outdated comments --- src/bin/bench_recursion.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 0682578d..1c9104f9 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -18,12 +18,6 @@ fn main() { env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init(); bench_prove::(); - - // bench_field_mul::(); - - // bench_fft(); - println!(); - // bench_gmimc::(); } fn bench_prove, const D: usize>() { From 2a38f8656ffa02c58b4665ece135ad090e2355de Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 20:53:15 +0200 Subject: [PATCH 105/117] PR feedback --- src/circuit_builder.rs | 2 +- src/gates/gate_tree.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 221aa27b..72c27208 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -245,7 +245,7 @@ impl, const D: usize> CircuitBuilder { .find(|g| g.gate.0.id() == gate.gate_type.0.id()) .unwrap() .prefix; - let mut prefixed_constants = Vec::new(); + let mut prefixed_constants = Vec::with_capacity(num_constants); prefixed_constants.extend(prefix.iter().map(|&b| if b { F::ONE } else { F::ZERO })); prefixed_constants.extend_from_slice(&gate.constants); prefixed_constants.resize(num_constants, F::ZERO); diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index f99ae076..1423de76 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -66,8 +66,8 @@ impl, const D: usize> Tree> { // The constraint polynomials are padded to the next power in `compute_vanishig_polys`. // So we can restrict our search space by setting `max_degree` to a power of 2. let max_degree = 1 << max_degree_bits; - for max_wires in 1..100 { - if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_wires) { + for max_constants in 1..100 { + if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_constants) { tree.shorten(); info!( "Found tree with max degree {} in {}s.", @@ -83,11 +83,11 @@ impl, const D: usize> Tree> { } /// Greedily add gates wherever possible. Returns `None` if this fails. - fn find_tree(gates: &[GateRef], max_degree: usize, max_wires: usize) -> Option { + fn find_tree(gates: &[GateRef], max_degree: usize, max_constants: usize) -> Option { let mut tree = Tree::default(); for g in gates { - tree.try_add_gate(g, max_degree, max_wires)?; + tree.try_add_gate(g, max_degree, max_constants)?; } Some(tree) } @@ -97,12 +97,12 @@ impl, const D: usize> Tree> { &mut self, g: &GateRef, max_degree: usize, - max_wires: usize, + max_constants: usize, ) -> Option<()> { // We want `gate.degree + depth <= max_degree` and `gate.num_constants + depth <= max_wires`. let depth = max_degree .checked_sub(g.0.degree())? - .min(max_wires.checked_sub(g.0.num_constants())?); + .min(max_constants.checked_sub(g.0.num_constants())?); self.try_add_gate_at_depth(g, depth) } @@ -249,7 +249,7 @@ mod tests { assert_ne!( &gates_with_prefix[i].1, &gates_with_prefix[j].1[0..gates_with_prefix[i].1.len()], - "Some gates share the same prefix" + "Some gates share an overlapping prefix" ); } } From 6605ca9d8950c50b09a249a3e63e50401c9d49bd Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 21:05:59 +0200 Subject: [PATCH 106/117] Add comment for `coset_shift` --- src/field/field.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/field/field.rs b/src/field/field.rs index 081423c8..516012d2 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -272,6 +272,7 @@ pub trait Field: (0..n).map(|_| Self::rand()).collect() } + /// Representative `g` of the coset used in FRI, so that LDEs in FRI are done over `gH`. fn coset_shift() -> Self { Self::MULTIPLICATIVE_GROUP_GENERATOR } From 3400caa19c788a3b7f104eac0336ba88dfb8edd4 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 24 Jun 2021 12:12:57 -0700 Subject: [PATCH 107/117] Fix recursion bench (#74) Nothing was routed in before --- src/bin/bench_recursion.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 1c9104f9..59b65e51 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -25,7 +25,7 @@ fn bench_prove, const D: usize>() { let config = CircuitConfig { num_wires: 134, - num_routed_wires: 12, + num_routed_wires: 27, security_bits: 128, rate_bits: 3, num_challenges: 3, @@ -39,11 +39,17 @@ fn bench_prove, const D: usize>() { let mut builder = CircuitBuilder::::new(config); + let zero = builder.zero(); + let zero_ext = builder.zero_extension(); + + let mut state = [zero; 12]; for _ in 0..10000 { - builder.add_gate_no_constants(gmimc_gate.clone()); + state = builder.permute(state); } - builder.add_gate(ConstantGate::get(), vec![F::NEG_ONE]); + // Random other gates. + builder.add(zero, zero); + builder.add_extension(zero_ext, zero_ext); let prover = builder.build_prover(); let inputs = PartialWitness::new(); From 19e7cb3942e86c372ba9d414fd453181e03e730c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 22:32:52 +0200 Subject: [PATCH 108/117] `into_iter` -> `into_par_iter` --- src/prover.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prover.rs b/src/prover.rs index 6ea0b8fc..67139128 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -279,7 +279,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( .collect(); transpose("ient_values) - .into_iter() + .into_par_iter() .map(PolynomialValues::new) .map(|values| values.coset_ifft(F::coset_shift())) .collect() From 54a15c012c1b936b634d5a0d564eb3f684ba185e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 22:38:40 +0200 Subject: [PATCH 109/117] Fixed bug and add division test in the base field. --- src/circuit_builder.rs | 2 +- src/gadgets/arithmetic.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 012e032f..b8d2b9c4 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -300,7 +300,7 @@ impl, const D: usize> CircuitBuilder { let degree_bits = log2_strict(degree); let subgroup = F::two_adic_subgroup(degree_bits); - let constant_vecs = self.constant_polys(); + let constant_vecs = self.constant_polys(&prefixed_gates); let constants_commitment = ListPolynomialCommitment::new(constant_vecs, self.config.fri_config.rate_bits, false); diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 7a9fd441..eb4db598 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -373,6 +373,29 @@ mod tests { use crate::fri::FriConfig; use crate::witness::PartialWitness; + #[test] + fn test_div() { + type F = CrandallField; + type FF = QuarticCrandallField; + const D: usize = 4; + + let config = CircuitConfig::large_config(); + + let mut builder = CircuitBuilder::::new(config); + + let x = F::rand(); + let y = F::rand(); + let z = x / y; + let xt = builder.constant(x); + let yt = builder.constant(y); + let zt = builder.constant(z); + let comp_zt = builder.div_unsafe(xt, yt); + builder.assert_equal(zt, comp_zt); + + let data = builder.build(); + let proof = data.prove(PartialWitness::new()); + } + #[test] fn test_div_extension() { type F = CrandallField; From 3ce9183970e254b452d40b01b14033ee4e089ece Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 22:57:50 +0200 Subject: [PATCH 110/117] Modify new test --- src/gadgets/arithmetic.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index eb4db598..2f8b1559 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -371,6 +371,8 @@ mod tests { use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; use crate::fri::FriConfig; + use crate::gates::arithmetic::ArithmeticGate; + use crate::target::Target; use crate::witness::PartialWitness; #[test] @@ -385,15 +387,22 @@ mod tests { let x = F::rand(); let y = F::rand(); - let z = x / y; - let xt = builder.constant(x); - let yt = builder.constant(y); - let zt = builder.constant(z); - let comp_zt = builder.div_unsafe(xt, yt); + let mut pw = PartialWitness::new(); + /// Computes x*x + 0*y = x^2. + let square_gate = builder.add_gate(ArithmeticGate::new(), vec![F::ONE, F::ZERO]); + pw.set_target(Target::wire(square_gate, 0), x); + pw.set_target(Target::wire(square_gate, 1), x); + let x2t = Target::wire(square_gate, ArithmeticGate::WIRE_OUTPUT); + let yt = Target::wire(square_gate, ArithmeticGate::WIRE_ADDEND); + pw.set_target(yt, y); + // Constant for x*x/y. + let zt = builder.constant(x * x / y); + // Computed division for x*x/y using the division gadget. + let comp_zt = builder.div_unsafe(x2t, yt); builder.assert_equal(zt, comp_zt); let data = builder.build(); - let proof = data.prove(PartialWitness::new()); + let proof = data.prove(pw); } #[test] From 2e9d3f768e832cc5c0714db7e2d94e93027ff4a9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Jun 2021 09:56:15 +0200 Subject: [PATCH 111/117] Better error message when quotient hasn't correct degree --- src/polynomial/polynomial.rs | 9 ++++++--- src/prover.rs | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index e176aaf8..3d6c12ba 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -2,6 +2,8 @@ use std::cmp::max; use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; +use anyhow::{ensure, Result}; + use crate::field::extension_field::Extendable; use crate::field::fft::{fft, ifft}; use crate::field::field::Field; @@ -139,19 +141,20 @@ impl PolynomialCoeffs { self.padded(self.len() << rate_bits) } - pub(crate) fn pad(&mut self, new_len: usize) { - assert!( + pub(crate) fn pad(&mut self, new_len: usize) -> Result<()> { + ensure!( new_len >= self.len(), "Trying to pad a polynomial of length {} to a length of {}.", self.len(), new_len ); self.coeffs.resize(new_len, F::ZERO); + Ok(()) } pub(crate) fn padded(&self, new_len: usize) -> Self { let mut poly = self.clone(); - poly.pad(new_len); + poly.pad(new_len).unwrap(); poly } diff --git a/src/prover.rs b/src/prover.rs index 67139128..42e5db98 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -107,7 +107,10 @@ pub(crate) fn prove, const D: usize>( .into_par_iter() .flat_map(|mut quotient_poly| { quotient_poly.trim(); - quotient_poly.pad(quotient_degree); + quotient_poly.pad(quotient_degree).expect( + "The quotient polynomial doesn't have the right degree.\ + This may be because the `Z`s polynomials are still too high degree.", + ); // Split t into degree-n chunks. quotient_poly.chunks(degree) }) From 727919b14f612853af24c689b0007b7fe3308468 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Jun 2021 10:20:20 +0200 Subject: [PATCH 112/117] Comment and test for `coset_ifft` --- src/polynomial/commitment.rs | 5 ++++- src/polynomial/polynomial.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 5ace678c..b0666a2f 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -30,7 +30,10 @@ impl ListPolynomialCommitment { /// Creates a list polynomial commitment for the polynomials interpolating the values in `values`. pub fn new(values: Vec>, rate_bits: usize, blinding: bool) -> Self { let degree = values[0].len(); - let polynomials = values.iter().map(|v| v.clone().ifft()).collect::>(); + let polynomials = values + .par_iter() + .map(|v| v.clone().ifft()) + .collect::>(); let lde_values = timed!( Self::lde_values(&polynomials, rate_bits, blinding), "to compute LDE" diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 3d6c12ba..888d7af0 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -36,6 +36,7 @@ impl PolynomialValues { ifft(self) } + /// Returns the polynomial whose evaluation on the coset `shift*H` is `self`. pub fn coset_ifft(self, shift: F) -> PolynomialCoeffs { let mut shifted_coeffs = self.ifft(); shifted_coeffs @@ -195,6 +196,7 @@ impl PolynomialCoeffs { fft(self) } + /// Returns the evaluation of the polynomial on the coset `shift*H`. pub fn coset_fft(self, shift: F) -> PolynomialValues { let modified_poly: Self = shift .powers() @@ -393,8 +395,31 @@ mod tests { .into_iter() .map(|x| poly.eval(x)) .collect::>(); - assert_eq!(coset_evals, naive_coset_evals); + + let ifft_coeffs = PolynomialValues::new(coset_evals).coset_ifft(shift); + assert_eq!(poly, ifft_coeffs.into()); + } + + #[test] + fn test_coset_ifft() { + type F = CrandallField; + + let k = 8; + let n = 1 << k; + let evals = PolynomialValues::new(F::rand_vec(n)); + let shift = F::rand(); + let coeffs = evals.clone().coset_ifft(shift); + + let generator = F::primitive_root_of_unity(k); + let naive_coset_evals = F::cyclic_subgroup_coset_known_order(generator, shift, n) + .into_iter() + .map(|x| coeffs.eval(x)) + .collect::>(); + assert_eq!(evals, naive_coset_evals.into()); + + let fft_evals = coeffs.coset_fft(shift); + assert_eq!(evals, fft_evals); } #[test] From 625377b4c071eb4b23a4923252146756550570b9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Jun 2021 11:24:26 +0200 Subject: [PATCH 113/117] Merge constant and sigma polynomials --- src/circuit_builder.rs | 39 +++++++++++++++++++---------------- src/circuit_data.rs | 31 +++++++++++++++++++--------- src/fri/recursive_verifier.rs | 3 +-- src/fri/verifier.rs | 3 +-- src/plonk_common.rs | 21 +++++++------------ src/polynomial/commitment.rs | 39 ++++++++++++++++++++++++----------- src/proof.rs | 10 +++++---- src/prover.rs | 14 +++++++------ src/verifier.rs | 3 +-- 9 files changed, 94 insertions(+), 69 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index b8d2b9c4..7d3bb137 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -230,7 +230,7 @@ impl, const D: usize> CircuitBuilder { } } - fn constant_polys(&self, gates: &[PrefixedGate]) -> Vec> { + fn constant_polys(&self, gates: &[PrefixedGate]) -> (Vec>, usize) { let num_constants = gates .iter() .map(|gate| gate.gate.0.num_constants() + gate.prefix.len()) @@ -253,10 +253,13 @@ impl, const D: usize> CircuitBuilder { }) .collect::>(); - transpose(&constants_per_gate) - .into_iter() - .map(PolynomialValues::new) - .collect() + ( + transpose(&constants_per_gate) + .into_iter() + .map(PolynomialValues::new) + .collect(), + num_constants, + ) } fn sigma_vecs(&self, k_is: &[F], subgroup: &[F]) -> Vec> { @@ -300,26 +303,26 @@ impl, const D: usize> CircuitBuilder { let degree_bits = log2_strict(degree); let subgroup = F::two_adic_subgroup(degree_bits); - let constant_vecs = self.constant_polys(&prefixed_gates); - let constants_commitment = - ListPolynomialCommitment::new(constant_vecs, self.config.fri_config.rate_bits, false); + let (constant_vecs, num_constants) = self.constant_polys(&prefixed_gates); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); - let sigmas_commitment = - ListPolynomialCommitment::new(sigma_vecs, self.config.fri_config.rate_bits, false); - let constants_root = constants_commitment.merkle_tree.root; - let sigmas_root = sigmas_commitment.merkle_tree.root; + let constants_sigmas_vecs = [constant_vecs, sigma_vecs].concat(); + let constants_sigmas_commitment = ListPolynomialCommitment::new( + constants_sigmas_vecs, + self.config.fri_config.rate_bits, + false, + ); + + let constants_sigmas_root = constants_sigmas_commitment.merkle_tree.root; let verifier_only = VerifierOnlyCircuitData { - constants_root, - sigmas_root, + constants_sigmas_root, }; let prover_only = ProverOnlyCircuitData { generators: self.generators, - constants_commitment, - sigmas_commitment, + constants_sigmas_commitment, subgroup, copy_constraints: self.copy_constraints, gate_instances: self.gate_instances, @@ -337,8 +340,7 @@ impl, const D: usize> CircuitBuilder { .expect("No gates?"); // TODO: This should also include an encoding of gate constraints. - let circuit_digest_parts = [constants_root.elements, sigmas_root.elements]; - let circuit_digest = hash_n_to_hash(circuit_digest_parts.concat(), false); + let circuit_digest = hash_n_to_hash(constants_sigmas_root.elements.to_vec(), false); let common = CommonCircuitData { config: self.config, @@ -346,6 +348,7 @@ impl, const D: usize> CircuitBuilder { gates: prefixed_gates, max_filtered_constraint_degree_bits: 3, // TODO: compute this correctly once filters land. num_gate_constraints, + num_constants, k_is, circuit_digest, }; diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4ec9c0d9..e46b0c41 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + use anyhow::Result; use crate::field::extension_field::Extendable; @@ -116,10 +118,8 @@ impl, const D: usize> VerifierCircuitData { /// Circuit data required by the prover, but not the verifier. pub(crate) struct ProverOnlyCircuitData, const D: usize> { pub generators: Vec>>, - /// Commitments to the constants polynomial. - pub constants_commitment: ListPolynomialCommitment, - /// Commitments to the sigma polynomial. - pub sigmas_commitment: ListPolynomialCommitment, + /// Commitments to the constants polynomials and sigma polynomials. + pub constants_sigmas_commitment: ListPolynomialCommitment, /// Subgroup of order `degree`. pub subgroup: Vec, /// The circuit's copy constraints. @@ -130,15 +130,12 @@ pub(crate) struct ProverOnlyCircuitData, const D: usize> { /// Circuit data required by the verifier, but not the prover. pub(crate) struct VerifierOnlyCircuitData { - /// A commitment to each constant polynomial. - pub(crate) constants_root: Hash, - - /// A commitment to each permutation polynomial. - pub(crate) sigmas_root: Hash, + /// A commitment to each constant polynomial and each permutation polynomial. + pub(crate) constants_sigmas_root: Hash, } /// Circuit data required by both the prover and the verifier. -pub(crate) struct CommonCircuitData, const D: usize> { +pub struct CommonCircuitData, const D: usize> { pub(crate) config: CircuitConfig, pub(crate) degree_bits: usize, @@ -152,6 +149,9 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, + /// The number of constant wires. + pub(crate) num_constants: usize, + /// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument. pub(crate) k_is: Vec, @@ -189,6 +189,17 @@ impl, const D: usize> CommonCircuitData { // 2 constraints for each Z check. self.config.num_challenges * 2 + self.num_gate_constraints } + + /// Range of the constant polynomials in the `constants_sigmas_commitment`. + pub fn constants_range(&self) -> Range { + 0..self.num_constants + } + + /// Range of the sigma polynomials in the `constants_sigmas_commitment`. + pub fn sigmas_range(&self) -> Range { + // `self.k_is.len() = num_routed_wires` is the number of sigma polynomials. + self.num_constants..self.num_constants + self.k_is.len() + } } /// The `Target` version of `VerifierCircuitData`, for use inside recursive circuits. Note that this diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 7c04d4a2..fc320f31 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -159,8 +159,7 @@ impl, const D: usize> CircuitBuilder { // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. let single_evals = [ - PlonkPolynomials::CONSTANTS, - PlonkPolynomials::SIGMAS, + PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::QUOTIENT, ] .iter() diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index d88e8e0b..3db4f6f5 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -162,8 +162,7 @@ fn fri_combine_initial, const D: usize>( // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. let single_evals = [ - PlonkPolynomials::CONSTANTS, - PlonkPolynomials::SIGMAS, + PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::QUOTIENT, ] .iter() diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 59961384..62dbb487 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -29,34 +29,29 @@ impl PolynomialsIndexBlinding { /// Holds the indices and blinding flags of the Plonk polynomials. pub struct PlonkPolynomials; impl PlonkPolynomials { - pub const CONSTANTS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { + pub const CONSTANTS_SIGMAS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { index: 0, blinding: false, }; - pub const SIGMAS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { - index: 1, - blinding: false, - }; pub const WIRES: PolynomialsIndexBlinding = PolynomialsIndexBlinding { - index: 2, + index: 1, blinding: true, }; pub const ZS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { - index: 3, + index: 2, blinding: true, }; pub const QUOTIENT: PolynomialsIndexBlinding = PolynomialsIndexBlinding { - index: 4, + index: 3, blinding: true, }; pub fn polynomials(i: usize) -> PolynomialsIndexBlinding { match i { - 0 => Self::CONSTANTS, - 1 => Self::SIGMAS, - 2 => Self::WIRES, - 3 => Self::ZS, - 4 => Self::QUOTIENT, + 0 => Self::CONSTANTS_SIGMAS, + 1 => Self::WIRES, + 2 => Self::ZS, + 3 => Self::QUOTIENT, _ => panic!("There are only 5 sets of polynomials in Plonk."), } } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index b0666a2f..d90b295b 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -1,6 +1,7 @@ use anyhow::Result; use rayon::prelude::*; +use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; @@ -119,14 +120,15 @@ impl ListPolynomialCommitment { /// Takes the commitments to the constants - sigmas - wires - zs - quotient — polynomials, /// and an opening point `zeta` and produces a batched opening proof + opening set. pub fn open_plonk( - commitments: &[&Self; 5], + commitments: &[&Self; 4], zeta: F::Extension, challenger: &mut Challenger, - config: &FriConfig, + common_data: &CommonCircuitData, ) -> (OpeningProof, OpeningSet) where F: Extendable, { + let config = &common_data.config.fri_config; assert!(D > 1, "Not implemented for D=1."); let degree_log = commitments[0].degree_log; let g = F::Extension::primitive_root_of_unity(degree_log); @@ -145,7 +147,7 @@ impl ListPolynomialCommitment { commitments[1], commitments[2], commitments[3], - commitments[4], + common_data, ); challenger.observe_opening_set(&os); @@ -157,8 +159,7 @@ impl ListPolynomialCommitment { // Polynomials opened at a single point. let single_polys = [ - PlonkPolynomials::CONSTANTS, - PlonkPolynomials::SIGMAS, + PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::QUOTIENT, ] .iter() @@ -291,6 +292,7 @@ mod tests { use anyhow::Result; use super::*; + use crate::circuit_data::CircuitConfig; use crate::plonk_common::PlonkPolynomials; fn gen_random_test_case, const D: usize>( @@ -318,7 +320,7 @@ mod tests { } fn check_batch_polynomial_commitment, const D: usize>() -> Result<()> { - let ks = [1, 2, 3, 5, 8]; + let ks = [10, 2, 3, 8]; let degree_log = 11; let fri_config = FriConfig { proof_of_work_bits: 2, @@ -326,12 +328,26 @@ mod tests { reduction_arity_bits: vec![2, 3, 1, 2], num_query_rounds: 3, }; + // We only care about `fri_config, num_constants`, and the length of `k_is` here. + let common_data = CommonCircuitData { + config: CircuitConfig { + fri_config, + ..CircuitConfig::large_config() + }, + degree_bits: 0, + gates: vec![], + max_filtered_constraint_degree_bits: 0, + num_gate_constraints: 0, + num_constants: 4, + k_is: vec![F::ONE; 6], + circuit_digest: Hash::from_partial(vec![]), + }; - let lpcs = (0..5) + let lpcs = (0..4) .map(|i| { ListPolynomialCommitment::::new( gen_random_test_case(ks[i], degree_log), - fri_config.rate_bits, + common_data.config.fri_config.rate_bits, PlonkPolynomials::polynomials(i).blinding, ) }) @@ -339,10 +355,10 @@ mod tests { let zeta = gen_random_point::(degree_log); let (proof, os) = ListPolynomialCommitment::open_plonk::( - &[&lpcs[0], &lpcs[1], &lpcs[2], &lpcs[3], &lpcs[4]], + &[&lpcs[0], &lpcs[1], &lpcs[2], &lpcs[3]], zeta, &mut Challenger::new(), - &fri_config, + &common_data, ); proof.verify( @@ -353,10 +369,9 @@ mod tests { lpcs[1].merkle_tree.root, lpcs[2].merkle_tree.root, lpcs[3].merkle_tree.root, - lpcs[4].merkle_tree.root, ], &mut Challenger::new(), - &fri_config, + &common_data.config.fri_config, ) } diff --git a/src/proof.rs b/src/proof.rs index 7536f3ee..47b67c9c 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,5 +1,6 @@ use std::convert::TryInto; +use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; @@ -160,11 +161,11 @@ impl, const D: usize> OpeningSet { pub fn new( z: F::Extension, g: F::Extension, - constant_commitment: &ListPolynomialCommitment, - plonk_sigmas_commitment: &ListPolynomialCommitment, + constants_sigmas_commitment: &ListPolynomialCommitment, wires_commitment: &ListPolynomialCommitment, plonk_zs_commitment: &ListPolynomialCommitment, quotient_polys_commitment: &ListPolynomialCommitment, + common_data: &CommonCircuitData, ) -> Self { let eval_commitment = |z: F::Extension, c: &ListPolynomialCommitment| { c.polynomials @@ -172,9 +173,10 @@ impl, const D: usize> OpeningSet { .map(|p| p.to_extension().eval(z)) .collect::>() }; + let constants_sigmas_eval = eval_commitment(z, constants_sigmas_commitment); Self { - constants: eval_commitment(z, constant_commitment), - plonk_s_sigmas: eval_commitment(z, plonk_sigmas_commitment), + constants: constants_sigmas_eval[common_data.constants_range()].to_vec(), + plonk_s_sigmas: constants_sigmas_eval[common_data.sigmas_range()].to_vec(), wires: eval_commitment(z, wires_commitment), plonk_zs: eval_commitment(z, plonk_zs_commitment), plonk_zs_right: eval_commitment(g * z, plonk_zs_commitment), diff --git a/src/prover.rs b/src/prover.rs index 42e5db98..728c17e5 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -134,15 +134,14 @@ pub(crate) fn prove, const D: usize>( let (opening_proof, openings) = timed!( ListPolynomialCommitment::open_plonk( &[ - &prover_data.constants_commitment, - &prover_data.sigmas_commitment, + &prover_data.constants_sigmas_commitment, &wires_commitment, &plonk_zs_commitment, "ient_polys_commitment, ], zeta, &mut challenger, - &common_data.config.fri_config + common_data, ), "to compute opening proofs" ); @@ -187,7 +186,9 @@ fn compute_z, const D: usize>( let x = subgroup[i - 1]; let mut numerator = F::ONE; let mut denominator = F::ONE; - let s_sigmas = prover_data.sigmas_commitment.original_values(i - 1); + let s_sigmas = &prover_data + .constants_sigmas_commitment + .original_values(i - 1)[common_data.sigmas_range()]; for j in 0..common_data.config.num_routed_wires { let wire_value = witness.get_wire(i - 1, j); let k_i = k_is[j]; @@ -247,8 +248,9 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( .map(|(i, x)| { let shifted_x = F::coset_shift() * x; let i_next = (i + next_step) % lde_size; - let local_constants = get_at_index(&prover_data.constants_commitment, i); - let s_sigmas = get_at_index(&prover_data.sigmas_commitment, i); + let local_constants_sigmas = get_at_index(&prover_data.constants_sigmas_commitment, i); + let local_constants = &local_constants_sigmas[common_data.constants_range()]; + let s_sigmas = &local_constants_sigmas[common_data.sigmas_range()]; let local_wires = get_at_index(wires_commitment, i); let local_plonk_zs = get_at_index(plonk_zs_commitment, i); let next_plonk_zs = get_at_index(plonk_zs_commitment, i_next); diff --git a/src/verifier.rs b/src/verifier.rs index a2750421..57bad7cf 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -64,8 +64,7 @@ pub(crate) fn verify, const D: usize>( let evaluations = proof.openings.clone(); let merkle_roots = &[ - verifier_data.constants_root, - verifier_data.sigmas_root, + verifier_data.constants_sigmas_root, proof.wires_root, proof.plonk_zs_root, proof.quotient_polys_root, From 810d1869a137a3cd86c9003d66b1f8142aa738a8 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Jun 2021 11:49:29 +0200 Subject: [PATCH 114/117] Minor improvements --- src/circuit_builder.rs | 25 ++++++++++++++----------- src/circuit_data.rs | 2 +- src/prover.rs | 16 ++++++++++++---- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 7d3bb137..79d10be2 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -17,6 +17,7 @@ use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; use crate::permutation_argument::TargetPartitions; +use crate::plonk_common::PlonkPolynomials; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; @@ -230,7 +231,7 @@ impl, const D: usize> CircuitBuilder { } } - fn constant_polys(&self, gates: &[PrefixedGate]) -> (Vec>, usize) { + fn constant_polys(&self, gates: &[PrefixedGate]) -> Vec> { let num_constants = gates .iter() .map(|gate| gate.gate.0.num_constants() + gate.prefix.len()) @@ -253,13 +254,10 @@ impl, const D: usize> CircuitBuilder { }) .collect::>(); - ( - transpose(&constants_per_gate) - .into_iter() - .map(PolynomialValues::new) - .collect(), - num_constants, - ) + transpose(&constants_per_gate) + .into_iter() + .map(PolynomialValues::new) + .collect() } fn sigma_vecs(&self, k_is: &[F], subgroup: &[F]) -> Vec> { @@ -303,7 +301,8 @@ impl, const D: usize> CircuitBuilder { let degree_bits = log2_strict(degree); let subgroup = F::two_adic_subgroup(degree_bits); - let (constant_vecs, num_constants) = self.constant_polys(&prefixed_gates); + let constant_vecs = self.constant_polys(&prefixed_gates); + let num_constants = constant_vecs.len(); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); @@ -312,7 +311,7 @@ impl, const D: usize> CircuitBuilder { let constants_sigmas_commitment = ListPolynomialCommitment::new( constants_sigmas_vecs, self.config.fri_config.rate_bits, - false, + PlonkPolynomials::CONSTANTS_SIGMAS.blinding, ); let constants_sigmas_root = constants_sigmas_commitment.merkle_tree.root; @@ -340,7 +339,11 @@ impl, const D: usize> CircuitBuilder { .expect("No gates?"); // TODO: This should also include an encoding of gate constraints. - let circuit_digest = hash_n_to_hash(constants_sigmas_root.elements.to_vec(), false); + let circuit_digest_parts = [ + constants_sigmas_root.elements.to_vec(), + vec![/* Add other circuit data here */], + ]; + let circuit_digest = hash_n_to_hash(circuit_digest_parts.concat(), false); let common = CommonCircuitData { config: self.config, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index e46b0c41..f2935e6c 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -190,7 +190,7 @@ impl, const D: usize> CommonCircuitData { self.config.num_challenges * 2 + self.num_gate_constraints } - /// Range of the constant polynomials in the `constants_sigmas_commitment`. + /// Range of the constants polynomials in the `constants_sigmas_commitment`. pub fn constants_range(&self) -> Range { 0..self.num_constants } diff --git a/src/prover.rs b/src/prover.rs index 728c17e5..a810c05f 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -7,7 +7,7 @@ use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; use crate::field::extension_field::Extendable; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{eval_vanishing_poly_base, ZeroPolyOnCoset}; +use crate::plonk_common::{eval_vanishing_poly_base, PlonkPolynomials, ZeroPolyOnCoset}; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; @@ -61,7 +61,11 @@ pub(crate) fn prove, const D: usize>( // TODO: Could try parallelizing the transpose, or not doing it explicitly, instead having // merkle_root_bit_rev_order do it implicitly. let wires_commitment = timed!( - ListPolynomialCommitment::new(wires_values, fri_config.rate_bits, true), + ListPolynomialCommitment::new( + wires_values, + fri_config.rate_bits, + PlonkPolynomials::WIRES.blinding + ), "to compute wires commitment" ); @@ -80,7 +84,11 @@ pub(crate) fn prove, const D: usize>( ); let plonk_zs_commitment = timed!( - ListPolynomialCommitment::new(plonk_z_vecs, fri_config.rate_bits, true), + ListPolynomialCommitment::new( + plonk_z_vecs, + fri_config.rate_bits, + PlonkPolynomials::ZS.blinding + ), "to commit to Z's" ); @@ -122,7 +130,7 @@ pub(crate) fn prove, const D: usize>( ListPolynomialCommitment::new_from_polys( all_quotient_poly_chunks, fri_config.rate_bits, - true + PlonkPolynomials::QUOTIENT.blinding ), "to commit to quotient polys" ); From c2b2ef921a668aaf21fdfeee14f1a558a8e2a59e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 28 Jun 2021 09:47:47 +0200 Subject: [PATCH 115/117] PR feedback --- src/circuit_data.rs | 3 +-- src/plonk_common.rs | 2 +- src/polynomial/commitment.rs | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index f2935e6c..ff910ce0 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -197,8 +197,7 @@ impl, const D: usize> CommonCircuitData { /// Range of the sigma polynomials in the `constants_sigmas_commitment`. pub fn sigmas_range(&self) -> Range { - // `self.k_is.len() = num_routed_wires` is the number of sigma polynomials. - self.num_constants..self.num_constants + self.k_is.len() + self.num_constants..self.num_constants + self.config.num_routed_wires } } diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 62dbb487..f6e0e228 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -52,7 +52,7 @@ impl PlonkPolynomials { 1 => Self::WIRES, 2 => Self::ZS, 3 => Self::QUOTIENT, - _ => panic!("There are only 5 sets of polynomials in Plonk."), + _ => panic!("There are only 4 sets of polynomials in Plonk."), } } } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index d90b295b..ecd3e875 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -328,10 +328,11 @@ mod tests { reduction_arity_bits: vec![2, 3, 1, 2], num_query_rounds: 3, }; - // We only care about `fri_config, num_constants`, and the length of `k_is` here. + // We only care about `fri_config, num_constants`, and `num_routed_wires` here. let common_data = CommonCircuitData { config: CircuitConfig { fri_config, + num_routed_wires: 6, ..CircuitConfig::large_config() }, degree_bits: 0, From 7734aed62cec6eaf30ea772c4c14ebe91688710a Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 28 Jun 2021 08:56:36 -0700 Subject: [PATCH 116/117] Performance tweaks (#77) --- src/circuit_builder.rs | 5 +++-- src/circuit_data.rs | 2 ++ src/polynomial/commitment.rs | 14 ++------------ src/prover.rs | 4 +--- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 79d10be2..a006ec98 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -21,7 +21,7 @@ use crate::plonk_common::PlonkPolynomials; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; -use crate::util::{log2_strict, transpose}; +use crate::util::{log2_strict, transpose, transpose_poly_values}; use crate::wire::Wire; pub struct CircuitBuilder, const D: usize> { @@ -307,7 +307,7 @@ impl, const D: usize> CircuitBuilder { let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); - let constants_sigmas_vecs = [constant_vecs, sigma_vecs].concat(); + let constants_sigmas_vecs = [constant_vecs, sigma_vecs.clone()].concat(); let constants_sigmas_commitment = ListPolynomialCommitment::new( constants_sigmas_vecs, self.config.fri_config.rate_bits, @@ -322,6 +322,7 @@ impl, const D: usize> CircuitBuilder { let prover_only = ProverOnlyCircuitData { generators: self.generators, constants_sigmas_commitment, + sigmas: transpose_poly_values(sigma_vecs), subgroup, copy_constraints: self.copy_constraints, gate_instances: self.gate_instances, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index ff910ce0..afb37628 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -120,6 +120,8 @@ pub(crate) struct ProverOnlyCircuitData, const D: usize> { pub generators: Vec>>, /// Commitments to the constants polynomials and sigma polynomials. pub constants_sigmas_commitment: ListPolynomialCommitment, + /// The transpose of the list of sigma polynomials. + pub sigmas: Vec>, /// Subgroup of order `degree`. pub subgroup: Vec, /// The circuit's copy constraints. diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index ecd3e875..da647ffa 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -19,7 +19,6 @@ pub const SALT_SIZE: usize = 2; pub struct ListPolynomialCommitment { pub polynomials: Vec>, - pub values: Vec>, pub merkle_tree: MerkleTree, pub degree: usize, pub degree_log: usize, @@ -40,7 +39,7 @@ impl ListPolynomialCommitment { "to compute LDE" ); - Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + Self::new_from_data(polynomials, lde_values, degree, rate_bits, blinding) } /// Creates a list polynomial commitment for the polynomials `polynomials`. @@ -50,21 +49,16 @@ impl ListPolynomialCommitment { blinding: bool, ) -> Self { let degree = polynomials[0].len(); - let values = polynomials - .iter() - .map(|v| v.clone().fft()) - .collect::>(); let lde_values = timed!( Self::lde_values(&polynomials, rate_bits, blinding), "to compute LDE" ); - Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + Self::new_from_data(polynomials, lde_values, degree, rate_bits, blinding) } fn new_from_data( polynomials: Vec>, - values: Vec>, lde_values: Vec>, degree: usize, rate_bits: usize, @@ -76,7 +70,6 @@ impl ListPolynomialCommitment { Self { polynomials, - values, merkle_tree, degree, degree_log: log2_strict(degree), @@ -108,9 +101,6 @@ impl ListPolynomialCommitment { .collect() } - pub fn original_values(&self, index: usize) -> Vec { - self.values.iter().map(|v| v.values[index]).collect() - } pub fn get_lde_values(&self, index: usize) -> &[F] { let index = reverse_bits(index, self.degree_log + self.rate_bits); let slice = &self.merkle_tree.leaves[index]; diff --git a/src/prover.rs b/src/prover.rs index a810c05f..e8cd7f46 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -194,9 +194,7 @@ fn compute_z, const D: usize>( let x = subgroup[i - 1]; let mut numerator = F::ONE; let mut denominator = F::ONE; - let s_sigmas = &prover_data - .constants_sigmas_commitment - .original_values(i - 1)[common_data.sigmas_range()]; + let s_sigmas = &prover_data.sigmas[i - 1]; for j in 0..common_data.config.num_routed_wires { let wire_value = witness.get_wire(i - 1, j); let k_i = k_is[j]; From 57c86143bc4aa4d73fa865c4ba1d14cbd4ab0679 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 29 Jun 2021 08:06:06 +0200 Subject: [PATCH 117/117] Fix mistake in comment --- src/plonk_common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plonk_common.rs b/src/plonk_common.rs index f6e0e228..db304c0a 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -256,7 +256,7 @@ impl ZeroPolyOnCoset { self.evals[i % self.rate] } - /// Returns `Z_H(g * w^i)`. + /// Returns `1 / Z_H(g * w^i)`. pub fn eval_inverse(&self, i: usize) -> F { self.inverses[i % self.rate] }