plonky2/src/polynomial/commitment.rs

395 lines
13 KiB
Rust
Raw Normal View History

use anyhow::Result;
use rayon::prelude::*;
2021-06-25 11:24:26 +02:00
use crate::circuit_data::CommonCircuitData;
2021-06-14 10:33:38 +02:00
use crate::field::extension_field::Extendable;
use crate::field::extension_field::{FieldExtension, Frobenius};
2021-04-30 15:07:54 +02:00
use crate::field::field::Field;
2021-07-14 21:43:55 -07:00
use crate::fri::{prover::fri_proof, verifier::verify_fri_proof};
2021-04-30 15:07:54 +02:00
use crate::merkle_tree::MerkleTree;
2021-05-03 15:17:05 +02:00
use crate::plonk_challenger::Challenger;
2021-06-17 21:57:31 +02:00
use crate::plonk_common::PlonkPolynomials;
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
2021-06-04 10:47:46 +02:00
use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet};
use crate::timed;
2021-06-17 22:06:53 +02:00
use crate::util::scaling::ReducingFactor;
2021-06-24 11:45:16 +02:00
use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transpose};
2021-04-30 15:07:54 +02:00
2021-05-06 17:09:55 +02:00
pub const SALT_SIZE: usize = 2;
2021-05-06 23:14:37 +02:00
pub struct ListPolynomialCommitment<F: Field> {
2021-05-03 15:17:05 +02:00
pub polynomials: Vec<PolynomialCoeffs<F>>,
2021-04-30 15:07:54 +02:00
pub merkle_tree: MerkleTree<F>,
2021-05-03 15:17:05 +02:00
pub degree: usize,
pub degree_log: usize,
pub rate_bits: usize,
pub blinding: bool,
2021-04-30 15:07:54 +02:00
}
impl<F: Field> ListPolynomialCommitment<F> {
/// 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();
2021-06-25 10:20:20 +02:00
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"
);
2021-06-28 08:56:36 -07:00
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 {
2021-05-03 15:17:05 +02:00
let degree = polynomials[0].len();
let lde_values = timed!(
Self::lde_values(&polynomials, rate_bits, blinding),
"to compute LDE"
);
2021-06-28 08:56:36 -07:00
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");
Self {
polynomials,
merkle_tree,
degree,
degree_log: log2_strict(degree),
rate_bits,
blinding,
}
}
fn lde_values(
polynomials: &[PolynomialCoeffs<F>],
rate_bits: usize,
blinding: bool,
) -> Vec<Vec<F>> {
let degree = polynomials[0].len();
polynomials
2021-05-07 16:49:27 +02:00
.par_iter()
2021-05-03 15:17:05 +02:00
.map(|p| {
assert_eq!(p.len(), degree, "Polynomial degree invalid.");
2021-06-24 14:11:47 +02:00
p.clone().lde(rate_bits).coset_fft(F::coset_shift()).values
2021-05-03 15:17:05 +02:00
})
.chain(if blinding {
2021-05-06 00:00:08 +02:00
// If blinding, salt with two random elements to each leaf vector.
2021-05-06 17:09:55 +02:00
(0..SALT_SIZE)
.map(|_| F::rand_vec(degree << rate_bits))
2021-05-03 15:17:05 +02:00
.collect()
2021-05-06 00:00:08 +02:00
} else {
Vec::new()
})
.collect()
2021-04-30 15:07:54 +02:00
}
2021-05-03 15:17:05 +02:00
2021-06-24 14:11:47 +02:00
pub fn get_lde_values(&self, index: usize) -> &[F] {
let index = reverse_bits(index, self.degree_log + self.rate_bits);
2021-06-24 11:45:16 +02:00
let slice = &self.merkle_tree.leaves[index];
&slice[..slice.len() - if self.blinding { SALT_SIZE } else { 0 }]
}
2021-05-07 16:22:13 +02:00
2021-06-01 11:17:54 +02:00
/// 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>(
2021-06-25 11:24:26 +02:00
commitments: &[&Self; 4],
zeta: F::Extension,
2021-05-06 17:09:55 +02:00
challenger: &mut Challenger<F>,
2021-06-25 11:24:26 +02:00
common_data: &CommonCircuitData<F, D>,
) -> (OpeningProof<F, D>, OpeningSet<F, D>)
2021-05-18 15:22:06 +02:00
where
2021-05-18 15:44:50 +02:00
F: Extendable<D>,
2021-05-18 15:22:06 +02:00
{
2021-06-25 11:24:26 +02:00
let config = &common_data.config.fri_config;
2021-06-01 21:55:05 +02:00
assert!(D > 1, "Not implemented for D=1.");
let degree_log = commitments[0].degree_log;
let g = F::Extension::primitive_root_of_unity(degree_log);
for p in &[zeta, g * zeta] {
2021-05-06 17:09:55 +02:00
assert_ne!(
p.exp(1 << degree_log as u64),
2021-05-18 15:22:06 +02:00
F::Extension::ONE,
2021-05-06 17:09:55 +02:00
"Opening point is in the subgroup."
);
}
let os = OpeningSet::new(
zeta,
g,
commitments[0],
commitments[1],
commitments[2],
commitments[3],
2021-06-25 11:24:26 +02:00
common_data,
);
challenger.observe_opening_set(&os);
2021-05-06 17:09:55 +02:00
2021-05-18 15:22:06 +02:00
let alpha = challenger.get_extension_challenge();
2021-06-17 22:06:53 +02:00
let mut alpha = ReducingFactor::new(alpha);
2021-05-06 17:09:55 +02:00
// Final low-degree polynomial that goes into FRI.
let mut final_poly = PolynomialCoeffs::empty();
let mut zs_polys = commitments[PlonkPolynomials::ZS_PARTIAL_PRODUCTS.index]
.polynomials
.iter()
.map(|p| p.to_extension())
.collect::<Vec<_>>();
let partial_products_polys = zs_polys.split_off(common_data.zs_range().end);
2021-06-01 11:17:54 +02:00
// Polynomials opened at a single point.
2021-06-17 21:34:04 +02:00
let single_polys = [
2021-06-25 11:24:26 +02:00
PlonkPolynomials::CONSTANTS_SIGMAS,
2021-06-17 21:34:04 +02:00
PlonkPolynomials::QUOTIENT,
]
.iter()
.flat_map(|&p| &commitments[p.index].polynomials)
.map(|p| p.to_extension())
.chain(partial_products_polys);
2021-06-23 11:30:57 +02:00
let single_composition_poly = alpha.reduce_polys(single_polys);
let single_quotient = Self::compute_quotient([zeta], single_composition_poly);
final_poly += single_quotient;
2021-06-17 21:34:04 +02:00
alpha.reset();
2021-06-01 11:17:54 +02:00
// Zs polynomials are opened at `zeta` and `g*zeta`.
let zs_composition_poly = alpha.reduce_polys(zs_polys.into_iter());
2021-06-23 11:30:57 +02:00
let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly);
alpha.shift_poly(&mut final_poly);
2021-06-23 11:30:57 +02:00
final_poly += zs_quotient;
2021-06-01 21:55:05 +02:00
// When working in an extension field, need to check that wires are in the base field.
2021-06-01 11:17:54 +02:00
// Check this by opening the wires polynomials at `zeta` and `zeta.frobenius()` and using the fact that
// a polynomial `f` is over the base field iff `f(z).frobenius()=f(z.frobenius())` with high probability.
2021-06-17 21:34:04 +02:00
let wire_polys = commitments[PlonkPolynomials::WIRES.index]
.polynomials
.iter()
.map(|p| p.to_extension());
2021-06-23 11:30:57 +02:00
let wire_composition_poly = alpha.reduce_polys(wire_polys);
let wires_quotient =
Self::compute_quotient([zeta, zeta.frobenius()], wire_composition_poly);
alpha.shift_poly(&mut final_poly);
2021-06-23 11:30:57 +02:00
final_poly += wires_quotient;
2021-05-06 17:09:55 +02:00
let lde_final_poly = final_poly.lde(config.rate_bits);
let lde_final_values = lde_final_poly
.clone()
.coset_fft(F::Extension::from_basefield(
F::MULTIPLICATIVE_GROUP_GENERATOR,
));
2021-05-06 17:09:55 +02:00
let fri_proof = fri_proof(
&commitments
2021-05-07 16:49:27 +02:00
.par_iter()
2021-05-06 17:09:55 +02:00
.map(|c| &c.merkle_tree)
.collect::<Vec<_>>(),
&lde_final_poly,
&lde_final_values,
2021-05-06 17:09:55 +02:00
challenger,
&config,
2021-05-06 17:09:55 +02:00
);
(
OpeningProof {
fri_proof,
quotient_degree: final_poly.len(),
2021-05-06 17:09:55 +02:00
},
os,
2021-05-06 17:09:55 +02:00
)
}
2021-05-05 18:32:24 +02:00
/// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial
/// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`.
2021-06-23 11:30:57 +02:00
fn compute_quotient<const D: usize, const N: usize>(
points: [F::Extension; N],
poly: PolynomialCoeffs<F::Extension>,
2021-05-18 15:22:06 +02:00
) -> PolynomialCoeffs<F::Extension>
where
2021-05-18 15:44:50 +02:00
F: Extendable<D>,
2021-05-18 15:22:06 +02:00
{
2021-06-23 11:30:57 +02:00
let quotient = if N == 1 {
poly.divide_by_linear(points[0]).0
} else if N == 2 {
// The denominator is `(X - p0)(X - p1) = p0 p1 - (p0 + p1) X + X^2`.
let denominator = vec![
points[0] * points[1],
-points[0] - points[1],
F::Extension::ONE,
]
.into();
poly.div_rem_long_division(&denominator).0 // Could also use `divide_by_linear` twice.
} else {
unreachable!("This shouldn't happen. Plonk should open polynomials at 1 or 2 points.")
};
2021-05-05 18:32:24 +02:00
quotient.padded(quotient.degree_plus_one().next_power_of_two())
2021-05-05 18:32:24 +02:00
}
2021-05-03 15:17:05 +02:00
}
2021-05-18 15:44:50 +02:00
pub struct OpeningProof<F: Field + Extendable<D>, const D: usize> {
fri_proof: FriProof<F, D>,
2021-05-06 15:14:43 +02:00
// TODO: Get the degree from `CommonCircuitData` instead.
2021-05-04 17:48:26 +02:00
quotient_degree: usize,
}
2021-05-18 15:44:50 +02:00
impl<F: Field + Extendable<D>, const D: usize> OpeningProof<F, D> {
2021-05-04 17:48:26 +02:00
pub fn verify(
&self,
zeta: F::Extension,
os: &OpeningSet<F, D>,
2021-05-06 17:09:55 +02:00
merkle_roots: &[Hash<F>],
2021-05-04 17:48:26 +02:00
challenger: &mut Challenger<F>,
common_data: &CommonCircuitData<F, D>,
2021-05-04 17:48:26 +02:00
) -> Result<()> {
challenger.observe_opening_set(os);
2021-05-04 17:48:26 +02:00
2021-05-18 15:22:06 +02:00
let alpha = challenger.get_extension_challenge();
2021-05-04 17:48:26 +02:00
verify_fri_proof(
log2_strict(self.quotient_degree),
&os,
zeta,
2021-05-04 17:48:26 +02:00
alpha,
2021-05-06 17:09:55 +02:00
merkle_roots,
2021-05-04 17:48:26 +02:00
&self.fri_proof,
challenger,
common_data,
2021-05-04 17:48:26 +02:00
)
}
}
2021-06-04 10:47:46 +02:00
pub struct OpeningProofTarget<const D: usize> {
fri_proof: FriProofTarget<D>,
}
2021-05-04 17:48:26 +02:00
#[cfg(test)]
mod tests {
use anyhow::Result;
use super::*;
2021-06-25 11:24:26 +02:00
use crate::circuit_data::CircuitConfig;
2021-07-15 07:34:46 -07:00
use crate::fri::FriConfig;
use crate::plonk_common::PlonkPolynomials;
2021-05-18 15:44:50 +02:00
fn gen_random_test_case<F: Field + Extendable<D>, const D: usize>(
2021-05-05 17:00:47 +02:00
k: usize,
degree_log: usize,
) -> Vec<PolynomialValues<F>> {
2021-05-05 17:00:47 +02:00
let degree = 1 << degree_log;
(0..k)
.map(|_| PolynomialValues::new(F::rand_vec(degree)))
.collect()
2021-05-05 17:00:47 +02:00
}
fn gen_random_point<F: Field + Extendable<D>, const D: usize>(
degree_log: usize,
) -> F::Extension {
let degree = 1 << degree_log;
2021-05-04 17:48:26 +02:00
let mut point = F::Extension::rand();
while point.exp(degree as u64).is_one() {
point = F::Extension::rand();
}
2021-05-04 17:48:26 +02:00
point
2021-05-06 17:09:55 +02:00
}
2021-05-18 16:06:47 +02:00
fn check_batch_polynomial_commitment<F: Field + Extendable<D>, const D: usize>() -> Result<()> {
let ks = [10, 2, 10, 8];
2021-05-31 18:00:53 +02:00
let degree_log = 11;
2021-05-06 17:09:55 +02:00
let fri_config = FriConfig {
proof_of_work_bits: 2,
2021-05-31 18:00:53 +02:00
rate_bits: 2,
reduction_arity_bits: vec![2, 3, 1, 2],
2021-05-06 17:09:55 +02:00
num_query_rounds: 3,
};
2021-06-28 09:47:47 +02:00
// We only care about `fri_config, num_constants`, and `num_routed_wires` here.
2021-06-25 11:24:26 +02:00
let common_data = CommonCircuitData {
config: CircuitConfig {
fri_config,
2021-06-28 09:47:47 +02:00
num_routed_wires: 6,
2021-06-25 11:24:26 +02:00
..CircuitConfig::large_config()
},
degree_bits: 0,
gates: vec![],
2021-07-08 15:13:29 +02:00
quotient_degree_factor: 0,
2021-06-25 11:24:26 +02:00
num_gate_constraints: 0,
num_constants: 4,
k_is: vec![F::ONE; 6],
2021-07-01 15:41:01 +02:00
num_partial_products: (0, 0),
2021-06-25 11:24:26 +02:00
circuit_digest: Hash::from_partial(vec![]),
};
2021-05-06 17:09:55 +02:00
2021-06-25 11:24:26 +02:00
let lpcs = (0..4)
2021-05-31 18:19:44 +02:00
.map(|i| {
ListPolynomialCommitment::<F>::new(
2021-05-31 18:19:44 +02:00
gen_random_test_case(ks[i], degree_log),
2021-06-25 11:24:26 +02:00
common_data.config.fri_config.rate_bits,
PlonkPolynomials::polynomials(i).blinding,
)
})
.collect::<Vec<_>>();
2021-05-06 17:09:55 +02:00
let zeta = gen_random_point::<F, D>(degree_log);
let (proof, os) = ListPolynomialCommitment::open_plonk::<D>(
2021-06-25 11:24:26 +02:00
&[&lpcs[0], &lpcs[1], &lpcs[2], &lpcs[3]],
zeta,
2021-05-06 17:09:55 +02:00
&mut Challenger::new(),
2021-06-25 11:24:26 +02:00
&common_data,
2021-05-06 17:09:55 +02:00
);
proof.verify(
zeta,
&os,
2021-05-06 17:09:55 +02:00
&[
lpcs[0].merkle_tree.root,
lpcs[1].merkle_tree.root,
lpcs[2].merkle_tree.root,
lpcs[3].merkle_tree.root,
2021-05-06 17:09:55 +02:00
],
2021-05-06 15:19:06 +02:00
&mut Challenger::new(),
&common_data,
2021-05-06 15:19:06 +02:00
)
2021-05-04 17:48:26 +02:00
}
2021-05-18 16:06:47 +02:00
2021-05-18 16:23:44 +02:00
mod quadratic {
2021-06-01 11:17:54 +02:00
use super::*;
2021-06-01 11:26:23 +02:00
use crate::field::crandall_field::CrandallField;
2021-06-01 11:17:54 +02:00
#[test]
fn test_batch_polynomial_commitment() -> Result<()> {
check_batch_polynomial_commitment::<CrandallField, 2>()
}
2021-05-18 16:06:47 +02:00
}
mod quartic {
use super::*;
2021-06-01 11:26:23 +02:00
use crate::field::crandall_field::CrandallField;
2021-06-01 11:17:54 +02:00
#[test]
fn test_batch_polynomial_commitment() -> Result<()> {
check_batch_polynomial_commitment::<CrandallField, 4>()
}
2021-05-18 16:06:47 +02:00
}
2021-04-30 15:07:54 +02:00
}