Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ default-run = "bench_recursion"
[dependencies]
env_logger = "0.8.3"
log = "0.4.14"
itertools = "0.10.0"
num = "0.3"
rand = "0.7.3"
rand_chacha = "0.2.2"
Expand Down
1 change: 1 addition & 0 deletions src/field/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ pub trait Field:
}

/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`.
#[derive(Clone)]
pub struct Powers<F: Field> {
base: F,
current: F,
Expand Down
144 changes: 12 additions & 132 deletions src/fri/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::polynomial::commitment::SALT_SIZE;

pub mod prover;
pub mod verifier;

Expand Down Expand Up @@ -25,6 +27,16 @@ pub struct FriConfig {
pub blinding: Vec<bool>,
}

impl FriConfig {
pub(crate) fn salt_size(&self, i: usize) -> usize {
if self.blinding[i] {
SALT_SIZE
} else {
0
}
}
}

fn fri_delta(rate_log: usize, conjecture: bool) -> f64 {
let rate = (1 << rate_log) as f64;
if conjecture {
Expand All @@ -47,135 +59,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: 77 additions & 27 deletions src/fri/verifier.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use crate::field::extension_field::{flatten, Extendable, FieldExtension};
use anyhow::{ensure, Result};
use itertools::izip;

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::polynomial::commitment::SALT_SIZE;
use crate::polynomial::polynomial::PolynomialCoeffs;
use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash};
use crate::plonk_common::reduce_with_iter;
use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet};
use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place};
use anyhow::{ensure, Result};

/// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity
/// and P' is the FRI reduced polynomial.
Expand Down Expand Up @@ -65,8 +66,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 +111,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 +144,77 @@ 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 subgroup_x = F::Extension::from_basefield(subgroup_x);
let mut alpha_powers = alpha.powers();
let mut sum = F::Extension::ZERO;

// We will add three terms to `sum`:
// - one for polynomials opened at `x` only
// - one for polynomials opened at `x` and `g x`
// - one for polynomials opened at `x` and its conjugate

let evals = [0, 1, 4]
.iter()
.enumerate()
.flat_map(|(i, (v, _))| &v[..v.len() - if config.blinding[i] { SALT_SIZE } else { 0 }])
.rev()
.fold(F::Extension::ZERO, |acc, &e| alpha * acc + e.into());
let numerator = e - interpolant.eval(subgroup_x.into());
let denominator = points
.flat_map(|&i| proof.unsalted_evals(i, config))
.map(|&e| F::Extension::from_basefield(e));
let openings = os
.constants
.iter()
.map(|&(x, _)| F::Extension::from_basefield(subgroup_x) - x)
.product();
numerator / denominator
.chain(&os.plonk_sigmas)
.chain(&os.quotient_polys);
let numerator = izip!(evals, openings, &mut alpha_powers)
.map(|(e, &o, a)| a * (e - o))
.sum::<F::Extension>();
let denominator = subgroup_x - zeta;
sum += numerator / denominator;

let ev: F::Extension = proof
.unsalted_evals(3, config)
.iter()
.zip(alpha_powers.clone())
.map(|(&e, a)| a * e.into())
.sum();
let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta;
let zs_interpol = interpolant(&[
(zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())),
(
zeta_right,
reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers),
),
]);
let numerator = ev - zs_interpol.eval(subgroup_x);
let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right);
sum += numerator / denominator;

let ev: F::Extension = proof
.unsalted_evals(2, config)
.iter()
.zip(alpha_powers.clone())
.map(|(&e, a)| a * e.into())
.sum();
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_iter(&os.wires, alpha_powers.clone())),
(zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)),
]);
let numerator = ev - wires_interpol.eval(subgroup_x);
let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob);
sum += numerator / denominator;

sum
}

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 +245,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
11 changes: 11 additions & 0 deletions src/plonk_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,14 @@ pub(crate) fn reduce_with_powers_recursive<F: Extendable<D>, const D: usize>(
) -> Target {
todo!()
}

pub(crate) fn reduce_with_iter<F: Field, I>(terms: &[F], coeffs: I) -> F
where
I: IntoIterator<Item = F>,
{
let mut sum = F::ZERO;
for (&term, coeff) in terms.iter().zip(coeffs) {
sum += coeff * term;
}
sum
}
Loading