plonky2/src/polynomial/commitment.rs

487 lines
15 KiB
Rust
Raw Normal View History

2021-04-30 15:07:54 +02:00
use crate::field::field::Field;
2021-05-04 19:56:34 +02:00
use crate::field::lagrange::interpolant;
2021-05-05 18:23:59 +02:00
use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig};
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;
use crate::plonk_common::reduce_with_powers;
2021-05-12 11:21:31 -07:00
use crate::polynomial::old_polynomial::Polynomial;
2021-05-04 19:56:34 +02:00
use crate::polynomial::polynomial::PolynomialCoeffs;
2021-05-07 11:30:03 +02:00
use crate::proof::{FriProof, Hash, OpeningSet};
2021-05-04 17:48:26 +02:00
use crate::util::{log2_strict, reverse_index_bits_in_place, transpose};
use anyhow::Result;
2021-05-07 16:49:27 +02:00
use rayon::prelude::*;
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 rate_bits: usize,
pub blinding: bool,
2021-04-30 15:07:54 +02:00
}
impl<F: Field> ListPolynomialCommitment<F> {
pub fn new(polynomials: Vec<PolynomialCoeffs<F>>, rate_bits: usize, blinding: bool) -> Self {
2021-05-03 15:17:05 +02:00
let degree = polynomials[0].len();
2021-05-04 19:56:34 +02:00
let lde_values = 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.");
p.clone()
.lde(rate_bits)
2021-05-03 15:17:05 +02:00
.coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR)
.values
})
.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()
})
2021-04-30 15:07:54 +02:00
.collect::<Vec<_>>();
2021-05-03 15:17:05 +02:00
2021-05-04 17:48:26 +02:00
let mut leaves = transpose(&lde_values);
reverse_index_bits_in_place(&mut leaves);
let merkle_tree = MerkleTree::new(leaves, false);
2021-04-30 15:07:54 +02:00
Self {
2021-05-03 15:17:05 +02:00
polynomials,
2021-04-30 15:07:54 +02:00
merkle_tree,
2021-05-03 15:17:05 +02:00
degree,
rate_bits,
blinding,
2021-04-30 15:07:54 +02:00
}
}
2021-05-03 15:17:05 +02:00
2021-05-07 16:22:13 +02:00
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 }]
2021-05-07 16:22:13 +02:00
}
2021-05-05 22:58:15 +02:00
pub fn open(
&self,
points: &[F],
challenger: &mut Challenger<F>,
config: &FriConfig,
2021-05-05 22:58:15 +02:00
) -> (OpeningProof<F>, Vec<Vec<F>>) {
assert_eq!(self.rate_bits, config.rate_bits);
assert_eq!(config.blinding.len(), 1);
assert_eq!(self.blinding, config.blinding[0]);
2021-05-03 15:17:05 +02:00
for p in points {
assert_ne!(
p.exp_usize(self.degree),
F::ONE,
"Opening point is in the subgroup."
);
}
let evaluations = points
2021-05-07 16:49:27 +02:00
.par_iter()
2021-05-03 15:17:05 +02:00
.map(|&x| {
self.polynomials
.iter()
.map(|p| p.eval(x))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
for evals in &evaluations {
challenger.observe_elements(evals);
}
let alpha = challenger.get_challenge();
2021-05-05 18:32:24 +02:00
// Scale polynomials by `alpha`.
2021-05-05 22:58:15 +02:00
let composition_poly = self
2021-05-03 15:17:05 +02:00
.polynomials
.iter()
.rev()
.fold(PolynomialCoeffs::zero(self.degree), |acc, p| {
&(&acc * alpha) + &p
});
2021-05-05 18:32:24 +02:00
// Scale evaluations by `alpha`.
2021-05-05 22:58:15 +02:00
let composition_evals = evaluations
2021-05-07 16:49:27 +02:00
.par_iter()
2021-05-03 15:17:05 +02:00
.map(|e| reduce_with_powers(e, alpha))
.collect::<Vec<_>>();
2021-05-05 22:58:15 +02:00
let quotient = Self::compute_quotient(points, &composition_evals, &composition_poly);
2021-05-04 17:48:26 +02:00
let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(self.rate_bits);
2021-05-04 19:56:34 +02:00
let lde_quotient_values = lde_quotient
.clone()
.coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR);
2021-05-04 17:48:26 +02:00
2021-05-03 15:17:05 +02:00
let fri_proof = fri_proof(
2021-05-06 17:09:55 +02:00
&[&self.merkle_tree],
2021-05-04 17:48:26 +02:00
&lde_quotient,
&lde_quotient_values,
2021-05-03 15:17:05 +02:00
challenger,
&config,
2021-05-03 15:17:05 +02:00
);
2021-05-04 17:48:26 +02:00
2021-05-05 22:58:15 +02:00
(
OpeningProof {
fri_proof,
quotient_degree: quotient.len(),
},
2021-05-04 17:48:26 +02:00
evaluations,
2021-05-05 22:58:15 +02:00
)
2021-05-03 15:17:05 +02:00
}
2021-05-05 18:32:24 +02:00
2021-05-06 17:09:55 +02:00
pub fn batch_open(
commitments: &[&Self],
points: &[F],
challenger: &mut Challenger<F>,
config: &FriConfig,
2021-05-07 11:30:03 +02:00
) -> (OpeningProof<F>, Vec<Vec<Vec<F>>>) {
2021-05-06 17:09:55 +02:00
let degree = commitments[0].degree;
assert_eq!(config.blinding.len(), commitments.len());
for (i, commitment) in commitments.iter().enumerate() {
assert_eq!(commitment.rate_bits, config.rate_bits, "Invalid rate.");
assert_eq!(
commitment.blinding, config.blinding[i],
"Invalid blinding paramater."
);
assert_eq!(
commitment.degree, degree,
"Trying to open polynomial commitments of different degrees."
);
}
2021-05-06 17:09:55 +02:00
for p in points {
assert_ne!(
p.exp_usize(degree),
F::ONE,
"Opening point is in the subgroup."
);
}
let evaluations = points
2021-05-07 16:49:27 +02:00
.par_iter()
2021-05-06 17:09:55 +02:00
.map(|&x| {
commitments
.iter()
2021-05-07 11:30:03 +02:00
.map(move |c| c.polynomials.iter().map(|p| p.eval(x)).collect::<Vec<_>>())
2021-05-06 17:09:55 +02:00
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
2021-05-07 11:30:03 +02:00
for evals_per_point in &evaluations {
for evals in evals_per_point {
challenger.observe_elements(evals);
}
2021-05-06 17:09:55 +02:00
}
let alpha = challenger.get_challenge();
// Scale polynomials by `alpha`.
let composition_poly = commitments
.iter()
.flat_map(|c| &c.polynomials)
.rev()
.map(|p| p.clone().into())
.fold(Polynomial::empty(), |acc, p| acc.scalar_mul(alpha).add(&p));
// Scale evaluations by `alpha`.
2021-05-07 16:22:13 +02:00
let composition_evals = &evaluations
2021-05-07 16:49:27 +02:00
.par_iter()
2021-05-07 16:22:13 +02:00
.map(|v| {
v.iter()
.flatten()
.rev()
.fold(F::ZERO, |acc, &e| acc * alpha + e)
})
2021-05-06 17:09:55 +02:00
.collect::<Vec<_>>();
let quotient = Self::compute_quotient(points, &composition_evals, &composition_poly);
let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(config.rate_bits);
2021-05-06 17:09:55 +02:00
let lde_quotient_values = lde_quotient
.clone()
.coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR);
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_quotient,
&lde_quotient_values,
challenger,
&config,
2021-05-06 17:09:55 +02:00
);
(
OpeningProof {
fri_proof,
quotient_degree: quotient.len(),
},
evaluations,
)
}
2021-05-07 11:30:03 +02:00
pub fn batch_open_plonk(
commitments: &[&Self; 5],
points: &[F],
challenger: &mut Challenger<F>,
config: &FriConfig,
2021-05-07 11:30:03 +02:00
) -> (OpeningProof<F>, Vec<OpeningSet<F>>) {
let (op, mut evaluations) = Self::batch_open(commitments, points, challenger, config);
2021-05-07 11:30:03 +02:00
let opening_sets = evaluations
2021-05-07 16:49:27 +02:00
.par_iter_mut()
2021-05-07 11:30:03 +02:00
.map(|evals| {
evals.reverse();
OpeningSet {
constants: evals.pop().unwrap(),
plonk_sigmas: evals.pop().unwrap(),
wires: evals.pop().unwrap(),
plonk_zs: evals.pop().unwrap(),
quotient_polys: evals.pop().unwrap(),
}
})
.collect();
(op, opening_sets)
}
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)`.
fn compute_quotient(
points: &[F],
evals: &[F],
poly: &PolynomialCoeffs<F>,
) -> PolynomialCoeffs<F> {
2021-05-05 18:32:24 +02:00
let pairs = points
.iter()
.zip(evals)
.map(|(&x, &e)| (x, e))
.collect::<Vec<_>>();
debug_assert!(pairs.iter().all(|&(x, e)| poly.eval(x) == e));
let interpolant = interpolant(&pairs);
let denominator = points.iter().fold(PolynomialCoeffs::one(), |acc, &x| {
&acc * &PolynomialCoeffs::new(vec![-x, F::ONE])
});
let numerator = poly - &interpolant;
let (mut quotient, rem) = numerator.div_rem(&denominator);
2021-05-05 18:32:24 +02:00
debug_assert!(rem.is_zero());
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
}
pub struct OpeningProof<F: Field> {
2021-05-04 17:48:26 +02:00
fri_proof: FriProof<F>,
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,
}
impl<F: Field> OpeningProof<F> {
pub fn verify(
&self,
points: &[F],
2021-05-07 16:22:13 +02:00
evaluations: &[Vec<Vec<F>>],
2021-05-06 17:09:55 +02:00
merkle_roots: &[Hash<F>],
2021-05-04 17:48:26 +02:00
challenger: &mut Challenger<F>,
fri_config: &FriConfig,
) -> Result<()> {
2021-05-07 16:22:13 +02:00
for evals_per_point in evaluations {
for evals in evals_per_point {
challenger.observe_elements(evals);
}
2021-05-04 17:48:26 +02:00
}
let alpha = challenger.get_challenge();
2021-05-05 22:58:15 +02:00
let scaled_evals = evaluations
2021-05-07 16:49:27 +02:00
.par_iter()
2021-05-07 16:22:13 +02:00
.map(|v| {
v.iter()
.flatten()
.rev()
.fold(F::ZERO, |acc, &e| acc * alpha + e)
})
2021-05-04 17:48:26 +02:00
.collect::<Vec<_>>();
let pairs = points
.iter()
.zip(&scaled_evals)
.map(|(&x, &e)| (x, e))
.collect::<Vec<_>>();
verify_fri_proof(
log2_strict(self.quotient_degree),
&pairs,
alpha,
2021-05-06 17:09:55 +02:00
merkle_roots,
2021-05-04 17:48:26 +02:00
&self.fri_proof,
challenger,
fri_config,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::crandall_field::CrandallField;
use anyhow::Result;
2021-05-05 17:00:47 +02:00
fn gen_random_test_case<F: Field>(
k: usize,
degree_log: usize,
num_points: usize,
) -> (Vec<PolynomialCoeffs<F>>, Vec<F>) {
let degree = 1 << degree_log;
let polys = (0..k)
2021-05-06 15:14:43 +02:00
.map(|_| PolynomialCoeffs::new(F::rand_vec(degree)))
2021-05-05 17:00:47 +02:00
.collect();
2021-05-06 15:14:43 +02:00
let mut points = F::rand_vec(num_points);
2021-05-05 17:00:47 +02:00
while points.iter().any(|&x| x.exp_usize(degree).is_one()) {
2021-05-06 15:14:43 +02:00
points = F::rand_vec(num_points);
2021-05-05 17:00:47 +02:00
}
(polys, points)
}
2021-05-04 17:48:26 +02:00
#[test]
fn test_polynomial_commitment() -> Result<()> {
type F = CrandallField;
2021-05-04 19:56:34 +02:00
let k = 10;
let degree_log = 11;
2021-05-05 17:00:47 +02:00
let num_points = 3;
2021-05-04 17:48:26 +02:00
let fri_config = FriConfig {
proof_of_work_bits: 2,
rate_bits: 2,
2021-05-04 19:56:34 +02:00
reduction_arity_bits: vec![3, 2, 1, 2],
num_query_rounds: 3,
blinding: vec![false],
2021-05-04 17:48:26 +02:00
};
2021-05-05 17:00:47 +02:00
let (polys, points) = gen_random_test_case::<F>(k, degree_log, num_points);
2021-05-04 17:48:26 +02:00
let lpc = ListPolynomialCommitment::new(polys, fri_config.rate_bits, false);
let (proof, evaluations) = lpc.open(&points, &mut Challenger::new(), &fri_config);
2021-05-06 15:19:06 +02:00
proof.verify(
&points,
2021-05-07 16:22:13 +02:00
&evaluations.into_iter().map(|e| vec![e]).collect::<Vec<_>>(),
2021-05-06 17:09:55 +02:00
&[lpc.merkle_tree.root],
2021-05-06 15:19:06 +02:00
&mut Challenger::new(),
&fri_config,
)
2021-05-05 17:00:47 +02:00
}
2021-05-04 17:48:26 +02:00
2021-05-05 17:00:47 +02:00
#[test]
fn test_polynomial_commitment_blinding() -> Result<()> {
type F = CrandallField;
2021-05-04 17:48:26 +02:00
2021-05-05 17:00:47 +02:00
let k = 10;
let degree_log = 11;
2021-05-04 17:48:26 +02:00
let num_points = 3;
2021-05-05 17:00:47 +02:00
let fri_config = FriConfig {
proof_of_work_bits: 2,
rate_bits: 2,
reduction_arity_bits: vec![3, 2, 1, 2],
num_query_rounds: 3,
blinding: vec![true],
2021-05-05 17:00:47 +02:00
};
let (polys, points) = gen_random_test_case::<F>(k, degree_log, num_points);
2021-05-04 17:48:26 +02:00
let lpc = ListPolynomialCommitment::new(polys, fri_config.rate_bits, true);
let (proof, evaluations) = lpc.open(&points, &mut Challenger::new(), &fri_config);
2021-05-06 15:19:06 +02:00
proof.verify(
&points,
2021-05-07 16:22:13 +02:00
&evaluations.into_iter().map(|e| vec![e]).collect::<Vec<_>>(),
2021-05-06 17:09:55 +02:00
&[lpc.merkle_tree.root],
&mut Challenger::new(),
&fri_config,
)
}
#[test]
fn test_batch_polynomial_commitment() -> Result<()> {
type F = CrandallField;
let k0 = 10;
let k1 = 3;
let k2 = 7;
let degree_log = 11;
let num_points = 5;
let fri_config = FriConfig {
proof_of_work_bits: 2,
rate_bits: 2,
reduction_arity_bits: vec![2, 3, 1, 2],
num_query_rounds: 3,
blinding: vec![false, false, false],
2021-05-06 17:09:55 +02:00
};
let (polys0, _) = gen_random_test_case::<F>(k0, degree_log, num_points);
let (polys1, _) = gen_random_test_case::<F>(k0, degree_log, num_points);
let (polys2, points) = gen_random_test_case::<F>(k0, degree_log, num_points);
let lpc0 = ListPolynomialCommitment::new(polys0, fri_config.rate_bits, false);
let lpc1 = ListPolynomialCommitment::new(polys1, fri_config.rate_bits, false);
let lpc2 = ListPolynomialCommitment::new(polys2, fri_config.rate_bits, false);
2021-05-06 17:09:55 +02:00
let (proof, evaluations) = ListPolynomialCommitment::batch_open(
&[&lpc0, &lpc1, &lpc2],
&points,
&mut Challenger::new(),
&fri_config,
2021-05-06 17:09:55 +02:00
);
proof.verify(
&points,
&evaluations,
&[
lpc0.merkle_tree.root,
lpc1.merkle_tree.root,
lpc2.merkle_tree.root,
],
&mut Challenger::new(),
&fri_config,
)
}
#[test]
fn test_batch_polynomial_commitment_blinding() -> Result<()> {
type F = CrandallField;
let k0 = 10;
let k1 = 3;
let k2 = 7;
let degree_log = 11;
let num_points = 5;
let fri_config = FriConfig {
proof_of_work_bits: 2,
rate_bits: 2,
reduction_arity_bits: vec![2, 3, 1, 2],
num_query_rounds: 3,
blinding: vec![true, false, true],
2021-05-06 17:09:55 +02:00
};
let (polys0, _) = gen_random_test_case::<F>(k0, degree_log, num_points);
let (polys1, _) = gen_random_test_case::<F>(k0, degree_log, num_points);
let (polys2, points) = gen_random_test_case::<F>(k0, degree_log, num_points);
let lpc0 = ListPolynomialCommitment::new(polys0, fri_config.rate_bits, true);
let lpc1 = ListPolynomialCommitment::new(polys1, fri_config.rate_bits, false);
let lpc2 = ListPolynomialCommitment::new(polys2, fri_config.rate_bits, true);
2021-05-06 17:09:55 +02:00
let (proof, evaluations) = ListPolynomialCommitment::batch_open(
&[&lpc0, &lpc1, &lpc2],
&points,
&mut Challenger::new(),
&fri_config,
2021-05-06 17:09:55 +02:00
);
proof.verify(
&points,
&evaluations,
&[
lpc0.merkle_tree.root,
lpc1.merkle_tree.root,
lpc2.merkle_tree.root,
],
2021-05-06 15:19:06 +02:00
&mut Challenger::new(),
&fri_config,
)
2021-05-04 17:48:26 +02:00
}
2021-04-30 15:07:54 +02:00
}