Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check base field #55

Merged
merged 12 commits into from
Jun 2, 2021
132 changes: 0 additions & 132 deletions src/fri/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,135 +47,3 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 {
1.0 / (2.0 * EPSILON * rate.sqrt())
}
}

#[cfg(test)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit annoying having to remove these tests, but since the FRI API is now very PLONK specific it doesn't make sense to update these tests since they'll end up being the same as those in commitment.rs.
If we need FRI for something other that polynomial commitments in the future, we could add some kind of FriInitialData trait and make the FRI API more general.

mod tests {
use anyhow::Result;
use rand::rngs::ThreadRng;
use rand::Rng;

use crate::field::crandall_field::CrandallField;
use crate::field::extension_field::Extendable;
use crate::field::field::Field;
use crate::fri::prover::fri_proof;
use crate::fri::verifier::verify_fri_proof;
use crate::merkle_tree::MerkleTree;
use crate::plonk_challenger::Challenger;
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
use crate::util::reverse_index_bits_in_place;

use super::*;

fn check_fri<F: Field + Extendable<D>, const D: usize>(
degree_log: usize,
rate_bits: usize,
reduction_arity_bits: Vec<usize>,
num_query_rounds: usize,
) -> Result<()> {
let n = 1 << degree_log;
let coeffs = PolynomialCoeffs::new(F::rand_vec(n)).lde(rate_bits);
let coset_lde = coeffs.clone().coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR);
let config = FriConfig {
num_query_rounds,
rate_bits,
proof_of_work_bits: 2,
reduction_arity_bits,
blinding: vec![false],
};
let tree = {
let mut leaves = coset_lde
.values
.iter()
.map(|&x| vec![x])
.collect::<Vec<_>>();
reverse_index_bits_in_place(&mut leaves);
MerkleTree::new(leaves, false)
};
let coset_lde = PolynomialValues::new(
coset_lde
.values
.into_iter()
.map(F::Extension::from)
.collect(),
);
let root = tree.root;
let mut challenger = Challenger::new();
let proof = fri_proof::<F, D>(
&[&tree],
&coeffs.to_extension::<D>(),
&coset_lde,
&mut challenger,
&config,
);

let mut challenger = Challenger::new();
verify_fri_proof(
degree_log,
&[],
F::Extension::ONE,
&[root],
&proof,
&mut challenger,
&config,
)?;

Ok(())
}

fn gen_arities(degree_log: usize, rng: &mut ThreadRng) -> Vec<usize> {
let mut arities = Vec::new();
let mut remaining = degree_log;
while remaining > 0 {
let arity = rng.gen_range(0, remaining + 1);
arities.push(arity);
remaining -= arity;
}
arities
}

fn check_fri_multi_params<F: Field + Extendable<D>, const D: usize>() -> Result<()> {
let mut rng = rand::thread_rng();
for degree_log in 1..6 {
for rate_bits in 0..3 {
for num_query_round in 0..4 {
for _ in 0..3 {
check_fri::<F, D>(
degree_log,
rate_bits,
gen_arities(degree_log, &mut rng),
num_query_round,
)?;
}
}
}
}
Ok(())
}

mod base {
use super::*;

#[test]
fn test_fri_multi_params() -> Result<()> {
check_fri_multi_params::<CrandallField, 1>()
}
}

mod quadratic {
use super::*;

#[test]
fn test_fri_multi_params() -> Result<()> {
check_fri_multi_params::<CrandallField, 2>()
}
}

mod quartic {
use super::*;

#[test]
fn test_fri_multi_params() -> Result<()> {
check_fri_multi_params::<CrandallField, 4>()
}
}
}
104 changes: 80 additions & 24 deletions src/fri/verifier.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::field::extension_field::{flatten, Extendable, FieldExtension};
use crate::field::extension_field::{flatten, Extendable, FieldExtension, OEF};
use crate::field::field::Field;
use crate::field::lagrange::{barycentric_weights, interpolant, interpolate};
use crate::fri::FriConfig;
use crate::hash::hash_n_to_1;
use crate::merkle_proofs::verify_merkle_proof;
use crate::plonk_challenger::Challenger;
use crate::plonk_common::reduce_with_powers;
use crate::polynomial::commitment::SALT_SIZE;
use crate::polynomial::polynomial::PolynomialCoeffs;
use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash};
use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet};
use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place};
use anyhow::{ensure, Result};

