Merge branch 'main' into permutation_argument

This commit is contained in:
wborgeaud 2021-06-17 14:00:44 +02:00
commit 1b99f8272f
39 changed files with 1817 additions and 125 deletions

View File

@ -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);
}

View File

@ -7,7 +7,7 @@ use crate::gates::gate::GateRef;
use crate::generator::WitnessGenerator;
use crate::polynomial::commitment::ListPolynomialCommitment;
use crate::proof::{Hash, HashTarget, Proof};
use crate::prover::prove;
use crate::prover::{prove, PLONK_BLINDING};
use crate::verifier::verify;
use crate::witness::PartialWitness;
@ -48,6 +48,23 @@ 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,
blinding: PLONK_BLINDING.to_vec(),
},
}
}
}
/// Circuit data required by the prover or the verifier.

View File

@ -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;

View File

@ -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));
}

View File

@ -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()
}

View File

@ -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()
);
}

View File

@ -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]

View File

@ -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()
}

View File

@ -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),
}
}
}

View File

@ -1,6 +1,7 @@
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,

View File

@ -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.")

View File

@ -0,0 +1,354 @@
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::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 = [0, 1, 4]
.iter()
.flat_map(|&i| proof.unsalted_evals(i, config))
.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(3, config)
.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(2, config)
.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);
}
}

View File

@ -1,6 +1,6 @@
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::fri::FriConfig;
@ -159,6 +159,7 @@ fn fri_combine_initial<F: Field + Extendable<D>, const D: usize>(
// - one for Zs, which are opened at `x` and `g x`
// - one for wire polynomials, which are opened at `x` and its conjugate
// Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials.
let single_evals = [0, 1, 4]
.iter()
.flat_map(|&i| proof.unsalted_evals(i, config))
@ -173,6 +174,7 @@ fn fri_combine_initial<F: Field + Extendable<D>, const D: usize>(
let single_denominator = subgroup_x - zeta;
sum += single_numerator / single_denominator;
// Polynomials opened at `x` and `g x`, i.e., the Zs polynomials.
let zs_evals = proof
.unsalted_evals(3, config)
.iter()
@ -190,20 +192,25 @@ fn fri_combine_initial<F: Field + Extendable<D>, const D: usize>(
let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right);
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)
.iter()
.map(|&e| F::Extension::from_basefield(e));
let wire_composition_eval = reduce_with_iter(wire_evals, alpha_powers.clone());
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 wire_eval = reduce_with_iter(&os.wires, alpha_powers.clone());
// 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 alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1);
let wire_eval_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius();
let wire_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]);
let wire_numerator = wire_composition_eval - wire_interpol.eval(subgroup_x);
let wire_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob);
sum += wire_numerator / wire_denominator;
sum
}
@ -276,7 +283,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;
}

View File

@ -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,107 @@ 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::prover::PLONK_BLINDING;
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
View 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);
}
}
}

View File

@ -0,0 +1,140 @@
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;
use std::marker::PhantomData;
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 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::lagrange::{interpolant, interpolate};
use crate::witness::PartialWitness;
use std::convert::TryInto;
#[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());
}
}

View File

@ -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;

View File

@ -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>,

View 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
View 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
View 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());
}
}

View File

@ -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
View 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))
}
}

View File

@ -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."
);
}

View File

@ -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
View 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>())
}
}

View File

@ -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 {

View File

@ -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());

View File

@ -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,

View File

@ -206,10 +206,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.

View File

@ -1,8 +1,8 @@
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};
@ -10,7 +10,7 @@ use crate::merkle_tree::MerkleTree;
use crate::plonk_challenger::Challenger;
use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter};
use crate::polynomial::polynomial::PolynomialCoeffs;
use crate::proof::{FriProof, Hash, OpeningSet};
use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet};
use crate::timed;
use crate::util::{log2_strict, reverse_index_bits_in_place, transpose};
@ -253,6 +253,10 @@ 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;

View File

@ -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::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,39 +67,33 @@ 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>)>,
}
@ -108,13 +105,28 @@ impl<F: Field> FriInitialTreeProof<F> {
}
}
pub struct FriInitialTreeProofTarget {
pub evals_proofs: Vec<(Vec<Target>, MerkleProofTarget)>,
}
impl FriInitialTreeProofTarget {
pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> &[Target] {
let evals = &self.evals_proofs[i].0;
&evals[..evals.len() - config.salt_size(i)]
}
}
/// 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>>,
}

View File

@ -115,7 +115,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,

View File

@ -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);

View File

@ -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()
}
}

View File

@ -7,7 +7,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
}

View File

@ -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(())
}

View File

@ -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()
}
}

View File

@ -1,9 +1,11 @@
use std::collections::HashMap;
use crate::field::extension_field::target::ExtensionTarget;
use crate::field::extension_field::{Extendable, FieldExtension};
use crate::field::field::Field;
use crate::target::Target;
use crate::wire::Wire;
use std::convert::TryInto;
#[derive(Clone, Debug)]
pub struct PartialWitness<F: Field> {
@ -39,6 +41,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()
}
@ -70,6 +81,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)
}