diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 27e58336..7ecf625f 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -17,10 +17,11 @@ 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; -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> { @@ -258,7 +259,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(); @@ -278,7 +279,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. @@ -296,6 +297,11 @@ impl, const D: usize> CircuitBuilder { let (gate_tree, max_filtered_constraint_degree, num_constants) = Tree::from_gates(gates); let prefixed_gates = PrefixedGate::from_tree(gate_tree); + let degree_bits = log2_strict(degree); + let subgroup = F::two_adic_subgroup(degree_bits); + + let constant_vecs = self.constant_polys(&prefixed_gates); + let num_constants = constant_vecs.len(); let constant_vecs = self.constant_polys(&prefixed_gates, num_constants); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), @@ -304,24 +310,25 @@ 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 sigmas_commitment = ListPolynomialCommitment::new( - sigma_vecs.into_iter().map(|v| v.ifft()).collect(), + let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); + + 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, - false, + PlonkPolynomials::CONSTANTS_SIGMAS.blinding, ); - let constants_root = constants_commitment.merkle_tree.root; - let sigmas_root = sigmas_commitment.merkle_tree.root; + 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, + sigmas: transpose_poly_values(sigma_vecs), + subgroup, copy_constraints: self.copy_constraints, gate_instances: self.gate_instances, }; @@ -337,17 +344,20 @@ 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_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, degree_bits, 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 93fa06e5..afb37628 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,12 @@ 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, + /// The transpose of the list of sigma polynomials. + pub sigmas: Vec>, + /// Subgroup of order `degree`. + pub subgroup: Vec, /// The circuit's copy constraints. pub copy_constraints: Vec<(Target, Target)>, /// The concrete placement of each gate in the circuit. @@ -128,15 +132,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, @@ -144,9 +145,15 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The types of gates used in this circuit, along with their prefixes. pub(crate) gates: Vec>, + /// 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. 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, @@ -177,13 +184,23 @@ impl, const D: usize> CommonCircuitData { } pub fn quotient_degree(&self) -> usize { - self.constraint_degree() - 1 + ((1 << self.max_filtered_constraint_degree_bits) - 1) * self.degree() } pub fn total_constraints(&self) -> usize { // 2 constraints for each Z check. self.config.num_challenges * 2 + self.num_gate_constraints } + + /// Range of the constants 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.num_constants..self.num_constants + self.config.num_routed_wires + } } /// The `Target` version of `VerifierCircuitData`, for use inside recursive circuits. Note that this 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/field/fft.rs b/src/field/fft.rs index e1c1a097..56764b47 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 } } @@ -179,10 +181,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 95d2f6e0..516012d2 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -104,21 +104,18 @@ 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. 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 { @@ -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; @@ -266,6 +271,11 @@ pub trait Field: fn rand_vec(n: usize) -> Vec { (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 + } } /// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. diff --git a/src/field/interpolation.rs b/src/field/interpolation.rs index 968eeca2..3d5e609c 100644 --- a/src/field/interpolation.rs +++ b/src/field/interpolation.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() @@ -104,8 +102,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/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/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 7a9fd441..2f8b1559 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -371,8 +371,40 @@ 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] + 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 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(pw); + } + #[test] fn test_div_extension() { type F = CrandallField; 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/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/plonk_common.rs b/src/plonk_common.rs index 617ba530..db304c0a 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -29,35 +29,30 @@ 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, - _ => panic!("There are only 5 sets of polynomials in Plonk."), + 0 => Self::CONSTANTS_SIGMAS, + 1 => Self::WIRES, + 2 => Self::ZS, + 3 => Self::QUOTIENT, + _ => panic!("There are only 4 sets of polynomials in Plonk."), } } } @@ -116,6 +111,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, + index: usize, x: F, vars: EvaluationVarsBase, local_plonk_zs: &[F], @@ -124,6 +120,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 +133,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(index, x) * (z_x - F::ONE)); let mut f_prime = F::ONE; let mut g_prime = F::ONE; @@ -226,6 +223,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 `1 / 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/polynomial/commitment.rs b/src/polynomial/commitment.rs index 4b2bf872..da647ffa 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; @@ -8,11 +9,11 @@ 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::polynomial::polynomial::PolynomialCoeffs; +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; @@ -20,18 +21,49 @@ pub struct ListPolynomialCommitment { pub polynomials: 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 { + /// 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 + .par_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, 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 lde_values = timed!( Self::lde_values(&polynomials, rate_bits, blinding), "to compute LDE" ); + Self::new_from_data(polynomials, lde_values, degree, rate_bits, blinding) + } + + fn new_from_data( + polynomials: 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"); @@ -40,6 +72,7 @@ impl ListPolynomialCommitment { polynomials, merkle_tree, degree, + degree_log: log2_strict(degree), rate_bits, blinding, } @@ -55,10 +88,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. @@ -71,24 +101,26 @@ 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 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 }] } /// 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 = 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!( @@ -105,7 +137,7 @@ impl ListPolynomialCommitment { commitments[1], commitments[2], commitments[3], - commitments[4], + common_data, ); challenger.observe_opening_set(&os); @@ -117,8 +149,7 @@ impl ListPolynomialCommitment { // Polynomials opened at a single point. let single_polys = [ - PlonkPolynomials::CONSTANTS, - PlonkPolynomials::SIGMAS, + PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::QUOTIENT, ] .iter() @@ -251,16 +282,17 @@ 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>( 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() } @@ -278,7 +310,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, @@ -286,12 +318,27 @@ mod tests { reduction_arity_bits: vec![2, 3, 1, 2], num_query_rounds: 3, }; + // 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, + 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, ) }) @@ -299,10 +346,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( @@ -313,10 +360,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/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 02f66684..888d7af0 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; @@ -34,6 +36,19 @@ 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 + .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,11 +142,21 @@ impl PolynomialCoeffs { self.padded(self.len() << rate_bits) } + 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 { - assert!(new_len >= self.len()); - let mut coeffs = self.coeffs.clone(); - coeffs.resize(new_len, F::ZERO); - Self { coeffs } + let mut poly = self.clone(); + poly.pad(new_len).unwrap(); + poly } /// Removes leading zero coefficients. @@ -171,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() @@ -369,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] 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 0d3f1be1..e8cd7f46 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -5,19 +5,16 @@ 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::plonk_common::{eval_vanishing_poly_base, PlonkPolynomials, ZeroPolyOnCoset}; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; use crate::timed; use crate::util::transpose; use crate::vars::EvaluationVarsBase; -use crate::wire::Wire; -use crate::witness::PartialWitness; +use crate::witness::{PartialWitness, Witness}; pub(crate) fn prove, const D: usize>( prover_data: &ProverOnlyCircuitData, @@ -25,16 +22,26 @@ 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(); - 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" ); + 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) @@ -42,16 +49,11 @@ 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 wires_polynomials: Vec> = timed!( - (0..num_wires) - .into_par_iter() - .map(|i| compute_wire_polynomial(i, &witness, degree)) + let wires_values: Vec> = timed!( + witness + .wire_values + .iter() + .map(|column| PolynomialValues::new(column.clone())) .collect(), "to compute wire polynomials" ); @@ -59,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_polynomials, fri_config.rate_bits, true), + ListPolynomialCommitment::new( + wires_values, + fri_config.rate_bits, + PlonkPolynomials::WIRES.blinding + ), "to compute wires commitment" ); @@ -72,10 +78,17 @@ 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), + ListPolynomialCommitment::new( + plonk_z_vecs, + fri_config.rate_bits, + PlonkPolynomials::ZS.blinding + ), "to commit to Z's" ); @@ -83,8 +96,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, @@ -98,20 +111,27 @@ 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); - let quotient_poly_coeff = vanishing_poly_coeff.divide_by_z_h(degree); + .flat_map(|mut quotient_poly| { + quotient_poly.trim(); + 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_coeff.chunks(degree) + quotient_poly.chunks(degree) }) .collect(), "to compute quotient polys" ); 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, + PlonkPolynomials::QUOTIENT.blinding + ), "to commit to quotient polys" ); @@ -122,15 +142,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" ); @@ -150,44 +169,97 @@ 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 { - PolynomialCoeffs::zero(common_data.degree()) // TODO +) -> 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[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 *= 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); + } + 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> { - let lde_size = common_data.lde_size(); - let lde_gen = common_data.lde_generator(); +) -> 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." + ); - let points = F::cyclic_subgroup_known_order(lde_gen, lde_size); - let values: Vec> = points + // 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(); + + // 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 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() .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 shifted_x = F::coset_shift() * x; + let i_next = (i + next_step) % lde_size; + 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); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_challenges); @@ -196,9 +268,10 @@ 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, + i, + shifted_x, vars, local_plonk_zs, next_plonk_zs, @@ -206,31 +279,19 @@ fn compute_vanishing_polys, const D: usize>( betas, gammas, alphas, - ) + &z_h_on_coset, + ); + let denominator_inv = z_h_on_coset.eval_inverse(i); + quotient_values + .iter_mut() + .for_each(|v| *v *= denominator_inv); + quotient_values }) .collect(); - transpose(&values) - .into_iter() + transpose("ient_values) + .into_par_iter() .map(PolynomialValues::new) + .map(|values| values.coset_ifft(F::coset_shift())) .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/vars.rs b/src/vars.rs index a8fa6527..7e9b372f 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], 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, diff --git a/src/witness.rs b/src/witness.rs index 989ec2bc..7294f6e4 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -10,6 +10,52 @@ use crate::gates::gate::GateInstance; use crate::target::Target; use crate::wire::Wire; +#[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] + } + + /// 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)] pub struct PartialWitness { pub(crate) target_values: HashMap, @@ -125,29 +171,14 @@ impl PartialWitness { } } - /// 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); + 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; } - } - Ok(()) + }); + Witness { wire_values } } }