Expand Down Expand Up @@ -65,8 +65,10 @@ fn fri_verify_proof_of_work<F: Field + Extendable<D>, const D: usize>(

pub fn verify_fri_proof<F: Field + Extendable<D>, const D: usize>(
purported_degree_log: usize,
// Point-evaluation pairs for polynomial commitments.
points: &[(F::Extension, F::Extension)],
// Openings of the PLONK polynomials.
os: &OpeningSet<F, D>,
// Point at which the PLONK polynomials are opened.
zeta: F::Extension,
// Scaling factor to combine polynomials.
alpha: F::Extension,
initial_merkle_roots: &[Hash<F>],
Expand Down Expand Up @@ -108,11 +110,10 @@ pub fn verify_fri_proof<F: Field + Extendable<D>, const D: usize>(
"Number of reductions should be non-zero."
);

let interpolant = interpolant(points);
for round_proof in &proof.query_round_proofs {
fri_verifier_query_round(
&interpolant,
points,
os,
zeta,
alpha,
initial_merkle_roots,
&proof,
Expand Down Expand Up @@ -142,29 +143,84 @@ fn fri_verify_initial_proof<F: Field>(
fn fri_combine_initial<F: Field + Extendable<D>, const D: usize>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this is getting pretty complicated; I tried to refactor a bit here, though with limited success. I guess there's only so much we can do.

  • I tried using the powers() iterator to supply all the powers of alpha, instead of tracking cur_alpha and poly_count. Since (currently) we multiply a few things by the same power of alpha, I had to clone the iterator in a few places.
  • Added an unsalted_evals helper
  • Converted subgroup_x once at the beginning
  • Added a comment

What do you think? Feel free to pick any changes you like and ignore the rest.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tweaks, they're all great! I've merged them all.

proof: &FriInitialTreeProof<F>,
alpha: F::Extension,
interpolant: &PolynomialCoeffs<F::Extension>,
points: &[(F::Extension, F::Extension)],
os: &OpeningSet<F, D>,
zeta: F::Extension,
subgroup_x: F,
config: &FriConfig,
) -> F::Extension {
let e = proof
.evals_proofs
assert!(D > 1, "Not implemented for D=1.");
let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits;

let mut cur_alpha = F::Extension::ONE;

let mut poly_count = 0;
let mut e = F::Extension::ZERO;

let ev = vec![0, 1, 4]
.iter()
.flat_map(|&i| {
let v = &proof.evals_proofs[i].0;
&v[..v.len() - if config.blinding[i] { SALT_SIZE } else { 0 }]
})
.rev()
.fold(F::Extension::ZERO, |acc, &e| {
poly_count += 1;
alpha * acc + e.into()
});
let composition_eval = [&os.constants, &os.plonk_sigmas, &os.quotient_polys]
.iter()
.enumerate()
.flat_map(|(i, (v, _))| &v[..v.len() - if config.blinding[i] { SALT_SIZE } else { 0 }])
.flat_map(|v| v.iter())
.rev()
.fold(F::Extension::ZERO, |acc, &e| alpha * acc + e.into());
let numerator = e - interpolant.eval(subgroup_x.into());
let denominator = points
.fold(F::Extension::ZERO, |acc, &e| acc * alpha + e);
let numerator = ev - composition_eval;
let denominator = F::Extension::from_basefield(subgroup_x) - zeta;
e += cur_alpha * numerator / denominator;
cur_alpha = alpha.exp(poly_count);

let ev = proof.evals_proofs[3].0
[..proof.evals_proofs[3].0.len() - if config.blinding[3] { SALT_SIZE } else { 0 }]
.iter()
.rev()
.fold(F::Extension::ZERO, |acc, &e| {
poly_count += 1;
alpha * acc + e.into()
});
let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta;
let zs_interpol = interpolant(&[
(zeta, reduce_with_powers(&os.plonk_zs, alpha)),
(zeta_right, reduce_with_powers(&os.plonk_zs_right, alpha)),
]);
let numerator = ev - zs_interpol.eval(subgroup_x.into());
let denominator = (F::Extension::from_basefield(subgroup_x) - zeta)
* (F::Extension::from_basefield(subgroup_x) - zeta_right);
e += cur_alpha * numerator / denominator;
cur_alpha = alpha.exp(poly_count);

let ev = proof.evals_proofs[2].0
[..proof.evals_proofs[2].0.len() - if config.blinding[2] { SALT_SIZE } else { 0 }]
.iter()
.map(|&(x, _)| F::Extension::from_basefield(subgroup_x) - x)
.product();
numerator / denominator
.rev()
.fold(F::Extension::ZERO, |acc, &e| {
poly_count += 1;
alpha * acc + e.into()
});
let zeta_frob = zeta.frobenius();
let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::<Vec<_>>();
let wires_interpol = interpolant(&[
(zeta, reduce_with_powers(&os.wires, alpha)),
(zeta_frob, reduce_with_powers(&wire_evals_frob, alpha)),
]);
let numerator = ev - wires_interpol.eval(subgroup_x.into());
let denominator = (F::Extension::from_basefield(subgroup_x) - zeta)
* (F::Extension::from_basefield(subgroup_x) - zeta_frob);
e += cur_alpha * numerator / denominator;

e
}

fn fri_verifier_query_round<F: Field + Extendable<D>, const D: usize>(
interpolant: &PolynomialCoeffs<F::Extension>,
points: &[(F::Extension, F::Extension)],
os: &OpeningSet<F, D>,
zeta: F::Extension,
alpha: F::Extension,
initial_merkle_roots: &[Hash<F>],
proof: &FriProof<F, D>,
Expand Down Expand Up @@ -195,8 +251,8 @@ fn fri_verifier_query_round<F: Field + Extendable<D>, const D: usize>(
fri_combine_initial(
&round_proof.initial_trees_proof,
alpha,
interpolant,
points,
os,
zeta,
subgroup_x,
config,
)
Expand Down
26 changes: 25 additions & 1 deletion src/plonk_challenger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::{Extendable, FieldExtension};
use crate::field::field::Field;
use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH};
use crate::proof::{Hash, HashTarget};
use crate::proof::{Hash, HashTarget, OpeningSet};
use crate::target::Target;

/// Observes prover messages, and generates challenges by hashing the transcript.
Expand Down Expand Up @@ -61,6 +61,30 @@ impl<F: Field> Challenger<F> {
}
}

pub fn observe_opening_set<const D: usize>(&mut self, os: &OpeningSet<F, D>)
where
F: Extendable<D>,
{
let OpeningSet {
constants,
plonk_sigmas,
wires,
plonk_zs,
plonk_zs_right,
quotient_polys,
} = os;
for v in &[
constants,
plonk_sigmas,
wires,
plonk_zs,
plonk_zs_right,
quotient_polys,
] {
self.observe_extension_elements(v);
}
}

pub fn observe_hash(&mut self, hash: &Hash<F>) {
self.observe_elements(&hash.elements)
}
Expand Down
Loading