mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-07 00:03:10 +00:00
Merge branch 'main' into fix_target_partition
# Conflicts: # src/witness.rs
This commit is contained in:
commit
6c864ca522
@ -8,7 +8,6 @@ use plonky2::fri::FriConfig;
|
||||
use plonky2::gates::constant::ConstantGate;
|
||||
use plonky2::gates::gmimc::GMiMCGate;
|
||||
use plonky2::hash::GMIMC_ROUNDS;
|
||||
use plonky2::prover::PLONK_BLINDING;
|
||||
use plonky2::witness::PartialWitness;
|
||||
|
||||
fn main() {
|
||||
@ -41,7 +40,6 @@ fn bench_prove<F: Field + Extendable<D>, const D: usize>() {
|
||||
rate_bits: 3,
|
||||
reduction_arity_bits: vec![1],
|
||||
num_query_rounds: 1,
|
||||
blinding: PLONK_BLINDING.to_vec(),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ use crate::circuit_data::{
|
||||
VerifierCircuitData, VerifierOnlyCircuitData,
|
||||
};
|
||||
use crate::field::cosets::get_unique_coset_shifts;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::constant::ConstantGate;
|
||||
use crate::gates::gate::{GateInstance, GateRef};
|
||||
@ -129,6 +130,12 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.assert_equal(src, dst);
|
||||
}
|
||||
|
||||
pub fn route_extension(&mut self, src: ExtensionTarget<D>, dst: ExtensionTarget<D>) {
|
||||
for i in 0..D {
|
||||
self.route(src.0[i], dst.0[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a generator which will copy `src` to `dst`.
|
||||
pub fn generate_copy(&mut self, src: Target, dst: Target) {
|
||||
self.add_generator(CopyGenerator { src, dst });
|
||||
@ -148,6 +155,17 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.copy_constraints.push((x, y));
|
||||
}
|
||||
|
||||
pub fn assert_zero(&mut self, x: Target) {
|
||||
let zero = self.zero();
|
||||
self.assert_equal(x, zero);
|
||||
}
|
||||
|
||||
pub fn assert_equal_extension(&mut self, x: ExtensionTarget<D>, y: ExtensionTarget<D>) {
|
||||
for i in 0..D {
|
||||
self.assert_equal(x.0[i], y.0[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_generators(&mut self, generators: Vec<Box<dyn WitnessGenerator<F>>>) {
|
||||
self.generators.extend(generators);
|
||||
}
|
||||
|
||||
@ -39,7 +39,6 @@ impl Default for CircuitConfig {
|
||||
rate_bits: 1,
|
||||
reduction_arity_bits: vec![1],
|
||||
num_query_rounds: 1,
|
||||
blinding: vec![true],
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -49,6 +48,22 @@ impl CircuitConfig {
|
||||
pub fn num_advice_wires(&self) -> usize {
|
||||
self.num_wires - self.num_routed_wires
|
||||
}
|
||||
|
||||
pub(crate) fn large_config() -> Self {
|
||||
Self {
|
||||
num_wires: 134,
|
||||
num_routed_wires: 12,
|
||||
security_bits: 128,
|
||||
rate_bits: 3,
|
||||
num_challenges: 3,
|
||||
fri_config: FriConfig {
|
||||
proof_of_work_bits: 1,
|
||||
rate_bits: 3,
|
||||
reduction_arity_bits: vec![1],
|
||||
num_query_rounds: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Circuit data required by the prover or the verifier.
|
||||
|
||||
@ -8,7 +8,7 @@ use num::Integer;
|
||||
|
||||
use crate::field::extension_field::quadratic::QuadraticCrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::extension_field::{Extendable, Frobenius};
|
||||
use crate::field::field::Field;
|
||||
|
||||
/// EPSILON = 9 * 2**28 - 1
|
||||
@ -444,6 +444,8 @@ fn split(x: u128) -> (u64, u64) {
|
||||
(x as u64, (x >> 64) as u64)
|
||||
}
|
||||
|
||||
impl Frobenius<1> for CrandallField {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::test_arithmetic;
|
||||
|
||||
@ -220,7 +220,6 @@ mod tests {
|
||||
let y = ExtensionAlgebra::from_basefield_array(arr1);
|
||||
let z = x * y;
|
||||
|
||||
dbg!(z.0, mul_mle(ts.clone()));
|
||||
assert_eq!(z.0, mul_mle(ts));
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::field::field::Field;
|
||||
use std::convert::TryInto;
|
||||
|
||||
pub mod algebra;
|
||||
pub mod quadratic;
|
||||
@ -12,32 +13,42 @@ pub mod target;
|
||||
pub trait OEF<const D: usize>: FieldExtension<D> {
|
||||
// Element W of BaseField, such that `X^d - W` is irreducible over BaseField.
|
||||
const W: Self::BaseField;
|
||||
|
||||
/// Frobenius automorphisms: x -> x^p, where p is the order of BaseField.
|
||||
fn frobenius(&self) -> Self {
|
||||
let arr = self.to_basefield_array();
|
||||
let k = (Self::BaseField::ORDER - 1) / (D as u64);
|
||||
let z0 = Self::W.exp(k);
|
||||
let mut z = Self::BaseField::ONE;
|
||||
let mut res = [Self::BaseField::ZERO; D];
|
||||
for i in 0..D {
|
||||
res[i] = arr[i] * z;
|
||||
z *= z0;
|
||||
}
|
||||
|
||||
Self::from_basefield_array(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> OEF<1> for F {
|
||||
const W: Self::BaseField = F::ZERO;
|
||||
}
|
||||
|
||||
pub trait Extendable<const D: usize>: Field + Sized {
|
||||
type Extension: Field + OEF<D, BaseField = Self> + From<Self>;
|
||||
pub trait Frobenius<const D: usize>: OEF<D> {
|
||||
/// FrobeniusField automorphisms: x -> x^p, where p is the order of BaseField.
|
||||
fn frobenius(&self) -> Self {
|
||||
self.repeated_frobenius(1)
|
||||
}
|
||||
|
||||
/// Repeated Frobenius automorphisms: x -> x^(p^k).
|
||||
fn repeated_frobenius(&self, count: usize) -> Self {
|
||||
if count == 0 {
|
||||
return *self;
|
||||
} else if count >= D {
|
||||
return self.repeated_frobenius(count % D);
|
||||
}
|
||||
let arr = self.to_basefield_array();
|
||||
let k = (Self::BaseField::ORDER - 1) / (D as u64);
|
||||
let z0 = Self::W.exp(k * count as u64);
|
||||
let mut res = [Self::BaseField::ZERO; D];
|
||||
for (i, z) in z0.powers().take(D).enumerate() {
|
||||
res[i] = arr[i] * z;
|
||||
}
|
||||
|
||||
Self::from_basefield_array(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Extendable<1> for F {
|
||||
pub trait Extendable<const D: usize>: Field + Sized {
|
||||
type Extension: Field + OEF<D, BaseField = Self> + Frobenius<D> + From<Self>;
|
||||
}
|
||||
|
||||
impl<F: Frobenius<1> + FieldExtension<1, BaseField = F>> Extendable<1> for F {
|
||||
type Extension = F;
|
||||
}
|
||||
|
||||
@ -88,10 +99,6 @@ where
|
||||
{
|
||||
debug_assert_eq!(l.len() % D, 0);
|
||||
l.chunks_exact(D)
|
||||
.map(|c| {
|
||||
let mut arr = [F::ZERO; D];
|
||||
arr.copy_from_slice(c);
|
||||
F::Extension::from_basefield_array(arr)
|
||||
})
|
||||
.map(|c| F::Extension::from_basefield_array(c.to_vec().try_into().unwrap()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi
|
||||
use rand::Rng;
|
||||
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::{FieldExtension, OEF};
|
||||
use crate::field::extension_field::{FieldExtension, Frobenius, OEF};
|
||||
use crate::field::field::Field;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
@ -18,6 +18,8 @@ impl OEF<2> for QuadraticCrandallField {
|
||||
const W: CrandallField = CrandallField(3);
|
||||
}
|
||||
|
||||
impl Frobenius<2> for QuadraticCrandallField {}
|
||||
|
||||
impl FieldExtension<2> for QuadraticCrandallField {
|
||||
type BaseField = CrandallField;
|
||||
|
||||
@ -65,7 +67,7 @@ impl Field for QuadraticCrandallField {
|
||||
return None;
|
||||
}
|
||||
|
||||
let a_pow_r_minus_1 = OEF::<2>::frobenius(self);
|
||||
let a_pow_r_minus_1 = self.frobenius();
|
||||
let a_pow_r = a_pow_r_minus_1 * *self;
|
||||
debug_assert!(FieldExtension::<2>::is_in_basefield(&a_pow_r));
|
||||
|
||||
@ -192,7 +194,7 @@ impl DivAssign for QuadraticCrandallField {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::field::extension_field::quadratic::QuadraticCrandallField;
|
||||
use crate::field::extension_field::{FieldExtension, OEF};
|
||||
use crate::field::extension_field::{FieldExtension, Frobenius, OEF};
|
||||
use crate::field::field::Field;
|
||||
|
||||
#[test]
|
||||
@ -233,7 +235,7 @@ mod tests {
|
||||
let x = F::rand();
|
||||
assert_eq!(
|
||||
x.exp(<F as FieldExtension<2>>::BaseField::ORDER),
|
||||
OEF::<2>::frobenius(&x)
|
||||
x.frobenius()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi
|
||||
use rand::Rng;
|
||||
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::{FieldExtension, OEF};
|
||||
use crate::field::extension_field::{FieldExtension, Frobenius, OEF};
|
||||
use crate::field::field::Field;
|
||||
|
||||
/// A quartic extension of `CrandallField`.
|
||||
@ -20,6 +20,8 @@ impl OEF<4> for QuarticCrandallField {
|
||||
const W: CrandallField = CrandallField(3);
|
||||
}
|
||||
|
||||
impl Frobenius<4> for QuarticCrandallField {}
|
||||
|
||||
impl FieldExtension<4> for QuarticCrandallField {
|
||||
type BaseField = CrandallField;
|
||||
|
||||
@ -93,9 +95,9 @@ impl Field for QuarticCrandallField {
|
||||
return None;
|
||||
}
|
||||
|
||||
let a_pow_p = OEF::<4>::frobenius(self);
|
||||
let a_pow_p = self.frobenius();
|
||||
let a_pow_p_plus_1 = a_pow_p * *self;
|
||||
let a_pow_p3_plus_p2 = OEF::<4>::frobenius(&OEF::<4>::frobenius(&a_pow_p_plus_1));
|
||||
let a_pow_p3_plus_p2 = a_pow_p_plus_1.repeated_frobenius(2);
|
||||
let a_pow_r_minus_1 = a_pow_p3_plus_p2 * a_pow_p;
|
||||
let a_pow_r = a_pow_r_minus_1 * *self;
|
||||
debug_assert!(FieldExtension::<4>::is_in_basefield(&a_pow_r));
|
||||
@ -241,7 +243,7 @@ impl DivAssign for QuarticCrandallField {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::extension_field::{FieldExtension, OEF};
|
||||
use crate::field::extension_field::{FieldExtension, Frobenius, OEF};
|
||||
use crate::field::field::Field;
|
||||
|
||||
fn exp_naive<F: Field>(x: F, power: u128) -> F {
|
||||
@ -292,11 +294,18 @@ mod tests {
|
||||
#[test]
|
||||
fn test_frobenius() {
|
||||
type F = QuarticCrandallField;
|
||||
const D: usize = 4;
|
||||
let x = F::rand();
|
||||
assert_eq!(
|
||||
exp_naive(x, <F as FieldExtension<4>>::BaseField::ORDER as u128),
|
||||
OEF::<4>::frobenius(&x)
|
||||
exp_naive(x, <F as FieldExtension<D>>::BaseField::ORDER as u128),
|
||||
x.frobenius()
|
||||
);
|
||||
for count in 2..D {
|
||||
assert_eq!(
|
||||
x.repeated_frobenius(count),
|
||||
(0..count).fold(x, |acc, _| acc.frobenius())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::algebra::ExtensionAlgebra;
|
||||
use crate::field::extension_field::{Extendable, FieldExtension, OEF};
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::mul_extension::MulExtensionGate;
|
||||
use crate::target::Target;
|
||||
|
||||
/// `Target`s representing an element of an extension field.
|
||||
@ -12,6 +16,50 @@ impl<const D: usize> ExtensionTarget<D> {
|
||||
pub fn to_target_array(&self) -> [Target; D] {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn frobenius<F: Extendable<D>>(&self, builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
self.repeated_frobenius(1, builder)
|
||||
}
|
||||
|
||||
pub fn repeated_frobenius<F: Extendable<D>>(
|
||||
&self,
|
||||
count: usize,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> Self {
|
||||
if count == 0 {
|
||||
return *self;
|
||||
} else if count >= D {
|
||||
return self.repeated_frobenius(count % D, builder);
|
||||
}
|
||||
let arr = self.to_target_array();
|
||||
let k = (F::ORDER - 1) / (D as u64);
|
||||
let z0 = F::W.exp(k * count as u64);
|
||||
let zs = z0
|
||||
.powers()
|
||||
.take(D)
|
||||
.map(|z| builder.constant(z))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut res = Vec::with_capacity(D);
|
||||
for (z, a) in zs.into_iter().zip(arr) {
|
||||
res.push(builder.mul(z, a));
|
||||
}
|
||||
|
||||
res.try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn from_range(gate: usize, range: Range<usize>) -> Self {
|
||||
debug_assert_eq!(range.end - range.start, D);
|
||||
Target::wires_from_range(gate, range).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> TryFrom<Vec<Target>> for ExtensionTarget<D> {
|
||||
type Error = Vec<Target>;
|
||||
|
||||
fn try_from(value: Vec<Target>) -> Result<Self, Self::Error> {
|
||||
Ok(Self(value.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
/// `Target`s representing an element of an extension of an extension field.
|
||||
@ -92,6 +140,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
sum
|
||||
}
|
||||
|
||||
/// TODO: Change this to using an `arithmetic_extension` function once `MulExtensionGate` supports addend.
|
||||
pub fn sub_extension(
|
||||
&mut self,
|
||||
mut a: ExtensionTarget<D>,
|
||||
@ -114,23 +163,31 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
a
|
||||
}
|
||||
|
||||
pub fn mul_extension_with_const(
|
||||
&mut self,
|
||||
const_0: F,
|
||||
multiplicand_0: ExtensionTarget<D>,
|
||||
multiplicand_1: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let gate = self.add_gate(MulExtensionGate::new(), vec![const_0]);
|
||||
|
||||
let wire_multiplicand_0 =
|
||||
ExtensionTarget::from_range(gate, MulExtensionGate::<D>::wires_multiplicand_0());
|
||||
let wire_multiplicand_1 =
|
||||
ExtensionTarget::from_range(gate, MulExtensionGate::<D>::wires_multiplicand_1());
|
||||
let wire_output = ExtensionTarget::from_range(gate, MulExtensionGate::<D>::wires_output());
|
||||
|
||||
self.route_extension(multiplicand_0, wire_multiplicand_0);
|
||||
self.route_extension(multiplicand_1, wire_multiplicand_1);
|
||||
wire_output
|
||||
}
|
||||
|
||||
pub fn mul_extension(
|
||||
&mut self,
|
||||
a: ExtensionTarget<D>,
|
||||
b: ExtensionTarget<D>,
|
||||
multiplicand_0: ExtensionTarget<D>,
|
||||
multiplicand_1: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let mut res = [self.zero(); D];
|
||||
for i in 0..D {
|
||||
for j in 0..D {
|
||||
res[(i + j) % D] = if i + j < D {
|
||||
self.mul_add(a.0[i], b.0[j], res[(i + j) % D])
|
||||
} else {
|
||||
// W * a[i] * b[i] + res[(i + j) % D]
|
||||
self.arithmetic(F::Extension::W, a.0[i], b.0[i], F::ONE, res[(i + j) % D])
|
||||
}
|
||||
}
|
||||
}
|
||||
ExtensionTarget(res)
|
||||
self.mul_extension_with_const(F::ONE, multiplicand_0, multiplicand_1)
|
||||
}
|
||||
|
||||
pub fn mul_ext_algebra(
|
||||
@ -164,6 +221,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
/// Like `mul_add`, but for `ExtensionTarget`s. Note that, unlike `mul_add`, this has no
|
||||
/// performance benefit over separate muls and adds.
|
||||
/// TODO: Change this to using an `arithmetic_extension` function once `MulExtensionGate` supports addend.
|
||||
pub fn mul_add_extension(
|
||||
&mut self,
|
||||
a: ExtensionTarget<D>,
|
||||
@ -174,12 +232,23 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.add_extension(product, c)
|
||||
}
|
||||
|
||||
/// Like `mul_sub`, but for `ExtensionTarget`s. Note that, unlike `mul_sub`, this has no
|
||||
/// performance benefit over separate muls and subs.
|
||||
/// TODO: Change this to using an `arithmetic_extension` function once `MulExtensionGate` supports addend.
|
||||
pub fn scalar_mul_sub_extension(
|
||||
&mut self,
|
||||
a: Target,
|
||||
b: ExtensionTarget<D>,
|
||||
c: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let product = self.scalar_mul_ext(a, b);
|
||||
self.sub_extension(product, c)
|
||||
}
|
||||
|
||||
/// Returns `a * b`, where `b` is in the extension field and `a` is in the base field.
|
||||
pub fn scalar_mul_ext(&mut self, a: Target, mut b: ExtensionTarget<D>) -> ExtensionTarget<D> {
|
||||
for i in 0..D {
|
||||
b.0[i] = self.mul(a, b.0[i]);
|
||||
}
|
||||
b
|
||||
pub fn scalar_mul_ext(&mut self, a: Target, b: ExtensionTarget<D>) -> ExtensionTarget<D> {
|
||||
let a_ext = self.convert_to_ext(a);
|
||||
self.mul_extension(a_ext, b)
|
||||
}
|
||||
|
||||
/// Returns `a * b`, where `b` is in the extension of the extension field, and `a` is in the
|
||||
@ -194,4 +263,26 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
b
|
||||
}
|
||||
|
||||
pub fn convert_to_ext(&mut self, t: Target) -> ExtensionTarget<D> {
|
||||
let zero = self.zero();
|
||||
let mut arr = [zero; D];
|
||||
arr[0] = t;
|
||||
ExtensionTarget(arr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Flatten the slice by sending every extension target to its D-sized canonical representation.
|
||||
pub fn flatten_target<const D: usize>(l: &[ExtensionTarget<D>]) -> Vec<Target> {
|
||||
l.iter()
|
||||
.flat_map(|x| x.to_target_array().to_vec())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Batch every D-sized chunks into extension targets.
|
||||
pub fn unflatten_target<F: Extendable<D>, const D: usize>(l: &[Target]) -> Vec<ExtensionTarget<D>> {
|
||||
debug_assert_eq!(l.len() % D, 0);
|
||||
l.chunks_exact(D)
|
||||
.map(|c| c.to_vec().try_into().unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi
|
||||
use num::Integer;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::field::extension_field::Frobenius;
|
||||
use crate::util::bits_u64;
|
||||
|
||||
/// A finite field with prime order less than 2^64.
|
||||
@ -283,3 +284,17 @@ impl<F: Field> Iterator for Powers<F> {
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Powers<F> {
|
||||
/// Apply the Frobenius automorphism `k` times.
|
||||
pub fn repeated_frobenius<const D: usize>(self, k: usize) -> Self
|
||||
where
|
||||
F: Frobenius<D>,
|
||||
{
|
||||
let Self { base, current } = self;
|
||||
Self {
|
||||
base: base.repeated_frobenius(k),
|
||||
current: current.repeated_frobenius(k),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,11 +65,23 @@ pub fn barycentric_weights<F: Field>(points: &[(F, F)]) -> Vec<F> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Interpolate the linear polynomial passing through `points` on `x`.
|
||||
pub fn interpolate2<F: Field>(points: [(F, F); 2], x: F) -> F {
|
||||
// a0 -> a1
|
||||
// b0 -> b1
|
||||
// x -> a1 + (x-a0)*(b1-a1)/(b0-a0)
|
||||
let (a0, a1) = points[0];
|
||||
let (b0, b1) = points[1];
|
||||
assert_ne!(a0, b0);
|
||||
a1 + (x - a0) * (b1 - a1) / (b0 - a0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::field::Field;
|
||||
use crate::field::lagrange::interpolant;
|
||||
use crate::polynomial::polynomial::PolynomialCoeffs;
|
||||
|
||||
#[test]
|
||||
@ -120,4 +132,18 @@ mod tests {
|
||||
fn eval_naive<F: Field>(coeffs: &PolynomialCoeffs<F>, domain: &[F]) -> Vec<(F, F)> {
|
||||
domain.iter().map(|&x| (x, coeffs.eval(x))).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interpolate2() {
|
||||
type F = QuarticCrandallField;
|
||||
let points = [(F::rand(), F::rand()), (F::rand(), F::rand())];
|
||||
let x = F::rand();
|
||||
|
||||
let ev0 = interpolant(&points).eval(x);
|
||||
let ev1 = interpolate(&points, x, &barycentric_weights(&points));
|
||||
let ev2 = interpolate2(points, x);
|
||||
|
||||
assert_eq!(ev0, ev1);
|
||||
assert_eq!(ev0, ev2);
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@ pub mod crandall_field;
|
||||
pub mod extension_field;
|
||||
pub mod fft;
|
||||
pub mod field;
|
||||
pub(crate) mod lagrange;
|
||||
pub(crate) mod interpolation;
|
||||
|
||||
#[cfg(test)]
|
||||
mod field_testing;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use crate::polynomial::commitment::SALT_SIZE;
|
||||
|
||||
pub mod prover;
|
||||
mod recursive_verifier;
|
||||
pub mod verifier;
|
||||
|
||||
/// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns,
|
||||
@ -21,20 +20,6 @@ pub struct FriConfig {
|
||||
|
||||
/// Number of query rounds to perform.
|
||||
pub num_query_rounds: usize,
|
||||
|
||||
/// Vector of the same length as the number of initial Merkle trees.
|
||||
/// `blinding[i]==true` iff the i-th tree is salted.
|
||||
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 {
|
||||
|
||||
@ -107,7 +107,7 @@ fn fri_proof_of_work<F: Field>(current_hash: Hash<F>, config: &FriConfig) -> F {
|
||||
)
|
||||
.to_canonical_u64()
|
||||
.leading_zeros()
|
||||
>= config.proof_of_work_bits
|
||||
>= config.proof_of_work_bits + F::ORDER.leading_zeros()
|
||||
})
|
||||
.map(F::from_canonical_u64)
|
||||
.expect("Proof of work failed.")
|
||||
|
||||
359
src/fri/recursive_verifier.rs
Normal file
359
src/fri/recursive_verifier.rs
Normal file
@ -0,0 +1,359 @@
|
||||
use itertools::izip;
|
||||
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::{flatten_target, ExtensionTarget};
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field::Field;
|
||||
use crate::fri::FriConfig;
|
||||
use crate::plonk_challenger::RecursiveChallenger;
|
||||
use crate::plonk_common::PlonkPolynomials;
|
||||
use crate::proof::{
|
||||
FriInitialTreeProofTarget, FriProofTarget, FriQueryRoundTarget, HashTarget, OpeningSetTarget,
|
||||
};
|
||||
use crate::target::Target;
|
||||
use crate::util::{log2_strict, reverse_index_bits_in_place};
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// 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.
|
||||
fn compute_evaluation(
|
||||
&mut self,
|
||||
x: Target,
|
||||
old_x_index: Target,
|
||||
arity_bits: usize,
|
||||
last_evals: &[ExtensionTarget<D>],
|
||||
beta: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
debug_assert_eq!(last_evals.len(), 1 << arity_bits);
|
||||
|
||||
let g = F::primitive_root_of_unity(arity_bits);
|
||||
|
||||
// The evaluation vector needs to be reordered first.
|
||||
let mut evals = last_evals.to_vec();
|
||||
reverse_index_bits_in_place(&mut evals);
|
||||
let mut old_x_index_bits = self.split_le(old_x_index, arity_bits);
|
||||
old_x_index_bits.reverse();
|
||||
let evals = self.rotate_left_from_bits(&old_x_index_bits, &evals);
|
||||
|
||||
// The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta.
|
||||
let points = g
|
||||
.powers()
|
||||
.map(|y| {
|
||||
let yt = self.constant(y);
|
||||
self.mul(x, yt)
|
||||
})
|
||||
.zip(evals)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.interpolate(&points, beta)
|
||||
}
|
||||
|
||||
fn fri_verify_proof_of_work(
|
||||
&mut self,
|
||||
proof: &FriProofTarget<D>,
|
||||
challenger: &mut RecursiveChallenger,
|
||||
config: &FriConfig,
|
||||
) {
|
||||
let mut inputs = challenger.get_hash(self).elements.to_vec();
|
||||
inputs.push(proof.pow_witness);
|
||||
|
||||
let hash = self.hash_n_to_m(inputs, 1, false)[0];
|
||||
self.assert_leading_zeros(hash, config.proof_of_work_bits + F::ORDER.leading_zeros());
|
||||
}
|
||||
|
||||
pub fn verify_fri_proof(
|
||||
&mut self,
|
||||
purported_degree_log: usize,
|
||||
// Openings of the PLONK polynomials.
|
||||
os: &OpeningSetTarget<D>,
|
||||
// Point at which the PLONK polynomials are opened.
|
||||
zeta: ExtensionTarget<D>,
|
||||
// Scaling factor to combine polynomials.
|
||||
alpha: ExtensionTarget<D>,
|
||||
initial_merkle_roots: &[HashTarget],
|
||||
proof: &FriProofTarget<D>,
|
||||
challenger: &mut RecursiveChallenger,
|
||||
config: &FriConfig,
|
||||
) {
|
||||
let total_arities = config.reduction_arity_bits.iter().sum::<usize>();
|
||||
debug_assert_eq!(
|
||||
purported_degree_log,
|
||||
log2_strict(proof.final_poly.len()) + total_arities - config.rate_bits,
|
||||
"Final polynomial has wrong degree."
|
||||
);
|
||||
|
||||
// Size of the LDE domain.
|
||||
let n = proof.final_poly.len() << total_arities;
|
||||
|
||||
// Recover the random betas used in the FRI reductions.
|
||||
let betas = proof
|
||||
.commit_phase_merkle_roots
|
||||
.iter()
|
||||
.map(|root| {
|
||||
challenger.observe_hash(root);
|
||||
challenger.get_extension_challenge(self)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
challenger.observe_extension_elements(&proof.final_poly.0);
|
||||
|
||||
// Check PoW.
|
||||
self.fri_verify_proof_of_work(proof, challenger, config);
|
||||
|
||||
// Check that parameters are coherent.
|
||||
debug_assert_eq!(
|
||||
config.num_query_rounds,
|
||||
proof.query_round_proofs.len(),
|
||||
"Number of query rounds does not match config."
|
||||
);
|
||||
debug_assert!(
|
||||
!config.reduction_arity_bits.is_empty(),
|
||||
"Number of reductions should be non-zero."
|
||||
);
|
||||
|
||||
for round_proof in &proof.query_round_proofs {
|
||||
self.fri_verifier_query_round(
|
||||
os,
|
||||
zeta,
|
||||
alpha,
|
||||
initial_merkle_roots,
|
||||
&proof,
|
||||
challenger,
|
||||
n,
|
||||
&betas,
|
||||
round_proof,
|
||||
config,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn fri_verify_initial_proof(
|
||||
&mut self,
|
||||
x_index: Target,
|
||||
proof: &FriInitialTreeProofTarget,
|
||||
initial_merkle_roots: &[HashTarget],
|
||||
) {
|
||||
for ((evals, merkle_proof), &root) in proof.evals_proofs.iter().zip(initial_merkle_roots) {
|
||||
self.verify_merkle_proof(evals.clone(), x_index, root, merkle_proof);
|
||||
}
|
||||
}
|
||||
|
||||
fn fri_combine_initial(
|
||||
&mut self,
|
||||
proof: &FriInitialTreeProofTarget,
|
||||
alpha: ExtensionTarget<D>,
|
||||
os: &OpeningSetTarget<D>,
|
||||
zeta: ExtensionTarget<D>,
|
||||
subgroup_x: Target,
|
||||
) -> ExtensionTarget<D> {
|
||||
assert!(D > 1, "Not implemented for D=1.");
|
||||
let config = &self.config.fri_config.clone();
|
||||
let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits;
|
||||
let subgroup_x = self.convert_to_ext(subgroup_x);
|
||||
let mut alpha_powers = self.powers(alpha);
|
||||
let mut sum = self.zero_extension();
|
||||
|
||||
// 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 `x.frobenius()`
|
||||
|
||||
// Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials.
|
||||
let single_evals = [
|
||||
PlonkPolynomials::CONSTANTS,
|
||||
PlonkPolynomials::SIGMAS,
|
||||
PlonkPolynomials::QUOTIENT,
|
||||
]
|
||||
.iter()
|
||||
.flat_map(|&p| proof.unsalted_evals(p))
|
||||
.map(|&e| self.convert_to_ext(e))
|
||||
.collect::<Vec<_>>();
|
||||
let single_openings = os
|
||||
.constants
|
||||
.iter()
|
||||
.chain(&os.plonk_sigmas)
|
||||
.chain(&os.quotient_polys);
|
||||
let mut single_numerator = self.zero_extension();
|
||||
for (e, &o) in izip!(single_evals, single_openings) {
|
||||
let a = alpha_powers.next(self);
|
||||
let diff = self.sub_extension(e, o);
|
||||
single_numerator = self.mul_add_extension(a, diff, single_numerator);
|
||||
}
|
||||
let single_denominator = self.sub_extension(subgroup_x, zeta);
|
||||
let quotient = self.div_unsafe_extension(single_numerator, single_denominator);
|
||||
sum = self.add_extension(sum, quotient);
|
||||
|
||||
// Polynomials opened at `x` and `g x`, i.e., the Zs polynomials.
|
||||
let zs_evals = proof
|
||||
.unsalted_evals(PlonkPolynomials::ZS)
|
||||
.iter()
|
||||
.map(|&e| self.convert_to_ext(e))
|
||||
.collect::<Vec<_>>();
|
||||
// TODO: Would probably be more efficient using `CircuitBuilder::reduce_with_powers_recursive`
|
||||
let mut zs_composition_eval = self.zero_extension();
|
||||
let mut alpha_powers_cloned = alpha_powers.clone();
|
||||
for &e in &zs_evals {
|
||||
let a = alpha_powers_cloned.next(self);
|
||||
zs_composition_eval = self.mul_add_extension(a, e, zs_composition_eval);
|
||||
}
|
||||
|
||||
let g = self.constant_extension(F::Extension::primitive_root_of_unity(degree_log));
|
||||
let zeta_right = self.mul_extension(g, zeta);
|
||||
let mut zs_ev_zeta = self.zero_extension();
|
||||
let mut alpha_powers_cloned = alpha_powers.clone();
|
||||
for &t in &os.plonk_zs {
|
||||
let a = alpha_powers_cloned.next(self);
|
||||
zs_ev_zeta = self.mul_add_extension(a, t, zs_ev_zeta);
|
||||
}
|
||||
let mut zs_ev_zeta_right = self.zero_extension();
|
||||
for &t in &os.plonk_zs_right {
|
||||
let a = alpha_powers.next(self);
|
||||
zs_ev_zeta_right = self.mul_add_extension(a, t, zs_ev_zeta);
|
||||
}
|
||||
let interpol_val = self.interpolate2(
|
||||
[(zeta, zs_ev_zeta), (zeta_right, zs_ev_zeta_right)],
|
||||
subgroup_x,
|
||||
);
|
||||
let zs_numerator = self.sub_extension(zs_composition_eval, interpol_val);
|
||||
let vanish_zeta = self.sub_extension(subgroup_x, zeta);
|
||||
let vanish_zeta_right = self.sub_extension(subgroup_x, zeta_right);
|
||||
let zs_denominator = self.mul_extension(vanish_zeta, vanish_zeta_right);
|
||||
let zs_quotient = self.div_unsafe_extension(zs_numerator, zs_denominator);
|
||||
sum = self.add_extension(sum, zs_quotient);
|
||||
|
||||
// Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials.
|
||||
let wire_evals = proof
|
||||
.unsalted_evals(PlonkPolynomials::WIRES)
|
||||
.iter()
|
||||
.map(|&e| self.convert_to_ext(e))
|
||||
.collect::<Vec<_>>();
|
||||
let mut wire_composition_eval = self.zero_extension();
|
||||
let mut alpha_powers_cloned = alpha_powers.clone();
|
||||
for &e in &wire_evals {
|
||||
let a = alpha_powers_cloned.next(self);
|
||||
wire_composition_eval = self.mul_add_extension(a, e, wire_composition_eval);
|
||||
}
|
||||
let mut alpha_powers_cloned = alpha_powers.clone();
|
||||
let wire_eval = os.wires.iter().fold(self.zero_extension(), |acc, &w| {
|
||||
let a = alpha_powers_cloned.next(self);
|
||||
self.mul_add_extension(a, w, acc)
|
||||
});
|
||||
let mut alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1, self);
|
||||
let wire_eval_frob = os
|
||||
.wires
|
||||
.iter()
|
||||
.fold(self.zero_extension(), |acc, &w| {
|
||||
let a = alpha_powers_frob.next(self);
|
||||
self.mul_add_extension(a, w, acc)
|
||||
})
|
||||
.frobenius(self);
|
||||
let zeta_frob = zeta.frobenius(self);
|
||||
let wire_interpol_val =
|
||||
self.interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)], subgroup_x);
|
||||
let wire_numerator = self.sub_extension(wire_composition_eval, wire_interpol_val);
|
||||
let vanish_zeta_frob = self.sub_extension(subgroup_x, zeta_frob);
|
||||
let wire_denominator = self.mul_extension(vanish_zeta, vanish_zeta_frob);
|
||||
let wire_quotient = self.div_unsafe_extension(wire_numerator, wire_denominator);
|
||||
sum = self.add_extension(sum, wire_quotient);
|
||||
|
||||
sum
|
||||
}
|
||||
|
||||
fn fri_verifier_query_round(
|
||||
&mut self,
|
||||
os: &OpeningSetTarget<D>,
|
||||
zeta: ExtensionTarget<D>,
|
||||
alpha: ExtensionTarget<D>,
|
||||
initial_merkle_roots: &[HashTarget],
|
||||
proof: &FriProofTarget<D>,
|
||||
challenger: &mut RecursiveChallenger,
|
||||
n: usize,
|
||||
betas: &[ExtensionTarget<D>],
|
||||
round_proof: &FriQueryRoundTarget<D>,
|
||||
config: &FriConfig,
|
||||
) {
|
||||
let n_log = log2_strict(n);
|
||||
let mut evaluations: Vec<Vec<ExtensionTarget<D>>> = Vec::new();
|
||||
// TODO: Do we need to range check `x_index` to a target smaller than `p`?
|
||||
let mut x_index = challenger.get_challenge(self);
|
||||
x_index = self.split_low_high(x_index, n_log, 64).0;
|
||||
let mut x_index_num_bits = n_log;
|
||||
let mut domain_size = n;
|
||||
self.fri_verify_initial_proof(
|
||||
x_index,
|
||||
&round_proof.initial_trees_proof,
|
||||
initial_merkle_roots,
|
||||
);
|
||||
let mut old_x_index = self.zero();
|
||||
// `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain.
|
||||
let g = self.constant(F::MULTIPLICATIVE_GROUP_GENERATOR);
|
||||
let phi = self.constant(F::primitive_root_of_unity(n_log));
|
||||
|
||||
let reversed_x = self.reverse_limbs::<2>(x_index, n_log);
|
||||
let phi = self.exp(phi, reversed_x, n_log);
|
||||
let mut subgroup_x = self.mul(g, phi);
|
||||
|
||||
for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() {
|
||||
let next_domain_size = domain_size >> arity_bits;
|
||||
let e_x = if i == 0 {
|
||||
self.fri_combine_initial(
|
||||
&round_proof.initial_trees_proof,
|
||||
alpha,
|
||||
os,
|
||||
zeta,
|
||||
subgroup_x,
|
||||
)
|
||||
} else {
|
||||
let last_evals = &evaluations[i - 1];
|
||||
// Infer P(y) from {P(x)}_{x^arity=y}.
|
||||
self.compute_evaluation(
|
||||
subgroup_x,
|
||||
old_x_index,
|
||||
config.reduction_arity_bits[i - 1],
|
||||
last_evals,
|
||||
betas[i - 1],
|
||||
)
|
||||
};
|
||||
let mut evals = round_proof.steps[i].evals.clone();
|
||||
// Insert P(y) into the evaluation vector, since it wasn't included by the prover.
|
||||
let (low_x_index, high_x_index) =
|
||||
self.split_low_high(x_index, arity_bits, x_index_num_bits);
|
||||
evals = self.insert(low_x_index, e_x, evals);
|
||||
evaluations.push(evals);
|
||||
self.verify_merkle_proof(
|
||||
flatten_target(&evaluations[i]),
|
||||
high_x_index,
|
||||
proof.commit_phase_merkle_roots[i],
|
||||
&round_proof.steps[i].merkle_proof,
|
||||
);
|
||||
|
||||
if i > 0 {
|
||||
// Update the point x to x^arity.
|
||||
for _ in 0..config.reduction_arity_bits[i - 1] {
|
||||
subgroup_x = self.square(subgroup_x);
|
||||
}
|
||||
}
|
||||
domain_size = next_domain_size;
|
||||
old_x_index = low_x_index;
|
||||
x_index = high_x_index;
|
||||
x_index_num_bits -= arity_bits;
|
||||
}
|
||||
|
||||
let last_evals = evaluations.last().unwrap();
|
||||
let final_arity_bits = *config.reduction_arity_bits.last().unwrap();
|
||||
let purported_eval = self.compute_evaluation(
|
||||
subgroup_x,
|
||||
old_x_index,
|
||||
final_arity_bits,
|
||||
last_evals,
|
||||
*betas.last().unwrap(),
|
||||
);
|
||||
for _ in 0..final_arity_bits {
|
||||
subgroup_x = self.square(subgroup_x);
|
||||
}
|
||||
|
||||
// Final check of FRI. After all the reductions, we check that the final polynomial is equal
|
||||
// to the one sent by the prover.
|
||||
let eval = proof.final_poly.eval_scalar(self, subgroup_x);
|
||||
self.assert_equal_extension(eval, purported_eval);
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,15 @@
|
||||
use anyhow::{ensure, Result};
|
||||
|
||||
use crate::field::extension_field::{flatten, Extendable, FieldExtension, OEF};
|
||||
use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius};
|
||||
use crate::field::field::Field;
|
||||
use crate::field::lagrange::{barycentric_weights, interpolant, interpolate};
|
||||
use crate::field::interpolation::{barycentric_weights, interpolate, interpolate2};
|
||||
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_iter;
|
||||
use crate::plonk_common::PlonkPolynomials;
|
||||
use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet};
|
||||
use crate::util::scaling::ReducingFactor;
|
||||
use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place};
|
||||
|
||||
/// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity
|
||||
@ -151,59 +152,77 @@ fn fri_combine_initial<F: Field + Extendable<D>, const D: usize>(
|
||||
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 alpha = ReducingFactor::new(alpha);
|
||||
let mut sum = F::Extension::ZERO;
|
||||
|
||||
// We will add three terms to `sum`:
|
||||
// - one for various polynomials which are opened at a single point `x`
|
||||
// - one for Zs, which are opened at `x` and `g x`
|
||||
// - one for wire polynomials, which are opened at `x` and its conjugate
|
||||
// - one for wire polynomials, which are opened at `x` and `x.frobenius()`
|
||||
|
||||
let single_evals = [0, 1, 4]
|
||||
.iter()
|
||||
.flat_map(|&i| proof.unsalted_evals(i, config))
|
||||
.map(|&e| F::Extension::from_basefield(e));
|
||||
// Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials.
|
||||
let single_evals = [
|
||||
PlonkPolynomials::CONSTANTS,
|
||||
PlonkPolynomials::SIGMAS,
|
||||
PlonkPolynomials::QUOTIENT,
|
||||
]
|
||||
.iter()
|
||||
.flat_map(|&p| proof.unsalted_evals(p))
|
||||
.map(|&e| F::Extension::from_basefield(e));
|
||||
let single_openings = os
|
||||
.constants
|
||||
.iter()
|
||||
.chain(&os.plonk_s_sigmas)
|
||||
.chain(&os.quotient_polys);
|
||||
let single_diffs = single_evals.zip(single_openings).map(|(e, &o)| e - o);
|
||||
let single_numerator = reduce_with_iter(single_diffs, &mut alpha_powers);
|
||||
let single_diffs = single_evals
|
||||
.into_iter()
|
||||
.zip(single_openings)
|
||||
.map(|(e, &o)| e - o)
|
||||
.collect::<Vec<_>>();
|
||||
let single_numerator = alpha.reduce(single_diffs.iter());
|
||||
let single_denominator = subgroup_x - zeta;
|
||||
sum += single_numerator / single_denominator;
|
||||
alpha.reset();
|
||||
|
||||
// Polynomials opened at `x` and `g x`, i.e., the Zs polynomials.
|
||||
let zs_evals = proof
|
||||
.unsalted_evals(3, config)
|
||||
.unsalted_evals(PlonkPolynomials::ZS)
|
||||
.iter()
|
||||
.map(|&e| F::Extension::from_basefield(e));
|
||||
let zs_composition_eval = reduce_with_iter(zs_evals, alpha_powers.clone());
|
||||
let zs_composition_eval = alpha.clone().reduce(zs_evals);
|
||||
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 zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x);
|
||||
let zs_interpol = interpolate2(
|
||||
[
|
||||
(zeta, alpha.clone().reduce(os.plonk_zs.iter())),
|
||||
(zeta_right, alpha.reduce(os.plonk_zs_right.iter())),
|
||||
],
|
||||
subgroup_x,
|
||||
);
|
||||
let zs_numerator = zs_composition_eval - zs_interpol;
|
||||
let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right);
|
||||
sum = alpha.shift(sum);
|
||||
sum += zs_numerator / zs_denominator;
|
||||
|
||||
// Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials.
|
||||
let wire_evals = proof
|
||||
.unsalted_evals(2, config)
|
||||
.unsalted_evals(PlonkPolynomials::WIRES)
|
||||
.iter()
|
||||
.map(|&e| F::Extension::from_basefield(e));
|
||||
let wire_composition_eval = reduce_with_iter(wire_evals, alpha_powers.clone());
|
||||
let wire_composition_eval = alpha.clone().reduce(wire_evals);
|
||||
let zeta_frob = zeta.frobenius();
|
||||
let wire_evals_frob = os.wires.iter().map(|e| e.frobenius());
|
||||
let wires_interpol = interpolant(&[
|
||||
(zeta, reduce_with_iter(&os.wires, alpha_powers.clone())),
|
||||
(zeta_frob, reduce_with_iter(wire_evals_frob, alpha_powers)),
|
||||
]);
|
||||
let wires_numerator = wire_composition_eval - wires_interpol.eval(subgroup_x);
|
||||
let wires_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob);
|
||||
sum += wires_numerator / wires_denominator;
|
||||
let mut alpha_frob = alpha.repeated_frobenius(D - 1);
|
||||
let wire_eval = alpha.reduce(os.wires.iter());
|
||||
// We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism.
|
||||
// Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities:
|
||||
// `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)`
|
||||
// So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha,
|
||||
// and one call at the end of the sum.
|
||||
let wire_eval_frob = alpha_frob.reduce(os.wires.iter()).frobenius();
|
||||
let wire_interpol = interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)], subgroup_x);
|
||||
let wire_numerator = wire_composition_eval - wire_interpol;
|
||||
let wire_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob);
|
||||
sum = alpha.shift(sum);
|
||||
sum += wire_numerator / wire_denominator;
|
||||
|
||||
sum
|
||||
}
|
||||
@ -276,7 +295,7 @@ fn fri_verifier_query_round<F: Field + Extendable<D>, const D: usize>(
|
||||
}
|
||||
}
|
||||
domain_size = next_domain_size;
|
||||
old_x_index = x_index;
|
||||
old_x_index = x_index & (arity - 1);
|
||||
x_index >>= arity_bits;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::{Extendable, FieldExtension};
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::arithmetic::ArithmeticGate;
|
||||
use crate::gates::mul_extension::MulExtensionGate;
|
||||
use crate::generator::SimpleGenerator;
|
||||
use crate::target::Target;
|
||||
use crate::wire::Wire;
|
||||
@ -169,6 +173,22 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
product
|
||||
}
|
||||
|
||||
// TODO: Optimize this, maybe with a new gate.
|
||||
/// Exponentiate `base` to the power of `exponent`, where `exponent < 2^num_bits`.
|
||||
pub fn exp(&mut self, base: Target, exponent: Target, num_bits: usize) -> Target {
|
||||
let mut current = base;
|
||||
let one = self.one();
|
||||
let mut product = one;
|
||||
let exponent_bits = self.split_le(exponent, num_bits);
|
||||
|
||||
for bit in exponent_bits.into_iter() {
|
||||
product = self.mul_many(&[bit, current, product]);
|
||||
current = self.mul(current, current);
|
||||
}
|
||||
|
||||
product
|
||||
}
|
||||
|
||||
/// Computes `q = x / y` by witnessing `q` and requiring that `q * y = x`. This can be unsafe in
|
||||
/// some cases, as it allows `0 / 0 = <anything>`.
|
||||
pub fn div_unsafe(&mut self, x: Target, y: Target) -> Target {
|
||||
@ -224,6 +244,38 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
q
|
||||
}
|
||||
|
||||
/// Computes `q = x / y` by witnessing `q` and requiring that `q * y = x`. This can be unsafe in
|
||||
/// some cases, as it allows `0 / 0 = <anything>`.
|
||||
pub fn div_unsafe_extension(
|
||||
&mut self,
|
||||
x: ExtensionTarget<D>,
|
||||
y: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
// Add an `ArithmeticGate` to compute `q * y`.
|
||||
let gate = self.add_gate(MulExtensionGate::new(), vec![F::ONE]);
|
||||
|
||||
let multiplicand_0 =
|
||||
Target::wires_from_range(gate, MulExtensionGate::<D>::wires_multiplicand_0());
|
||||
let multiplicand_0 = ExtensionTarget(multiplicand_0.try_into().unwrap());
|
||||
let multiplicand_1 =
|
||||
Target::wires_from_range(gate, MulExtensionGate::<D>::wires_multiplicand_1());
|
||||
let multiplicand_1 = ExtensionTarget(multiplicand_1.try_into().unwrap());
|
||||
let output = Target::wires_from_range(gate, MulExtensionGate::<D>::wires_output());
|
||||
let output = ExtensionTarget(output.try_into().unwrap());
|
||||
|
||||
self.add_generator(QuotientGeneratorExtension {
|
||||
numerator: x,
|
||||
denominator: y,
|
||||
quotient: multiplicand_0,
|
||||
});
|
||||
|
||||
self.route_extension(y, multiplicand_1);
|
||||
|
||||
self.assert_equal_extension(output, x);
|
||||
|
||||
multiplicand_0
|
||||
}
|
||||
}
|
||||
|
||||
struct QuotientGenerator {
|
||||
@ -243,3 +295,106 @@ impl<F: Field> SimpleGenerator<F> for QuotientGenerator {
|
||||
PartialWitness::singleton_target(self.quotient, num / den)
|
||||
}
|
||||
}
|
||||
|
||||
struct QuotientGeneratorExtension<const D: usize> {
|
||||
numerator: ExtensionTarget<D>,
|
||||
denominator: ExtensionTarget<D>,
|
||||
quotient: ExtensionTarget<D>,
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> SimpleGenerator<F> for QuotientGeneratorExtension<D> {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
let mut deps = self.numerator.to_target_array().to_vec();
|
||||
deps.extend(&self.denominator.to_target_array());
|
||||
deps
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>) -> PartialWitness<F> {
|
||||
let num = witness.get_extension_target(self.numerator);
|
||||
let dem = witness.get_extension_target(self.denominator);
|
||||
let quotient = num / dem;
|
||||
let mut pw = PartialWitness::new();
|
||||
for i in 0..D {
|
||||
pw.set_target(
|
||||
self.quotient.to_target_array()[i],
|
||||
quotient.to_basefield_array()[i],
|
||||
);
|
||||
}
|
||||
pw
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`.
|
||||
#[derive(Clone)]
|
||||
pub struct PowersTarget<const D: usize> {
|
||||
base: ExtensionTarget<D>,
|
||||
current: ExtensionTarget<D>,
|
||||
}
|
||||
|
||||
impl<const D: usize> PowersTarget<D> {
|
||||
pub fn next<F: Extendable<D>>(
|
||||
&mut self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let result = self.current;
|
||||
self.current = builder.mul_extension(self.base, self.current);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn repeated_frobenius<F: Extendable<D>>(
|
||||
self,
|
||||
k: usize,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> Self {
|
||||
let Self { base, current } = self;
|
||||
Self {
|
||||
base: base.repeated_frobenius(k, builder),
|
||||
current: current.repeated_frobenius(k, builder),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
pub fn powers(&mut self, base: ExtensionTarget<D>) -> PowersTarget<D> {
|
||||
PowersTarget {
|
||||
base,
|
||||
current: self.one_extension(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::field::Field;
|
||||
use crate::fri::FriConfig;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
#[test]
|
||||
fn test_div_extension() {
|
||||
type F = CrandallField;
|
||||
type FF = QuarticCrandallField;
|
||||
const D: usize = 4;
|
||||
|
||||
let config = CircuitConfig::large_config();
|
||||
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let x = FF::rand();
|
||||
let y = FF::rand();
|
||||
let x = FF::TWO;
|
||||
let y = FF::ONE;
|
||||
let z = x / y;
|
||||
let xt = builder.constant_extension(x);
|
||||
let yt = builder.constant_extension(y);
|
||||
let zt = builder.constant_extension(z);
|
||||
let comp_zt = builder.div_unsafe_extension(xt, yt);
|
||||
builder.assert_equal_extension(zt, comp_zt);
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(PartialWitness::new());
|
||||
}
|
||||
}
|
||||
|
||||
71
src/gadgets/insert.rs
Normal file
71
src/gadgets/insert.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::target::Target;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Inserts a `Target` in a vector at a non-deterministic index. This is done by rotating to the
|
||||
/// left, inserting at 0 and then rotating to the right.
|
||||
/// Note: `index` is not range-checked.
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
index: Target,
|
||||
element: ExtensionTarget<D>,
|
||||
v: Vec<ExtensionTarget<D>>,
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let mut v = self.rotate_left(index, &v);
|
||||
v.insert(0, element);
|
||||
self.rotate_right(index, &v)
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::field::Field;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
fn real_insert<const D: usize>(
|
||||
index: usize,
|
||||
element: ExtensionTarget<D>,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let mut res = v.to_vec();
|
||||
res.insert(index, element);
|
||||
res
|
||||
}
|
||||
|
||||
fn test_insert_given_len(len_log: usize) {
|
||||
type F = CrandallField;
|
||||
type FF = QuarticCrandallField;
|
||||
let len = 1 << len_log;
|
||||
let config = CircuitConfig::large_config();
|
||||
let mut builder = CircuitBuilder::<F, 4>::new(config);
|
||||
let v = (0..len - 1)
|
||||
.map(|_| builder.constant_extension(FF::rand()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for i in 0..len {
|
||||
let it = builder.constant(F::from_canonical_usize(i));
|
||||
let elem = builder.constant_extension(FF::rand());
|
||||
let inserted = real_insert(i, elem, &v);
|
||||
let purported_inserted = builder.insert(it, elem, v.clone());
|
||||
|
||||
for (x, y) in inserted.into_iter().zip(purported_inserted) {
|
||||
builder.route_extension(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(PartialWitness::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
for len_log in 1..3 {
|
||||
test_insert_given_len(len_log);
|
||||
}
|
||||
}
|
||||
}
|
||||
142
src/gadgets/interpolation.rs
Normal file
142
src/gadgets/interpolation.rs
Normal file
@ -0,0 +1,142 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::interpolation::InterpolationGate;
|
||||
use crate::target::Target;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Interpolate two points. No need for an `InterpolationGate` since the coefficients
|
||||
/// of the linear interpolation polynomial can be easily computed with arithmetic operations.
|
||||
pub fn interpolate2(
|
||||
&mut self,
|
||||
interpolation_points: [(ExtensionTarget<D>, ExtensionTarget<D>); 2],
|
||||
evaluation_point: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
// a0 -> a1
|
||||
// b0 -> b1
|
||||
// x -> a1 + (x-a0)*(b1-a1)/(b0-a0)
|
||||
|
||||
let x_m_a0 = self.sub_extension(evaluation_point, interpolation_points[0].0);
|
||||
let b1_m_a1 = self.sub_extension(interpolation_points[1].1, interpolation_points[0].1);
|
||||
let b0_m_a0 = self.sub_extension(interpolation_points[1].0, interpolation_points[0].0);
|
||||
let quotient = self.div_unsafe_extension(b1_m_a1, b0_m_a0);
|
||||
|
||||
self.mul_add_extension(x_m_a0, quotient, interpolation_points[0].1)
|
||||
}
|
||||
|
||||
/// Interpolate a list of point/evaluation pairs at a given point.
|
||||
/// Returns the evaluation of the interpolated polynomial at `evaluation_point`.
|
||||
pub fn interpolate(
|
||||
&mut self,
|
||||
interpolation_points: &[(Target, ExtensionTarget<D>)],
|
||||
evaluation_point: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let gate = InterpolationGate::<F, D> {
|
||||
num_points: interpolation_points.len(),
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
let gate_index =
|
||||
self.add_gate_no_constants(InterpolationGate::new(interpolation_points.len()));
|
||||
for (i, &(p, v)) in interpolation_points.iter().enumerate() {
|
||||
self.route(p, Target::wire(gate_index, gate.wire_point(i)));
|
||||
self.route_extension(
|
||||
v,
|
||||
ExtensionTarget::from_range(gate_index, gate.wires_value(i)),
|
||||
);
|
||||
}
|
||||
self.route_extension(
|
||||
evaluation_point,
|
||||
ExtensionTarget::from_range(gate_index, gate.wires_evaluation_point()),
|
||||
);
|
||||
|
||||
ExtensionTarget::from_range(gate_index, gate.wires_evaluation_value())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::TryInto;
|
||||
|
||||
use super::*;
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::extension_field::FieldExtension;
|
||||
use crate::field::field::Field;
|
||||
use crate::field::interpolation::{interpolant, interpolate};
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
#[test]
|
||||
fn test_interpolate() {
|
||||
type F = CrandallField;
|
||||
type FF = QuarticCrandallField;
|
||||
let config = CircuitConfig {
|
||||
num_routed_wires: 18,
|
||||
..CircuitConfig::large_config()
|
||||
};
|
||||
let mut builder = CircuitBuilder::<F, 4>::new(config);
|
||||
|
||||
let len = 2;
|
||||
let points = (0..len)
|
||||
.map(|_| (F::rand(), FF::rand()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let homogeneous_points = points
|
||||
.iter()
|
||||
.map(|&(a, b)| (<FF as FieldExtension<4>>::from_basefield(a), b))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let true_interpolant = interpolant(&homogeneous_points);
|
||||
|
||||
let z = FF::rand();
|
||||
let true_eval = true_interpolant.eval(z);
|
||||
|
||||
let points_target = points
|
||||
.iter()
|
||||
.map(|&(p, v)| (builder.constant(p), builder.constant_extension(v)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zt = builder.constant_extension(z);
|
||||
|
||||
let eval = builder.interpolate(&points_target, zt);
|
||||
let true_eval_target = builder.constant_extension(true_eval);
|
||||
builder.assert_equal_extension(eval, true_eval_target);
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(PartialWitness::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interpolate2() {
|
||||
type F = CrandallField;
|
||||
type FF = QuarticCrandallField;
|
||||
let config = CircuitConfig::large_config();
|
||||
let mut builder = CircuitBuilder::<F, 4>::new(config);
|
||||
|
||||
let len = 2;
|
||||
let points = (0..len)
|
||||
.map(|_| (FF::rand(), FF::rand()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let true_interpolant = interpolant(&points);
|
||||
|
||||
let z = FF::rand();
|
||||
let true_eval = true_interpolant.eval(z);
|
||||
|
||||
let points_target = points
|
||||
.iter()
|
||||
.map(|&(p, v)| (builder.constant_extension(p), builder.constant_extension(v)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zt = builder.constant_extension(z);
|
||||
|
||||
let eval = builder.interpolate2(points_target.try_into().unwrap(), zt);
|
||||
let true_eval_target = builder.constant_extension(true_eval);
|
||||
builder.assert_equal_extension(eval, true_eval_target);
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(PartialWitness::new());
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,9 @@
|
||||
pub mod arithmetic;
|
||||
pub mod hash;
|
||||
pub mod insert;
|
||||
pub mod interpolation;
|
||||
pub mod polynomial;
|
||||
pub mod range_check;
|
||||
pub mod rotate;
|
||||
pub mod split_base;
|
||||
pub(crate) mod split_join;
|
||||
|
||||
@ -6,6 +6,10 @@ use crate::target::Target;
|
||||
pub struct PolynomialCoeffsExtTarget<const D: usize>(pub Vec<ExtensionTarget<D>>);
|
||||
|
||||
impl<const D: usize> PolynomialCoeffsExtTarget<D> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn eval_scalar<F: Extendable<D>>(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
|
||||
63
src/gadgets/range_check.rs
Normal file
63
src/gadgets/range_check.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::generator::SimpleGenerator;
|
||||
use crate::target::Target;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Checks that `x < 2^n_log` using a `BaseSumGate`.
|
||||
pub fn range_check(&mut self, x: Target, n_log: usize) {
|
||||
let gate = self.add_gate(BaseSumGate::<2>::new(n_log), vec![]);
|
||||
let sum = Target::wire(gate, BaseSumGate::<2>::WIRE_SUM);
|
||||
self.route(x, sum);
|
||||
}
|
||||
|
||||
/// Returns `(a,b)` such that `x = a + 2^n_log * b` with `a < 2^n_log`.
|
||||
/// `x` is assumed to be range-checked for having `num_bits` bits.
|
||||
pub fn split_low_high(&mut self, x: Target, n_log: usize, num_bits: usize) -> (Target, Target) {
|
||||
let low_gate = self.add_gate(BaseSumGate::<2>::new(n_log), vec![]);
|
||||
let high_gate = self.add_gate(BaseSumGate::<2>::new(num_bits - n_log), vec![]);
|
||||
let low = Target::wire(low_gate, BaseSumGate::<2>::WIRE_SUM);
|
||||
let high = Target::wire(high_gate, BaseSumGate::<2>::WIRE_SUM);
|
||||
self.add_generator(LowHighGenerator {
|
||||
integer: x,
|
||||
n_log,
|
||||
low,
|
||||
high,
|
||||
});
|
||||
|
||||
let pow2 = self.constant(F::from_canonical_u64(1 << n_log));
|
||||
let comp_x = self.mul_add(high, pow2, low);
|
||||
self.assert_equal(x, comp_x);
|
||||
|
||||
(low, high)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LowHighGenerator {
|
||||
integer: Target,
|
||||
n_log: usize,
|
||||
low: Target,
|
||||
high: Target,
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for LowHighGenerator {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.integer]
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>) -> PartialWitness<F> {
|
||||
let integer_value = witness.get_target(self.integer).to_canonical_u64();
|
||||
let low = integer_value & ((1 << self.n_log) - 1);
|
||||
let high = integer_value >> self.n_log;
|
||||
|
||||
let mut result = PartialWitness::new();
|
||||
result.set_target(self.low, F::from_canonical_u64(low));
|
||||
result.set_target(self.high, F::from_canonical_u64(high));
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
161
src/gadgets/rotate.rs
Normal file
161
src/gadgets/rotate.rs
Normal file
@ -0,0 +1,161 @@
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::target::Target;
|
||||
use crate::util::log2_ceil;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Selects `x` or `y` based on `b`, which is assumed to be binary.
|
||||
/// In particular, this returns `if b { x } else { y }`.
|
||||
/// Note: This does not range-check `b`.
|
||||
// TODO: This uses 10 gates per call. If addends are added to `MulExtensionGate`, this will be
|
||||
// reduced to 2 gates. We could also use a new degree 2 `SelectGate` for this.
|
||||
// If `num_routed_wire` is larger than 26, we could batch two `select` in one gate.
|
||||
pub fn select(
|
||||
&mut self,
|
||||
b: Target,
|
||||
x: ExtensionTarget<D>,
|
||||
y: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let b_y_minus_y = self.scalar_mul_sub_extension(b, y, y);
|
||||
self.scalar_mul_sub_extension(b, x, b_y_minus_y)
|
||||
}
|
||||
|
||||
/// Left-rotates an array `k` times if `b=1` else return the same array.
|
||||
pub fn rotate_left_fixed(
|
||||
&mut self,
|
||||
b: Target,
|
||||
k: usize,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let len = v.len();
|
||||
debug_assert!(k < len, "Trying to rotate by more than the vector length.");
|
||||
let mut res = Vec::new();
|
||||
|
||||
for i in 0..len {
|
||||
res.push(self.select(b, v[(i + k) % len], v[i]));
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Left-rotates an array `k` times if `b=1` else return the same array.
|
||||
pub fn rotate_right_fixed(
|
||||
&mut self,
|
||||
b: Target,
|
||||
k: usize,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let len = v.len();
|
||||
debug_assert!(k < len, "Trying to rotate by more than the vector length.");
|
||||
let mut res = Vec::new();
|
||||
|
||||
for i in 0..len {
|
||||
res.push(self.select(b, v[(len + i - k) % len], v[i]));
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Left-rotates an vector by the `Target` having bits given in little-endian by `num_rotation_bits`.
|
||||
pub fn rotate_left_from_bits(
|
||||
&mut self,
|
||||
num_rotation_bits: &[Target],
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let mut v = v.to_vec();
|
||||
|
||||
for i in 0..num_rotation_bits.len() {
|
||||
v = self.rotate_left_fixed(num_rotation_bits[i], 1 << i, &v);
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
pub fn rotate_right_from_bits(
|
||||
&mut self,
|
||||
num_rotation_bits: &[Target],
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let mut v = v.to_vec();
|
||||
|
||||
for i in 0..num_rotation_bits.len() {
|
||||
v = self.rotate_right_fixed(num_rotation_bits[i], 1 << i, &v);
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
/// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be
|
||||
/// less than `2^len_bits`.
|
||||
pub fn rotate_left(
|
||||
&mut self,
|
||||
num_rotation: Target,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let len_bits = log2_ceil(v.len());
|
||||
let bits = self.split_le(num_rotation, len_bits);
|
||||
|
||||
self.rotate_left_from_bits(&bits, v)
|
||||
}
|
||||
|
||||
pub fn rotate_right(
|
||||
&mut self,
|
||||
num_rotation: Target,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let len_bits = log2_ceil(v.len());
|
||||
let bits = self.split_le(num_rotation, len_bits);
|
||||
|
||||
self.rotate_right_from_bits(&bits, v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::field::Field;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
fn real_rotate<const D: usize>(
|
||||
num_rotation: usize,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let mut res = v.to_vec();
|
||||
res.rotate_left(num_rotation);
|
||||
res
|
||||
}
|
||||
|
||||
fn test_rotate_given_len(len: usize) {
|
||||
type F = CrandallField;
|
||||
type FF = QuarticCrandallField;
|
||||
let config = CircuitConfig::large_config();
|
||||
let mut builder = CircuitBuilder::<F, 4>::new(config);
|
||||
let v = (0..len)
|
||||
.map(|_| builder.constant_extension(FF::rand()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for i in 0..len {
|
||||
let it = builder.constant(F::from_canonical_usize(i));
|
||||
let rotated = real_rotate(i, &v);
|
||||
let purported_rotated = builder.rotate_left(it, &v);
|
||||
|
||||
for (x, y) in rotated.into_iter().zip(purported_rotated) {
|
||||
builder.assert_equal_extension(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(PartialWitness::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rotate() {
|
||||
for len in 1..5 {
|
||||
test_rotate_given_len(len);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
src/gadgets/split_base.rs
Normal file
71
src/gadgets/split_base.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::target::Target;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Split the given element into a list of targets, where each one represents a
|
||||
/// base-B limb of the element, with little-endian ordering.
|
||||
pub(crate) fn split_le_base<const B: usize>(
|
||||
&mut self,
|
||||
x: Target,
|
||||
num_limbs: usize,
|
||||
) -> Vec<Target> {
|
||||
let gate = self.add_gate(BaseSumGate::<B>::new(num_limbs), vec![]);
|
||||
let sum = Target::wire(gate, BaseSumGate::<B>::WIRE_SUM);
|
||||
self.route(x, sum);
|
||||
|
||||
Target::wires_from_range(
|
||||
gate,
|
||||
BaseSumGate::<B>::START_LIMBS..BaseSumGate::<B>::START_LIMBS + num_limbs,
|
||||
)
|
||||
}
|
||||
|
||||
/// Asserts that `x`'s big-endian bit representation has at least `leading_zeros` leading zeros.
|
||||
pub(crate) fn assert_leading_zeros(&mut self, x: Target, leading_zeros: u32) {
|
||||
self.range_check(x, (64 - leading_zeros) as usize);
|
||||
}
|
||||
|
||||
pub(crate) fn reverse_limbs<const B: usize>(&mut self, x: Target, num_limbs: usize) -> Target {
|
||||
let gate = self.add_gate(BaseSumGate::<B>::new(num_limbs), vec![]);
|
||||
let sum = Target::wire(gate, BaseSumGate::<B>::WIRE_SUM);
|
||||
self.route(x, sum);
|
||||
|
||||
Target::wire(gate, BaseSumGate::<B>::WIRE_REVERSED_SUM)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::field::Field;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
#[test]
|
||||
fn test_split_base() {
|
||||
type F = CrandallField;
|
||||
let config = CircuitConfig::large_config();
|
||||
let mut builder = CircuitBuilder::<F, 4>::new(config);
|
||||
let x = F::from_canonical_usize(0b110100000); // 416 = 1532 in base 6.
|
||||
let xt = builder.constant(x);
|
||||
let limbs = builder.split_le_base::<6>(xt, 24);
|
||||
let one = builder.one();
|
||||
let two = builder.two();
|
||||
let three = builder.constant(F::from_canonical_u64(3));
|
||||
let five = builder.constant(F::from_canonical_u64(5));
|
||||
builder.route(limbs[0], two);
|
||||
builder.route(limbs[1], three);
|
||||
builder.route(limbs[2], five);
|
||||
builder.route(limbs[3], one);
|
||||
let rev = builder.constant(F::from_canonical_u64(11));
|
||||
let revt = builder.reverse_limbs::<2>(xt, 9);
|
||||
builder.route(revt, rev);
|
||||
|
||||
builder.assert_leading_zeros(xt, 64 - 9);
|
||||
let data = builder.build();
|
||||
|
||||
let proof = data.prove(PartialWitness::new());
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::generator::{SimpleGenerator, WitnessGenerator};
|
||||
use crate::target::Target;
|
||||
use crate::util::ceil_div_usize;
|
||||
use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
@ -21,6 +23,53 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
});
|
||||
bit_targets
|
||||
}
|
||||
|
||||
/// Split the given integer into a list of wires, where each one represents a
|
||||
/// bit of the integer, with little-endian ordering.
|
||||
/// Verifies that the decomposition is correct by using `k` `BaseSum<2>` gates
|
||||
/// with `k` such that `k*num_routed_wires>=num_bits`.
|
||||
pub(crate) fn split_le(&mut self, integer: Target, num_bits: usize) -> Vec<Target> {
|
||||
if num_bits == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
let bits_per_gate = self.config.num_routed_wires - BaseSumGate::<2>::START_LIMBS;
|
||||
let k = ceil_div_usize(num_bits, bits_per_gate);
|
||||
let gates = (0..k)
|
||||
.map(|_| self.add_gate_no_constants(BaseSumGate::<2>::new(bits_per_gate)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut bits = Vec::with_capacity(num_bits);
|
||||
for &gate in &gates {
|
||||
bits.extend(Target::wires_from_range(
|
||||
gate,
|
||||
BaseSumGate::<2>::START_LIMBS..BaseSumGate::<2>::START_LIMBS + bits_per_gate,
|
||||
));
|
||||
}
|
||||
bits.drain(num_bits..);
|
||||
|
||||
let zero = self.zero();
|
||||
let one = self.one();
|
||||
let mut acc = zero;
|
||||
for &gate in gates.iter().rev() {
|
||||
let sum = Target::wire(gate, BaseSumGate::<2>::WIRE_SUM);
|
||||
acc = self.arithmetic(
|
||||
F::from_canonical_usize(1 << bits_per_gate),
|
||||
acc,
|
||||
one,
|
||||
F::ONE,
|
||||
sum,
|
||||
);
|
||||
}
|
||||
self.assert_equal(acc, integer);
|
||||
|
||||
self.add_generator(WireSplitGenerator {
|
||||
integer,
|
||||
gates,
|
||||
num_limbs: bits_per_gate,
|
||||
});
|
||||
|
||||
bits
|
||||
}
|
||||
}
|
||||
|
||||
/// Generator for a little-endian split.
|
||||
@ -79,3 +128,39 @@ impl<F: Field> SimpleGenerator<F> for SplitGenerator {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WireSplitGenerator {
|
||||
integer: Target,
|
||||
gates: Vec<usize>,
|
||||
num_limbs: usize,
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for WireSplitGenerator {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.integer]
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>) -> PartialWitness<F> {
|
||||
let mut integer_value = witness.get_target(self.integer).to_canonical_u64();
|
||||
|
||||
let mut result = PartialWitness::new();
|
||||
for &gate in &self.gates {
|
||||
let sum = Target::wire(gate, BaseSumGate::<2>::WIRE_SUM);
|
||||
result.set_target(
|
||||
sum,
|
||||
F::from_canonical_u64(integer_value & ((1 << self.num_limbs) - 1)),
|
||||
);
|
||||
integer_value >>= self.num_limbs;
|
||||
}
|
||||
|
||||
debug_assert_eq!(
|
||||
integer_value,
|
||||
0,
|
||||
"Integer too large to fit in {} many `BaseSumGate`s",
|
||||
self.gates.len()
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
182
src/gates/base_sum.rs
Normal file
182
src/gates/base_sum.rs
Normal file
@ -0,0 +1,182 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::{SimpleGenerator, WitnessGenerator};
|
||||
use crate::plonk_common::{reduce_with_powers, reduce_with_powers_recursive};
|
||||
use crate::target::Target;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
/// A gate which can decompose a number into base B little-endian limbs,
|
||||
/// and compute the limb-reversed (i.e. big-endian) sum.
|
||||
#[derive(Debug)]
|
||||
pub struct BaseSumGate<const B: usize> {
|
||||
num_limbs: usize,
|
||||
}
|
||||
|
||||
impl<const B: usize> BaseSumGate<B> {
|
||||
pub fn new<F: Extendable<D>, const D: usize>(num_limbs: usize) -> GateRef<F, D> {
|
||||
GateRef::new(BaseSumGate::<B> { num_limbs })
|
||||
}
|
||||
|
||||
pub const WIRE_SUM: usize = 0;
|
||||
pub const WIRE_REVERSED_SUM: usize = 1;
|
||||
pub const START_LIMBS: usize = 2;
|
||||
|
||||
/// Returns the index of the `i`th limb wire.
|
||||
pub fn limbs(&self) -> Range<usize> {
|
||||
Self::START_LIMBS..Self::START_LIMBS + self.num_limbs
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize, const B: usize> Gate<F, D> for BaseSumGate<B> {
|
||||
fn id(&self) -> String {
|
||||
format!("{:?} + Base: {}", self, B)
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let sum = vars.local_wires[Self::WIRE_SUM];
|
||||
let reversed_sum = vars.local_wires[Self::WIRE_REVERSED_SUM];
|
||||
let mut limbs = vars.local_wires[self.limbs()].to_vec();
|
||||
let computed_sum = reduce_with_powers(&limbs, F::Extension::from_canonical_usize(B));
|
||||
limbs.reverse();
|
||||
let computed_reversed_sum =
|
||||
reduce_with_powers(&limbs, F::Extension::from_canonical_usize(B));
|
||||
let mut constraints = vec![computed_sum - sum, computed_reversed_sum - reversed_sum];
|
||||
for limb in limbs {
|
||||
constraints.push(
|
||||
(0..B)
|
||||
.map(|i| limb - F::Extension::from_canonical_usize(i))
|
||||
.product(),
|
||||
);
|
||||
}
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
vars: EvaluationTargets<D>,
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let base = builder.constant(F::from_canonical_usize(B));
|
||||
let sum = vars.local_wires[Self::WIRE_SUM];
|
||||
let reversed_sum = vars.local_wires[Self::WIRE_REVERSED_SUM];
|
||||
let mut limbs = vars.local_wires[self.limbs()].to_vec();
|
||||
let computed_sum = reduce_with_powers_recursive(builder, &limbs, base);
|
||||
limbs.reverse();
|
||||
let computed_reversed_sum = reduce_with_powers_recursive(builder, &limbs, base);
|
||||
let mut constraints = vec![
|
||||
builder.sub_extension(computed_sum, sum),
|
||||
builder.sub_extension(computed_reversed_sum, reversed_sum),
|
||||
];
|
||||
for limb in limbs {
|
||||
constraints.push({
|
||||
let mut acc = builder.one_extension();
|
||||
(0..B).for_each(|i| {
|
||||
let it = builder.constant_extension(F::from_canonical_usize(i).into());
|
||||
let diff = builder.sub_extension(limb, it);
|
||||
acc = builder.mul_extension(acc, diff);
|
||||
});
|
||||
acc
|
||||
});
|
||||
}
|
||||
constraints
|
||||
}
|
||||
|
||||
fn generators(
|
||||
&self,
|
||||
gate_index: usize,
|
||||
_local_constants: &[F],
|
||||
) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
let gen = BaseSplitGenerator::<B> {
|
||||
gate_index,
|
||||
num_limbs: self.num_limbs,
|
||||
};
|
||||
vec![Box::new(gen)]
|
||||
}
|
||||
|
||||
// 2 for the sum and reversed sum, then `num_limbs` for the limbs.
|
||||
fn num_wires(&self) -> usize {
|
||||
self.num_limbs + 2
|
||||
}
|
||||
|
||||
fn num_constants(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
// Bounded by the range-check (x-0)*(x-1)*...*(x-B+1).
|
||||
fn degree(&self) -> usize {
|
||||
B
|
||||
}
|
||||
|
||||
// 2 for checking the sum and reversed sum, then `num_limbs` for range-checking the limbs.
|
||||
fn num_constraints(&self) -> usize {
|
||||
2 + self.num_limbs
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BaseSplitGenerator<const B: usize> {
|
||||
gate_index: usize,
|
||||
num_limbs: usize,
|
||||
}
|
||||
|
||||
impl<F: Field, const B: usize> SimpleGenerator<F> for BaseSplitGenerator<B> {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![Target::wire(self.gate_index, BaseSumGate::<B>::WIRE_SUM)]
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>) -> PartialWitness<F> {
|
||||
let sum_value = witness
|
||||
.get_target(Target::wire(self.gate_index, BaseSumGate::<B>::WIRE_SUM))
|
||||
.to_canonical_u64() as usize;
|
||||
debug_assert_eq!(
|
||||
(0..self.num_limbs).fold(sum_value, |acc, _| acc / B),
|
||||
0,
|
||||
"Integer too large to fit in given number of limbs"
|
||||
);
|
||||
|
||||
let limbs = (BaseSumGate::<B>::START_LIMBS..BaseSumGate::<B>::START_LIMBS + self.num_limbs)
|
||||
.map(|i| Target::wire(self.gate_index, i));
|
||||
let limbs_value = (0..self.num_limbs)
|
||||
.scan(sum_value, |acc, _| {
|
||||
let tmp = *acc % B;
|
||||
*acc /= B;
|
||||
Some(F::from_canonical_usize(tmp))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let b_field = F::from_canonical_usize(B);
|
||||
let reversed_sum = limbs_value
|
||||
.iter()
|
||||
.fold(F::ZERO, |acc, &x| acc * b_field + x);
|
||||
|
||||
let mut result = PartialWitness::new();
|
||||
result.set_target(
|
||||
Target::wire(self.gate_index, BaseSumGate::<B>::WIRE_REVERSED_SUM),
|
||||
reversed_sum,
|
||||
);
|
||||
for (b, b_value) in limbs.zip(limbs_value) {
|
||||
result.set_target(b, b_value);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::gates::gate_testing::test_low_degree;
|
||||
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
test_low_degree(BaseSumGate::<6>::new::<CrandallField, 4>(11))
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,7 @@ use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::algebra::PolynomialCoeffsAlgebra;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::{Extendable, FieldExtension};
|
||||
use crate::field::lagrange::interpolant;
|
||||
use crate::field::interpolation::interpolant;
|
||||
use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::{SimpleGenerator, WitnessGenerator};
|
||||
@ -22,8 +22,8 @@ use crate::witness::PartialWitness;
|
||||
/// given point.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct InterpolationGate<F: Extendable<D>, const D: usize> {
|
||||
num_points: usize,
|
||||
_phantom: PhantomData<F>,
|
||||
pub num_points: usize,
|
||||
pub _phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> InterpolationGate<F, D> {
|
||||
@ -355,9 +355,7 @@ mod tests {
|
||||
};
|
||||
|
||||
assert!(
|
||||
gate.eval_unfiltered(vars.clone())
|
||||
.iter()
|
||||
.all(|x| x.is_zero()),
|
||||
gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()),
|
||||
"Gate constraints are not satisfied."
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
pub(crate) mod arithmetic;
|
||||
pub mod base_sum;
|
||||
pub mod constant;
|
||||
pub(crate) mod gate;
|
||||
pub mod gmimc;
|
||||
mod interpolation;
|
||||
pub mod interpolation;
|
||||
pub mod mul_extension;
|
||||
pub(crate) mod noop;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
145
src/gates/mul_extension.rs
Normal file
145
src/gates/mul_extension.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::{Extendable, FieldExtension};
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::{SimpleGenerator, WitnessGenerator};
|
||||
use crate::target::Target;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
/// A gate which can multiply two field extension elements.
|
||||
/// TODO: Add an addend if `NUM_ROUTED_WIRES` is large enough.
|
||||
#[derive(Debug)]
|
||||
pub struct MulExtensionGate<const D: usize>;
|
||||
|
||||
impl<const D: usize> MulExtensionGate<D> {
|
||||
pub fn new<F: Extendable<D>>() -> GateRef<F, D> {
|
||||
GateRef::new(MulExtensionGate)
|
||||
}
|
||||
|
||||
pub fn wires_multiplicand_0() -> Range<usize> {
|
||||
0..D
|
||||
}
|
||||
pub fn wires_multiplicand_1() -> Range<usize> {
|
||||
D..2 * D
|
||||
}
|
||||
pub fn wires_output() -> Range<usize> {
|
||||
2 * D..3 * D
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> Gate<F, D> for MulExtensionGate<D> {
|
||||
fn id(&self) -> String {
|
||||
format!("{:?}", self)
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let const_0 = vars.local_constants[0];
|
||||
let multiplicand_0 = vars.get_local_ext_algebra(Self::wires_multiplicand_0());
|
||||
let multiplicand_1 = vars.get_local_ext_algebra(Self::wires_multiplicand_1());
|
||||
let output = vars.get_local_ext_algebra(Self::wires_output());
|
||||
let computed_output = multiplicand_0 * multiplicand_1 * const_0.into();
|
||||
(output - computed_output).to_basefield_array().to_vec()
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
vars: EvaluationTargets<D>,
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let const_0 = vars.local_constants[0];
|
||||
let multiplicand_0 = vars.get_local_ext_algebra(Self::wires_multiplicand_0());
|
||||
let multiplicand_1 = vars.get_local_ext_algebra(Self::wires_multiplicand_1());
|
||||
let output = vars.get_local_ext_algebra(Self::wires_output());
|
||||
let computed_output = builder.mul_ext_algebra(multiplicand_0, multiplicand_1);
|
||||
let computed_output = builder.scalar_mul_ext_algebra(const_0, computed_output);
|
||||
let diff = builder.sub_ext_algebra(output, computed_output);
|
||||
diff.to_ext_target_array().to_vec()
|
||||
}
|
||||
|
||||
fn generators(
|
||||
&self,
|
||||
gate_index: usize,
|
||||
local_constants: &[F],
|
||||
) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
let gen = MulExtensionGenerator {
|
||||
gate_index,
|
||||
const_0: local_constants[0],
|
||||
};
|
||||
vec![Box::new(gen)]
|
||||
}
|
||||
|
||||
fn num_wires(&self) -> usize {
|
||||
12
|
||||
}
|
||||
|
||||
fn num_constants(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn degree(&self) -> usize {
|
||||
3
|
||||
}
|
||||
|
||||
fn num_constraints(&self) -> usize {
|
||||
D
|
||||
}
|
||||
}
|
||||
|
||||
struct MulExtensionGenerator<F: Extendable<D>, const D: usize> {
|
||||
gate_index: usize,
|
||||
const_0: F,
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> SimpleGenerator<F> for MulExtensionGenerator<F, D> {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
MulExtensionGate::<D>::wires_multiplicand_0()
|
||||
.chain(MulExtensionGate::<D>::wires_multiplicand_1())
|
||||
.map(|i| {
|
||||
Target::Wire(Wire {
|
||||
gate: self.gate_index,
|
||||
input: i,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>) -> PartialWitness<F> {
|
||||
let multiplicand_0_target = ExtensionTarget::from_range(
|
||||
self.gate_index,
|
||||
MulExtensionGate::<D>::wires_multiplicand_0(),
|
||||
);
|
||||
let multiplicand_0 = witness.get_extension_target(multiplicand_0_target);
|
||||
|
||||
let multiplicand_1_target = ExtensionTarget::from_range(
|
||||
self.gate_index,
|
||||
MulExtensionGate::<D>::wires_multiplicand_1(),
|
||||
);
|
||||
let multiplicand_1 = witness.get_extension_target(multiplicand_1_target);
|
||||
|
||||
let output_target =
|
||||
ExtensionTarget::from_range(self.gate_index, MulExtensionGate::<D>::wires_output());
|
||||
|
||||
let computed_output =
|
||||
F::Extension::from_basefield(self.const_0) * multiplicand_0 * multiplicand_1;
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
pw.set_extension_target(output_target, computed_output);
|
||||
pw
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::gates::gate_testing::test_low_degree;
|
||||
use crate::gates::mul_extension::MulExtensionGate;
|
||||
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
test_low_degree(MulExtensionGate::<4>::new::<CrandallField>())
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::WitnessGenerator;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
|
||||
/// A gate which takes a single constant parameter and outputs that value.
|
||||
/// A gate which does nothing.
|
||||
pub struct NoopGate;
|
||||
|
||||
impl NoopGate {
|
||||
|
||||
@ -62,7 +62,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
leaf_data: Vec<Target>,
|
||||
leaf_index: Target,
|
||||
merkle_root: HashTarget,
|
||||
proof: MerkleProofTarget,
|
||||
proof: &MerkleProofTarget,
|
||||
) {
|
||||
let zero = self.zero();
|
||||
let height = proof.siblings.len();
|
||||
@ -71,7 +71,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
let mut state: HashTarget = self.hash_or_noop(leaf_data);
|
||||
let mut acc_leaf_index = zero;
|
||||
|
||||
for (bit, sibling) in purported_index_bits.into_iter().zip(proof.siblings) {
|
||||
for (bit, &sibling) in purported_index_bits.into_iter().zip(&proof.siblings) {
|
||||
let gate = self
|
||||
.add_gate_no_constants(GMiMCGate::<F, D, GMIMC_ROUNDS>::with_automatic_constants());
|
||||
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
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, OpeningSet};
|
||||
use crate::target::Target;
|
||||
use std::convert::TryInto;
|
||||
|
||||
/// Observes prover messages, and generates challenges by hashing the transcript.
|
||||
#[derive(Clone)]
|
||||
@ -41,9 +43,7 @@ impl<F: Field> Challenger<F> {
|
||||
where
|
||||
F: Extendable<D>,
|
||||
{
|
||||
for &e in &element.to_basefield_array() {
|
||||
self.observe_element(e);
|
||||
}
|
||||
self.observe_elements(&element.to_basefield_array());
|
||||
}
|
||||
|
||||
pub fn observe_elements(&mut self, elements: &[F]) {
|
||||
@ -177,7 +177,7 @@ impl<F: Field> Default for Challenger<F> {
|
||||
}
|
||||
|
||||
/// A recursive version of `Challenger`.
|
||||
pub(crate) struct RecursiveChallenger {
|
||||
pub struct RecursiveChallenger {
|
||||
sponge_state: [Target; SPONGE_WIDTH],
|
||||
input_buffer: Vec<Target>,
|
||||
output_buffer: Vec<Target>,
|
||||
@ -212,6 +212,16 @@ impl RecursiveChallenger {
|
||||
self.observe_elements(&hash.elements)
|
||||
}
|
||||
|
||||
pub fn observe_extension_element<const D: usize>(&mut self, element: ExtensionTarget<D>) {
|
||||
self.observe_elements(&element.0);
|
||||
}
|
||||
|
||||
pub fn observe_extension_elements<const D: usize>(&mut self, elements: &[ExtensionTarget<D>]) {
|
||||
for &element in elements {
|
||||
self.observe_extension_element(element);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_challenge<F: Extendable<D>, const D: usize>(
|
||||
&mut self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
@ -255,6 +265,27 @@ impl RecursiveChallenger {
|
||||
(0..n).map(|_| self.get_challenge(builder)).collect()
|
||||
}
|
||||
|
||||
pub fn get_hash<F: Extendable<D>, const D: usize>(
|
||||
&mut self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> HashTarget {
|
||||
HashTarget {
|
||||
elements: [
|
||||
self.get_challenge(builder),
|
||||
self.get_challenge(builder),
|
||||
self.get_challenge(builder),
|
||||
self.get_challenge(builder),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_extension_challenge<F: Extendable<D>, const D: usize>(
|
||||
&mut self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
self.get_n_challenges(builder, D).try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Absorb any buffered inputs. After calling this, the input buffer will be empty.
|
||||
fn absorb_buffered_inputs<F: Extendable<D>, const D: usize>(
|
||||
&mut self,
|
||||
|
||||
@ -6,10 +6,62 @@ use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::gate::GateRef;
|
||||
use crate::polynomial::commitment::SALT_SIZE;
|
||||
use crate::polynomial::polynomial::PolynomialCoeffs;
|
||||
use crate::target::Target;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
|
||||
/// Holds the Merkle tree index and blinding flag of a set of polynomials used in FRI.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct PolynomialsIndexBlinding {
|
||||
pub(crate) index: usize,
|
||||
pub(crate) blinding: bool,
|
||||
}
|
||||
impl PolynomialsIndexBlinding {
|
||||
pub fn salt_size(&self) -> usize {
|
||||
if self.blinding {
|
||||
SALT_SIZE
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Holds the indices and blinding flags of the Plonk polynomials.
|
||||
pub struct PlonkPolynomials;
|
||||
impl PlonkPolynomials {
|
||||
pub const CONSTANTS: PolynomialsIndexBlinding = PolynomialsIndexBlinding {
|
||||
index: 0,
|
||||
blinding: false,
|
||||
};
|
||||
pub const SIGMAS: PolynomialsIndexBlinding = PolynomialsIndexBlinding {
|
||||
index: 1,
|
||||
blinding: false,
|
||||
};
|
||||
pub const WIRES: PolynomialsIndexBlinding = PolynomialsIndexBlinding {
|
||||
index: 2,
|
||||
blinding: true,
|
||||
};
|
||||
pub const ZS: PolynomialsIndexBlinding = PolynomialsIndexBlinding {
|
||||
index: 3,
|
||||
blinding: true,
|
||||
};
|
||||
pub const QUOTIENT: PolynomialsIndexBlinding = PolynomialsIndexBlinding {
|
||||
index: 4,
|
||||
blinding: true,
|
||||
};
|
||||
|
||||
pub fn polynomials(i: usize) -> PolynomialsIndexBlinding {
|
||||
match i {
|
||||
0 => Self::CONSTANTS,
|
||||
1 => Self::SIGMAS,
|
||||
2 => Self::WIRES,
|
||||
3 => Self::ZS,
|
||||
4 => Self::QUOTIENT,
|
||||
_ => panic!("There are only 5 sets of polynomials in Plonk."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate the vanishing polynomial at `x`. In this context, the vanishing polynomial is a random
|
||||
/// linear combination of gate constraints, plus some other terms relating to the permutation
|
||||
/// argument. All such terms should vanish on `H`.
|
||||
@ -206,10 +258,15 @@ pub(crate) fn reduce_with_powers<F: Field>(terms: &[F], alpha: F) -> F {
|
||||
|
||||
pub(crate) fn reduce_with_powers_recursive<F: Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
terms: Vec<Target>,
|
||||
terms: &[ExtensionTarget<D>],
|
||||
alpha: Target,
|
||||
) -> Target {
|
||||
todo!()
|
||||
) -> ExtensionTarget<D> {
|
||||
let mut sum = builder.zero_extension();
|
||||
for &term in terms.iter().rev() {
|
||||
sum = builder.scalar_mul_ext(alpha, sum);
|
||||
sum = builder.add_extension(sum, term);
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
/// Reduce a sequence of field elements by the given coefficients.
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
use anyhow::Result;
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::field::extension_field::FieldExtension;
|
||||
use crate::field::extension_field::{Extendable, OEF};
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::extension_field::{FieldExtension, Frobenius};
|
||||
use crate::field::field::Field;
|
||||
use crate::field::lagrange::interpolant;
|
||||
use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig};
|
||||
use crate::merkle_tree::MerkleTree;
|
||||
use crate::plonk_challenger::Challenger;
|
||||
use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter};
|
||||
use crate::plonk_common::PlonkPolynomials;
|
||||
use crate::polynomial::polynomial::PolynomialCoeffs;
|
||||
use crate::proof::{FriProof, Hash, OpeningSet};
|
||||
use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet};
|
||||
use crate::timed;
|
||||
use crate::util::scaling::ReducingFactor;
|
||||
use crate::util::{log2_strict, reverse_index_bits_in_place, transpose};
|
||||
|
||||
pub const SALT_SIZE: usize = 2;
|
||||
@ -110,60 +110,50 @@ impl<F: Field> ListPolynomialCommitment<F> {
|
||||
challenger.observe_opening_set(&os);
|
||||
|
||||
let alpha = challenger.get_extension_challenge();
|
||||
let mut alpha_powers = alpha.powers();
|
||||
let mut alpha = ReducingFactor::new(alpha);
|
||||
|
||||
// Final low-degree polynomial that goes into FRI.
|
||||
let mut final_poly = PolynomialCoeffs::empty();
|
||||
|
||||
// Polynomials opened at a single point.
|
||||
let single_polys = [0, 1, 4]
|
||||
.iter()
|
||||
.flat_map(|&i| &commitments[i].polynomials)
|
||||
.map(|p| p.to_extension());
|
||||
let single_os = [&os.constants, &os.plonk_s_sigmas, &os.quotient_polys];
|
||||
let single_evals = single_os.iter().flat_map(|v| v.iter());
|
||||
let single_composition_poly = reduce_polys_with_iter(single_polys, alpha_powers.clone());
|
||||
let single_composition_eval = reduce_with_iter(single_evals, &mut alpha_powers);
|
||||
let single_polys = [
|
||||
PlonkPolynomials::CONSTANTS,
|
||||
PlonkPolynomials::SIGMAS,
|
||||
PlonkPolynomials::QUOTIENT,
|
||||
]
|
||||
.iter()
|
||||
.flat_map(|&p| &commitments[p.index].polynomials)
|
||||
.map(|p| p.to_extension());
|
||||
let single_composition_poly = alpha.reduce_polys(single_polys);
|
||||
|
||||
let single_quotient = Self::compute_quotient(
|
||||
&[zeta],
|
||||
&[single_composition_eval],
|
||||
&single_composition_poly,
|
||||
);
|
||||
final_poly = &final_poly + &single_quotient;
|
||||
let single_quotient = Self::compute_quotient([zeta], single_composition_poly);
|
||||
final_poly += single_quotient;
|
||||
alpha.reset();
|
||||
|
||||
// Zs polynomials are opened at `zeta` and `g*zeta`.
|
||||
let zs_polys = commitments[3].polynomials.iter().map(|p| p.to_extension());
|
||||
let zs_composition_poly = reduce_polys_with_iter(zs_polys, alpha_powers.clone());
|
||||
let zs_composition_evals = [
|
||||
reduce_with_iter(&os.plonk_zs, alpha_powers.clone()),
|
||||
reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers),
|
||||
];
|
||||
let zs_polys = commitments[PlonkPolynomials::ZS.index]
|
||||
.polynomials
|
||||
.iter()
|
||||
.map(|p| p.to_extension());
|
||||
let zs_composition_poly = alpha.reduce_polys(zs_polys);
|
||||
|
||||
let zs_quotient = Self::compute_quotient(
|
||||
&[zeta, g * zeta],
|
||||
&zs_composition_evals,
|
||||
&zs_composition_poly,
|
||||
);
|
||||
final_poly = &final_poly + &zs_quotient;
|
||||
let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly);
|
||||
alpha.shift_poly(&mut final_poly);
|
||||
final_poly += zs_quotient;
|
||||
|
||||
// When working in an extension field, need to check that wires are in the base field.
|
||||
// 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.
|
||||
let wire_polys = commitments[2].polynomials.iter().map(|p| p.to_extension());
|
||||
let wire_composition_poly = reduce_polys_with_iter(wire_polys, alpha_powers.clone());
|
||||
let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::<Vec<_>>();
|
||||
let wire_composition_evals = [
|
||||
reduce_with_iter(&os.wires, alpha_powers.clone()),
|
||||
reduce_with_iter(&wire_evals_frob, alpha_powers),
|
||||
];
|
||||
let wire_polys = commitments[PlonkPolynomials::WIRES.index]
|
||||
.polynomials
|
||||
.iter()
|
||||
.map(|p| p.to_extension());
|
||||
let wire_composition_poly = alpha.reduce_polys(wire_polys);
|
||||
|
||||
let wires_quotient = Self::compute_quotient(
|
||||
&[zeta, zeta.frobenius()],
|
||||
&wire_composition_evals,
|
||||
&wire_composition_poly,
|
||||
);
|
||||
final_poly = &final_poly + &wires_quotient;
|
||||
let wires_quotient =
|
||||
Self::compute_quotient([zeta, zeta.frobenius()], wire_composition_poly);
|
||||
alpha.shift_poly(&mut final_poly);
|
||||
final_poly += wires_quotient;
|
||||
|
||||
let lde_final_poly = final_poly.lde(config.rate_bits);
|
||||
let lde_final_values = lde_final_poly
|
||||
@ -194,28 +184,27 @@ 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<const D: usize>(
|
||||
points: &[F::Extension],
|
||||
evals: &[F::Extension],
|
||||
poly: &PolynomialCoeffs<F::Extension>,
|
||||
fn compute_quotient<const D: usize, const N: usize>(
|
||||
points: [F::Extension; N],
|
||||
poly: PolynomialCoeffs<F::Extension>,
|
||||
) -> PolynomialCoeffs<F::Extension>
|
||||
where
|
||||
F: Extendable<D>,
|
||||
{
|
||||
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::Extension::ONE])
|
||||
});
|
||||
let numerator = poly - &interpolant;
|
||||
let (quotient, rem) = numerator.div_rem(&denominator);
|
||||
debug_assert!(rem.is_zero());
|
||||
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.")
|
||||
};
|
||||
|
||||
quotient.padded(quotient.degree_plus_one().next_power_of_two())
|
||||
}
|
||||
@ -253,12 +242,16 @@ impl<F: Field + Extendable<D>, const D: usize> OpeningProof<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OpeningProofTarget<const D: usize> {
|
||||
fri_proof: FriProofTarget<D>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
use rand::Rng;
|
||||
|
||||
use super::*;
|
||||
use crate::plonk_common::PlonkPolynomials;
|
||||
|
||||
fn gen_random_test_case<F: Field + Extendable<D>, const D: usize>(
|
||||
k: usize,
|
||||
@ -284,17 +277,6 @@ mod tests {
|
||||
point
|
||||
}
|
||||
|
||||
fn gen_random_blindings() -> Vec<bool> {
|
||||
let mut rng = rand::thread_rng();
|
||||
vec![
|
||||
rng.gen_bool(0.5),
|
||||
rng.gen_bool(0.5),
|
||||
rng.gen_bool(0.5),
|
||||
rng.gen_bool(0.5),
|
||||
rng.gen_bool(0.5),
|
||||
]
|
||||
}
|
||||
|
||||
fn check_batch_polynomial_commitment<F: Field + Extendable<D>, const D: usize>() -> Result<()> {
|
||||
let ks = [1, 2, 3, 5, 8];
|
||||
let degree_log = 11;
|
||||
@ -303,7 +285,6 @@ mod tests {
|
||||
rate_bits: 2,
|
||||
reduction_arity_bits: vec![2, 3, 1, 2],
|
||||
num_query_rounds: 3,
|
||||
blinding: gen_random_blindings(),
|
||||
};
|
||||
|
||||
let lpcs = (0..5)
|
||||
@ -311,7 +292,7 @@ mod tests {
|
||||
ListPolynomialCommitment::<F>::new(
|
||||
gen_random_test_case(ks[i], degree_log),
|
||||
fri_config.rate_bits,
|
||||
fri_config.blinding[i],
|
||||
PlonkPolynomials::polynomials(i).blinding,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -26,7 +26,7 @@ impl<F: Field> PolynomialCoeffs<F> {
|
||||
.to_vec()
|
||||
.into();
|
||||
let mut q = rev_q.rev();
|
||||
let mut qb = &q * b;
|
||||
let qb = &q * b;
|
||||
let mut r = self - &qb;
|
||||
q.trim();
|
||||
r.trim();
|
||||
@ -59,8 +59,7 @@ impl<F: Field> PolynomialCoeffs<F> {
|
||||
quotient.coeffs[cur_q_degree] = cur_q_coeff;
|
||||
|
||||
for (i, &div_coeff) in b.coeffs.iter().enumerate() {
|
||||
remainder.coeffs[cur_q_degree + i] =
|
||||
remainder.coeffs[cur_q_degree + i] - (cur_q_coeff * div_coeff);
|
||||
remainder.coeffs[cur_q_degree + i] -= cur_q_coeff * div_coeff;
|
||||
}
|
||||
remainder.trim();
|
||||
}
|
||||
@ -97,7 +96,7 @@ impl<F: Field> PolynomialCoeffs<F> {
|
||||
let denominators = (0..a_eval.len())
|
||||
.map(|i| {
|
||||
if i != 0 {
|
||||
root_pow = root_pow * root_n;
|
||||
root_pow *= root_n;
|
||||
}
|
||||
denominator_g * root_pow - F::ONE
|
||||
})
|
||||
@ -125,8 +124,25 @@ impl<F: Field> PolynomialCoeffs<F> {
|
||||
p
|
||||
}
|
||||
|
||||
/// Let `self=p(X)`, this returns `(p(X)-p(z))/(X-z)` and `p(z)`.
|
||||
/// See https://en.wikipedia.org/wiki/Horner%27s_method
|
||||
pub(crate) fn divide_by_linear(&self, z: F) -> (PolynomialCoeffs<F>, F) {
|
||||
let mut bs = self
|
||||
.coeffs
|
||||
.iter()
|
||||
.rev()
|
||||
.scan(F::ZERO, |acc, &c| {
|
||||
*acc = *acc * z + c;
|
||||
Some(*acc)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let ev = bs.pop().unwrap_or(F::ZERO);
|
||||
bs.reverse();
|
||||
(Self { coeffs: bs }, ev)
|
||||
}
|
||||
|
||||
/// Computes the inverse of `self` modulo `x^n`.
|
||||
pub(crate) fn inv_mod_xn(&self, n: usize) -> Self {
|
||||
pub fn inv_mod_xn(&self, n: usize) -> Self {
|
||||
assert!(self.coeffs[0].is_nonzero(), "Inverse doesn't exist.");
|
||||
|
||||
let h = if self.len() < n {
|
||||
@ -166,7 +182,10 @@ impl<F: Field> PolynomialCoeffs<F> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::field::Field;
|
||||
use crate::polynomial::polynomial::PolynomialCoeffs;
|
||||
|
||||
@ -199,4 +218,49 @@ mod tests {
|
||||
let computed_q = a.divide_by_z_h(4);
|
||||
assert_eq!(computed_q, q);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_division_by_linear() {
|
||||
type F = QuarticCrandallField;
|
||||
let n = 1_000_000;
|
||||
let poly = PolynomialCoeffs::new(F::rand_vec(n));
|
||||
let z = F::rand();
|
||||
let ev = poly.eval(z);
|
||||
|
||||
let timer = Instant::now();
|
||||
let (quotient, ev2) = poly.div_rem(&PolynomialCoeffs::new(vec![-z, F::ONE]));
|
||||
println!("{:.3}s for usual", timer.elapsed().as_secs_f32());
|
||||
assert_eq!(ev2.trimmed().coeffs, vec![ev]);
|
||||
|
||||
let timer = Instant::now();
|
||||
let (quotient, ev3) = poly.div_rem_long_division(&PolynomialCoeffs::new(vec![-z, F::ONE]));
|
||||
println!("{:.3}s for long division", timer.elapsed().as_secs_f32());
|
||||
assert_eq!(ev3.trimmed().coeffs, vec![ev]);
|
||||
|
||||
let timer = Instant::now();
|
||||
let horn = poly.divide_by_linear(z);
|
||||
println!("{:.3}s for Horner", timer.elapsed().as_secs_f32());
|
||||
assert_eq!((quotient, ev), horn);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_division_by_quadratic() {
|
||||
type F = QuarticCrandallField;
|
||||
let n = 1_000_000;
|
||||
let poly = PolynomialCoeffs::new(F::rand_vec(n));
|
||||
let quad = PolynomialCoeffs::new(F::rand_vec(2));
|
||||
|
||||
let timer = Instant::now();
|
||||
let (quotient0, rem0) = poly.div_rem(&quad);
|
||||
println!("{:.3}s for usual", timer.elapsed().as_secs_f32());
|
||||
|
||||
let timer = Instant::now();
|
||||
let (quotient1, rem1) = poly.div_rem_long_division(&quad);
|
||||
println!("{:.3}s for long division", timer.elapsed().as_secs_f32());
|
||||
|
||||
assert_eq!(quotient0.trimmed(), quotient1.trimmed());
|
||||
assert_eq!(rem0.trimmed(), rem1.trimmed());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::cmp::max;
|
||||
use std::iter::Sum;
|
||||
use std::ops::{Add, Mul, Sub};
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::fft::{fft, ifft};
|
||||
@ -243,6 +243,46 @@ impl<F: Field> Sub for &PolynomialCoeffs<F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> AddAssign for PolynomialCoeffs<F> {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
let len = max(self.len(), rhs.len());
|
||||
self.coeffs.resize(len, F::ZERO);
|
||||
for (l, r) in self.coeffs.iter_mut().zip(rhs.coeffs) {
|
||||
*l += r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> AddAssign<&Self> for PolynomialCoeffs<F> {
|
||||
fn add_assign(&mut self, rhs: &Self) {
|
||||
let len = max(self.len(), rhs.len());
|
||||
self.coeffs.resize(len, F::ZERO);
|
||||
for (l, &r) in self.coeffs.iter_mut().zip(&rhs.coeffs) {
|
||||
*l += r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> SubAssign for PolynomialCoeffs<F> {
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
let len = max(self.len(), rhs.len());
|
||||
self.coeffs.resize(len, F::ZERO);
|
||||
for (l, r) in self.coeffs.iter_mut().zip(rhs.coeffs) {
|
||||
*l -= r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> SubAssign<&Self> for PolynomialCoeffs<F> {
|
||||
fn sub_assign(&mut self, rhs: &Self) {
|
||||
let len = max(self.len(), rhs.len());
|
||||
self.coeffs.resize(len, F::ZERO);
|
||||
for (l, &r) in self.coeffs.iter_mut().zip(&rhs.coeffs) {
|
||||
*l -= r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Mul<F> for &PolynomialCoeffs<F> {
|
||||
type Output = PolynomialCoeffs<F>;
|
||||
|
||||
@ -252,6 +292,12 @@ impl<F: Field> Mul<F> for &PolynomialCoeffs<F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> MulAssign<F> for PolynomialCoeffs<F> {
|
||||
fn mul_assign(&mut self, rhs: F) {
|
||||
self.coeffs.iter_mut().for_each(|x| *x *= rhs);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Mul for &PolynomialCoeffs<F> {
|
||||
type Output = PolynomialCoeffs<F>;
|
||||
|
||||
|
||||
79
src/proof.rs
79
src/proof.rs
@ -1,10 +1,12 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field::Field;
|
||||
use crate::fri::FriConfig;
|
||||
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
||||
use crate::merkle_proofs::{MerkleProof, MerkleProofTarget};
|
||||
use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof};
|
||||
use crate::plonk_common::PolynomialsIndexBlinding;
|
||||
use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof, OpeningProofTarget};
|
||||
use crate::polynomial::polynomial::PolynomialCoeffs;
|
||||
use crate::target::Target;
|
||||
|
||||
@ -34,6 +36,7 @@ impl<F: Field> Hash<F> {
|
||||
}
|
||||
|
||||
/// Represents a ~256 bit hash output.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct HashTarget {
|
||||
pub(crate) elements: [Target; 4],
|
||||
}
|
||||
@ -64,57 +67,66 @@ pub struct Proof<F: Field + Extendable<D>, const D: usize> {
|
||||
pub plonk_zs_root: Hash<F>,
|
||||
/// Merkle root of LDEs of the quotient polynomial components.
|
||||
pub quotient_polys_root: Hash<F>,
|
||||
|
||||
/// Purported values of each polynomial at the challenge point.
|
||||
pub openings: OpeningSet<F, D>,
|
||||
|
||||
/// A FRI argument for each FRI query.
|
||||
pub opening_proof: OpeningProof<F, D>,
|
||||
}
|
||||
|
||||
pub struct ProofTarget {
|
||||
/// Merkle root of LDEs of wire values.
|
||||
pub struct ProofTarget<const D: usize> {
|
||||
pub wires_root: HashTarget,
|
||||
/// Merkle root of LDEs of Z, in the context of Plonk's permutation argument.
|
||||
pub plonk_zs_root: HashTarget,
|
||||
/// Merkle root of LDEs of the quotient polynomial components.
|
||||
pub quotient_polys_root: HashTarget,
|
||||
|
||||
/// Purported values of each polynomial at each challenge point.
|
||||
pub openings: Vec<OpeningSetTarget>,
|
||||
|
||||
/// A FRI argument for each FRI query.
|
||||
pub fri_proofs: Vec<FriProofTarget>,
|
||||
pub openings: Vec<OpeningSetTarget<D>>,
|
||||
pub opening_proof: Vec<OpeningProofTarget<D>>,
|
||||
}
|
||||
|
||||
/// Evaluations and Merkle proof produced by the prover in a FRI query step.
|
||||
// TODO: Implement FriQueryStepTarget
|
||||
pub struct FriQueryStep<F: Field + Extendable<D>, const D: usize> {
|
||||
pub evals: Vec<F::Extension>,
|
||||
pub merkle_proof: MerkleProof<F>,
|
||||
}
|
||||
|
||||
pub struct FriQueryStepTarget<const D: usize> {
|
||||
pub evals: Vec<ExtensionTarget<D>>,
|
||||
pub merkle_proof: MerkleProofTarget,
|
||||
}
|
||||
|
||||
/// Evaluations and Merkle proofs of the original set of polynomials,
|
||||
/// before they are combined into a composition polynomial.
|
||||
// TODO: Implement FriInitialTreeProofTarget
|
||||
pub struct FriInitialTreeProof<F: Field> {
|
||||
pub evals_proofs: Vec<(Vec<F>, MerkleProof<F>)>,
|
||||
}
|
||||
|
||||
impl<F: Field> FriInitialTreeProof<F> {
|
||||
pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> &[F] {
|
||||
let evals = &self.evals_proofs[i].0;
|
||||
&evals[..evals.len() - config.salt_size(i)]
|
||||
pub(crate) fn unsalted_evals(&self, polynomials: PolynomialsIndexBlinding) -> &[F] {
|
||||
let evals = &self.evals_proofs[polynomials.index].0;
|
||||
&evals[..evals.len() - polynomials.salt_size()]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FriInitialTreeProofTarget {
|
||||
pub evals_proofs: Vec<(Vec<Target>, MerkleProofTarget)>,
|
||||
}
|
||||
|
||||
impl FriInitialTreeProofTarget {
|
||||
pub(crate) fn unsalted_evals(&self, polynomials: PolynomialsIndexBlinding) -> &[Target] {
|
||||
let evals = &self.evals_proofs[polynomials.index].0;
|
||||
&evals[..evals.len() - polynomials.salt_size()]
|
||||
}
|
||||
}
|
||||
|
||||
/// Proof for a FRI query round.
|
||||
// TODO: Implement FriQueryRoundTarget
|
||||
pub struct FriQueryRound<F: Field + Extendable<D>, const D: usize> {
|
||||
pub initial_trees_proof: FriInitialTreeProof<F>,
|
||||
pub steps: Vec<FriQueryStep<F, D>>,
|
||||
}
|
||||
|
||||
pub struct FriQueryRoundTarget<const D: usize> {
|
||||
pub initial_trees_proof: FriInitialTreeProofTarget,
|
||||
pub steps: Vec<FriQueryStepTarget<D>>,
|
||||
}
|
||||
|
||||
pub struct FriProof<F: Field + Extendable<D>, const D: usize> {
|
||||
/// A Merkle root for each reduced polynomial in the commit phase.
|
||||
pub commit_phase_merkle_roots: Vec<Hash<F>>,
|
||||
@ -126,18 +138,14 @@ pub struct FriProof<F: Field + Extendable<D>, const D: usize> {
|
||||
pub pow_witness: F,
|
||||
}
|
||||
|
||||
/// Represents a single FRI query, i.e. a path through the reduction tree.
|
||||
pub struct FriProofTarget {
|
||||
/// A Merkle root for each reduced polynomial in the commit phase.
|
||||
pub struct FriProofTarget<const D: usize> {
|
||||
pub commit_phase_merkle_roots: Vec<HashTarget>,
|
||||
/// Merkle proofs for the original purported codewords, i.e. the subject of the LDT.
|
||||
pub initial_merkle_proofs: Vec<MerkleProofTarget>,
|
||||
/// Merkle proofs for the reduced polynomials that were sent in the commit phase.
|
||||
pub intermediate_merkle_proofs: Vec<MerkleProofTarget>,
|
||||
/// The final polynomial in coefficient form.
|
||||
pub final_poly: Vec<Target>,
|
||||
pub query_round_proofs: Vec<FriQueryRoundTarget<D>>,
|
||||
pub final_poly: PolynomialCoeffsExtTarget<D>,
|
||||
pub pow_witness: Target,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// The purported values of each polynomial at a single point.
|
||||
pub struct OpeningSet<F: Field + Extendable<D>, const D: usize> {
|
||||
pub constants: Vec<F::Extension>,
|
||||
@ -176,10 +184,11 @@ impl<F: Field + Extendable<D>, const D: usize> OpeningSet<F, D> {
|
||||
}
|
||||
|
||||
/// The purported values of each polynomial at a single point.
|
||||
pub struct OpeningSetTarget {
|
||||
pub constants: Vec<Target>,
|
||||
pub plonk_sigmas: Vec<Target>,
|
||||
pub wires: Vec<Target>,
|
||||
pub plonk_zs: Vec<Target>,
|
||||
pub quotient_polys: Vec<Target>,
|
||||
pub struct OpeningSetTarget<const D: usize> {
|
||||
pub constants: Vec<ExtensionTarget<D>>,
|
||||
pub plonk_sigmas: Vec<ExtensionTarget<D>>,
|
||||
pub wires: Vec<ExtensionTarget<D>>,
|
||||
pub plonk_zs: Vec<ExtensionTarget<D>>,
|
||||
pub plonk_zs_right: Vec<ExtensionTarget<D>>,
|
||||
pub quotient_polys: Vec<ExtensionTarget<D>>,
|
||||
}
|
||||
|
||||
@ -19,9 +19,6 @@ use crate::vars::EvaluationVarsBase;
|
||||
use crate::wire::Wire;
|
||||
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: Extendable<D>, const D: usize>(
|
||||
prover_data: &ProverOnlyCircuitData<F, D>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
@ -122,7 +119,7 @@ pub(crate) fn prove<F: Extendable<D>, const D: usize>(
|
||||
|
||||
let zeta = challenger.get_extension_challenge();
|
||||
|
||||
let (opening_proof, mut openings) = timed!(
|
||||
let (opening_proof, openings) = timed!(
|
||||
ListPolynomialCommitment::open_plonk(
|
||||
&[
|
||||
&prover_data.constants_commitment,
|
||||
|
||||
@ -13,7 +13,7 @@ pub fn add_recursive_verifier<F: Extendable<D>, const D: usize>(
|
||||
inner_config: CircuitConfig,
|
||||
inner_circuit: VerifierCircuitTarget,
|
||||
inner_gates: Vec<GateRef<F, D>>,
|
||||
inner_proof: ProofTarget,
|
||||
inner_proof: ProofTarget<D>,
|
||||
) {
|
||||
assert!(builder.config.num_wires >= MIN_WIRES);
|
||||
assert!(builder.config.num_wires >= MIN_ROUTED_WIRES);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::wire::Wire;
|
||||
use std::ops::Range;
|
||||
|
||||
/// A location in the witness.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
@ -21,4 +22,8 @@ impl Target {
|
||||
Target::VirtualAdviceTarget { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wires_from_range(gate: usize, range: Range<usize>) -> Vec<Self> {
|
||||
range.map(|i| Self::wire(gate, i)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
pub mod scaling;
|
||||
pub(crate) mod timing;
|
||||
|
||||
use crate::field::field::Field;
|
||||
@ -7,7 +8,7 @@ pub(crate) fn bits_u64(n: u64) -> usize {
|
||||
(64 - n.leading_zeros()) as usize
|
||||
}
|
||||
|
||||
pub(crate) fn ceil_div_usize(a: usize, b: usize) -> usize {
|
||||
pub(crate) const fn ceil_div_usize(a: usize, b: usize) -> usize {
|
||||
(a + b - 1) / b
|
||||
}
|
||||
|
||||
|
||||
75
src/util/scaling.rs
Normal file
75
src/util/scaling.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use crate::field::extension_field::Frobenius;
|
||||
use crate::field::field::Field;
|
||||
use crate::polynomial::polynomial::PolynomialCoeffs;
|
||||
|
||||
/// When verifying the composition polynomial in FRI we have to compute sums of the form
|
||||
/// `(sum_0^k a^i * x_i)/d_0 + (sum_k^r a^i * y_i)/d_1`
|
||||
/// The most efficient way to do this is to compute both quotient separately using Horner's method,
|
||||
/// scale the second one by `a^(r-1-k)`, and add them up.
|
||||
/// This struct abstract away these operations by implementing Horner's method and keeping track
|
||||
/// of the number of multiplications by `a` to compute the scaling factor.
|
||||
/// See https://github.com/mir-protocol/plonky2/pull/69 for more details and discussions.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ReducingFactor<F: Field> {
|
||||
base: F,
|
||||
count: u64,
|
||||
}
|
||||
|
||||
impl<F: Field> ReducingFactor<F> {
|
||||
pub fn new(base: F) -> Self {
|
||||
Self { base, count: 0 }
|
||||
}
|
||||
|
||||
fn mul(&mut self, x: F) -> F {
|
||||
self.count += 1;
|
||||
self.base * x
|
||||
}
|
||||
|
||||
fn mul_poly(&mut self, p: &mut PolynomialCoeffs<F>) {
|
||||
self.count += 1;
|
||||
*p *= self.base;
|
||||
}
|
||||
|
||||
pub fn reduce(&mut self, iter: impl DoubleEndedIterator<Item = impl Borrow<F>>) -> F {
|
||||
iter.rev()
|
||||
.fold(F::ZERO, |acc, x| self.mul(acc) + *x.borrow())
|
||||
}
|
||||
|
||||
pub fn reduce_polys(
|
||||
&mut self,
|
||||
polys: impl DoubleEndedIterator<Item = impl Borrow<PolynomialCoeffs<F>>>,
|
||||
) -> PolynomialCoeffs<F> {
|
||||
polys.rev().fold(PolynomialCoeffs::empty(), |mut acc, x| {
|
||||
self.mul_poly(&mut acc);
|
||||
acc += x.borrow();
|
||||
acc
|
||||
})
|
||||
}
|
||||
|
||||
pub fn shift(&mut self, x: F) -> F {
|
||||
let tmp = self.base.exp(self.count) * x;
|
||||
self.count = 0;
|
||||
tmp
|
||||
}
|
||||
|
||||
pub fn shift_poly(&mut self, p: &mut PolynomialCoeffs<F>) {
|
||||
*p *= self.base.exp(self.count);
|
||||
self.count = 0;
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.count = 0;
|
||||
}
|
||||
|
||||
pub fn repeated_frobenius<const D: usize>(&self, count: usize) -> Self
|
||||
where
|
||||
F: Frobenius<D>,
|
||||
{
|
||||
Self {
|
||||
base: self.base.repeated_frobenius(count),
|
||||
count: self.count,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,13 +55,13 @@ pub(crate) fn verify<F: Extendable<D>, const D: usize>(
|
||||
);
|
||||
|
||||
// Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta.
|
||||
let quotient_polys_zeta = proof.openings.quotient_polys;
|
||||
let quotient_polys_zeta = &proof.openings.quotient_polys;
|
||||
let z_h_zeta = eval_zero_poly(common_data.degree(), zeta);
|
||||
for i in 0..num_challenges {
|
||||
ensure!(vanishing_polys_zeta[i] == z_h_zeta * quotient_polys_zeta[i]);
|
||||
}
|
||||
|
||||
let evaluations = todo!();
|
||||
let evaluations = proof.openings.clone();
|
||||
|
||||
let merkle_roots = &[
|
||||
verifier_data.constants_root,
|
||||
@ -71,9 +71,13 @@ pub(crate) fn verify<F: Extendable<D>, const D: usize>(
|
||||
proof.quotient_polys_root,
|
||||
];
|
||||
|
||||
proof
|
||||
.opening_proof
|
||||
.verify(zeta, evaluations, merkle_roots, &mut challenger, fri_config)?;
|
||||
proof.opening_proof.verify(
|
||||
zeta,
|
||||
&evaluations,
|
||||
merkle_roots,
|
||||
&mut challenger,
|
||||
fri_config,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use std::ops::Range;
|
||||
|
||||
/// Represents a wire in the circuit.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
@ -13,4 +14,8 @@ impl Wire {
|
||||
pub fn is_routable(&self, config: &CircuitConfig) -> bool {
|
||||
self.input < config.num_routed_wires
|
||||
}
|
||||
|
||||
pub fn from_range(gate: usize, range: Range<usize>) -> Vec<Self> {
|
||||
range.map(|i| Wire { gate, input: i }).collect()
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::{Extendable, FieldExtension};
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::gate::GateInstance;
|
||||
@ -42,6 +43,15 @@ impl<F: Field> PartialWitness<F> {
|
||||
targets.iter().map(|&t| self.get_target(t)).collect()
|
||||
}
|
||||
|
||||
pub fn get_extension_target<const D: usize>(&self, et: ExtensionTarget<D>) -> F::Extension
|
||||
where
|
||||
F: Extendable<D>,
|
||||
{
|
||||
F::Extension::from_basefield_array(
|
||||
self.get_targets(&et.to_target_array()).try_into().unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn try_get_target(&self, target: Target) -> Option<F> {
|
||||
self.target_values.get(&target).cloned()
|
||||
}
|
||||
@ -73,6 +83,19 @@ impl<F: Field> PartialWitness<F> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_extension_target<const D: usize>(
|
||||
&mut self,
|
||||
et: ExtensionTarget<D>,
|
||||
value: F::Extension,
|
||||
) where
|
||||
F: Extendable<D>,
|
||||
{
|
||||
let limbs = value.to_basefield_array();
|
||||
(0..D).for_each(|i| {
|
||||
self.set_target(et.0[i], limbs[i]);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_wire(&mut self, wire: Wire, value: F) {
|
||||
self.set_target(Target::Wire(wire), value)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user