Working FRI with field extensions

This commit is contained in:
wborgeaud 2021-05-18 15:22:06 +02:00
parent bd20ffa52d
commit a2cf2c03b6
14 changed files with 407 additions and 130 deletions

View File

@ -3,11 +3,13 @@ use env_logger::Env;
use plonky2::circuit_builder::CircuitBuilder;
use plonky2::circuit_data::CircuitConfig;
use plonky2::field::crandall_field::CrandallField;
use plonky2::field::extension_field::Extendable;
use plonky2::field::field::Field;
use plonky2::fri::FriConfig;
use plonky2::gates::constant::ConstantGate;
use plonky2::gates::gmimc::GMiMCGate;
use plonky2::hash::GMIMC_ROUNDS;
use plonky2::polynomial::commitment::EXTENSION_DEGREE;
use plonky2::prover::PLONK_BLINDING;
use plonky2::witness::PartialWitness;
@ -27,7 +29,7 @@ fn main() {
// bench_gmimc::<CrandallField>();
}
fn bench_prove<F: Field>() {
fn bench_prove<F: Field + Extendable<EXTENSION_DEGREE>>() {
let gmimc_gate = GMiMCGate::<F, GMIMC_ROUNDS>::with_automatic_constants();
let config = CircuitConfig {

View File

@ -1,9 +1,10 @@
use crate::field::extension_field::Extendable;
use crate::field::field::Field;
use crate::fri::FriConfig;
use crate::gates::gate::GateRef;
use crate::generator::WitnessGenerator;
use crate::merkle_tree::MerkleTree;
use crate::polynomial::commitment::ListPolynomialCommitment;
use crate::polynomial::commitment::{ListPolynomialCommitment, EXTENSION_DEGREE};
use crate::proof::{Hash, HashTarget, Proof};
use crate::prover::prove;
use crate::verifier::verify;
@ -55,7 +56,7 @@ pub struct CircuitData<F: Field> {
pub(crate) common: CommonCircuitData<F>,
}
impl<F: Field> CircuitData<F> {
impl<F: Field + Extendable<EXTENSION_DEGREE>> CircuitData<F> {
pub fn prove(&self, inputs: PartialWitness<F>) -> Proof<F> {
prove(&self.prover_only, &self.common, inputs)
}
@ -77,7 +78,7 @@ pub struct ProverCircuitData<F: Field> {
pub(crate) common: CommonCircuitData<F>,
}
impl<F: Field> ProverCircuitData<F> {
impl<F: Field + Extendable<EXTENSION_DEGREE>> ProverCircuitData<F> {
pub fn prove(&self, inputs: PartialWitness<F>) -> Proof<F> {
prove(&self.prover_only, &self.common, inputs)
}

View File

@ -4,6 +4,9 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi
use num::Integer;
use crate::field::extension_field::quadratic::QuadraticCrandallField;
use crate::field::extension_field::quartic::QuarticCrandallField;
use crate::field::extension_field::{Extendable, FieldExtension};
use crate::field::field::Field;
use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
@ -412,6 +415,14 @@ impl DivAssign for CrandallField {
}
}
impl Extendable<2> for CrandallField {
type Extension = QuadraticCrandallField;
}
impl Extendable<4> for CrandallField {
type Extension = QuarticCrandallField;
}
/// Reduces to a 64-bit value. The result might not be in canonical form; it could be in between the
/// field order and `2^64`.
#[inline]

View File

@ -1,2 +1,96 @@
pub mod binary;
use crate::field::extension_field::quadratic::QuadraticFieldExtension;
use crate::field::extension_field::quartic::QuarticFieldExtension;
use crate::field::field::Field;
pub mod quadratic;
pub mod quartic;
pub trait Extendable<const D: usize>: Sized {
type Extension: Field + FieldExtension<D, BaseField = Self> + From<Self>;
}
impl<F: Field> Extendable<1> for F {
type Extension = Self;
}
pub trait FieldExtension<const D: usize>: Field {
type BaseField: Field;
fn to_basefield_array(&self) -> [Self::BaseField; D];
fn from_basefield_array(arr: [Self::BaseField; D]) -> Self;
fn from_basefield(x: Self::BaseField) -> Self;
}
impl<F: Field> FieldExtension<1> for F {
type BaseField = F;
fn to_basefield_array(&self) -> [Self::BaseField; 1] {
[*self]
}
fn from_basefield_array(arr: [Self::BaseField; 1]) -> Self {
arr[0]
}
fn from_basefield(x: Self::BaseField) -> Self {
x
}
}
impl<FE: QuadraticFieldExtension> FieldExtension<2> for FE {
type BaseField = FE::BaseField;
fn to_basefield_array(&self) -> [Self::BaseField; 2] {
self.to_canonical_representation()
}
fn from_basefield_array(arr: [Self::BaseField; 2]) -> Self {
Self::from_canonical_representation(arr)
}
fn from_basefield(x: Self::BaseField) -> Self {
x.into()
}
}
impl<FE: QuarticFieldExtension> FieldExtension<4> for FE {
type BaseField = FE::BaseField;
fn to_basefield_array(&self) -> [Self::BaseField; 4] {
self.to_canonical_representation()
}
fn from_basefield_array(arr: [Self::BaseField; 4]) -> Self {
Self::from_canonical_representation(arr)
}
fn from_basefield(x: Self::BaseField) -> Self {
x.into()
}
}
/// Flatten the slice by sending every extension field element to its D-sized canonical representation.
pub fn flatten<F: Field, const D: usize>(l: &[F::Extension]) -> Vec<F>
where
F: Extendable<D>,
{
l.iter()
.flat_map(|x| x.to_basefield_array().to_vec())
.collect()
}
/// Batch every D-sized chunks into extension field elements.
pub fn unflatten<F: Field, const D: usize>(l: &[F]) -> Vec<F::Extension>
where
F: Extendable<D>,
{
l.chunks_exact(D)
.map(|c| {
let mut arr = [F::ZERO; D];
arr.copy_from_slice(c);
F::Extension::from_basefield_array(arr)
})
.collect()
}

View File

@ -6,7 +6,9 @@ use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
pub trait BinaryFieldExtension: Field {
pub trait QuadraticFieldExtension:
Field + From<<Self as QuadraticFieldExtension>::BaseField>
{
type BaseField: Field;
// Element W of BaseField, such that `X^2 - W` is irreducible over BaseField.
@ -35,9 +37,9 @@ pub trait BinaryFieldExtension: Field {
}
#[derive(Copy, Clone)]
pub struct BinaryCrandallField([CrandallField; 2]);
pub struct QuadraticCrandallField([CrandallField; 2]);
impl BinaryFieldExtension for BinaryCrandallField {
impl QuadraticFieldExtension for QuadraticCrandallField {
type BaseField = CrandallField;
// Verifiable in Sage with
// ``R.<x> = GF(p)[]; assert (x^2 -3).is_irreducible()`.
@ -57,15 +59,21 @@ impl BinaryFieldExtension for BinaryCrandallField {
}
}
impl PartialEq for BinaryCrandallField {
impl From<<Self as QuadraticFieldExtension>::BaseField> for QuadraticCrandallField {
fn from(x: <Self as QuadraticFieldExtension>::BaseField) -> Self {
Self([x, <Self as QuadraticFieldExtension>::BaseField::ZERO])
}
}
impl PartialEq for QuadraticCrandallField {
fn eq(&self, other: &Self) -> bool {
self.to_canonical_representation() == other.to_canonical_representation()
}
}
impl Eq for BinaryCrandallField {}
impl Eq for QuadraticCrandallField {}
impl Hash for BinaryCrandallField {
impl Hash for QuadraticCrandallField {
fn hash<H: Hasher>(&self, state: &mut H) {
for l in &self.to_canonical_representation() {
Hash::hash(l, state);
@ -73,14 +81,14 @@ impl Hash for BinaryCrandallField {
}
}
impl Field for BinaryCrandallField {
impl Field for QuadraticCrandallField {
const ZERO: Self = Self([CrandallField::ZERO; 2]);
const ONE: Self = Self([CrandallField::ONE, CrandallField::ZERO]);
const TWO: Self = Self([CrandallField::TWO, CrandallField::ZERO]);
const NEG_ONE: Self = Self([CrandallField::NEG_ONE, CrandallField::ZERO]);
// Does not fit in 64-bits.
const ORDER: u64 = 0;
const ORDER: u64 = 0xffffffffffffffff; // Otherwise F::ORDER.leading_zeros() is misleading.
const TWO_ADICITY: usize = 29;
const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([CrandallField(3), CrandallField::ONE]);
const POWER_OF_TWO_GENERATOR: Self =
@ -99,38 +107,57 @@ impl Field for BinaryCrandallField {
Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse()))
}
// It's important that the primitive roots of unity are the same as the ones in the base field,
// otherwise the FFT doesn't commute with field inclusion.
fn primitive_root_of_unity(n_log: usize) -> Self {
if n_log <= CrandallField::TWO_ADICITY {
Self([
CrandallField::primitive_root_of_unity(n_log),
CrandallField::ZERO,
])
} else {
// The root of unity isn't in the base field so we need to compute it manually.
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
}
}
fn to_canonical_u64(&self) -> u64 {
self.0[0].to_canonical_u64()
}
fn from_canonical_u64(n: u64) -> Self {
Self([
<Self as BinaryFieldExtension>::BaseField::from_canonical_u64(n),
<Self as BinaryFieldExtension>::BaseField::ZERO,
<Self as QuadraticFieldExtension>::BaseField::from_canonical_u64(n),
<Self as QuadraticFieldExtension>::BaseField::ZERO,
])
}
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
Self([
<Self as BinaryFieldExtension>::BaseField::rand_from_rng(rng),
<Self as BinaryFieldExtension>::BaseField::rand_from_rng(rng),
<Self as QuadraticFieldExtension>::BaseField::rand_from_rng(rng),
<Self as QuadraticFieldExtension>::BaseField::rand_from_rng(rng),
])
}
}
impl Display for BinaryCrandallField {
impl Display for QuadraticCrandallField {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} + {}*a", self.0[0], self.0[1])
}
}
impl Debug for BinaryCrandallField {
impl Debug for QuadraticCrandallField {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl Neg for BinaryCrandallField {
impl Neg for QuadraticCrandallField {
type Output = Self;
#[inline]
@ -139,7 +166,7 @@ impl Neg for BinaryCrandallField {
}
}
impl Add for BinaryCrandallField {
impl Add for QuadraticCrandallField {
type Output = Self;
#[inline]
@ -148,19 +175,19 @@ impl Add for BinaryCrandallField {
}
}
impl AddAssign for BinaryCrandallField {
impl AddAssign for QuadraticCrandallField {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Sum for BinaryCrandallField {
impl Sum for QuadraticCrandallField {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ZERO, |acc, x| acc + x)
}
}
impl Sub for BinaryCrandallField {
impl Sub for QuadraticCrandallField {
type Output = Self;
#[inline]
@ -169,14 +196,14 @@ impl Sub for BinaryCrandallField {
}
}
impl SubAssign for BinaryCrandallField {
impl SubAssign for QuadraticCrandallField {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Mul for BinaryCrandallField {
impl Mul for QuadraticCrandallField {
type Output = Self;
#[inline]
@ -191,20 +218,20 @@ impl Mul for BinaryCrandallField {
}
}
impl MulAssign for BinaryCrandallField {
impl MulAssign for QuadraticCrandallField {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl Product for BinaryCrandallField {
impl Product for QuadraticCrandallField {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ONE, |acc, x| acc * x)
}
}
impl Div for BinaryCrandallField {
impl Div for QuadraticCrandallField {
type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
@ -213,7 +240,7 @@ impl Div for BinaryCrandallField {
}
}
impl DivAssign for BinaryCrandallField {
impl DivAssign for QuadraticCrandallField {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
@ -221,7 +248,9 @@ impl DivAssign for BinaryCrandallField {
#[cfg(test)]
mod tests {
use crate::field::extension_field::binary::{BinaryCrandallField, BinaryFieldExtension};
use crate::field::extension_field::quadratic::{
QuadraticCrandallField, QuadraticFieldExtension,
};
use crate::field::field::Field;
fn exp_naive<F: Field>(x: F, power: u64) -> F {
@ -239,7 +268,7 @@ mod tests {
#[test]
fn test_add_neg_sub_mul() {
type F = BinaryCrandallField;
type F = QuadraticCrandallField;
let x = F::rand();
let y = F::rand();
let z = F::rand();
@ -247,7 +276,7 @@ mod tests {
assert_eq!(-x, F::ZERO - x);
assert_eq!(
x + x,
x.scalar_mul(<F as BinaryFieldExtension>::BaseField::TWO)
x.scalar_mul(<F as QuadraticFieldExtension>::BaseField::TWO)
);
assert_eq!(x * (-x), -x.square());
assert_eq!(x + y, y + x);
@ -260,7 +289,7 @@ mod tests {
#[test]
fn test_inv_div() {
type F = BinaryCrandallField;
type F = QuadraticCrandallField;
let x = F::rand();
let y = F::rand();
let z = F::rand();
@ -274,10 +303,10 @@ mod tests {
#[test]
fn test_frobenius() {
type F = BinaryCrandallField;
type F = QuadraticCrandallField;
let x = F::rand();
assert_eq!(
exp_naive(x, <F as BinaryFieldExtension>::BaseField::ORDER),
exp_naive(x, <F as QuadraticFieldExtension>::BaseField::ORDER),
x.frobenius()
);
}
@ -285,7 +314,7 @@ mod tests {
#[test]
fn test_field_order() {
// F::ORDER = 340282366831806780677557380898690695169 = 18446744071293632512 *18446744071293632514 + 1
type F = BinaryCrandallField;
type F = QuadraticCrandallField;
let x = F::rand();
assert_eq!(
exp_naive(exp_naive(x, 18446744071293632512), 18446744071293632514),

View File

@ -6,7 +6,7 @@ use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
pub trait QuarticFieldExtension: Field {
pub trait QuarticFieldExtension: Field + From<<Self as QuarticFieldExtension>::BaseField> {
type BaseField: Field;
// Element W of BaseField, such that `X^4 - W` is irreducible over BaseField.
@ -65,6 +65,17 @@ impl QuarticFieldExtension for QuarticCrandallField {
}
}
impl From<<Self as QuarticFieldExtension>::BaseField> for QuarticCrandallField {
fn from(x: <Self as QuarticFieldExtension>::BaseField) -> Self {
Self([
x,
<Self as QuarticFieldExtension>::BaseField::ZERO,
<Self as QuarticFieldExtension>::BaseField::ZERO,
<Self as QuarticFieldExtension>::BaseField::ZERO,
])
}
}
impl PartialEq for QuarticCrandallField {
fn eq(&self, other: &Self) -> bool {
self.to_canonical_representation() == other.to_canonical_representation()
@ -103,7 +114,7 @@ impl Field for QuarticCrandallField {
]);
// Does not fit in 64-bits.
const ORDER: u64 = 0;
const ORDER: u64 = 0xffffffffffffffff; // Otherwise F::ORDER.leading_zeros() is misleading.
const TWO_ADICITY: usize = 30;
const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([
CrandallField(3),
@ -134,6 +145,27 @@ impl Field for QuarticCrandallField {
Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse()))
}
// It's important that the primitive roots of unity are the same as the ones in the base field,
// otherwise the FFT doesn't commute with field inclusion.
fn primitive_root_of_unity(n_log: usize) -> Self {
if n_log <= CrandallField::TWO_ADICITY {
Self([
CrandallField::primitive_root_of_unity(n_log),
CrandallField::ZERO,
CrandallField::ZERO,
CrandallField::ZERO,
])
} else {
// The root of unity isn't in the base field so we need to compute it manually.
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
}
}
fn to_canonical_u64(&self) -> u64 {
self.0[0].to_canonical_u64()
}

View File

@ -52,13 +52,17 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 {
mod tests {
use super::*;
use crate::field::crandall_field::CrandallField;
use crate::field::extension_field::quadratic::QuadraticCrandallField;
use crate::field::extension_field::quartic::QuarticCrandallField;
use crate::field::extension_field::{flatten, FieldExtension};
use crate::field::fft::ifft;
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;
use crate::polynomial::commitment::EXTENSION_DEGREE;
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
use crate::util::reverse_index_bits_in_place;
use anyhow::Result;
use rand::rngs::ThreadRng;
@ -71,6 +75,7 @@ mod tests {
num_query_rounds: usize,
) -> Result<()> {
type F = CrandallField;
type F2 = QuadraticCrandallField;
let n = 1 << degree_log;
let coeffs = PolynomialCoeffs::new(F::rand_vec(n)).lde(rate_bits);
@ -91,15 +96,23 @@ mod tests {
reverse_index_bits_in_place(&mut leaves);
MerkleTree::new(leaves, false)
};
let coset_lde =
PolynomialValues::new(coset_lde.values.into_iter().map(|x| F2::from(x)).collect());
let root = tree.root;
let mut challenger = Challenger::new();
let proof = fri_proof(&[&tree], &coeffs, &coset_lde, &mut challenger, &config);
let proof = fri_proof(
&[&tree],
&coeffs.to_extension::<EXTENSION_DEGREE>(),
&coset_lde,
&mut challenger,
&config,
);
let mut challenger = Challenger::new();
verify_fri_proof(
degree_log,
&[],
F::ONE,
F2::ONE,
&[root],
&proof,
&mut challenger,

View File

@ -1,9 +1,11 @@
use crate::field::extension_field::{flatten, unflatten, Extendable, FieldExtension};
use crate::field::field::Field;
use crate::fri::FriConfig;
use crate::hash::hash_n_to_1;
use crate::merkle_tree::MerkleTree;
use crate::plonk_challenger::Challenger;
use crate::plonk_common::reduce_with_powers;
use crate::polynomial::commitment::EXTENSION_DEGREE;
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep, Hash};
use crate::util::reverse_index_bits_in_place;
@ -12,12 +14,15 @@ use crate::util::reverse_index_bits_in_place;
pub fn fri_proof<F: Field>(
initial_merkle_trees: &[&MerkleTree<F>],
// Coefficients of the polynomial on which the LDT is performed. Only the first `1/rate` coefficients are non-zero.
lde_polynomial_coeffs: &PolynomialCoeffs<F>,
lde_polynomial_coeffs: &PolynomialCoeffs<F::Extension>,
// Evaluation of the polynomial on the large domain.
lde_polynomial_values: &PolynomialValues<F>,
lde_polynomial_values: &PolynomialValues<F::Extension>,
challenger: &mut Challenger<F>,
config: &FriConfig,
) -> FriProof<F> {
) -> FriProof<F>
where
F: Extendable<EXTENSION_DEGREE>,
{
let n = lde_polynomial_values.values.len();
assert_eq!(lde_polynomial_coeffs.coeffs.len(), n);
@ -46,11 +51,14 @@ pub fn fri_proof<F: Field>(
}
fn fri_committed_trees<F: Field>(
polynomial_coeffs: &PolynomialCoeffs<F>,
polynomial_values: &PolynomialValues<F>,
polynomial_coeffs: &PolynomialCoeffs<F::Extension>,
polynomial_values: &PolynomialValues<F::Extension>,
challenger: &mut Challenger<F>,
config: &FriConfig,
) -> (Vec<MerkleTree<F>>, PolynomialCoeffs<F>) {
) -> (Vec<MerkleTree<F>>, PolynomialCoeffs<F::Extension>)
where
F: Extendable<EXTENSION_DEGREE>,
{
let mut values = polynomial_values.clone();
let mut coeffs = polynomial_coeffs.clone();
@ -66,7 +74,7 @@ fn fri_committed_trees<F: Field>(
values
.values
.chunks(arity)
.map(|chunk| chunk.to_vec())
.map(|chunk: &[F::Extension]| flatten(chunk))
.collect(),
false,
);
@ -74,7 +82,7 @@ fn fri_committed_trees<F: Field>(
challenger.observe_hash(&tree.root);
trees.push(tree);
let beta = challenger.get_challenge();
let beta = challenger.get_extension_challenge();
// P(x) = sum_{i<r} x^i * P_i(x^r) becomes sum_{i<r} beta^i * P_i(x).
coeffs = PolynomialCoeffs::new(
coeffs
@ -85,10 +93,10 @@ fn fri_committed_trees<F: Field>(
);
shift = shift.exp_u32(arity as u32);
// TODO: Is it faster to interpolate?
values = coeffs.clone().coset_fft(shift);
values = coeffs.clone().coset_fft(shift.into())
}
challenger.observe_elements(&coeffs.coeffs);
challenger.observe_extension_elements(&coeffs.coeffs);
(trees, coeffs)
}
@ -112,7 +120,7 @@ fn fri_proof_of_work<F: Field>(current_hash: Hash<F>, config: &FriConfig) -> F {
.expect("Proof of work failed.")
}
fn fri_prover_query_rounds<F: Field>(
fn fri_prover_query_rounds<F: Field + Extendable<EXTENSION_DEGREE>>(
initial_merkle_trees: &[&MerkleTree<F>],
trees: &[MerkleTree<F>],
challenger: &mut Challenger<F>,
@ -124,7 +132,7 @@ fn fri_prover_query_rounds<F: Field>(
.collect()
}
fn fri_prover_query_round<F: Field>(
fn fri_prover_query_round<F: Field + Extendable<EXTENSION_DEGREE>>(
initial_merkle_trees: &[&MerkleTree<F>],
trees: &[MerkleTree<F>],
challenger: &mut Challenger<F>,
@ -134,6 +142,7 @@ fn fri_prover_query_round<F: Field>(
let mut query_steps = Vec::new();
let x = challenger.get_challenge();
let mut x_index = x.to_canonical_u64() as usize % n;
dbg!(x_index, n);
let initial_proof = initial_merkle_trees
.iter()
.map(|t| (t.get(x_index).to_vec(), t.prove(x_index)))
@ -141,7 +150,7 @@ fn fri_prover_query_round<F: Field>(
for (i, tree) in trees.iter().enumerate() {
let arity_bits = config.reduction_arity_bits[i];
let arity = 1 << arity_bits;
let mut evals = tree.get(x_index >> arity_bits).to_vec();
let mut evals = unflatten(tree.get(x_index >> arity_bits));
evals.remove(x_index & (arity - 1));
let merkle_proof = tree.prove(x_index >> arity_bits);

View File

@ -1,10 +1,11 @@
use crate::field::extension_field::{flatten, Extendable, FieldExtension};
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::commitment::{EXTENSION_DEGREE, SALT_SIZE};
use crate::polynomial::polynomial::PolynomialCoeffs;
use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash};
use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place};
@ -16,9 +17,12 @@ fn compute_evaluation<F: Field>(
x: F,
old_x_index: usize,
arity_bits: usize,
last_evals: &[F],
beta: F,
) -> F {
last_evals: &[F::Extension],
beta: F::Extension,
) -> F::Extension
where
F: Extendable<EXTENSION_DEGREE>,
{
debug_assert_eq!(last_evals.len(), 1 << arity_bits);
let g = F::primitive_root_of_unity(arity_bits);
@ -32,8 +36,9 @@ fn compute_evaluation<F: Field>(
let points = g
.powers()
.zip(evals)
.map(|(y, e)| (x * y, e))
.map(|(y, e)| ((x * y).into(), e))
.collect::<Vec<_>>();
dbg!(&points);
let barycentric_weights = barycentric_weights(&points);
interpolate(&points, beta, &barycentric_weights)
}
@ -42,7 +47,10 @@ fn fri_verify_proof_of_work<F: Field>(
proof: &FriProof<F>,
challenger: &mut Challenger<F>,
config: &FriConfig,
) -> Result<()> {
) -> Result<()>
where
F: Extendable<EXTENSION_DEGREE>,
{
let hash = hash_n_to_1(
challenger
.get_hash()
@ -65,14 +73,17 @@ fn fri_verify_proof_of_work<F: Field>(
pub fn verify_fri_proof<F: Field>(
purported_degree_log: usize,
// Point-evaluation pairs for polynomial commitments.
points: &[(F, F)],
points: &[(F::Extension, F::Extension)],
// Scaling factor to combine polynomials.
alpha: F,
alpha: F::Extension,
initial_merkle_roots: &[Hash<F>],
proof: &FriProof<F>,
challenger: &mut Challenger<F>,
config: &FriConfig,
) -> Result<()> {
) -> Result<()>
where
F: Extendable<EXTENSION_DEGREE>,
{
let total_arities = config.reduction_arity_bits.iter().sum::<usize>();
ensure!(
purported_degree_log
@ -89,10 +100,10 @@ pub fn verify_fri_proof<F: Field>(
.iter()
.map(|root| {
challenger.observe_hash(root);
challenger.get_challenge()
challenger.get_extension_challenge()
})
.collect::<Vec<_>>();
challenger.observe_elements(&proof.final_poly.coeffs);
challenger.observe_extension_elements(&proof.final_poly.coeffs);
// Check PoW.
fri_verify_proof_of_work(proof, challenger, config)?;
@ -140,37 +151,46 @@ fn fri_verify_initial_proof<F: Field>(
fn fri_combine_initial<F: Field>(
proof: &FriInitialTreeProof<F>,
alpha: F,
interpolant: &PolynomialCoeffs<F>,
points: &[(F, F)],
alpha: F::Extension,
interpolant: &PolynomialCoeffs<F::Extension>,
points: &[(F::Extension, F::Extension)],
subgroup_x: F,
config: &FriConfig,
) -> F {
) -> F::Extension
where
F: Extendable<EXTENSION_DEGREE>,
{
let e = proof
.evals_proofs
.iter()
.enumerate()
.flat_map(|(i, (v, _))| &v[..v.len() - if config.blinding[i] { SALT_SIZE } else { 0 }])
.rev()
.fold(F::ZERO, |acc, &e| alpha * acc + e);
let numerator = e - interpolant.eval(subgroup_x);
let denominator = points.iter().map(|&(x, _)| subgroup_x - x).product();
.fold(F::Extension::ZERO, |acc, &e| alpha * acc + e.into());
let numerator = e - interpolant.eval(subgroup_x.into());
let denominator = points
.iter()
.map(|&(x, _)| F::Extension::from_basefield(subgroup_x) - x)
.product();
numerator / denominator
}
fn fri_verifier_query_round<F: Field>(
interpolant: &PolynomialCoeffs<F>,
points: &[(F, F)],
alpha: F,
interpolant: &PolynomialCoeffs<F::Extension>,
points: &[(F::Extension, F::Extension)],
alpha: F::Extension,
initial_merkle_roots: &[Hash<F>],
proof: &FriProof<F>,
challenger: &mut Challenger<F>,
n: usize,
betas: &[F],
betas: &[F::Extension],
round_proof: &FriQueryRound<F>,
config: &FriConfig,
) -> Result<()> {
let mut evaluations: Vec<Vec<F>> = Vec::new();
) -> Result<()>
where
F: Extendable<EXTENSION_DEGREE>,
{
let mut evaluations: Vec<Vec<F::Extension>> = Vec::new();
let x = challenger.get_challenge();
let mut domain_size = n;
let mut x_index = x.to_canonical_u64() as usize % n;
@ -212,7 +232,7 @@ fn fri_verifier_query_round<F: Field>(
evals.insert(x_index & (arity - 1), e_x);
evaluations.push(evals);
verify_merkle_proof(
evaluations[i].clone(),
flatten(&evaluations[i]),
x_index >> arity_bits,
proof.commit_phase_merkle_roots[i],
&round_proof.steps[i].merkle_proof,
@ -246,7 +266,7 @@ fn fri_verifier_query_round<F: Field>(
// Final check of FRI. After all the reductions, we check that the final polynomial is equal
// to the one sent by the prover.
ensure!(
proof.final_poly.eval(subgroup_x) == purported_eval,
proof.final_poly.eval(subgroup_x.into()) == purported_eval,
"Final polynomial evaluation is invalid."
);

View File

@ -1,4 +1,5 @@
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};
@ -36,12 +37,30 @@ impl<F: Field> Challenger<F> {
self.input_buffer.push(element);
}
pub fn observe_extension_element<const D: usize>(&mut self, element: &F::Extension)
where
F: Extendable<D>,
{
for &e in &element.to_basefield_array() {
self.observe_element(e);
}
}
pub fn observe_elements(&mut self, elements: &[F]) {
for &element in elements {
self.observe_element(element);
}
}
pub fn observe_extension_elements<const D: usize>(&mut self, elements: &[F::Extension])
where
F: Extendable<D>,
{
for element in elements {
self.observe_extension_element(element);
}
}
pub fn observe_hash(&mut self, hash: &Hash<F>) {
self.observe_elements(&hash.elements)
}
@ -76,6 +95,13 @@ impl<F: Field> Challenger<F> {
(0..n).map(|_| self.get_challenge()).collect()
}
pub fn get_n_extension_challenges<const D: usize>(&mut self, n: usize) -> Vec<F::Extension>
where
F: Extendable<D>,
{
(0..n).map(|_| self.get_extension_challenge()).collect()
}
pub fn get_hash(&mut self) -> Hash<F> {
Hash {
elements: [
@ -87,6 +113,15 @@ impl<F: Field> Challenger<F> {
}
}
pub fn get_extension_challenge<const D: usize>(&mut self) -> F::Extension
where
F: Extendable<D>,
{
let mut arr = [F::ZERO; D];
arr.copy_from_slice(&self.get_n_challenges(D));
F::Extension::from_basefield_array(arr)
}
/// Absorb any buffered inputs. After calling this, the input buffer will be empty.
fn absorb_buffered_inputs(&mut self) {
if self.input_buffer.is_empty() {

View File

@ -1,6 +1,8 @@
use anyhow::Result;
use rayon::prelude::*;
use crate::field::extension_field::Extendable;
use crate::field::extension_field::FieldExtension;
use crate::field::field::Field;
use crate::field::lagrange::interpolant;
use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig};
@ -13,6 +15,7 @@ use crate::timed;
use crate::util::{log2_strict, reverse_index_bits_in_place, transpose};
pub const SALT_SIZE: usize = 2;
pub const EXTENSION_DEGREE: usize = 2;
pub struct ListPolynomialCommitment<F: Field> {
pub polynomials: Vec<PolynomialCoeffs<F>>,
@ -76,17 +79,20 @@ impl<F: Field> ListPolynomialCommitment<F> {
pub fn open(
&self,
points: &[F],
points: &[F::Extension],
challenger: &mut Challenger<F>,
config: &FriConfig,
) -> (OpeningProof<F>, Vec<Vec<F>>) {
) -> (OpeningProof<F>, Vec<Vec<F::Extension>>)
where
F: Extendable<EXTENSION_DEGREE>,
{
assert_eq!(self.rate_bits, config.rate_bits);
assert_eq!(config.blinding.len(), 1);
assert_eq!(self.blinding, config.blinding[0]);
for p in points {
assert_ne!(
p.exp_usize(self.degree),
F::ONE,
F::Extension::ONE,
"Opening point is in the subgroup."
);
}
@ -96,15 +102,17 @@ impl<F: Field> ListPolynomialCommitment<F> {
.map(|&x| {
self.polynomials
.iter()
.map(|p| p.eval(x))
.map(|p| p.to_extension().eval(x))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
for evals in &evaluations {
challenger.observe_elements(evals);
for e in evals {
challenger.observe_extension_element(e);
}
}
let alpha = challenger.get_challenge();
let alpha = challenger.get_extension_challenge();
// Scale polynomials by `alpha`.
let composition_poly = self
@ -112,7 +120,7 @@ impl<F: Field> ListPolynomialCommitment<F> {
.iter()
.rev()
.fold(PolynomialCoeffs::zero(self.degree), |acc, p| {
&(&acc * alpha) + &p
&(&acc * alpha) + &p.to_extension()
});
// Scale evaluations by `alpha`.
let composition_evals = evaluations
@ -125,7 +133,7 @@ impl<F: Field> ListPolynomialCommitment<F> {
let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(self.rate_bits);
let lde_quotient_values = lde_quotient
.clone()
.coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR);
.coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR.into());
let fri_proof = fri_proof(
&[&self.merkle_tree],
@ -146,10 +154,13 @@ impl<F: Field> ListPolynomialCommitment<F> {
pub fn batch_open(
commitments: &[&Self],
points: &[F],
points: &[F::Extension],
challenger: &mut Challenger<F>,
config: &FriConfig,
) -> (OpeningProof<F>, Vec<Vec<Vec<F>>>) {
) -> (OpeningProof<F>, Vec<Vec<Vec<F::Extension>>>)
where
F: Extendable<EXTENSION_DEGREE>,
{
let degree = commitments[0].degree;
assert_eq!(config.blinding.len(), commitments.len());
for (i, commitment) in commitments.iter().enumerate() {
@ -166,7 +177,7 @@ impl<F: Field> ListPolynomialCommitment<F> {
for p in points {
assert_ne!(
p.exp_usize(degree),
F::ONE,
F::Extension::ONE,
"Opening point is in the subgroup."
);
}
@ -176,26 +187,30 @@ impl<F: Field> ListPolynomialCommitment<F> {
.map(|&x| {
commitments
.iter()
.map(move |c| c.polynomials.iter().map(|p| p.eval(x)).collect::<Vec<_>>())
.map(move |c| {
c.polynomials
.iter()
.map(|p| p.to_extension().eval(x))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
for evals_per_point in &evaluations {
for evals in evals_per_point {
challenger.observe_elements(evals);
challenger.observe_extension_elements(evals);
}
}
let alpha = challenger.get_challenge();
let alpha = challenger.get_extension_challenge();
// Scale polynomials by `alpha`.
let composition_poly = commitments
.iter()
.flat_map(|c| &c.polynomials)
.rev()
.map(|p| p.clone().into())
.fold(PolynomialCoeffs::zero(degree), |acc, p| {
&(&acc * alpha) + &p
&(&acc * alpha) + &p.to_extension()
});
// Scale evaluations by `alpha`.
let composition_evals = &evaluations
@ -204,16 +219,16 @@ impl<F: Field> ListPolynomialCommitment<F> {
v.iter()
.flatten()
.rev()
.fold(F::ZERO, |acc, &e| acc * alpha + e)
.fold(F::Extension::ZERO, |acc, &e| acc * alpha + e)
})
.collect::<Vec<_>>();
let quotient = Self::compute_quotient(points, &composition_evals, &composition_poly);
let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(config.rate_bits);
let lde_quotient_values = lde_quotient
.clone()
.coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR);
let lde_quotient_values = lde_quotient.clone().coset_fft(F::Extension::from_basefield(
F::MULTIPLICATIVE_GROUP_GENERATOR,
));
let fri_proof = fri_proof(
&commitments
@ -237,10 +252,13 @@ impl<F: Field> ListPolynomialCommitment<F> {
pub fn batch_open_plonk(
commitments: &[&Self; 5],
points: &[F],
points: &[F::Extension],
challenger: &mut Challenger<F>,
config: &FriConfig,
) -> (OpeningProof<F>, Vec<OpeningSet<F>>) {
) -> (OpeningProof<F>, Vec<OpeningSet<F::Extension>>)
where
F: Extendable<EXTENSION_DEGREE>,
{
let (op, mut evaluations) = Self::batch_open(commitments, points, challenger, config);
let opening_sets = evaluations
.par_iter_mut()
@ -261,10 +279,13 @@ impl<F: Field> ListPolynomialCommitment<F> {
/// 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> {
points: &[F::Extension],
evals: &[F::Extension],
poly: &PolynomialCoeffs<F::Extension>,
) -> PolynomialCoeffs<F::Extension>
where
F: Extendable<EXTENSION_DEGREE>,
{
let pairs = points
.iter()
.zip(evals)
@ -274,7 +295,7 @@ impl<F: Field> ListPolynomialCommitment<F> {
let interpolant = interpolant(&pairs);
let denominator = points.iter().fold(PolynomialCoeffs::one(), |acc, &x| {
&acc * &PolynomialCoeffs::new(vec![-x, F::ONE])
&acc * &PolynomialCoeffs::new(vec![-x, F::Extension::ONE])
});
let numerator = poly - &interpolant;
let (mut quotient, rem) = numerator.div_rem(&denominator);
@ -284,28 +305,28 @@ impl<F: Field> ListPolynomialCommitment<F> {
}
}
pub struct OpeningProof<F: Field> {
pub struct OpeningProof<F: Field + Extendable<EXTENSION_DEGREE>> {
fri_proof: FriProof<F>,
// TODO: Get the degree from `CommonCircuitData` instead.
quotient_degree: usize,
}
impl<F: Field> OpeningProof<F> {
impl<F: Field + Extendable<EXTENSION_DEGREE>> OpeningProof<F> {
pub fn verify(
&self,
points: &[F],
evaluations: &[Vec<Vec<F>>],
points: &[F::Extension],
evaluations: &[Vec<Vec<F::Extension>>],
merkle_roots: &[Hash<F>],
challenger: &mut Challenger<F>,
fri_config: &FriConfig,
) -> Result<()> {
for evals_per_point in evaluations {
for evals in evals_per_point {
challenger.observe_elements(evals);
challenger.observe_extension_elements(evals);
}
}
let alpha = challenger.get_challenge();
let alpha = challenger.get_extension_challenge();
let scaled_evals = evaluations
.par_iter()
@ -313,7 +334,7 @@ impl<F: Field> OpeningProof<F> {
v.iter()
.flatten()
.rev()
.fold(F::ZERO, |acc, &e| acc * alpha + e)
.fold(F::Extension::ZERO, |acc, &e| acc * alpha + e)
})
.collect::<Vec<_>>();
@ -343,19 +364,19 @@ mod tests {
use super::*;
fn gen_random_test_case<F: Field>(
fn gen_random_test_case<F: Field + Extendable<EXTENSION_DEGREE>>(
k: usize,
degree_log: usize,
num_points: usize,
) -> (Vec<PolynomialCoeffs<F>>, Vec<F>) {
) -> (Vec<PolynomialCoeffs<F>>, Vec<F::Extension>) {
let degree = 1 << degree_log;
let polys = (0..k)
.map(|_| PolynomialCoeffs::new(F::rand_vec(degree)))
.collect();
let mut points = F::rand_vec(num_points);
let mut points = F::Extension::rand_vec(num_points);
while points.iter().any(|&x| x.exp_usize(degree).is_one()) {
points = F::rand_vec(num_points);
points = F::Extension::rand_vec(num_points);
}
(polys, points)

View File

@ -1,6 +1,7 @@
use std::cmp::max;
use std::ops::{Add, Mul, Sub};
use crate::field::extension_field::{Extendable, FieldExtension};
use crate::field::fft::{fft, ifft};
use crate::field::field::Field;
use crate::util::{log2_ceil, log2_strict};
@ -167,6 +168,13 @@ impl<F: Field> PolynomialCoeffs<F> {
.into();
modified_poly.fft()
}
pub fn to_extension<const D: usize>(&self) -> PolynomialCoeffs<F::Extension>
where
F: Extendable<D>,
{
PolynomialCoeffs::new(self.coeffs.iter().map(|&c| c.into()).collect())
}
}
impl<F: Field> PartialEq for PolynomialCoeffs<F> {

View File

@ -1,6 +1,7 @@
use crate::field::extension_field::Extendable;
use crate::field::field::Field;
use crate::merkle_proofs::{MerkleProof, MerkleProofTarget};
use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof};
use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof, EXTENSION_DEGREE};
use crate::polynomial::polynomial::PolynomialCoeffs;
use crate::target::Target;
use std::convert::TryInto;
@ -54,7 +55,7 @@ impl HashTarget {
}
}
pub struct Proof<F: Field> {
pub struct Proof<F: Field + Extendable<EXTENSION_DEGREE>> {
/// Merkle root of LDEs of wire values.
pub wires_root: Hash<F>,
/// Merkle root of LDEs of Z, in the context of Plonk's permutation argument.
@ -63,7 +64,7 @@ pub struct Proof<F: Field> {
pub quotient_polys_root: Hash<F>,
/// Purported values of each polynomial at each challenge point.
pub openings: Vec<OpeningSet<F>>,
pub openings: Vec<OpeningSet<F::Extension>>,
/// A FRI argument for each FRI query.
pub opening_proof: OpeningProof<F>,
@ -86,8 +87,8 @@ pub struct ProofTarget {
/// Evaluations and Merkle proof produced by the prover in a FRI query step.
// TODO: Implement FriQueryStepTarget
pub struct FriQueryStep<F: Field> {
pub evals: Vec<F>,
pub struct FriQueryStep<F: Field + Extendable<EXTENSION_DEGREE>> {
pub evals: Vec<F::Extension>,
pub merkle_proof: MerkleProof<F>,
}
@ -100,18 +101,18 @@ pub struct FriInitialTreeProof<F: Field> {
/// Proof for a FRI query round.
// TODO: Implement FriQueryRoundTarget
pub struct FriQueryRound<F: Field> {
pub struct FriQueryRound<F: Field + Extendable<EXTENSION_DEGREE>> {
pub initial_trees_proof: FriInitialTreeProof<F>,
pub steps: Vec<FriQueryStep<F>>,
}
pub struct FriProof<F: Field> {
pub struct FriProof<F: Field + Extendable<EXTENSION_DEGREE>> {
/// A Merkle root for each reduced polynomial in the commit phase.
pub commit_phase_merkle_roots: Vec<Hash<F>>,
/// Query rounds proofs
pub query_round_proofs: Vec<FriQueryRound<F>>,
/// The final polynomial in coefficient form.
pub final_poly: PolynomialCoeffs<F>,
pub final_poly: PolynomialCoeffs<F::Extension>,
/// Witness showing that the prover did PoW.
pub pow_witness: F,
}

View File

@ -4,12 +4,13 @@ use log::info;
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_l_1, evaluate_gate_constraints, reduce_with_powers_multi};
use crate::polynomial::commitment::ListPolynomialCommitment;
use crate::polynomial::commitment::{ListPolynomialCommitment, EXTENSION_DEGREE};
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
use crate::proof::Proof;
use crate::timed;
@ -21,7 +22,7 @@ use crate::witness::PartialWitness;
/// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments.
pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true];
pub(crate) fn prove<F: Field>(
pub(crate) fn prove<F: Field + Extendable<EXTENSION_DEGREE>>(
prover_data: &ProverOnlyCircuitData<F>,
common_data: &CommonCircuitData<F>,
inputs: PartialWitness<F>,
@ -109,7 +110,7 @@ pub(crate) fn prove<F: Field>(
challenger.observe_hash(&quotient_polys_commitment.merkle_tree.root);
let zetas = challenger.get_n_challenges(config.num_challenges);
let zetas = challenger.get_n_extension_challenges(config.num_challenges);
let (opening_proof, openings) = timed!(
ListPolynomialCommitment::batch_open_plonk(