mirror of
https://github.com/saymrwulf/pasta_curves-source.git
synced 2026-09-05 20:10:32 +00:00
Merge pull request #2 from zcash/universal-circuits
Implementation of generalized PLONK
This commit is contained in:
commit
4c9a05ba74
7 changed files with 946 additions and 397 deletions
|
|
@ -313,6 +313,29 @@ pub fn compute_inner_product<F: Field>(a: &[F], b: &[F]) -> F {
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Divides polynomial `a` in `X` by `X - b` with
|
||||||
|
/// no remainder.
|
||||||
|
pub fn kate_division<'a, F: Field, I: IntoIterator<Item = &'a F>>(a: I, mut b: F) -> Vec<F>
|
||||||
|
where
|
||||||
|
I::IntoIter: DoubleEndedIterator + ExactSizeIterator,
|
||||||
|
{
|
||||||
|
b = -b;
|
||||||
|
let a = a.into_iter();
|
||||||
|
|
||||||
|
let mut q = vec![F::zero(); a.len() - 1];
|
||||||
|
|
||||||
|
let mut tmp = F::zero();
|
||||||
|
for (q, r) in q.iter_mut().rev().zip(a.rev()) {
|
||||||
|
let mut lead_coeff = *r;
|
||||||
|
lead_coeff.sub_assign(&tmp);
|
||||||
|
*q = lead_coeff;
|
||||||
|
tmp = lead_coeff;
|
||||||
|
tmp.mul_assign(&b);
|
||||||
|
}
|
||||||
|
|
||||||
|
q
|
||||||
|
}
|
||||||
|
|
||||||
/// This simple utility function will parallelize an operation that is to be
|
/// This simple utility function will parallelize an operation that is to be
|
||||||
/// performed over a mutable slice.
|
/// performed over a mutable slice.
|
||||||
pub fn parallelize<T: Send, F: Fn(&mut [T], usize) + Send + Clone>(v: &mut [T], f: F) {
|
pub fn parallelize<T: Send, F: Fn(&mut [T], usize) + Send + Clone>(v: &mut [T], f: F) {
|
||||||
|
|
|
||||||
224
src/plonk.rs
224
src/plonk.rs
|
|
@ -23,45 +23,29 @@ pub use verifier::*;
|
||||||
|
|
||||||
use domain::EvaluationDomain;
|
use domain::EvaluationDomain;
|
||||||
|
|
||||||
// TODO: remove this
|
|
||||||
const GATE_DEGREE: u32 = 3;
|
|
||||||
|
|
||||||
/// This is a structured reference string (SRS) that is (deterministically)
|
/// This is a structured reference string (SRS) that is (deterministically)
|
||||||
/// computed from a specific circuit and parameters for the polynomial
|
/// computed from a specific circuit and parameters for the polynomial
|
||||||
/// commitment scheme.
|
/// commitment scheme.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SRS<C: CurveAffine> {
|
pub struct SRS<C: CurveAffine> {
|
||||||
sa: (Vec<C::Scalar>, Vec<C::Scalar>),
|
|
||||||
sb: (Vec<C::Scalar>, Vec<C::Scalar>),
|
|
||||||
sc: (Vec<C::Scalar>, Vec<C::Scalar>),
|
|
||||||
sd: (Vec<C::Scalar>, Vec<C::Scalar>),
|
|
||||||
sm: (Vec<C::Scalar>, Vec<C::Scalar>),
|
|
||||||
sa_commitment: C,
|
|
||||||
sb_commitment: C,
|
|
||||||
sc_commitment: C,
|
|
||||||
sd_commitment: C,
|
|
||||||
sm_commitment: C,
|
|
||||||
domain: EvaluationDomain<C::Scalar>,
|
domain: EvaluationDomain<C::Scalar>,
|
||||||
|
fixed_commitments: Vec<C>,
|
||||||
|
fixed_polys: Vec<Vec<C::Scalar>>,
|
||||||
|
fixed_cosets: Vec<Vec<C::Scalar>>,
|
||||||
|
meta: MetaCircuit<C::Scalar>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is an object which represents a (Turbo)PLONK proof.
|
/// This is an object which represents a (Turbo)PLONK proof.
|
||||||
|
// This structure must never allow points at infinity.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Proof<C: CurveAffine> {
|
pub struct Proof<C: CurveAffine> {
|
||||||
a_commitment: C,
|
advice_commitments: Vec<C>,
|
||||||
b_commitment: C,
|
|
||||||
c_commitment: C,
|
|
||||||
d_commitment: C,
|
|
||||||
h_commitments: Vec<C>,
|
h_commitments: Vec<C>,
|
||||||
a_eval_x: C::Scalar,
|
advice_evals: Vec<C::Scalar>,
|
||||||
b_eval_x: C::Scalar,
|
fixed_evals: Vec<C::Scalar>,
|
||||||
c_eval_x: C::Scalar,
|
h_evals: Vec<C::Scalar>,
|
||||||
d_eval_x: C::Scalar,
|
f_commitment: C,
|
||||||
sa_eval_x: C::Scalar,
|
q_evals: Vec<C::Scalar>,
|
||||||
sb_eval_x: C::Scalar,
|
|
||||||
sc_eval_x: C::Scalar,
|
|
||||||
sd_eval_x: C::Scalar,
|
|
||||||
sm_eval_x: C::Scalar,
|
|
||||||
h_evals_x: Vec<C::Scalar>,
|
|
||||||
opening: OpeningProof<C>,
|
opening: OpeningProof<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,6 +61,8 @@ pub enum Error {
|
||||||
IncompatibleParams,
|
IncompatibleParams,
|
||||||
/// The constraint system is not satisfied.
|
/// The constraint system is not satisfied.
|
||||||
ConstraintSystemFailure,
|
ConstraintSystemFailure,
|
||||||
|
/// Out of bounds index passed to a backend
|
||||||
|
BoundsFailure,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_point<C: CurveAffine, H: Hasher<C::Base>>(
|
fn hash_point<C: CurveAffine, H: Hasher<C::Base>>(
|
||||||
|
|
@ -98,32 +84,186 @@ fn test_proving() {
|
||||||
use crate::arithmetic::{EqAffine, Field, Fp, Fq};
|
use crate::arithmetic::{EqAffine, Field, Fp, Fq};
|
||||||
use crate::polycommit::Params;
|
use crate::polycommit::Params;
|
||||||
use crate::transcript::DummyHash;
|
use crate::transcript::DummyHash;
|
||||||
|
use std::marker::PhantomData;
|
||||||
const K: u32 = 5;
|
const K: u32 = 5;
|
||||||
|
|
||||||
// Initialize the polynomial commitment parameters
|
// Initialize the polynomial commitment parameters
|
||||||
let params: Params<EqAffine> = Params::new::<DummyHash<Fq>>(K);
|
let params: Params<EqAffine> = Params::new::<DummyHash<Fq>>(K);
|
||||||
|
|
||||||
|
struct PLONKConfig {
|
||||||
|
a: AdviceWire,
|
||||||
|
b: AdviceWire,
|
||||||
|
c: AdviceWire,
|
||||||
|
|
||||||
|
sa: FixedWire,
|
||||||
|
sb: FixedWire,
|
||||||
|
sc: FixedWire,
|
||||||
|
sm: FixedWire,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct Variable(AdviceWire, usize);
|
||||||
|
|
||||||
|
trait StandardCS<FF: Field> {
|
||||||
|
fn raw_multiply<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<(FF, FF, FF), Error>;
|
||||||
|
fn raw_add<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<(FF, FF, FF), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
struct MyCircuit<F: Field> {
|
struct MyCircuit<F: Field> {
|
||||||
a: Option<F>,
|
a: Option<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct StandardPLONK<'a, F: Field, CS: ConstraintSystem<F> + 'a> {
|
||||||
|
cs: &'a mut CS,
|
||||||
|
config: PLONKConfig,
|
||||||
|
current_gate: usize,
|
||||||
|
_marker: PhantomData<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, FF: Field, CS: ConstraintSystem<FF>> StandardPLONK<'a, FF, CS> {
|
||||||
|
fn new(cs: &'a mut CS, config: PLONKConfig) -> Self {
|
||||||
|
StandardPLONK {
|
||||||
|
cs,
|
||||||
|
config,
|
||||||
|
current_gate: 0,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, FF: Field, CS: ConstraintSystem<FF>> StandardCS<FF> for StandardPLONK<'a, FF, CS> {
|
||||||
|
fn raw_multiply<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<(FF, FF, FF), Error>,
|
||||||
|
{
|
||||||
|
let index = self.current_gate;
|
||||||
|
self.current_gate += 1;
|
||||||
|
let mut value = None;
|
||||||
|
self.cs.assign_advice(self.config.a, index, || {
|
||||||
|
value = Some(f()?);
|
||||||
|
Ok(value.ok_or(Error::SynthesisError)?.0)
|
||||||
|
})?;
|
||||||
|
self.cs.assign_advice(self.config.b, index, || {
|
||||||
|
Ok(value.ok_or(Error::SynthesisError)?.1)
|
||||||
|
})?;
|
||||||
|
self.cs.assign_advice(self.config.c, index, || {
|
||||||
|
Ok(value.ok_or(Error::SynthesisError)?.2)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.cs
|
||||||
|
.assign_fixed(self.config.sa, index, || Ok(FF::zero()))?;
|
||||||
|
self.cs
|
||||||
|
.assign_fixed(self.config.sb, index, || Ok(FF::zero()))?;
|
||||||
|
self.cs
|
||||||
|
.assign_fixed(self.config.sc, index, || Ok(FF::one()))?;
|
||||||
|
self.cs
|
||||||
|
.assign_fixed(self.config.sm, index, || Ok(FF::one()))?;
|
||||||
|
Ok((
|
||||||
|
Variable(self.config.a, index),
|
||||||
|
Variable(self.config.b, index),
|
||||||
|
Variable(self.config.c, index),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
fn raw_add<F>(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<(FF, FF, FF), Error>,
|
||||||
|
{
|
||||||
|
let index = self.current_gate;
|
||||||
|
self.current_gate += 1;
|
||||||
|
let mut value = None;
|
||||||
|
self.cs.assign_advice(self.config.a, index, || {
|
||||||
|
value = Some(f()?);
|
||||||
|
Ok(value.ok_or(Error::SynthesisError)?.0)
|
||||||
|
})?;
|
||||||
|
self.cs.assign_advice(self.config.b, index, || {
|
||||||
|
Ok(value.ok_or(Error::SynthesisError)?.1)
|
||||||
|
})?;
|
||||||
|
self.cs.assign_advice(self.config.c, index, || {
|
||||||
|
Ok(value.ok_or(Error::SynthesisError)?.2)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.cs
|
||||||
|
.assign_fixed(self.config.sa, index, || Ok(FF::one()))?;
|
||||||
|
self.cs
|
||||||
|
.assign_fixed(self.config.sb, index, || Ok(FF::one()))?;
|
||||||
|
self.cs
|
||||||
|
.assign_fixed(self.config.sc, index, || Ok(FF::one()))?;
|
||||||
|
self.cs
|
||||||
|
.assign_fixed(self.config.sm, index, || Ok(FF::zero()))?;
|
||||||
|
Ok((
|
||||||
|
Variable(self.config.a, index),
|
||||||
|
Variable(self.config.b, index),
|
||||||
|
Variable(self.config.c, index),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<F: Field> Circuit<F> for MyCircuit<F> {
|
impl<F: Field> Circuit<F> for MyCircuit<F> {
|
||||||
fn synthesize(&self, cs: &mut impl ConstraintSystem<F>) -> Result<(), Error> {
|
type Config = PLONKConfig;
|
||||||
|
|
||||||
|
fn configure(meta: &mut MetaCircuit<F>) -> PLONKConfig {
|
||||||
|
let a = meta.advice_wire();
|
||||||
|
let b = meta.advice_wire();
|
||||||
|
let c = meta.advice_wire();
|
||||||
|
|
||||||
|
let sa = meta.fixed_wire();
|
||||||
|
let sb = meta.fixed_wire();
|
||||||
|
let sc = meta.fixed_wire();
|
||||||
|
let sm = meta.fixed_wire();
|
||||||
|
|
||||||
|
meta.create_gate(|meta| {
|
||||||
|
let a = meta.query_advice(a, 0);
|
||||||
|
let b = meta.query_advice(b, 0);
|
||||||
|
let c = meta.query_advice(c, 0);
|
||||||
|
|
||||||
|
let sa = meta.query_fixed(sa, 0);
|
||||||
|
let sb = meta.query_fixed(sb, 0);
|
||||||
|
let sc = meta.query_fixed(sc, 0);
|
||||||
|
let sm = meta.query_fixed(sm, 0);
|
||||||
|
|
||||||
|
a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one()))
|
||||||
|
});
|
||||||
|
|
||||||
|
PLONKConfig {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
sa,
|
||||||
|
sb,
|
||||||
|
sc,
|
||||||
|
sm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synthesize(
|
||||||
|
&self,
|
||||||
|
cs: &mut impl ConstraintSystem<F>,
|
||||||
|
config: PLONKConfig,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut cs = StandardPLONK::new(cs, config);
|
||||||
|
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
let (_, _, _, _) = cs.multiply(|| {
|
let mut a_squared = None;
|
||||||
let a = self.a.ok_or(Error::SynthesisError)?;
|
let (_, _, _) = cs.raw_multiply(|| {
|
||||||
let a2 = a.square();
|
a_squared = self.a.map(|a| a.square());
|
||||||
Ok((a, a, a2))
|
Ok((
|
||||||
|
self.a.ok_or(Error::SynthesisError)?,
|
||||||
|
self.a.ok_or(Error::SynthesisError)?,
|
||||||
|
a_squared.ok_or(Error::SynthesisError)?,
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
//cs.copy(a, b);
|
let (_, _, _) = cs.raw_add(|| {
|
||||||
let (_, _, _, _) = cs.add(|| {
|
let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2));
|
||||||
let a = self.a.ok_or(Error::SynthesisError)?;
|
Ok((
|
||||||
let a2 = a.square();
|
self.a.ok_or(Error::SynthesisError)?,
|
||||||
let a3 = a + a2;
|
a_squared.ok_or(Error::SynthesisError)?,
|
||||||
Ok((a, a2, a3))
|
fin.ok_or(Error::SynthesisError)?,
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
//cs.copy(a, d);
|
|
||||||
//cs.copy(c, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -134,8 +274,10 @@ fn test_proving() {
|
||||||
a: Some((-Fp::from_u64(2) + Fp::ROOT_OF_UNITY).pow(&[100, 0, 0, 0])),
|
a: Some((-Fp::from_u64(2) + Fp::ROOT_OF_UNITY).pow(&[100, 0, 0, 0])),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let empty_circuit: MyCircuit<Fp> = MyCircuit { a: None };
|
||||||
|
|
||||||
// Initialize the SRS
|
// Initialize the SRS
|
||||||
let srs = SRS::generate(¶ms, &circuit).expect("SRS generation should not fail");
|
let srs = SRS::generate(¶ms, &empty_circuit).expect("SRS generation should not fail");
|
||||||
|
|
||||||
// Create a proof
|
// Create a proof
|
||||||
let proof = Proof::create::<DummyHash<Fq>, DummyHash<Fp>, _>(¶ms, &srs, &circuit)
|
let proof = Proof::create::<DummyHash<Fq>, DummyHash<Fp>, _>(¶ms, &srs, &circuit)
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,37 @@
|
||||||
use super::Error;
|
use core::cmp::max;
|
||||||
|
use core::ops::{Add, Mul};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::Error;
|
||||||
use crate::arithmetic::Field;
|
use crate::arithmetic::Field;
|
||||||
|
|
||||||
/// This represents a PLONK wire, which could be a fixed (selector) wire or an
|
use super::domain::Rotation;
|
||||||
/// advice wire.
|
/// This represents a wire which has a fixed (permanent) value
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub enum Wire {
|
pub struct FixedWire(pub usize);
|
||||||
/// A wires
|
|
||||||
A(usize),
|
/// This represents a wire which has a witness-specific value
|
||||||
/// B wires
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
B(usize),
|
pub struct AdviceWire(pub usize);
|
||||||
/// C wires
|
|
||||||
C(usize),
|
|
||||||
/// D wires
|
|
||||||
D(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This trait allows a [`Circuit`] to direct some backend to assign a witness
|
/// This trait allows a [`Circuit`] to direct some backend to assign a witness
|
||||||
/// for a constraint system.
|
/// for a constraint system.
|
||||||
pub trait ConstraintSystem<F: Field> {
|
pub trait ConstraintSystem<F: Field> {
|
||||||
/// Creates a gate.
|
/// Assign an advice wire value (witness)
|
||||||
fn create_gate(
|
fn assign_advice(
|
||||||
&mut self,
|
&mut self,
|
||||||
sa: F,
|
wire: AdviceWire,
|
||||||
sb: F,
|
row: usize,
|
||||||
sc: F,
|
to: impl FnOnce() -> Result<F, Error>,
|
||||||
sd: F,
|
) -> Result<(), Error>;
|
||||||
sm: F,
|
|
||||||
f: impl Fn() -> Result<(F, F, F, F), Error>,
|
|
||||||
) -> Result<(Wire, Wire, Wire, Wire), Error>;
|
|
||||||
|
|
||||||
/// a * b - c = 0
|
/// Assign a fixed value
|
||||||
fn multiply(
|
fn assign_fixed(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: impl Fn() -> Result<(F, F, F), Error>,
|
wire: FixedWire,
|
||||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
row: usize,
|
||||||
self.create_gate(F::zero(), F::zero(), F::one(), F::zero(), F::one(), || {
|
to: impl FnOnce() -> Result<F, Error>,
|
||||||
let (a, b, c) = f()?;
|
) -> Result<(), Error>;
|
||||||
Ok((a, b, c, F::zero()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// a + b - c = 0
|
|
||||||
fn add(
|
|
||||||
&mut self,
|
|
||||||
f: impl Fn() -> Result<(F, F, F), Error>,
|
|
||||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
|
||||||
self.create_gate(F::one(), F::one(), F::one(), F::zero(), F::zero(), || {
|
|
||||||
let (a, b, c) = f()?;
|
|
||||||
Ok((a, b, c, F::zero()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn copy(&mut self, left: Wire, right: Wire);
|
// fn copy(&mut self, left: Wire, right: Wire);
|
||||||
}
|
}
|
||||||
|
|
@ -59,8 +40,186 @@ pub trait ConstraintSystem<F: Field> {
|
||||||
/// backend prover can ask the circuit to synthesize using some given
|
/// backend prover can ask the circuit to synthesize using some given
|
||||||
/// [`ConstraintSystem`] implementation.
|
/// [`ConstraintSystem`] implementation.
|
||||||
pub trait Circuit<F: Field> {
|
pub trait Circuit<F: Field> {
|
||||||
|
/// This is a configuration object that stores things like wires.
|
||||||
|
type Config;
|
||||||
|
|
||||||
|
/// The circuit is given an opportunity to describe the exact gate
|
||||||
|
/// arrangement, wire arrangement, etc.
|
||||||
|
fn configure(meta: &mut MetaCircuit<F>) -> Self::Config;
|
||||||
|
|
||||||
/// Given the provided `cs`, synthesize the circuit. The concrete type of
|
/// Given the provided `cs`, synthesize the circuit. The concrete type of
|
||||||
/// the caller will be different depending on the context, and they may or
|
/// the caller will be different depending on the context, and they may or
|
||||||
/// may not expect to have a witness present.
|
/// may not expect to have a witness present.
|
||||||
fn synthesize(&self, cs: &mut impl ConstraintSystem<F>) -> Result<(), Error>;
|
fn synthesize(
|
||||||
|
&self,
|
||||||
|
cs: &mut impl ConstraintSystem<F>,
|
||||||
|
config: Self::Config,
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Low-degree polynomial representing an identity that must hold over the committed wires.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Polynomial<F> {
|
||||||
|
/// This is a fixed wire queried at a certain relative location
|
||||||
|
Fixed(usize),
|
||||||
|
/// This is an advice (witness) wire queried at a certain relative location
|
||||||
|
Advice(usize),
|
||||||
|
/// This is the sum of two polynomials
|
||||||
|
Sum(Box<Polynomial<F>>, Box<Polynomial<F>>),
|
||||||
|
/// This is the product of two polynomials
|
||||||
|
Product(Box<Polynomial<F>>, Box<Polynomial<F>>),
|
||||||
|
/// This is a scaled polynomial
|
||||||
|
Scaled(Box<Polynomial<F>>, F),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> Polynomial<F> {
|
||||||
|
/// Evaluate the polynomial using the provided closures to perform the
|
||||||
|
/// operations.
|
||||||
|
pub fn evaluate<T>(
|
||||||
|
&self,
|
||||||
|
fixed_wire: &impl Fn(usize) -> T,
|
||||||
|
advice_wire: &impl Fn(usize) -> T,
|
||||||
|
sum: &impl Fn(T, T) -> T,
|
||||||
|
product: &impl Fn(T, T) -> T,
|
||||||
|
scaled: &impl Fn(T, F) -> T,
|
||||||
|
) -> T {
|
||||||
|
match self {
|
||||||
|
Polynomial::Fixed(index) => fixed_wire(*index),
|
||||||
|
Polynomial::Advice(index) => advice_wire(*index),
|
||||||
|
Polynomial::Sum(a, b) => {
|
||||||
|
let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||||
|
let b = b.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||||
|
sum(a, b)
|
||||||
|
}
|
||||||
|
Polynomial::Product(a, b) => {
|
||||||
|
let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||||
|
let b = b.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||||
|
product(a, b)
|
||||||
|
}
|
||||||
|
Polynomial::Scaled(a, f) => {
|
||||||
|
let a = a.evaluate(fixed_wire, advice_wire, sum, product, scaled);
|
||||||
|
scaled(a, *f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the degree of this polynomial
|
||||||
|
pub fn degree(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Polynomial::Fixed(_) => 1,
|
||||||
|
Polynomial::Advice(_) => 1,
|
||||||
|
Polynomial::Sum(a, b) => max(a.degree(), b.degree()),
|
||||||
|
Polynomial::Product(a, b) => a.degree() + b.degree(),
|
||||||
|
Polynomial::Scaled(poly, _) => poly.degree(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> Add for Polynomial<F> {
|
||||||
|
type Output = Polynomial<F>;
|
||||||
|
fn add(self, rhs: Polynomial<F>) -> Polynomial<F> {
|
||||||
|
Polynomial::Sum(Box::new(self), Box::new(rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> Mul for Polynomial<F> {
|
||||||
|
type Output = Polynomial<F>;
|
||||||
|
fn mul(self, rhs: Polynomial<F>) -> Polynomial<F> {
|
||||||
|
Polynomial::Product(Box::new(self), Box::new(rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> Mul<F> for Polynomial<F> {
|
||||||
|
type Output = Polynomial<F>;
|
||||||
|
fn mul(self, rhs: F) -> Polynomial<F> {
|
||||||
|
Polynomial::Scaled(Box::new(self), rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents an index into a vector where each entry corresponds to a distinct
|
||||||
|
/// point that polynomials are queried at.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct PointIndex(pub usize);
|
||||||
|
|
||||||
|
/// This is a description of the circuit environment, such as the gate, wire and
|
||||||
|
/// permutation arrangements.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MetaCircuit<F> {
|
||||||
|
pub(crate) num_fixed_wires: usize,
|
||||||
|
pub(crate) num_advice_wires: usize,
|
||||||
|
// permutations: Vec<Vec<Wire>>,
|
||||||
|
pub(crate) gates: Vec<Polynomial<F>>,
|
||||||
|
pub(crate) advice_queries: Vec<(AdviceWire, Rotation)>,
|
||||||
|
pub(crate) fixed_queries: Vec<(FixedWire, Rotation)>,
|
||||||
|
|
||||||
|
// Mapping from a witness vector rotation to the index in the point vector.
|
||||||
|
pub(crate) rotations: HashMap<Rotation, PointIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> Default for MetaCircuit<F> {
|
||||||
|
fn default() -> MetaCircuit<F> {
|
||||||
|
let mut rotations = HashMap::new();
|
||||||
|
rotations.insert(Rotation::default(), PointIndex(0));
|
||||||
|
|
||||||
|
MetaCircuit {
|
||||||
|
num_fixed_wires: 0,
|
||||||
|
num_advice_wires: 0,
|
||||||
|
gates: vec![],
|
||||||
|
fixed_queries: Vec::new(),
|
||||||
|
advice_queries: Vec::new(),
|
||||||
|
rotations,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> MetaCircuit<F> {
|
||||||
|
/// Query a fixed wire at a relative position
|
||||||
|
pub fn query_fixed(&mut self, wire: FixedWire, at: i32) -> Polynomial<F> {
|
||||||
|
let at = Rotation(at);
|
||||||
|
{
|
||||||
|
let len = self.rotations.len();
|
||||||
|
self.rotations.entry(at).or_insert(PointIndex(len));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check for existing query so we don't make redundant queries
|
||||||
|
let index = self.fixed_queries.len();
|
||||||
|
self.fixed_queries.push((wire, at));
|
||||||
|
|
||||||
|
Polynomial::Fixed(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query an advice wire at a relative position
|
||||||
|
pub fn query_advice(&mut self, wire: AdviceWire, at: i32) -> Polynomial<F> {
|
||||||
|
let at = Rotation(at);
|
||||||
|
{
|
||||||
|
let len = self.rotations.len();
|
||||||
|
self.rotations.entry(at).or_insert(PointIndex(len));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check for existing query so we don't make redundant queries
|
||||||
|
let index = self.advice_queries.len();
|
||||||
|
self.advice_queries.push((wire, at));
|
||||||
|
|
||||||
|
Polynomial::Advice(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new gate
|
||||||
|
pub fn create_gate(&mut self, f: impl FnOnce(&mut Self) -> Polynomial<F>) {
|
||||||
|
let poly = f(self);
|
||||||
|
self.gates.push(poly);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a new fixed wire
|
||||||
|
pub fn fixed_wire(&mut self) -> FixedWire {
|
||||||
|
let tmp = FixedWire(self.num_fixed_wires);
|
||||||
|
self.num_fixed_wires += 1;
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a new advice wire
|
||||||
|
pub fn advice_wire(&mut self) -> AdviceWire {
|
||||||
|
let tmp = AdviceWire(self.num_advice_wires);
|
||||||
|
self.num_advice_wires += 1;
|
||||||
|
tmp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,16 @@
|
||||||
use crate::arithmetic::{best_fft, parallelize, Field, Group};
|
use crate::arithmetic::{best_fft, parallelize, Field, Group};
|
||||||
|
|
||||||
|
/// Describes a relative location in the evaluation domain; applying a rotation
|
||||||
|
/// by i will rotate the vector in the evaluation domain by i.
|
||||||
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
|
pub struct Rotation(pub i32);
|
||||||
|
|
||||||
|
impl Default for Rotation {
|
||||||
|
fn default() -> Rotation {
|
||||||
|
Rotation(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This structure contains precomputed constants and other details needed for
|
/// This structure contains precomputed constants and other details needed for
|
||||||
/// performing operations on an evaluation domain of size $2^k$ in the context
|
/// performing operations on an evaluation domain of size $2^k$ in the context
|
||||||
/// of PLONK.
|
/// of PLONK.
|
||||||
|
|
@ -8,6 +19,7 @@ pub struct EvaluationDomain<G: Group> {
|
||||||
n: u64,
|
n: u64,
|
||||||
k: u32,
|
k: u32,
|
||||||
extended_k: u32,
|
extended_k: u32,
|
||||||
|
omega: G::Scalar,
|
||||||
omega_inv: G::Scalar,
|
omega_inv: G::Scalar,
|
||||||
extended_omega: G::Scalar,
|
extended_omega: G::Scalar,
|
||||||
extended_omega_inv: G::Scalar,
|
extended_omega_inv: G::Scalar,
|
||||||
|
|
@ -91,6 +103,7 @@ impl<G: Group> EvaluationDomain<G> {
|
||||||
n,
|
n,
|
||||||
k,
|
k,
|
||||||
extended_k,
|
extended_k,
|
||||||
|
omega,
|
||||||
omega_inv,
|
omega_inv,
|
||||||
extended_omega,
|
extended_omega,
|
||||||
extended_omega_inv,
|
extended_omega_inv,
|
||||||
|
|
@ -103,32 +116,46 @@ impl<G: Group> EvaluationDomain<G> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This takes us from an n-length vector into the coset evaluation domain.
|
/// This takes us from an n-length vector into the coefficient form.
|
||||||
/// Also returns the polynomial.
|
|
||||||
///
|
///
|
||||||
/// This function will panic if the provided vector is not the correct
|
/// This function will panic if the provided vector is not the correct
|
||||||
/// length.
|
/// length.
|
||||||
pub fn obtain_coset(&self, mut a: Vec<G>) -> (Vec<G>, Vec<G>) {
|
pub fn obtain_poly(&self, mut a: Vec<G>) -> Vec<G> {
|
||||||
assert_eq!(a.len(), 1 << self.k);
|
assert_eq!(a.len(), 1 << self.k);
|
||||||
|
|
||||||
// Perform inverse FFT to obtain the polynomial in coefficient form
|
// Perform inverse FFT to obtain the polynomial in coefficient form
|
||||||
Self::ifft(&mut a, self.omega_inv, self.k, self.ifft_divisor);
|
Self::ifft(&mut a, self.omega_inv, self.k, self.ifft_divisor);
|
||||||
|
|
||||||
// Keep this polynomial around; we'll need to evaluate it at arbitrary
|
a
|
||||||
// points later.
|
}
|
||||||
let old = a.clone();
|
|
||||||
|
|
||||||
// Distributes powers so that an FFT will move us into the coset
|
/// This takes us from an n-length coefficient vector into the coset
|
||||||
// evaluation domain.
|
/// evaluation domain, rotating by `rotation` if desired.
|
||||||
Self::distribute_powers(&mut a, self.g_coset);
|
///
|
||||||
|
/// This function will panic if the provided vector is not the correct
|
||||||
|
/// length.
|
||||||
|
pub fn obtain_coset(&self, mut a: Vec<G>, rotation: Rotation) -> Vec<G> {
|
||||||
|
assert_eq!(a.len(), 1 << self.k);
|
||||||
|
|
||||||
// Resize to account for the quotient polynomial's size
|
assert!(rotation.0 != i32::MIN);
|
||||||
a.resize(1 << self.extended_k, G::group_zero());
|
if rotation.0 == 0 {
|
||||||
|
// In this special case, the powers of zeta repeat so we do not need
|
||||||
// Move into coset evaluation domain
|
// to compute them.
|
||||||
|
Self::distribute_powers_zeta(&mut a, self.g_coset);
|
||||||
|
} else {
|
||||||
|
let mut g = G::Scalar::ZETA;
|
||||||
|
if rotation.0 > 0 {
|
||||||
|
g *= &self.omega.pow_vartime(&[rotation.0 as u64, 0, 0, 0]);
|
||||||
|
} else {
|
||||||
|
g *= &self
|
||||||
|
.omega_inv
|
||||||
|
.pow_vartime(&[rotation.0.abs() as u64, 0, 0, 0]);
|
||||||
|
}
|
||||||
|
Self::distribute_powers(&mut a, g);
|
||||||
|
}
|
||||||
|
a.resize(self.coset_len(), G::group_zero());
|
||||||
best_fft(&mut a, self.extended_omega, self.extended_k);
|
best_fft(&mut a, self.extended_omega, self.extended_k);
|
||||||
|
a
|
||||||
(a, old)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This takes us from the coset evaluation domain and gets us the quotient
|
/// This takes us from the coset evaluation domain and gets us the quotient
|
||||||
|
|
@ -137,7 +164,7 @@ impl<G: Group> EvaluationDomain<G> {
|
||||||
/// This function will panic if the provided vector is not the correct
|
/// This function will panic if the provided vector is not the correct
|
||||||
/// length.
|
/// length.
|
||||||
pub fn from_coset(&self, mut a: Vec<G>) -> Vec<G> {
|
pub fn from_coset(&self, mut a: Vec<G>) -> Vec<G> {
|
||||||
assert_eq!(a.len(), 1 << self.extended_k);
|
assert_eq!(a.len(), self.coset_len());
|
||||||
|
|
||||||
// Inverse FFT
|
// Inverse FFT
|
||||||
Self::ifft(
|
Self::ifft(
|
||||||
|
|
@ -162,7 +189,7 @@ impl<G: Group> EvaluationDomain<G> {
|
||||||
/// This divides the polynomial (in the coset domain) by the vanishing
|
/// This divides the polynomial (in the coset domain) by the vanishing
|
||||||
/// polynomial.
|
/// polynomial.
|
||||||
pub fn divide_by_vanishing_poly(&self, mut h_poly: Vec<G>) -> Vec<G> {
|
pub fn divide_by_vanishing_poly(&self, mut h_poly: Vec<G>) -> Vec<G> {
|
||||||
assert_eq!(h_poly.len(), 1 << self.extended_k);
|
assert_eq!(h_poly.len(), self.coset_len());
|
||||||
|
|
||||||
// Divide to obtain the quotient polynomial in the coset evaluation
|
// Divide to obtain the quotient polynomial in the coset evaluation
|
||||||
// domain.
|
// domain.
|
||||||
|
|
@ -176,7 +203,7 @@ impl<G: Group> EvaluationDomain<G> {
|
||||||
h_poly
|
h_poly
|
||||||
}
|
}
|
||||||
|
|
||||||
fn distribute_powers(mut a: &mut [G], g: G::Scalar) {
|
fn distribute_powers_zeta(mut a: &mut [G], g: G::Scalar) {
|
||||||
let coset_powers = [g, g.square()];
|
let coset_powers = [g, g.square()];
|
||||||
parallelize(&mut a, |a, mut index| {
|
parallelize(&mut a, |a, mut index| {
|
||||||
for a in a {
|
for a in a {
|
||||||
|
|
@ -190,6 +217,16 @@ impl<G: Group> EvaluationDomain<G> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn distribute_powers(mut a: &mut [G], g: G::Scalar) {
|
||||||
|
parallelize(&mut a, |a, index| {
|
||||||
|
let mut cur = g.pow_vartime(&[index as u64, 0, 0, 0]);
|
||||||
|
for a in a {
|
||||||
|
a.group_scale(&cur);
|
||||||
|
cur *= &g;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) {
|
fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) {
|
||||||
best_fft(a, omega_inv, log_n);
|
best_fft(a, omega_inv, log_n);
|
||||||
parallelize(a, |a, _| {
|
parallelize(a, |a, _| {
|
||||||
|
|
@ -199,4 +236,28 @@ impl<G: Group> EvaluationDomain<G> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn coset_len(&self) -> usize {
|
||||||
|
1 << self.extended_k
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_omega(&self) -> G::Scalar {
|
||||||
|
self.omega
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_omega_inv(&self) -> G::Scalar {
|
||||||
|
self.omega_inv
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate_omega(&self, constant: G::Scalar, rotation: Rotation) -> G::Scalar {
|
||||||
|
let mut point = constant;
|
||||||
|
if rotation.0 >= 0 {
|
||||||
|
point *= &self.get_omega().pow(&[rotation.0 as u64, 0, 0, 0]);
|
||||||
|
} else {
|
||||||
|
point *= &self
|
||||||
|
.get_omega_inv()
|
||||||
|
.pow(&[rotation.0.abs() as u64, 0, 0, 0]);
|
||||||
|
}
|
||||||
|
point
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
use super::{
|
use super::{
|
||||||
circuit::{Circuit, ConstraintSystem, Wire},
|
circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit},
|
||||||
|
domain::Rotation,
|
||||||
hash_point, Error, Proof, SRS,
|
hash_point, Error, Proof, SRS,
|
||||||
};
|
};
|
||||||
use crate::arithmetic::{
|
use crate::arithmetic::{
|
||||||
eval_polynomial, get_challenge_scalar, Challenge, Curve, CurveAffine, Field,
|
eval_polynomial, get_challenge_scalar, kate_division, parallelize, Challenge, Curve,
|
||||||
|
CurveAffine, Field,
|
||||||
};
|
};
|
||||||
use crate::polycommit::Params;
|
use crate::polycommit::Params;
|
||||||
use crate::transcript::Hasher;
|
use crate::transcript::Hasher;
|
||||||
|
|
@ -22,121 +24,130 @@ impl<C: CurveAffine> Proof<C> {
|
||||||
circuit: &ConcreteCircuit,
|
circuit: &ConcreteCircuit,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
struct WitnessCollection<F: Field> {
|
struct WitnessCollection<F: Field> {
|
||||||
a: Vec<F>,
|
advice: Vec<Vec<F>>,
|
||||||
b: Vec<F>,
|
|
||||||
c: Vec<F>,
|
|
||||||
d: Vec<F>,
|
|
||||||
sa: Vec<F>,
|
|
||||||
sb: Vec<F>,
|
|
||||||
sc: Vec<F>,
|
|
||||||
sd: Vec<F>,
|
|
||||||
sm: Vec<F>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Field> ConstraintSystem<F> for WitnessCollection<F> {
|
impl<F: Field> ConstraintSystem<F> for WitnessCollection<F> {
|
||||||
fn create_gate(
|
fn assign_advice(
|
||||||
&mut self,
|
&mut self,
|
||||||
sa: F,
|
wire: AdviceWire,
|
||||||
sb: F,
|
row: usize,
|
||||||
sc: F,
|
to: impl FnOnce() -> Result<F, Error>,
|
||||||
sd: F,
|
) -> Result<(), Error> {
|
||||||
sm: F,
|
*self
|
||||||
f: impl Fn() -> Result<(F, F, F, F), Error>,
|
.advice
|
||||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
.get_mut(wire.0)
|
||||||
let (a, b, c, d) = f()?;
|
.and_then(|v| v.get_mut(row))
|
||||||
let tmp = Ok((
|
.ok_or(Error::BoundsFailure)? = to()?;
|
||||||
Wire::A(self.a.len()),
|
|
||||||
Wire::B(self.a.len()),
|
Ok(())
|
||||||
Wire::C(self.a.len()),
|
}
|
||||||
Wire::D(self.a.len()),
|
|
||||||
));
|
fn assign_fixed(
|
||||||
self.a.push(a);
|
&mut self,
|
||||||
self.b.push(b);
|
_: FixedWire,
|
||||||
self.c.push(c);
|
_: usize,
|
||||||
self.d.push(d);
|
_: impl FnOnce() -> Result<F, Error>,
|
||||||
self.sa.push(sa);
|
) -> Result<(), Error> {
|
||||||
self.sb.push(sb);
|
// We only care about advice wires here
|
||||||
self.sc.push(sc);
|
|
||||||
self.sd.push(sd);
|
Ok(())
|
||||||
self.sm.push(sm);
|
|
||||||
tmp
|
|
||||||
}
|
}
|
||||||
// fn copy(&mut self, left: Wire, right: Wire) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut meta = MetaCircuit::default();
|
||||||
|
let config = ConcreteCircuit::configure(&mut meta);
|
||||||
|
|
||||||
let mut witness = WitnessCollection {
|
let mut witness = WitnessCollection {
|
||||||
a: vec![],
|
advice: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_advice_wires],
|
||||||
b: vec![],
|
|
||||||
c: vec![],
|
|
||||||
d: vec![],
|
|
||||||
sa: vec![],
|
|
||||||
sb: vec![],
|
|
||||||
sc: vec![],
|
|
||||||
sd: vec![],
|
|
||||||
sm: vec![],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Synthesize the circuit to obtain the witness and other information.
|
// Synthesize the circuit to obtain the witness and other information.
|
||||||
circuit.synthesize(&mut witness)?;
|
circuit.synthesize(&mut witness, config)?;
|
||||||
|
|
||||||
// Create a transcript for obtaining Fiat-Shamir challenges.
|
// Create a transcript for obtaining Fiat-Shamir challenges.
|
||||||
let mut transcript = HBase::init(C::Base::one());
|
let mut transcript = HBase::init(C::Base::one());
|
||||||
|
|
||||||
if witness.a.len() > params.n as usize {
|
// Compute commitments to advice wire polynomials
|
||||||
// The polynomial commitment does not support a high enough degree
|
let advice_blinds: Vec<_> = witness.advice.iter().map(|_| C::Scalar::random()).collect();
|
||||||
// polynomial to commit to our wires because this circuit has too
|
let advice_commitments = witness
|
||||||
// many gates.
|
.advice
|
||||||
return Err(Error::IncompatibleParams);
|
.iter()
|
||||||
|
.zip(advice_blinds.iter())
|
||||||
|
.map(|(poly, blind)| params.commit_lagrange(poly, *blind).to_affine())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for commitment in &advice_commitments {
|
||||||
|
hash_point(&mut transcript, commitment)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
witness.a.resize(params.n as usize, C::Scalar::zero());
|
|
||||||
witness.b.resize(params.n as usize, C::Scalar::zero());
|
|
||||||
witness.c.resize(params.n as usize, C::Scalar::zero());
|
|
||||||
witness.d.resize(params.n as usize, C::Scalar::zero());
|
|
||||||
witness.sa.resize(params.n as usize, C::Scalar::zero());
|
|
||||||
witness.sb.resize(params.n as usize, C::Scalar::zero());
|
|
||||||
witness.sc.resize(params.n as usize, C::Scalar::zero());
|
|
||||||
witness.sd.resize(params.n as usize, C::Scalar::zero());
|
|
||||||
witness.sm.resize(params.n as usize, C::Scalar::zero());
|
|
||||||
|
|
||||||
// Compute commitments to the various wire values
|
|
||||||
let a_blind = C::Scalar::one(); // TODO: not random
|
|
||||||
let b_blind = C::Scalar::one(); // TODO: not random
|
|
||||||
let c_blind = C::Scalar::one(); // TODO: not random
|
|
||||||
let d_blind = C::Scalar::one(); // TODO: not random
|
|
||||||
let a_commitment = params.commit_lagrange(&witness.a, a_blind).to_affine();
|
|
||||||
let b_commitment = params.commit_lagrange(&witness.b, b_blind).to_affine();
|
|
||||||
let c_commitment = params.commit_lagrange(&witness.c, c_blind).to_affine();
|
|
||||||
let d_commitment = params.commit_lagrange(&witness.d, d_blind).to_affine();
|
|
||||||
|
|
||||||
hash_point(&mut transcript, &a_commitment)?;
|
|
||||||
hash_point(&mut transcript, &b_commitment)?;
|
|
||||||
hash_point(&mut transcript, &c_commitment)?;
|
|
||||||
hash_point(&mut transcript, &d_commitment)?;
|
|
||||||
|
|
||||||
let domain = &srs.domain;
|
let domain = &srs.domain;
|
||||||
|
|
||||||
let (a_coset, a_poly) = domain.obtain_coset(witness.a);
|
let advice_polys: Vec<_> = witness
|
||||||
let (b_coset, b_poly) = domain.obtain_coset(witness.b);
|
.advice
|
||||||
let (c_coset, c_poly) = domain.obtain_coset(witness.c);
|
.into_iter()
|
||||||
let (d_coset, d_poly) = domain.obtain_coset(witness.d);
|
.map(|poly| domain.obtain_poly(poly))
|
||||||
|
.collect();
|
||||||
|
|
||||||
// (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc)
|
let advice_cosets: Vec<_> = meta
|
||||||
let mut h_poly = Vec::with_capacity(a_coset.len());
|
.advice_queries
|
||||||
for ((((((((a, b), c), d), sa), sb), sc), sd), sm) in a_coset
|
|
||||||
.iter()
|
.iter()
|
||||||
.zip(b_coset.iter())
|
.map(|&(wire, at)| {
|
||||||
.zip(c_coset.iter())
|
let poly = advice_polys[wire.0].clone();
|
||||||
.zip(d_coset.iter())
|
domain.obtain_coset(poly, at)
|
||||||
.zip(srs.sa.0.iter())
|
})
|
||||||
.zip(srs.sb.0.iter())
|
.collect();
|
||||||
.zip(srs.sc.0.iter())
|
|
||||||
.zip(srs.sd.0.iter())
|
// Obtain challenge for keeping all separate gates linearly independent
|
||||||
.zip(srs.sm.0.iter())
|
let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
{
|
|
||||||
h_poly.push((*a) * sa + &((*b) * sb) + &((*a) * sm * b) + &((*d) * sd) - &((*c) * sc));
|
// Evaluate the circuit using the custom gates provided
|
||||||
|
let mut h_poly = vec![C::Scalar::zero(); domain.coset_len()];
|
||||||
|
for (i, poly) in meta.gates.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
for h in h_poly.iter_mut() {
|
||||||
|
*h *= &x_2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let evaluation: Vec<C::Scalar> = poly.evaluate(
|
||||||
|
&|index| srs.fixed_cosets[index].clone(),
|
||||||
|
&|index| advice_cosets[index].clone(),
|
||||||
|
&|mut a, b| {
|
||||||
|
parallelize(&mut a, |a, start| {
|
||||||
|
for (a, b) in a.iter_mut().zip(b[start..].iter()) {
|
||||||
|
*a += b;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
a
|
||||||
|
},
|
||||||
|
&|mut a, b| {
|
||||||
|
parallelize(&mut a, |a, start| {
|
||||||
|
for (a, b) in a.iter_mut().zip(b[start..].iter()) {
|
||||||
|
*a *= b;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
a
|
||||||
|
},
|
||||||
|
&|mut a, scalar| {
|
||||||
|
parallelize(&mut a, |a, _| {
|
||||||
|
for a in a {
|
||||||
|
*a *= &scalar;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
a
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(h_poly.len(), evaluation.len());
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
h_poly = evaluation;
|
||||||
|
} else {
|
||||||
|
for (h, e) in h_poly.iter_mut().zip(evaluation.into_iter()) {
|
||||||
|
*h += &e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Divide by t(X) = X^{params.n} - 1.
|
// Divide by t(X) = X^{params.n} - 1.
|
||||||
|
|
@ -151,7 +162,7 @@ impl<C: CurveAffine> Proof<C> {
|
||||||
.map(|v| v.to_vec())
|
.map(|v| v.to_vec())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
drop(h_poly);
|
drop(h_poly);
|
||||||
let h_blinds = vec![C::Scalar::one(); h_pieces.len()]; // TODO: not random
|
let h_blinds: Vec<_> = h_pieces.iter().map(|_| C::Scalar::random()).collect();
|
||||||
|
|
||||||
// Compute commitments to each h(X) piece
|
// Compute commitments to each h(X) piece
|
||||||
let h_commitments: Vec<_> = h_pieces
|
let h_commitments: Vec<_> = h_pieces
|
||||||
|
|
@ -165,39 +176,44 @@ impl<C: CurveAffine> Proof<C> {
|
||||||
hash_point(&mut transcript, c)?;
|
hash_point(&mut transcript, c)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let x: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
// Evaluate polynomials at x
|
// Evaluate polynomials at omega^i x_3
|
||||||
let a_eval_x = eval_polynomial(&a_poly, x);
|
let advice_evals: Vec<_> = meta
|
||||||
let b_eval_x = eval_polynomial(&b_poly, x);
|
.advice_queries
|
||||||
let c_eval_x = eval_polynomial(&c_poly, x);
|
|
||||||
let d_eval_x = eval_polynomial(&d_poly, x);
|
|
||||||
let sa_eval_x = eval_polynomial(&srs.sa.1, x);
|
|
||||||
let sb_eval_x = eval_polynomial(&srs.sb.1, x);
|
|
||||||
let sc_eval_x = eval_polynomial(&srs.sc.1, x);
|
|
||||||
let sd_eval_x = eval_polynomial(&srs.sd.1, x);
|
|
||||||
let sm_eval_x = eval_polynomial(&srs.sm.1, x);
|
|
||||||
|
|
||||||
let h_evals_x: Vec<_> = h_pieces
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|poly| eval_polynomial(poly, x))
|
.map(|&(wire, at)| eval_polynomial(&advice_polys[wire.0], domain.rotate_omega(x_3, at)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let fixed_evals: Vec<_> = meta
|
||||||
|
.fixed_queries
|
||||||
|
.iter()
|
||||||
|
.map(|&(wire, at)| {
|
||||||
|
eval_polynomial(&srs.fixed_polys[wire.0], domain.rotate_omega(x_3, at))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let h_evals: Vec<_> = h_pieces
|
||||||
|
.iter()
|
||||||
|
.map(|poly| eval_polynomial(poly, x_3))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// We set up a second transcript on the scalar field to hash in openings of
|
// We set up a second transcript on the scalar field to hash in openings of
|
||||||
// our polynomial commitments.
|
// our polynomial commitments.
|
||||||
let mut transcript_scalar = HScalar::init(C::Scalar::one());
|
let mut transcript_scalar = HScalar::init(C::Scalar::one());
|
||||||
transcript_scalar.absorb(a_eval_x);
|
|
||||||
transcript_scalar.absorb(b_eval_x);
|
|
||||||
transcript_scalar.absorb(c_eval_x);
|
|
||||||
transcript_scalar.absorb(d_eval_x);
|
|
||||||
transcript_scalar.absorb(sa_eval_x);
|
|
||||||
transcript_scalar.absorb(sb_eval_x);
|
|
||||||
transcript_scalar.absorb(sc_eval_x);
|
|
||||||
transcript_scalar.absorb(sd_eval_x);
|
|
||||||
transcript_scalar.absorb(sm_eval_x);
|
|
||||||
|
|
||||||
// Hash each h(x) piece
|
// Hash each advice evaluation
|
||||||
for eval in h_evals_x.iter() {
|
for eval in advice_evals.iter() {
|
||||||
|
transcript_scalar.absorb(*eval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash each fixed evaluation
|
||||||
|
for eval in fixed_evals.iter() {
|
||||||
|
transcript_scalar.absorb(*eval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash each h(x) piece evaluation
|
||||||
|
for eval in h_evals.iter() {
|
||||||
transcript_scalar.absorb(*eval);
|
transcript_scalar.absorb(*eval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,61 +221,146 @@ impl<C: CurveAffine> Proof<C> {
|
||||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||||
transcript.absorb(transcript_scalar_point);
|
transcript.absorb(transcript_scalar_point);
|
||||||
|
|
||||||
let y: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
let mut q_commitment = h_commitments[0].clone().to_projective();
|
// Collapse openings at same points together into single openings using
|
||||||
let mut q_poly = h_pieces[0].clone();
|
// x_4 challenge.
|
||||||
let mut q_blind = h_blinds[0];
|
let mut q_polys: Vec<Option<Vec<_>>> = vec![None; meta.rotations.len()];
|
||||||
|
let mut q_blinds = vec![C::Scalar::zero(); meta.rotations.len()];
|
||||||
|
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.rotations.len()];
|
||||||
{
|
{
|
||||||
let mut accumulate = |poly: &[_], blind: &C::Scalar, commitment: C| {
|
let mut accumulate = |point_index: usize, new_poly: &Vec<_>, blind, eval| {
|
||||||
for (a, q) in poly.iter().zip(q_poly.iter_mut()) {
|
q_polys[point_index]
|
||||||
*q = (*q * &y) + a;
|
.as_mut()
|
||||||
}
|
.map(|poly| {
|
||||||
q_commitment = (q_commitment * y) + &commitment.to_projective();
|
parallelize(poly, |q, start| {
|
||||||
q_blind = (q_blind * &y) + blind;
|
for (q, a) in q.iter_mut().zip(new_poly[start..].iter()) {
|
||||||
|
*q *= &x_4;
|
||||||
|
*q += a;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
q_polys[point_index] = Some(new_poly.clone());
|
||||||
|
Some(())
|
||||||
|
});
|
||||||
|
q_blinds[point_index] *= &x_4;
|
||||||
|
q_blinds[point_index] += &blind;
|
||||||
|
q_evals[point_index] *= &x_4;
|
||||||
|
q_evals[point_index] += &eval;
|
||||||
};
|
};
|
||||||
|
|
||||||
for ((poly, blind), commitment) in h_pieces
|
for (query_index, &(wire, ref at)) in meta.advice_queries.iter().enumerate() {
|
||||||
.iter()
|
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||||
.zip(h_blinds.iter())
|
|
||||||
.zip(h_commitments.iter())
|
accumulate(
|
||||||
.skip(1)
|
point_index,
|
||||||
{
|
&advice_polys[wire.0],
|
||||||
accumulate(&poly, blind, *commitment);
|
advice_blinds[wire.0],
|
||||||
|
advice_evals[query_index],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
accumulate(&a_poly, &a_blind, a_commitment);
|
for (query_index, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() {
|
||||||
accumulate(&b_poly, &b_blind, b_commitment);
|
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||||
accumulate(&c_poly, &c_blind, c_commitment);
|
|
||||||
accumulate(&d_poly, &d_blind, d_commitment);
|
accumulate(
|
||||||
accumulate(&srs.sa.1, &Field::one(), srs.sa_commitment);
|
point_index,
|
||||||
accumulate(&srs.sb.1, &Field::one(), srs.sb_commitment);
|
&srs.fixed_polys[wire.0],
|
||||||
accumulate(&srs.sc.1, &Field::one(), srs.sc_commitment);
|
C::Scalar::one(),
|
||||||
accumulate(&srs.sd.1, &Field::one(), srs.sd_commitment);
|
fixed_evals[query_index],
|
||||||
accumulate(&srs.sm.1, &Field::one(), srs.sm_commitment);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We query the h(X) polynomial at x_3
|
||||||
|
let current_index = (*meta.rotations.get(&Rotation::default()).unwrap()).0;
|
||||||
|
for ((h_poly, h_blind), h_eval) in h_pieces
|
||||||
|
.into_iter()
|
||||||
|
.zip(h_blinds.iter())
|
||||||
|
.zip(h_evals.iter())
|
||||||
|
{
|
||||||
|
accumulate(current_index, &h_poly, *h_blind, *h_eval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
|
let mut f_poly: Option<Vec<C::Scalar>> = None;
|
||||||
|
for (&row, &point_index) in meta.rotations.iter() {
|
||||||
|
let mut poly = q_polys[point_index.0].as_ref().unwrap().clone();
|
||||||
|
let point = domain.rotate_omega(x_3, row);
|
||||||
|
poly[0] -= &q_evals[point_index.0];
|
||||||
|
let mut poly = kate_division(&poly, point);
|
||||||
|
poly.push(C::Scalar::zero());
|
||||||
|
|
||||||
|
f_poly = f_poly
|
||||||
|
.map(|mut f_poly| {
|
||||||
|
parallelize(&mut f_poly, |q, start| {
|
||||||
|
for (q, a) in q.iter_mut().zip(poly[start..].iter()) {
|
||||||
|
*q *= &x_5;
|
||||||
|
*q += a;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
f_poly
|
||||||
|
})
|
||||||
|
.or_else(|| Some(poly));
|
||||||
|
}
|
||||||
|
let mut f_poly = f_poly.unwrap();
|
||||||
|
let mut f_blind = C::Scalar::random();
|
||||||
|
|
||||||
|
let f_commitment = params.commit(&f_poly, f_blind).to_affine();
|
||||||
|
|
||||||
|
hash_point(&mut transcript, &f_commitment)?;
|
||||||
|
|
||||||
|
let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
|
let mut q_evals = vec![];
|
||||||
|
|
||||||
|
for (_, &point_index) in meta.rotations.iter() {
|
||||||
|
q_evals.push(eval_polynomial(
|
||||||
|
&q_polys[point_index.0].as_ref().unwrap(),
|
||||||
|
x_6,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for eval in q_evals.iter() {
|
||||||
|
transcript_scalar.absorb(*eval);
|
||||||
|
}
|
||||||
|
|
||||||
|
let transcript_scalar_point =
|
||||||
|
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||||
|
transcript.absorb(transcript_scalar_point);
|
||||||
|
|
||||||
|
let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
|
for (_, &point_index) in meta.rotations.iter() {
|
||||||
|
f_blind *= &x_7;
|
||||||
|
f_blind += &q_blinds[point_index.0];
|
||||||
|
|
||||||
|
parallelize(&mut f_poly, |f, start| {
|
||||||
|
for (f, a) in f
|
||||||
|
.iter_mut()
|
||||||
|
.zip(q_polys[point_index.0].as_ref().unwrap()[start..].iter())
|
||||||
|
{
|
||||||
|
*f *= &x_7;
|
||||||
|
*f += a;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's prove that the q_commitment opens at x to the expected value.
|
// Let's prove that the q_commitment opens at x to the expected value.
|
||||||
let opening = params
|
let opening = params
|
||||||
.create_proof(&mut transcript, &q_poly, q_blind, x)
|
.create_proof(&mut transcript, &f_poly, f_blind, x_6)
|
||||||
.map_err(|_| Error::ConstraintSystemFailure)?;
|
.map_err(|_| Error::ConstraintSystemFailure)?;
|
||||||
|
|
||||||
Ok(Proof {
|
Ok(Proof {
|
||||||
a_commitment,
|
advice_commitments,
|
||||||
b_commitment,
|
|
||||||
c_commitment,
|
|
||||||
d_commitment,
|
|
||||||
h_commitments,
|
h_commitments,
|
||||||
a_eval_x,
|
advice_evals,
|
||||||
b_eval_x,
|
fixed_evals,
|
||||||
c_eval_x,
|
h_evals,
|
||||||
d_eval_x,
|
f_commitment,
|
||||||
sa_eval_x,
|
q_evals,
|
||||||
sb_eval_x,
|
|
||||||
sc_eval_x,
|
|
||||||
sd_eval_x,
|
|
||||||
sm_eval_x,
|
|
||||||
h_evals_x,
|
|
||||||
opening,
|
opening,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
128
src/plonk/srs.rs
128
src/plonk/srs.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{
|
use super::{
|
||||||
circuit::{Circuit, ConstraintSystem, Wire},
|
circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit},
|
||||||
domain::EvaluationDomain,
|
domain::EvaluationDomain,
|
||||||
Error, GATE_DEGREE, SRS,
|
Error, SRS,
|
||||||
};
|
};
|
||||||
use crate::arithmetic::{Curve, CurveAffine, Field};
|
use crate::arithmetic::{Curve, CurveAffine, Field};
|
||||||
use crate::polycommit::Params;
|
use crate::polycommit::Params;
|
||||||
|
|
@ -14,92 +14,80 @@ impl<C: CurveAffine> SRS<C> {
|
||||||
circuit: &ConcreteCircuit,
|
circuit: &ConcreteCircuit,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
struct Assembly<F: Field> {
|
struct Assembly<F: Field> {
|
||||||
sa: Vec<F>,
|
fixed: Vec<Vec<F>>,
|
||||||
sb: Vec<F>,
|
|
||||||
sc: Vec<F>,
|
|
||||||
sd: Vec<F>,
|
|
||||||
sm: Vec<F>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Field> ConstraintSystem<F> for Assembly<F> {
|
impl<F: Field> ConstraintSystem<F> for Assembly<F> {
|
||||||
fn create_gate(
|
fn assign_advice(
|
||||||
&mut self,
|
&mut self,
|
||||||
sa: F,
|
_: AdviceWire,
|
||||||
sb: F,
|
_: usize,
|
||||||
sc: F,
|
_: impl FnOnce() -> Result<F, Error>,
|
||||||
sd: F,
|
) -> Result<(), Error> {
|
||||||
sm: F,
|
// We only care about fixed wires here
|
||||||
_: impl Fn() -> Result<(F, F, F, F), Error>,
|
Ok(())
|
||||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
}
|
||||||
let tmp = Ok((
|
|
||||||
Wire::A(self.sa.len()),
|
fn assign_fixed(
|
||||||
Wire::B(self.sa.len()),
|
&mut self,
|
||||||
Wire::C(self.sa.len()),
|
wire: FixedWire,
|
||||||
Wire::D(self.sa.len()),
|
row: usize,
|
||||||
));
|
to: impl FnOnce() -> Result<F, Error>,
|
||||||
self.sa.push(sa);
|
) -> Result<(), Error> {
|
||||||
self.sb.push(sb);
|
*self
|
||||||
self.sc.push(sc);
|
.fixed
|
||||||
self.sd.push(sd);
|
.get_mut(wire.0)
|
||||||
self.sm.push(sm);
|
.and_then(|v| v.get_mut(row))
|
||||||
tmp
|
.ok_or(Error::BoundsFailure)? = to()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut meta = MetaCircuit::default();
|
||||||
|
let config = ConcreteCircuit::configure(&mut meta);
|
||||||
|
|
||||||
let mut assembly: Assembly<C::Scalar> = Assembly {
|
let mut assembly: Assembly<C::Scalar> = Assembly {
|
||||||
sa: vec![],
|
fixed: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_fixed_wires],
|
||||||
sb: vec![],
|
|
||||||
sc: vec![],
|
|
||||||
sd: vec![],
|
|
||||||
sm: vec![],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Synthesize the circuit to obtain SRS
|
// Synthesize the circuit to obtain SRS
|
||||||
circuit.synthesize(&mut assembly)?;
|
circuit.synthesize(&mut assembly, config)?;
|
||||||
|
|
||||||
assembly.sa.resize(params.n as usize, C::Scalar::zero());
|
let fixed_commitments = assembly
|
||||||
assembly.sb.resize(params.n as usize, C::Scalar::zero());
|
.fixed
|
||||||
assembly.sc.resize(params.n as usize, C::Scalar::zero());
|
.iter()
|
||||||
assembly.sd.resize(params.n as usize, C::Scalar::zero());
|
.map(|poly| params.commit_lagrange(poly, C::Scalar::one()).to_affine())
|
||||||
assembly.sm.resize(params.n as usize, C::Scalar::zero());
|
.collect();
|
||||||
|
|
||||||
// Compute commitments to the fixed wire values
|
let mut degree = 1;
|
||||||
let sa_commitment = params
|
for poly in meta.gates.iter() {
|
||||||
.commit_lagrange(&assembly.sa, C::Scalar::one())
|
degree = std::cmp::max(degree, poly.degree());
|
||||||
.to_affine();
|
}
|
||||||
let sb_commitment = params
|
|
||||||
.commit_lagrange(&assembly.sb, C::Scalar::one())
|
|
||||||
.to_affine();
|
|
||||||
let sc_commitment = params
|
|
||||||
.commit_lagrange(&assembly.sc, C::Scalar::one())
|
|
||||||
.to_affine();
|
|
||||||
let sd_commitment = params
|
|
||||||
.commit_lagrange(&assembly.sd, C::Scalar::one())
|
|
||||||
.to_affine();
|
|
||||||
let sm_commitment = params
|
|
||||||
.commit_lagrange(&assembly.sm, C::Scalar::one())
|
|
||||||
.to_affine();
|
|
||||||
|
|
||||||
let domain = EvaluationDomain::new(GATE_DEGREE, params.k);
|
let domain = EvaluationDomain::new(degree as u32, params.k);
|
||||||
|
|
||||||
let sa = domain.obtain_coset(assembly.sa);
|
let fixed_polys: Vec<_> = assembly
|
||||||
let sb = domain.obtain_coset(assembly.sb);
|
.fixed
|
||||||
let sc = domain.obtain_coset(assembly.sc);
|
.into_iter()
|
||||||
let sd = domain.obtain_coset(assembly.sd);
|
.map(|poly| domain.obtain_poly(poly))
|
||||||
let sm = domain.obtain_coset(assembly.sm);
|
.collect();
|
||||||
|
|
||||||
|
let fixed_cosets = meta
|
||||||
|
.fixed_queries
|
||||||
|
.iter()
|
||||||
|
.map(|&(wire, at)| {
|
||||||
|
let poly = fixed_polys[wire.0].clone();
|
||||||
|
domain.obtain_coset(poly, at)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
Ok(SRS {
|
Ok(SRS {
|
||||||
sa,
|
|
||||||
sb,
|
|
||||||
sc,
|
|
||||||
sd,
|
|
||||||
sm,
|
|
||||||
sa_commitment,
|
|
||||||
sb_commitment,
|
|
||||||
sc_commitment,
|
|
||||||
sd_commitment,
|
|
||||||
sm_commitment,
|
|
||||||
domain,
|
domain,
|
||||||
|
fixed_commitments,
|
||||||
|
fixed_polys,
|
||||||
|
fixed_cosets,
|
||||||
|
meta,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{hash_point, Proof, SRS};
|
use super::{domain::Rotation, hash_point, Proof, SRS};
|
||||||
use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field};
|
use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field};
|
||||||
use crate::polycommit::Params;
|
use crate::polycommit::Params;
|
||||||
use crate::transcript::Hasher;
|
use crate::transcript::Hasher;
|
||||||
|
|
@ -13,35 +13,34 @@ impl<C: CurveAffine> Proof<C> {
|
||||||
// Create a transcript for obtaining Fiat-Shamir challenges.
|
// Create a transcript for obtaining Fiat-Shamir challenges.
|
||||||
let mut transcript = HBase::init(C::Base::one());
|
let mut transcript = HBase::init(C::Base::one());
|
||||||
|
|
||||||
hash_point(&mut transcript, &self.a_commitment)
|
// Hash the prover's advice commitments into the transcript
|
||||||
.expect("proof cannot contain points at infinity");
|
for commitment in &self.advice_commitments {
|
||||||
hash_point(&mut transcript, &self.b_commitment)
|
hash_point(&mut transcript, commitment)
|
||||||
.expect("proof cannot contain points at infinity");
|
.expect("proof cannot contain points at infinity");
|
||||||
hash_point(&mut transcript, &self.c_commitment)
|
}
|
||||||
.expect("proof cannot contain points at infinity");
|
|
||||||
hash_point(&mut transcript, &self.d_commitment)
|
|
||||||
.expect("proof cannot contain points at infinity");
|
|
||||||
|
|
||||||
|
// Sample x_2 challenge, which keeps the gates linearly independent.
|
||||||
|
let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
|
// Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1
|
||||||
for c in &self.h_commitments {
|
for c in &self.h_commitments {
|
||||||
hash_point(&mut transcript, c).expect("proof cannot contain points at infinity");
|
hash_point(&mut transcript, c).expect("proof cannot contain points at infinity");
|
||||||
}
|
}
|
||||||
|
|
||||||
let x: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
// Sample x_3 challenge, which is used to ensure the circuit is
|
||||||
|
// satisfied with high probability.
|
||||||
|
let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
// We set up a second transcript on the scalar field to hash in openings of
|
// Hash together all the openings provided by the prover into a new
|
||||||
// our polynomial commitments.
|
// transcript on the scalar field.
|
||||||
let mut transcript_scalar = HScalar::init(C::Scalar::one());
|
let mut transcript_scalar = HScalar::init(C::Scalar::one());
|
||||||
transcript_scalar.absorb(self.a_eval_x);
|
|
||||||
transcript_scalar.absorb(self.b_eval_x);
|
|
||||||
transcript_scalar.absorb(self.c_eval_x);
|
|
||||||
transcript_scalar.absorb(self.d_eval_x);
|
|
||||||
transcript_scalar.absorb(self.sa_eval_x);
|
|
||||||
transcript_scalar.absorb(self.sb_eval_x);
|
|
||||||
transcript_scalar.absorb(self.sc_eval_x);
|
|
||||||
transcript_scalar.absorb(self.sd_eval_x);
|
|
||||||
transcript_scalar.absorb(self.sm_eval_x);
|
|
||||||
|
|
||||||
for eval in &self.h_evals_x {
|
for eval in self
|
||||||
|
.advice_evals
|
||||||
|
.iter()
|
||||||
|
.chain(self.fixed_evals.iter())
|
||||||
|
.chain(self.h_evals.iter())
|
||||||
|
{
|
||||||
transcript_scalar.absorb(*eval);
|
transcript_scalar.absorb(*eval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,60 +48,136 @@ impl<C: CurveAffine> Proof<C> {
|
||||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||||
transcript.absorb(transcript_scalar_point);
|
transcript.absorb(transcript_scalar_point);
|
||||||
|
|
||||||
let y: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
// Evaluate the circuit using the custom gates provided
|
||||||
|
let mut h_eval = C::Scalar::zero();
|
||||||
|
for poly in srs.meta.gates.iter() {
|
||||||
|
h_eval *= &x_2;
|
||||||
|
|
||||||
let mut q_commitment = self.h_commitments[0].clone().to_projective();
|
let evaluation: C::Scalar = poly.evaluate(
|
||||||
let mut expected_opening = self.h_evals_x[0];
|
&|index| self.fixed_evals[index],
|
||||||
{
|
&|index| self.advice_evals[index],
|
||||||
let mut accumulate = |commitment: C, opening: C::Scalar| {
|
&|a, b| a + &b,
|
||||||
q_commitment = commitment.to_projective() + &(q_commitment * y);
|
&|a, b| a * &b,
|
||||||
expected_opening = opening + &(expected_opening * &y);
|
&|a, scalar| a * &scalar,
|
||||||
};
|
);
|
||||||
|
|
||||||
for (commitment, eval) in self.h_commitments.iter().zip(self.h_evals_x.iter()).skip(1) {
|
h_eval += &evaluation;
|
||||||
accumulate(*commitment, *eval);
|
|
||||||
}
|
|
||||||
|
|
||||||
accumulate(self.a_commitment, self.a_eval_x);
|
|
||||||
accumulate(self.b_commitment, self.b_eval_x);
|
|
||||||
accumulate(self.c_commitment, self.c_eval_x);
|
|
||||||
accumulate(self.d_commitment, self.d_eval_x);
|
|
||||||
accumulate(srs.sa_commitment, self.sa_eval_x);
|
|
||||||
accumulate(srs.sb_commitment, self.sb_eval_x);
|
|
||||||
accumulate(srs.sc_commitment, self.sc_eval_x);
|
|
||||||
accumulate(srs.sd_commitment, self.sd_eval_x);
|
|
||||||
accumulate(srs.sm_commitment, self.sm_eval_x);
|
|
||||||
}
|
}
|
||||||
let q_commitment = q_commitment.to_affine();
|
let xn = x_3.pow(&[params.n as u64, 0, 0, 0]);
|
||||||
|
|
||||||
let xn = x.pow(&[params.n as u64, 0, 0, 0]);
|
|
||||||
|
|
||||||
// Compute the expected h(x) value
|
// Compute the expected h(x) value
|
||||||
let mut h_eval_x = C::Scalar::zero();
|
let mut expected_h_eval = C::Scalar::zero();
|
||||||
let mut cur = C::Scalar::one();
|
let mut cur = C::Scalar::one();
|
||||||
for eval in &self.h_evals_x {
|
for eval in &self.h_evals {
|
||||||
h_eval_x += &(cur * eval);
|
expected_h_eval += &(cur * eval);
|
||||||
cur *= &xn;
|
cur *= &xn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the circuit is satisfied.
|
if h_eval != (expected_h_eval * &(xn - &C::Scalar::one())) {
|
||||||
// (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc)
|
|
||||||
if self.a_eval_x * &self.sa_eval_x
|
|
||||||
+ &(self.b_eval_x * &self.sb_eval_x)
|
|
||||||
+ &(self.a_eval_x * &self.sm_eval_x * &self.b_eval_x)
|
|
||||||
+ &(self.d_eval_x * &self.sd_eval_x)
|
|
||||||
- &(self.c_eval_x * &self.sc_eval_x)
|
|
||||||
!= h_eval_x * &(xn - &C::Scalar::one())
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We are now convinced the circuit is satisfied so long as the
|
||||||
|
// polynomial commitments open to the correct values.
|
||||||
|
|
||||||
|
// Sample x_4 for compressing openings at the same points together
|
||||||
|
let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
|
// Compress the commitments and expected evaluations at x_3 together
|
||||||
|
// using the challenge x_4
|
||||||
|
let mut q_commitments: Vec<Option<C::Projective>> = vec![None; srs.meta.rotations.len()];
|
||||||
|
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.rotations.len()];
|
||||||
|
{
|
||||||
|
let mut accumulate = |point_index: usize, new_commitment, eval| {
|
||||||
|
q_commitments[point_index] = q_commitments[point_index]
|
||||||
|
.map(|mut commitment| {
|
||||||
|
commitment *= x_4;
|
||||||
|
commitment += new_commitment;
|
||||||
|
commitment
|
||||||
|
})
|
||||||
|
.or_else(|| Some(new_commitment.to_projective()));
|
||||||
|
q_evals[point_index] *= &x_4;
|
||||||
|
q_evals[point_index] += &eval;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (query_index, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() {
|
||||||
|
let point_index = (*srs.meta.rotations.get(at).unwrap()).0;
|
||||||
|
accumulate(
|
||||||
|
point_index,
|
||||||
|
self.advice_commitments[wire.0],
|
||||||
|
self.advice_evals[query_index],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (query_index, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() {
|
||||||
|
let point_index = (*srs.meta.rotations.get(at).unwrap()).0;
|
||||||
|
accumulate(
|
||||||
|
point_index,
|
||||||
|
srs.fixed_commitments[wire.0],
|
||||||
|
self.fixed_evals[query_index],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_index = (*srs.meta.rotations.get(&Rotation::default()).unwrap()).0;
|
||||||
|
for (h_commitment, h_eval) in self.h_commitments.iter().zip(self.h_evals.iter()) {
|
||||||
|
accumulate(current_index, *h_commitment, *h_eval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample a challenge x_5 for keeping the multi-point quotient
|
||||||
|
// polynomial terms linearly independent.
|
||||||
|
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
|
// Obtain the commitment to the multi-point quotient polynomial f(X).
|
||||||
|
hash_point(&mut transcript, &self.f_commitment)
|
||||||
|
.expect("proof cannot contain points at infinity");
|
||||||
|
|
||||||
|
// Sample a challenge x_6 for checking that f(X) was committed to
|
||||||
|
// correctly.
|
||||||
|
let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
|
for eval in self.q_evals.iter() {
|
||||||
|
transcript_scalar.absorb(*eval);
|
||||||
|
}
|
||||||
|
|
||||||
|
let transcript_scalar_point =
|
||||||
|
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||||
|
transcript.absorb(transcript_scalar_point);
|
||||||
|
|
||||||
|
// We can compute the expected f_eval at x_6 using the q_evals provided
|
||||||
|
// by the prover and from x_5
|
||||||
|
let mut f_eval = C::Scalar::zero();
|
||||||
|
for (&row, &point_index) in srs.meta.rotations.iter() {
|
||||||
|
let mut eval = self.q_evals[point_index.0];
|
||||||
|
|
||||||
|
let point = srs.domain.rotate_omega(x_3, row);
|
||||||
|
eval = eval - &q_evals[point_index.0];
|
||||||
|
eval = eval * &(x_6 - &point).invert().unwrap();
|
||||||
|
|
||||||
|
f_eval *= &x_5;
|
||||||
|
f_eval += &eval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample a challenge x_7 that we will use to collapse the openings of
|
||||||
|
// the various remaining polynomials at x_6 together.
|
||||||
|
let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||||
|
|
||||||
|
// Compute the final commitment that has to be opened
|
||||||
|
let mut f_commitment: C::Projective = self.f_commitment.to_projective();
|
||||||
|
for (_, &point_index) in srs.meta.rotations.iter() {
|
||||||
|
f_commitment *= x_7;
|
||||||
|
f_commitment = f_commitment + &q_commitments[point_index.0].as_ref().unwrap();
|
||||||
|
f_eval *= &x_7;
|
||||||
|
f_eval += &self.q_evals[point_index.0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the opening proof
|
||||||
params.verify_proof(
|
params.verify_proof(
|
||||||
&self.opening,
|
&self.opening,
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
x,
|
x_6,
|
||||||
&q_commitment,
|
&f_commitment.to_affine(),
|
||||||
expected_opening,
|
f_eval,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue