mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-07 16:23:12 +00:00
Merge branch 'main' into add_routed_wires
# Conflicts: # src/gadgets/arithmetic.rs
This commit is contained in:
commit
a017e79f65
@ -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<F: Extendable<D>, const D: usize> {
|
||||
@ -239,12 +240,11 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
fn constant_polys(&self, gates: &[PrefixedGate<F, D>]) -> Vec<PolynomialValues<F>> {
|
||||
let num_constants = gates
|
||||
.iter()
|
||||
.map(|gate| gate.gate.0.num_constants() + gate.prefix.len())
|
||||
.max()
|
||||
.unwrap();
|
||||
fn constant_polys(
|
||||
&self,
|
||||
gates: &[PrefixedGate<F, D>],
|
||||
num_constants: usize,
|
||||
) -> Vec<PolynomialValues<F>> {
|
||||
let constants_per_gate = self
|
||||
.gate_instances
|
||||
.iter()
|
||||
@ -268,7 +268,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn sigma_vecs(&self, k_is: &[F]) -> Vec<PolynomialValues<F>> {
|
||||
fn sigma_vecs(&self, k_is: &[F], subgroup: &[F]) -> Vec<PolynomialValues<F>> {
|
||||
let degree = self.gate_instances.len();
|
||||
let degree_log = log2_strict(degree);
|
||||
let mut target_partitions = TargetPartitions::new();
|
||||
@ -288,7 +288,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
|
||||
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.
|
||||
@ -303,35 +303,34 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
info!("degree after blinding & padding: {}", degree);
|
||||
|
||||
let gates = self.gates.iter().cloned().collect();
|
||||
let gate_tree = Tree::from_gates(gates);
|
||||
let (gate_tree, max_filtered_constraint_degree, num_constants) = Tree::from_gates(gates);
|
||||
let prefixed_gates = PrefixedGate::from_tree(gate_tree);
|
||||
|
||||
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,
|
||||
false,
|
||||
);
|
||||
let degree_bits = log2_strict(degree);
|
||||
let subgroup = F::two_adic_subgroup(degree_bits);
|
||||
|
||||
let constant_vecs = self.constant_polys(&prefixed_gates, num_constants);
|
||||
|
||||
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,
|
||||
};
|
||||
@ -347,17 +346,20 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
.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,
|
||||
num_gate_constraints,
|
||||
num_constants,
|
||||
k_is,
|
||||
circuit_digest,
|
||||
};
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::field::extension_field::Extendable;
|
||||
@ -116,10 +118,12 @@ impl<F: Extendable<D>, const D: usize> VerifierCircuitData<F, D> {
|
||||
/// Circuit data required by the prover, but not the verifier.
|
||||
pub(crate) struct ProverOnlyCircuitData<F: Extendable<D>, const D: usize> {
|
||||
pub generators: Vec<Box<dyn WitnessGenerator<F>>>,
|
||||
/// Commitments to the constants polynomial.
|
||||
pub constants_commitment: ListPolynomialCommitment<F>,
|
||||
/// Commitments to the sigma polynomial.
|
||||
pub sigmas_commitment: ListPolynomialCommitment<F>,
|
||||
/// Commitments to the constants polynomials and sigma polynomials.
|
||||
pub constants_sigmas_commitment: ListPolynomialCommitment<F>,
|
||||
/// The transpose of the list of sigma polynomials.
|
||||
pub sigmas: Vec<Vec<F>>,
|
||||
/// Subgroup of order `degree`.
|
||||
pub subgroup: Vec<F>,
|
||||
/// 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<F: Extendable<D>, const D: usize> {
|
||||
|
||||
/// Circuit data required by the verifier, but not the prover.
|
||||
pub(crate) struct VerifierOnlyCircuitData<F: Field> {
|
||||
/// A commitment to each constant polynomial.
|
||||
pub(crate) constants_root: Hash<F>,
|
||||
|
||||
/// A commitment to each permutation polynomial.
|
||||
pub(crate) sigmas_root: Hash<F>,
|
||||
/// A commitment to each constant polynomial and each permutation polynomial.
|
||||
pub(crate) constants_sigmas_root: Hash<F>,
|
||||
}
|
||||
|
||||
/// Circuit data required by both the prover and the verifier.
|
||||
pub(crate) struct CommonCircuitData<F: Extendable<D>, const D: usize> {
|
||||
pub struct CommonCircuitData<F: Extendable<D>, const D: usize> {
|
||||
pub(crate) config: CircuitConfig,
|
||||
|
||||
pub(crate) degree_bits: usize,
|
||||
@ -144,9 +145,15 @@ pub(crate) struct CommonCircuitData<F: Extendable<D>, const D: usize> {
|
||||
/// The types of gates used in this circuit, along with their prefixes.
|
||||
pub(crate) gates: Vec<PrefixedGate<F, D>>,
|
||||
|
||||
/// The maximum degree of a filter times a constraint by any gate.
|
||||
pub(crate) max_filtered_constraint_degree: 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<F>,
|
||||
|
||||
@ -177,13 +184,23 @@ impl<F: Extendable<D>, const D: usize> CommonCircuitData<F, D> {
|
||||
}
|
||||
|
||||
pub fn quotient_degree(&self) -> usize {
|
||||
self.constraint_degree() - 1
|
||||
(self.max_filtered_constraint_degree - 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<usize> {
|
||||
0..self.num_constants
|
||||
}
|
||||
|
||||
/// Range of the sigma polynomials in the `constants_sigmas_commitment`.
|
||||
pub fn sigmas_range(&self) -> Range<usize> {
|
||||
self.num_constants..self.num_constants + self.config.num_routed_wires
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Target` version of `VerifierCircuitData`, for use inside recursive circuits. Note that this
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -44,12 +44,14 @@ pub(crate) fn fft_precompute<F: Field>(degree: usize) -> FftPrecomputation<F> {
|
||||
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();
|
||||
|
||||
@ -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<Self> {
|
||||
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<Self> {
|
||||
let generator = Self::primitive_root_of_unity(n_log);
|
||||
generator.powers().take(1 << n_log).collect()
|
||||
}
|
||||
|
||||
fn cyclic_subgroup_unknown_order(generator: Self) -> Vec<Self> {
|
||||
@ -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<Self> {
|
||||
(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, ...`.
|
||||
|
||||
@ -10,10 +10,8 @@ use crate::util::log2_ceil;
|
||||
pub(crate) fn interpolant<F: Field>(points: &[(F, F)]) -> PolynomialCoeffs<F> {
|
||||
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 };
|
||||
|
||||
|
||||
@ -159,8 +159,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
// 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()
|
||||
|
||||
@ -162,8 +162,7 @@ fn fri_combine_initial<F: Field + Extendable<D>, 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()
|
||||
|
||||
@ -57,8 +57,9 @@ impl<F: Extendable<D>, const D: usize> Tree<GateRef<F, D>> {
|
||||
/// 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<GateRef<F, D>>) -> Self {
|
||||
/// We return the first tree found in this manner, along with it's maximum filtered degree
|
||||
/// and the number of constant wires needed when using this tree.
|
||||
pub fn from_gates(mut gates: Vec<GateRef<F, D>>) -> (Self, usize, usize) {
|
||||
let timer = std::time::Instant::now();
|
||||
gates.sort_unstable_by_key(|g| (-(g.0.degree() as isize), -(g.0.num_constants() as isize)));
|
||||
|
||||
@ -67,14 +68,30 @@ impl<F: Extendable<D>, const D: usize> Tree<GateRef<F, D>> {
|
||||
// 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_constants in 1..100 {
|
||||
if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_constants) {
|
||||
tree.shorten();
|
||||
if let Some(mut best_tree) = Self::find_tree(&gates, max_degree, max_constants) {
|
||||
let mut best_num_constants = best_tree.num_constants();
|
||||
let mut best_degree = max_degree;
|
||||
// Iterate backwards from `max_degree` to try to find a tree with a lower degree
|
||||
// but the same number of constants.
|
||||
'optdegree: for degree in (0..max_degree).rev() {
|
||||
if let Some(mut tree) = Self::find_tree(&gates, degree, max_constants) {
|
||||
let num_constants = tree.num_constants();
|
||||
if num_constants > best_num_constants {
|
||||
break 'optdegree;
|
||||
} else {
|
||||
best_degree = degree;
|
||||
best_num_constants = num_constants;
|
||||
best_tree = tree;
|
||||
}
|
||||
}
|
||||
}
|
||||
info!(
|
||||
"Found tree with max degree {} in {}s.",
|
||||
max_degree,
|
||||
"Found tree with max degree {} and {} constants wires in {}s.",
|
||||
best_degree,
|
||||
best_num_constants,
|
||||
timer.elapsed().as_secs_f32()
|
||||
);
|
||||
return tree;
|
||||
return (best_tree, best_degree, best_num_constants);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,6 +106,7 @@ impl<F: Extendable<D>, const D: usize> Tree<GateRef<F, D>> {
|
||||
for g in gates {
|
||||
tree.try_add_gate(g, max_degree, max_constants)?;
|
||||
}
|
||||
tree.shorten();
|
||||
Some(tree)
|
||||
}
|
||||
|
||||
@ -180,6 +198,24 @@ impl<F: Extendable<D>, const D: usize> Tree<GateRef<F, D>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the tree's maximum filtered constraint degree.
|
||||
fn max_filtered_degree(&self) -> usize {
|
||||
self.traversal()
|
||||
.into_iter()
|
||||
.map(|(g, p)| g.0.degree() + p.len())
|
||||
.max()
|
||||
.expect("Empty tree.")
|
||||
}
|
||||
|
||||
/// Returns the number of constant wires needed to fit all prefixes and gate constants.
|
||||
fn num_constants(&self) -> usize {
|
||||
self.traversal()
|
||||
.into_iter()
|
||||
.map(|(g, p)| g.0.num_constants() + p.len())
|
||||
.max()
|
||||
.expect("Empty tree.")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -210,7 +246,7 @@ mod tests {
|
||||
];
|
||||
let len = gates.len();
|
||||
|
||||
let tree = Tree::from_gates(gates.clone());
|
||||
let (tree, _, _) = Tree::from_gates(gates.clone());
|
||||
let mut gates_with_prefix = tree.traversal();
|
||||
for (g, p) in &gates_with_prefix {
|
||||
info!(
|
||||
|
||||
@ -110,9 +110,9 @@ impl WirePartitions {
|
||||
&self,
|
||||
degree_log: usize,
|
||||
k_is: &[F],
|
||||
subgroup: &[F],
|
||||
) -> Vec<PolynomialValues<F>> {
|
||||
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::<Vec<_>>();
|
||||
PolynomialValues::new(values)
|
||||
})
|
||||
|
||||
@ -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<F: Extendable<D>, const D: usize>(
|
||||
/// Like `eval_vanishing_poly`, but specialized for base field points.
|
||||
pub(crate) fn eval_vanishing_poly_base<F: Extendable<D>, const D: usize>(
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
index: usize,
|
||||
x: F,
|
||||
vars: EvaluationVarsBase<F>,
|
||||
local_plonk_zs: &[F],
|
||||
@ -124,6 +120,7 @@ pub(crate) fn eval_vanishing_poly_base<F: Extendable<D>, const D: usize>(
|
||||
betas: &[F],
|
||||
gammas: &[F],
|
||||
alphas: &[F],
|
||||
z_h_on_coset: &ZeroPolyOnCoset<F>,
|
||||
) -> Vec<F> {
|
||||
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<F: Extendable<D>, 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<F: Field>(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<F: Field> {
|
||||
/// `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<F>,
|
||||
/// Holds the multiplicative inverses of `evals`.
|
||||
inverses: Vec<F>,
|
||||
}
|
||||
impl<F: Field> ZeroPolyOnCoset<F> {
|
||||
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::<Vec<_>>();
|
||||
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<F: Field>(n: usize, x: F) -> F {
|
||||
|
||||
@ -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<F: Field> {
|
||||
pub polynomials: Vec<PolynomialCoeffs<F>>,
|
||||
pub merkle_tree: MerkleTree<F>,
|
||||
pub degree: usize,
|
||||
pub degree_log: usize,
|
||||
pub rate_bits: usize,
|
||||
pub blinding: bool,
|
||||
}
|
||||
|
||||
impl<F: Field> ListPolynomialCommitment<F> {
|
||||
pub fn new(polynomials: Vec<PolynomialCoeffs<F>>, rate_bits: usize, blinding: bool) -> Self {
|
||||
/// Creates a list polynomial commitment for the polynomials interpolating the values in `values`.
|
||||
pub fn new(values: Vec<PolynomialValues<F>>, rate_bits: usize, blinding: bool) -> Self {
|
||||
let degree = values[0].len();
|
||||
let polynomials = values
|
||||
.par_iter()
|
||||
.map(|v| v.clone().ifft())
|
||||
.collect::<Vec<_>>();
|
||||
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<PolynomialCoeffs<F>>,
|
||||
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<PolynomialCoeffs<F>>,
|
||||
lde_values: Vec<Vec<F>>,
|
||||
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<F: Field> ListPolynomialCommitment<F> {
|
||||
polynomials,
|
||||
merkle_tree,
|
||||
degree,
|
||||
degree_log: log2_strict(degree),
|
||||
rate_bits,
|
||||
blinding,
|
||||
}
|
||||
@ -55,10 +88,7 @@ impl<F: Field> ListPolynomialCommitment<F> {
|
||||
.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<F: Field> ListPolynomialCommitment<F> {
|
||||
.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<const D: usize>(
|
||||
commitments: &[&Self; 5],
|
||||
commitments: &[&Self; 4],
|
||||
zeta: F::Extension,
|
||||
challenger: &mut Challenger<F>,
|
||||
config: &FriConfig,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> (OpeningProof<F, D>, OpeningSet<F, D>)
|
||||
where
|
||||
F: Extendable<D>,
|
||||
{
|
||||
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<F: Field> ListPolynomialCommitment<F> {
|
||||
commitments[1],
|
||||
commitments[2],
|
||||
commitments[3],
|
||||
commitments[4],
|
||||
common_data,
|
||||
);
|
||||
challenger.observe_opening_set(&os);
|
||||
|
||||
@ -117,8 +149,7 @@ impl<F: Field> ListPolynomialCommitment<F> {
|
||||
|
||||
// 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<F: Field + Extendable<D>, const D: usize>(
|
||||
k: usize,
|
||||
degree_log: usize,
|
||||
) -> Vec<PolynomialCoeffs<F>> {
|
||||
) -> Vec<PolynomialValues<F>> {
|
||||
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<F: Field + Extendable<D>, 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: 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::<F>::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::<F, D>(degree_log);
|
||||
let (proof, os) = ListPolynomialCommitment::open_plonk::<D>(
|
||||
&[&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,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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<F: Field> PolynomialValues<F> {
|
||||
ifft(self)
|
||||
}
|
||||
|
||||
/// Returns the polynomial whose evaluation on the coset `shift*H` is `self`.
|
||||
pub fn coset_ifft(self, shift: F) -> PolynomialCoeffs<F> {
|
||||
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<Self>, rate_bits: usize) -> Vec<Self> {
|
||||
polys.into_iter().map(|p| p.lde(rate_bits)).collect()
|
||||
}
|
||||
@ -127,11 +142,21 @@ impl<F: Field> PolynomialCoeffs<F> {
|
||||
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<F: Field> PolynomialCoeffs<F> {
|
||||
fft(self)
|
||||
}
|
||||
|
||||
/// Returns the evaluation of the polynomial on the coset `shift*H`.
|
||||
pub fn coset_fft(self, shift: F) -> PolynomialValues<F> {
|
||||
let modified_poly: Self = shift
|
||||
.powers()
|
||||
@ -369,8 +395,31 @@ mod tests {
|
||||
.into_iter()
|
||||
.map(|x| poly.eval(x))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
assert_eq!(evals, naive_coset_evals.into());
|
||||
|
||||
let fft_evals = coeffs.coset_fft(shift);
|
||||
assert_eq!(evals, fft_evals);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
10
src/proof.rs
10
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<F: Field + Extendable<D>, const D: usize> OpeningSet<F, D> {
|
||||
pub fn new(
|
||||
z: F::Extension,
|
||||
g: F::Extension,
|
||||
constant_commitment: &ListPolynomialCommitment<F>,
|
||||
plonk_sigmas_commitment: &ListPolynomialCommitment<F>,
|
||||
constants_sigmas_commitment: &ListPolynomialCommitment<F>,
|
||||
wires_commitment: &ListPolynomialCommitment<F>,
|
||||
plonk_zs_commitment: &ListPolynomialCommitment<F>,
|
||||
quotient_polys_commitment: &ListPolynomialCommitment<F>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Self {
|
||||
let eval_commitment = |z: F::Extension, c: &ListPolynomialCommitment<F>| {
|
||||
c.polynomials
|
||||
@ -172,9 +173,10 @@ impl<F: Field + Extendable<D>, const D: usize> OpeningSet<F, D> {
|
||||
.map(|p| p.to_extension().eval(z))
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
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),
|
||||
|
||||
210
src/prover.rs
210
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::util::{log2_ceil, transpose};
|
||||
use crate::vars::EvaluationVarsBase;
|
||||
use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
use crate::witness::{PartialWitness, Witness};
|
||||
|
||||
pub(crate) fn prove<F: Extendable<D>, const D: usize>(
|
||||
prover_data: &ProverOnlyCircuitData<F, D>,
|
||||
@ -25,16 +22,26 @@ pub(crate) fn prove<F: Extendable<D>, const D: usize>(
|
||||
inputs: PartialWitness<F>,
|
||||
) -> Proof<F, D> {
|
||||
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<F: Extendable<D>, 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<PolynomialCoeffs<F>> = timed!(
|
||||
(0..num_wires)
|
||||
.into_par_iter()
|
||||
.map(|i| compute_wire_polynomial(i, &witness, degree))
|
||||
let wires_values: Vec<PolynomialValues<F>> = timed!(
|
||||
witness
|
||||
.wire_values
|
||||
.iter()
|
||||
.map(|column| PolynomialValues::new(column.clone()))
|
||||
.collect(),
|
||||
"to compute wire polynomials"
|
||||
);
|
||||
@ -59,7 +61,11 @@ pub(crate) fn prove<F: Extendable<D>, 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<F: Extendable<D>, 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<F: Extendable<D>, 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<F: Extendable<D>, 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<F: Extendable<D>, 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,94 @@ pub(crate) fn prove<F: Extendable<D>, const D: usize>(
|
||||
}
|
||||
|
||||
fn compute_zs<F: Extendable<D>, const D: usize>(
|
||||
witness: &Witness<F>,
|
||||
betas: &[F],
|
||||
gammas: &[F],
|
||||
prover_data: &ProverOnlyCircuitData<F, D>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Vec<PolynomialCoeffs<F>> {
|
||||
) -> Vec<PolynomialValues<F>> {
|
||||
(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<F: Extendable<D>, const D: usize>(
|
||||
witness: &Witness<F>,
|
||||
beta: F,
|
||||
gamma: F,
|
||||
prover_data: &ProverOnlyCircuitData<F, D>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
_i: usize,
|
||||
) -> PolynomialCoeffs<F> {
|
||||
PolynomialCoeffs::zero(common_data.degree()) // TODO
|
||||
) -> PolynomialValues<F> {
|
||||
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<F: Extendable<D>, const D: usize>(
|
||||
fn compute_quotient_polys<'a, F: Extendable<D>, const D: usize>(
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
prover_data: &ProverOnlyCircuitData<F, D>,
|
||||
wires_commitment: &ListPolynomialCommitment<F>,
|
||||
plonk_zs_commitment: &ListPolynomialCommitment<F>,
|
||||
prover_data: &'a ProverOnlyCircuitData<F, D>,
|
||||
wires_commitment: &'a ListPolynomialCommitment<F>,
|
||||
plonk_zs_commitment: &'a ListPolynomialCommitment<F>,
|
||||
betas: &[F],
|
||||
gammas: &[F],
|
||||
alphas: &[F],
|
||||
) -> Vec<PolynomialValues<F>> {
|
||||
let lde_size = common_data.lde_size();
|
||||
let lde_gen = common_data.lde_generator();
|
||||
) -> Vec<PolynomialCoeffs<F>> {
|
||||
let num_challenges = common_data.config.num_challenges;
|
||||
let max_filtered_constraint_degree_bits = log2_ceil(common_data.max_filtered_constraint_degree);
|
||||
assert!(
|
||||
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<Vec<F>> = 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 - 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 << max_filtered_constraint_degree_bits;
|
||||
|
||||
let points =
|
||||
F::two_adic_subgroup(common_data.degree_bits + max_filtered_constraint_degree_bits);
|
||||
let lde_size = points.len();
|
||||
|
||||
// Retrieve the LDE values at index `i`.
|
||||
let get_at_index = |comm: &'a ListPolynomialCommitment<F>, i: usize| -> &'a [F] {
|
||||
comm.get_lde_values(i * step)
|
||||
};
|
||||
|
||||
let z_h_on_coset =
|
||||
ZeroPolyOnCoset::new(common_data.degree_bits, max_filtered_constraint_degree_bits);
|
||||
|
||||
let quotient_values: Vec<Vec<F>> = 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 +265,10 @@ fn compute_vanishing_polys<F: Extendable<D>, 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 +276,19 @@ fn compute_vanishing_polys<F: Extendable<D>, 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<F: Field>(
|
||||
input: usize,
|
||||
witness: &PartialWitness<F>,
|
||||
degree: usize,
|
||||
) -> PolynomialCoeffs<F> {
|
||||
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()
|
||||
}
|
||||
|
||||
@ -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<D>, 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],
|
||||
|
||||
@ -64,8 +64,7 @@ pub(crate) fn verify<F: Extendable<D>, 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,
|
||||
|
||||
@ -10,6 +10,52 @@ use crate::gates::gate::GateInstance;
|
||||
use crate::target::Target;
|
||||
use crate::wire::Wire;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Witness<F: Field> {
|
||||
pub(crate) wire_values: Vec<Vec<F>>,
|
||||
}
|
||||
|
||||
impl<F: Field> Witness<F> {
|
||||
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<const D: usize>(
|
||||
&self,
|
||||
copy_constraints: &[(Target, Target)],
|
||||
gate_instances: &[GateInstance<F, D>],
|
||||
) -> Result<()>
|
||||
where
|
||||
F: Extendable<D>,
|
||||
{
|
||||
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<F: Field> {
|
||||
pub(crate) target_values: HashMap<Target, F>,
|
||||
@ -137,29 +183,14 @@ impl<F: Field> PartialWitness<F> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that the copy constraints are satisfied in the witness.
|
||||
pub fn check_copy_constraints<const D: usize>(
|
||||
&self,
|
||||
copy_constraints: &[(Target, Target)],
|
||||
gate_instances: &[GateInstance<F, D>],
|
||||
) -> Result<()>
|
||||
where
|
||||
F: Extendable<D>,
|
||||
{
|
||||
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<F> {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user