(WIP) broken implementation of generalized PLONK

This commit is contained in:
Sean Bowe 2020-08-27 10:10:55 -06:00
parent 6051814c4b
commit ad106f1119
No known key found for this signature in database
GPG key ID: 95684257D8F8B031
7 changed files with 509 additions and 413 deletions

View file

@ -313,6 +313,29 @@ pub fn compute_inner_product<F: Field>(a: &[F], b: &[F]) -> F {
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
/// performed over a mutable slice.
pub fn parallelize<T: Send, F: Fn(&mut [T], usize) + Send + Clone>(v: &mut [T], f: F) {

View file

@ -23,28 +23,15 @@ pub use verifier::*;
use domain::EvaluationDomain;
// TODO: remove this
const GATE_DEGREE: u32 = 3;
/// This is a structured reference string (SRS) that is (deterministically)
/// computed from a specific circuit and parameters for the polynomial
/// commitment scheme.
#[derive(Debug)]
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>,
fixed_commitments: Vec<C>,
fixed_polys: Vec<(Vec<C::Scalar>, Vec<C::Scalar>)>,
fixed_polys: Vec<Vec<C::Scalar>>,
fixed_cosets: Vec<Vec<C::Scalar>>,
meta: MetaCircuit<C::Scalar>,
}
@ -52,22 +39,13 @@ pub struct SRS<C: CurveAffine> {
// This structure must never allow points at infinity.
#[derive(Debug, Clone)]
pub struct Proof<C: CurveAffine> {
a_commitment: C,
b_commitment: C,
c_commitment: C,
d_commitment: C,
advice_commitments: Vec<C>,
h_commitments: Vec<C>,
a_eval_x: C::Scalar,
b_eval_x: C::Scalar,
c_eval_x: C::Scalar,
d_eval_x: C::Scalar,
sa_eval_x: C::Scalar,
sb_eval_x: C::Scalar,
sc_eval_x: C::Scalar,
sd_eval_x: C::Scalar,
sm_eval_x: C::Scalar,
advice_evals_x: Vec<C::Scalar>,
fixed_evals_x: Vec<C::Scalar>,
h_evals_x: Vec<C::Scalar>,
f_commitment: C,
q_evals: Vec<C::Scalar>,
opening: OpeningProof<C>,
}
@ -167,30 +145,13 @@ fn test_proving() {
cs: &mut impl ConstraintSystem<F>,
config: MyConfig,
) -> Result<(), Error> {
for _ in 0..10 {
let (_, _, _, _) = cs.multiply(|| {
let a = self.a.ok_or(Error::SynthesisError)?;
let a2 = a.square();
Ok((a, a, a2))
})?;
//cs.copy(a, b);
let (_, _, _, _) = cs.add(|| {
let a = self.a.ok_or(Error::SynthesisError)?;
let a2 = a.square();
let a3 = a + a2;
Ok((a, a2, a3))
})?;
//cs.copy(a, d);
//cs.copy(c, e);
}
// Similar to the above...
let mut row = 0;
for _ in 0..10 {
cs.assign_advice(config.a, row, || self.a.ok_or(Error::SynthesisError))?;
cs.assign_advice(config.b, row, || self.a.ok_or(Error::SynthesisError))?;
let a_squared = self.a.map(|a| a.square());
cs.assign_advice(config.c, row, || self.a.ok_or(Error::SynthesisError))?;
cs.assign_advice(config.c, row, || a_squared.ok_or(Error::SynthesisError))?;
// Multiplication gate
cs.assign_fixed(config.sa, row, || Ok(Field::zero()))?;
cs.assign_fixed(config.sb, row, || Ok(Field::zero()))?;

View file

@ -5,20 +5,6 @@ use std::collections::HashMap;
use super::Error;
use crate::arithmetic::Field;
/// This represents a PLONK wire, which could be a fixed (selector) wire or an
/// advice wire.
#[derive(Copy, Clone, Debug)]
pub enum Wire {
/// A wires
A,
/// B wires
B,
/// C wires
C,
/// D wires
D,
}
/// This represents a wire which has a fixed (permanent) value
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct FixedWire(pub usize);
@ -27,10 +13,6 @@ pub struct FixedWire(pub usize);
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct AdviceWire(pub usize);
/// Represents a pointer to a value in the constraint system.
#[derive(Clone, Debug)]
pub struct Variable(pub Wire, pub usize);
/// This trait allows a [`Circuit`] to direct some backend to assign a witness
/// for a constraint system.
pub trait ConstraintSystem<F: Field> {
@ -50,39 +32,6 @@ pub trait ConstraintSystem<F: Field> {
to: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error>;
/// Creates a gate.
fn create_gate(
&mut self,
sa: F,
sb: F,
sc: F,
sd: F,
sm: F,
f: impl Fn() -> Result<(F, F, F, F), Error>,
) -> Result<(Variable, Variable, Variable, Variable), Error>;
/// a * b - c = 0
fn multiply(
&mut self,
f: impl Fn() -> Result<(F, F, F), Error>,
) -> Result<(Variable, Variable, Variable, Variable), Error> {
self.create_gate(F::zero(), F::zero(), F::one(), F::zero(), F::one(), || {
let (a, b, c) = f()?;
Ok((a, b, c, F::zero()))
})
}
/// a + b - c = 0
fn add(
&mut self,
f: impl Fn() -> Result<(F, F, F), Error>,
) -> Result<(Variable, Variable, Variable, Variable), 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);
}
@ -111,9 +60,9 @@ pub trait Circuit<F: Field> {
#[derive(Clone, Debug)]
pub enum Polynomial<F> {
/// This is a fixed wire queried at a certain relative location
Fixed(FixedWire, i32),
Fixed(usize),
/// This is an advice (witness) wire queried at a certain relative location
Advice(AdviceWire, i32),
Advice(usize),
/// This is the sum of two polynomials
Sum(Box<Polynomial<F>>, Box<Polynomial<F>>),
/// This is the product of two polynomials
@ -123,17 +72,19 @@ pub enum Polynomial<F> {
}
impl<F: Field> Polynomial<F> {
fn evaluate<T>(
/// Evaluate the polynomial using the provided closures to perform the
/// operations.
pub fn evaluate<T>(
&self,
fixed_wire: &impl Fn(FixedWire, i32) -> T,
advice_wire: &impl Fn(AdviceWire, i32) -> T,
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(a, location) => fixed_wire(*a, *location),
Polynomial::Advice(a, location) => advice_wire(*a, *location),
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);
@ -150,13 +101,12 @@ impl<F: Field> Polynomial<F> {
}
}
}
}
impl<F: Field> Polynomial<F> {
fn degree(&self) -> usize {
/// Compute the degree of this polynomial
pub fn degree(&self) -> usize {
match self {
Polynomial::Fixed(_, _) => 1,
Polynomial::Advice(_, _) => 1,
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(),
@ -192,20 +142,24 @@ pub struct MetaCircuit<F> {
pub(crate) num_fixed_wires: usize,
pub(crate) num_advice_wires: usize,
// permutations: Vec<Vec<Wire>>,
gates: Vec<Polynomial<F>>,
advice_queries: HashMap<(AdviceWire, i32), usize>,
fixed_queries: HashMap<(FixedWire, i32), usize>,
// num_queries: usize,
pub(crate) gates: Vec<Polynomial<F>>,
pub(crate) advice_queries: Vec<(AdviceWire, i32)>,
pub(crate) fixed_queries: Vec<(FixedWire, i32)>,
pub(crate) query_rows: HashMap<i32, usize>,
}
impl<F: Field> Default for MetaCircuit<F> {
fn default() -> MetaCircuit<F> {
let mut query_rows = HashMap::new();
query_rows.insert(0, 0);
MetaCircuit {
num_fixed_wires: 0,
num_advice_wires: 0,
gates: vec![],
fixed_queries: HashMap::new(),
advice_queries: HashMap::new(),
fixed_queries: Vec::new(),
advice_queries: Vec::new(),
query_rows,
}
}
}
@ -213,18 +167,30 @@ impl<F: Field> Default for MetaCircuit<F> {
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 len = self.fixed_queries.len();
self.fixed_queries.entry((wire, at)).or_insert_with(|| len);
{
let len = self.query_rows.len();
self.query_rows.entry(at).or_insert(len);
}
Polynomial::Fixed(wire, at)
// 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 len = self.advice_queries.len();
self.advice_queries.entry((wire, at)).or_insert_with(|| len);
{
let len = self.query_rows.len();
self.query_rows.entry(at).or_insert(len);
}
Polynomial::Advice(wire, at)
// 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
@ -239,6 +205,7 @@ impl<F: Field> MetaCircuit<F> {
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);

View file

@ -138,7 +138,7 @@ impl<G: Group> EvaluationDomain<G> {
}
Self::distribute_powers(&mut a, g);
}
a.resize(1 << self.extended_k, G::group_zero());
a.resize(self.coset_len(), G::group_zero());
best_fft(&mut a, self.extended_omega, self.extended_k);
a
}
@ -149,7 +149,7 @@ impl<G: Group> EvaluationDomain<G> {
/// This function will panic if the provided vector is not the correct
/// length.
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
Self::ifft(
@ -174,7 +174,7 @@ impl<G: Group> EvaluationDomain<G> {
/// This divides the polynomial (in the coset domain) by the vanishing
/// polynomial.
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
// domain.
@ -221,4 +221,16 @@ 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
}
}

View file

@ -1,9 +1,10 @@
use super::{
circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit, Variable, Wire},
circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit},
hash_point, Error, Proof, SRS,
};
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::transcript::Hasher;
@ -22,15 +23,6 @@ impl<C: CurveAffine> Proof<C> {
circuit: &ConcreteCircuit,
) -> Result<Self, Error> {
struct WitnessCollection<F: Field> {
a: 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>,
advice: Vec<Vec<F>>,
}
@ -60,52 +52,12 @@ impl<C: CurveAffine> Proof<C> {
Ok(())
}
fn create_gate(
&mut self,
sa: F,
sb: F,
sc: F,
sd: F,
sm: F,
f: impl Fn() -> Result<(F, F, F, F), Error>,
) -> Result<(Variable, Variable, Variable, Variable), Error> {
let (a, b, c, d) = f()?;
let tmp = Ok((
Variable(Wire::A, self.a.len()),
Variable(Wire::B, self.a.len()),
Variable(Wire::C, self.a.len()),
Variable(Wire::D, self.a.len()),
));
self.a.push(a);
self.b.push(b);
self.c.push(c);
self.d.push(d);
self.sa.push(sa);
self.sb.push(sb);
self.sc.push(sc);
self.sd.push(sd);
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 {
a: vec![],
b: vec![],
c: vec![],
d: vec![],
sa: vec![],
sb: vec![],
sc: vec![],
sd: vec![],
sm: vec![],
advice: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_advice_wires],
};
@ -115,34 +67,8 @@ impl<C: CurveAffine> Proof<C> {
// Create a transcript for obtaining Fiat-Shamir challenges.
let mut transcript = HBase::init(C::Base::one());
if witness.a.len() > params.n as usize {
// The polynomial commitment does not support a high enough degree
// polynomial to commit to our wires because this circuit has too
// many gates.
return Err(Error::IncompatibleParams);
}
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();
let advice_blinds = vec![C::Scalar::one(); witness.advice.len()]; // TODO: not random
// Compute commitments to advice wire polynomials
let advice_blinds: Vec<_> = witness.advice.iter().map(|_| C::Scalar::random()).collect();
let advice_commitments = witness
.advice
.iter()
@ -150,50 +76,77 @@ impl<C: CurveAffine> Proof<C> {
.map(|(poly, blind)| params.commit_lagrange(poly, *blind).to_affine())
.collect();
hash_point(&mut transcript, &a_commitment)?;
hash_point(&mut transcript, &b_commitment)?;
hash_point(&mut transcript, &c_commitment)?;
hash_point(&mut transcript, &d_commitment)?;
for commitment in &advice_commitments {
hash_point(&mut transcript, commitment)?;
}
let domain = &srs.domain;
let a_poly = domain.obtain_poly(witness.a);
let b_poly = domain.obtain_poly(witness.b);
let c_poly = domain.obtain_poly(witness.c);
let d_poly = domain.obtain_poly(witness.d);
let a_coset = domain.obtain_coset(a_poly.clone(), 0);
let b_coset = domain.obtain_coset(b_poly.clone(), 0);
let c_coset = domain.obtain_coset(c_poly.clone(), 0);
let d_coset = domain.obtain_coset(d_poly.clone(), 0);
let advice_polys: Vec<_> = witness
.advice
.into_iter()
.map(|poly| {
let poly = domain.obtain_poly(poly);
let coset = domain.obtain_coset(poly.clone(), 0);
(poly, coset)
.map(|poly| domain.obtain_poly(poly))
.collect();
let advice_cosets: Vec<_> = meta
.advice_queries
.iter()
.map(|&(wire, at)| {
let poly = advice_polys[wire.0].clone();
domain.obtain_coset(poly, at)
})
.collect();
// (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc)
let mut h_poly = Vec::with_capacity(a_coset.len());
for ((((((((a, b), c), d), sa), sb), sc), sd), sm) in a_coset
.iter()
.zip(b_coset.iter())
.zip(c_coset.iter())
.zip(d_coset.iter())
.zip(srs.sa.0.iter())
.zip(srs.sb.0.iter())
.zip(srs.sc.0.iter())
.zip(srs.sd.0.iter())
.zip(srs.sm.0.iter())
{
h_poly.push((*a) * sa + &((*b) * sb) + &((*a) * sm * b) + &((*d) * sd) - &((*c) * sc));
// Obtain challenge for keeping all separate gates linearly independent
let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
// 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.into_iter().zip(b[start..].iter()) {
*a += b;
}
});
a
},
&|mut a, b| {
parallelize(&mut a, |a, start| {
for (a, b) in a.into_iter().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.
@ -208,7 +161,7 @@ impl<C: CurveAffine> Proof<C> {
.map(|v| v.to_vec())
.collect::<Vec<_>>();
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
let h_commitments: Vec<_> = h_pieces
@ -222,38 +175,59 @@ impl<C: CurveAffine> Proof<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
let a_eval_x = eval_polynomial(&a_poly, x);
let b_eval_x = eval_polynomial(&b_poly, x);
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);
// Evaluate polynomials at omega^i x_3
let advice_evals_x: Vec<_> = meta
.advice_queries
.iter()
.map(|&(wire, at)| {
let mut point = x_3;
if at >= 0 {
point *= &domain.get_omega().pow(&[at as u64, 0, 0, 0]);
} else {
point *= &domain.get_omega_inv().pow(&[at.abs() as u64, 0, 0, 0]);
}
eval_polynomial(&advice_polys[wire.0], point)
})
.collect();
let fixed_evals_x: Vec<_> = meta
.fixed_queries
.iter()
.map(|&(wire, at)| {
let mut point = x_3;
if at >= 0 {
point *= &domain.get_omega().pow(&[at as u64, 0, 0, 0]);
} else {
point *= &domain.get_omega_inv().pow(&[at.abs() as u64, 0, 0, 0]);
}
eval_polynomial(&srs.fixed_polys[wire.0], point)
})
.collect();
let h_evals_x: Vec<_> = h_pieces
.iter()
.map(|poly| eval_polynomial(poly, x))
.map(|poly| eval_polynomial(poly, x_3))
.collect();
// We set up a second transcript on the scalar field to hash in openings of
// our polynomial commitments.
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 advice_evals_x.iter() {
transcript_scalar.absorb(*eval);
}
// Hash each fixed evaluation
for eval in fixed_evals_x.iter() {
transcript_scalar.absorb(*eval);
}
// Hash each h(x) piece evaluation
for eval in h_evals_x.iter() {
transcript_scalar.absorb(*eval);
}
@ -262,62 +236,164 @@ impl<C: CurveAffine> Proof<C> {
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
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();
let mut q_poly = h_pieces[0].clone();
let mut q_blind = h_blinds[0];
// Collapse openings at same points together into single openings using
// x_4 challenge.
let mut q_polys: Vec<Option<Vec<_>>> = vec![None; meta.query_rows.len()];
let mut q_blinds = vec![C::Scalar::zero(); meta.query_rows.len()];
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.query_rows.len()];
{
let mut accumulate = |poly: &[_], blind: &C::Scalar, commitment: C| {
for (a, q) in poly.iter().zip(q_poly.iter_mut()) {
*q = (*q * &y) + a;
}
q_commitment = (q_commitment * y) + &commitment.to_projective();
q_blind = (q_blind * &y) + blind;
};
for (i, &(wire, ref at)) in meta.advice_queries.iter().enumerate() {
let query_row = *meta.query_rows.get(at).unwrap();
for ((poly, blind), commitment) in h_pieces
.iter()
.zip(h_blinds.iter())
.zip(h_commitments.iter())
.skip(1)
{
accumulate(&poly, blind, *commitment);
if q_polys[query_row].is_none() {
q_polys[query_row] = Some(advice_polys[wire.0].clone());
q_blinds[query_row] = advice_blinds[wire.0];
q_evals[query_row] = advice_evals_x[i];
} else {
parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| {
for (q, a) in q.iter_mut().zip(advice_polys[wire.0][start..].iter()) {
*q *= &x_4;
*q += a;
}
});
q_blinds[query_row] *= &x_4;
q_blinds[query_row] += &advice_blinds[wire.0];
q_evals[query_row] *= &x_4;
q_evals[query_row] += &advice_evals_x[i];
}
}
accumulate(&a_poly, &a_blind, a_commitment);
accumulate(&b_poly, &b_blind, b_commitment);
accumulate(&c_poly, &c_blind, c_commitment);
accumulate(&d_poly, &d_blind, d_commitment);
accumulate(&srs.sa.1, &Field::one(), srs.sa_commitment);
accumulate(&srs.sb.1, &Field::one(), srs.sb_commitment);
accumulate(&srs.sc.1, &Field::one(), srs.sc_commitment);
accumulate(&srs.sd.1, &Field::one(), srs.sd_commitment);
accumulate(&srs.sm.1, &Field::one(), srs.sm_commitment);
for (i, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() {
let query_row = *meta.query_rows.get(at).unwrap();
if q_polys[query_row].is_none() {
q_polys[query_row] = Some(srs.fixed_polys[wire.0].clone());
q_blinds[query_row] = C::Scalar::one();
q_evals[query_row] = fixed_evals_x[i];
} else {
parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| {
for (q, a) in q.iter_mut().zip(srs.fixed_polys[wire.0][start..].iter()) {
*q *= &x_4;
*q += a;
}
});
q_blinds[query_row] *= &x_4;
q_blinds[query_row] += &C::Scalar::one();
q_evals[query_row] *= &x_4;
q_evals[query_row] += &fixed_evals_x[i];
}
}
for ((h_poly, h_blind), h_eval) in h_pieces
.into_iter()
.zip(h_blinds.iter())
.zip(h_evals_x.iter())
{
// We query the h(X) polynomial at x_3
let cur_row = *meta.query_rows.get(&0).unwrap();
if q_polys[cur_row].is_none() {
q_polys[cur_row] = Some(h_poly);
q_blinds[cur_row] = *h_blind;
q_evals[cur_row] = *h_eval;
} else {
parallelize(q_polys[cur_row].as_mut().unwrap(), |q, start| {
for (q, a) in q.iter_mut().zip(h_poly[start..].iter()) {
*q *= &x_4;
*q += a;
}
});
q_blinds[cur_row] *= &x_4;
q_blinds[cur_row] += h_blind;
q_evals[cur_row] *= &x_4;
q_evals[cur_row] += h_eval;
}
}
}
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
let mut f_poly = None;
for (&row, &col) in meta.query_rows.iter() {
let mut poly = q_polys[col].as_ref().unwrap().clone();
let mut point = x_3;
if row >= 0 {
point *= &domain.get_omega().pow_vartime(&[row as u64, 0, 0, 0]);
} else {
point *= &domain
.get_omega_inv()
.pow_vartime(&[row.abs() as u64, 0, 0, 0]);
}
poly[0] -= &q_evals[col];
let mut poly = kate_division(&poly, point);
poly.push(C::Scalar::zero());
if f_poly.is_none() {
f_poly = Some(poly);
} else {
parallelize(f_poly.as_mut().unwrap(), |q, start| {
for (q, a) in q.iter_mut().zip(poly[start..].iter()) {
*q *= &x_5;
*q += a;
}
});
}
}
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 (_, &col) in meta.query_rows.iter() {
q_evals.push(eval_polynomial(&q_polys[col].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 (_, &col) in meta.query_rows.iter() {
f_blind *= &x_7;
f_blind += &q_blinds[col];
parallelize(&mut f_poly, |f, start| {
for (f, a) in f
.iter_mut()
.zip(q_polys[col].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 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)?;
Ok(Proof {
a_commitment,
b_commitment,
c_commitment,
d_commitment,
advice_commitments,
h_commitments,
a_eval_x,
b_eval_x,
c_eval_x,
d_eval_x,
sa_eval_x,
sb_eval_x,
sc_eval_x,
sd_eval_x,
sm_eval_x,
advice_evals_x,
fixed_evals_x,
h_evals_x,
f_commitment,
q_evals,
opening,
})
}

View file

@ -1,7 +1,7 @@
use super::{
circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit, Variable, Wire},
circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit},
domain::EvaluationDomain,
Error, GATE_DEGREE, SRS,
Error, SRS,
};
use crate::arithmetic::{Curve, CurveAffine, Field};
use crate::polycommit::Params;
@ -14,11 +14,6 @@ impl<C: CurveAffine> SRS<C> {
circuit: &ConcreteCircuit,
) -> Result<Self, Error> {
struct Assembly<F: Field> {
sa: Vec<F>,
sb: Vec<F>,
sc: Vec<F>,
sd: Vec<F>,
sm: Vec<F>,
fixed: Vec<Vec<F>>,
}
@ -47,113 +42,51 @@ impl<C: CurveAffine> SRS<C> {
Ok(())
}
fn create_gate(
&mut self,
sa: F,
sb: F,
sc: F,
sd: F,
sm: F,
_: impl Fn() -> Result<(F, F, F, F), Error>,
) -> Result<(Variable, Variable, Variable, Variable), Error> {
let tmp = Ok((
Variable(Wire::A, self.sa.len()),
Variable(Wire::B, self.sa.len()),
Variable(Wire::C, self.sa.len()),
Variable(Wire::D, self.sa.len()),
));
self.sa.push(sa);
self.sb.push(sb);
self.sc.push(sc);
self.sd.push(sd);
self.sm.push(sm);
tmp
}
}
let mut meta = MetaCircuit::default();
let config = ConcreteCircuit::configure(&mut meta);
let mut assembly: Assembly<C::Scalar> = Assembly {
sa: vec![],
sb: vec![],
sc: vec![],
sd: vec![],
sm: vec![],
fixed: vec![vec![C::Scalar::zero(); params.n as usize]; meta.num_fixed_wires],
};
// Synthesize the circuit to obtain SRS
circuit.synthesize(&mut assembly, config)?;
assembly.sa.resize(params.n as usize, C::Scalar::zero());
assembly.sb.resize(params.n as usize, C::Scalar::zero());
assembly.sc.resize(params.n as usize, C::Scalar::zero());
assembly.sd.resize(params.n as usize, C::Scalar::zero());
assembly.sm.resize(params.n as usize, C::Scalar::zero());
// Compute commitments to the fixed wire values
let sa_commitment = params
.commit_lagrange(&assembly.sa, C::Scalar::one())
.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 fixed_commitments = assembly
.fixed
.iter()
.map(|poly| params.commit_lagrange(poly, C::Scalar::one()).to_affine())
.collect();
let domain = EvaluationDomain::new(GATE_DEGREE, params.k);
let mut degree = 1;
for poly in meta.gates.iter() {
degree = std::cmp::max(degree, poly.degree());
}
let sa_poly = domain.obtain_poly(assembly.sa);
let sb_poly = domain.obtain_poly(assembly.sb);
let sc_poly = domain.obtain_poly(assembly.sc);
let sd_poly = domain.obtain_poly(assembly.sd);
let sm_poly = domain.obtain_poly(assembly.sm);
let sa_coset = domain.obtain_coset(sa_poly.clone(), 0);
let sb_coset = domain.obtain_coset(sb_poly.clone(), 0);
let sc_coset = domain.obtain_coset(sc_poly.clone(), 0);
let sd_coset = domain.obtain_coset(sd_poly.clone(), 0);
let sm_coset = domain.obtain_coset(sm_poly.clone(), 0);
let domain = EvaluationDomain::new(degree as u32, params.k);
let fixed_polys = assembly
let fixed_polys: Vec<_> = assembly
.fixed
.into_iter()
.map(|poly| {
let coeffs = domain.obtain_poly(poly);
let coset = domain.obtain_coset(coeffs.clone(), 0);
(coeffs, coset)
.map(|poly| domain.obtain_poly(poly))
.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 {
sa: (sa_coset, sa_poly),
sb: (sb_coset, sb_poly),
sc: (sc_coset, sc_poly),
sd: (sd_coset, sd_poly),
sm: (sm_coset, sm_poly),
sa_commitment,
sb_commitment,
sc_commitment,
sd_commitment,
sm_commitment,
domain,
fixed_commitments,
fixed_polys,
fixed_cosets,
meta,
})
}

View file

@ -13,48 +13,178 @@ impl<C: CurveAffine> Proof<C> {
// Create a transcript for obtaining Fiat-Shamir challenges.
let mut transcript = HBase::init(C::Base::one());
hash_point(&mut transcript, &self.a_commitment)
.expect("proof cannot contain points at infinity");
hash_point(&mut transcript, &self.b_commitment)
.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");
for commitment in &self.advice_commitments {
hash_point(&mut transcript, commitment)
.expect("proof cannot contain points at infinity");
}
let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
for c in &self.h_commitments {
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()));
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
// our polynomial commitments.
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.advice_evals_x.iter() {
transcript_scalar.absorb(*eval);
}
for eval in self.fixed_evals_x.iter() {
transcript_scalar.absorb(*eval);
}
for eval in &self.h_evals_x {
transcript_scalar.absorb(*eval);
}
// 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 evaluation: C::Scalar = poly.evaluate(
&|index| self.fixed_evals_x[index],
&|index| self.advice_evals_x[index],
&|a, b| a + &b,
&|a, b| a * &b,
&|a, scalar| a * &scalar,
);
h_eval += &evaluation;
}
let xn = x_3.pow(&[params.n as u64, 0, 0, 0]);
h_eval *= &(xn - &C::Scalar::one());
// Compute the expected h(x) value
let mut expected_h_eval = C::Scalar::zero();
let mut cur = C::Scalar::one();
for eval in &self.h_evals_x {
expected_h_eval += &(cur * eval);
cur *= &xn;
}
if h_eval != expected_h_eval {
return false;
}
let transcript_scalar_point =
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
transcript.absorb(transcript_scalar_point);
let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
let mut q_commitments: Vec<_> = vec![None; srs.meta.query_rows.len()];
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.query_rows.len()];
{
for (i, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() {
let query_row = *srs.meta.query_rows.get(at).unwrap();
if q_commitments[query_row].is_none() {
q_commitments[query_row] =
Some(self.advice_commitments[wire.0].to_projective());
q_evals[query_row] = self.advice_evals_x[i];
} else {
q_commitments[query_row].as_mut().map(|commitment| {
*commitment *= x_4;
*commitment += self.advice_commitments[wire.0];
});
q_evals[query_row] *= &x_4;
q_evals[query_row] += &self.advice_evals_x[i];
}
}
for (i, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() {
let query_row = *srs.meta.query_rows.get(at).unwrap();
if q_commitments[query_row].is_none() {
q_commitments[query_row] = Some(srs.fixed_commitments[wire.0].to_projective());
q_evals[query_row] = self.fixed_evals_x[i];
} else {
q_commitments[query_row].as_mut().map(|commitment| {
*commitment *= x_4;
*commitment += srs.fixed_commitments[wire.0];
});
q_evals[query_row] *= &x_4;
q_evals[query_row] += &self.fixed_evals_x[i];
}
}
for (h_commitment, h_eval) in self.h_commitments.iter().zip(self.h_evals_x.iter()) {
// We query the h(X) polynomial at x_3
let cur_row = *srs.meta.query_rows.get(&0).unwrap();
if q_commitments[cur_row].is_none() {
q_commitments[cur_row] = Some(h_commitment.to_projective());
q_evals[cur_row] = *h_eval;
} else {
q_commitments[cur_row].as_mut().map(|commitment| {
*commitment *= x_4;
*commitment += *h_commitment;
});
q_evals[cur_row] *= &x_4;
q_evals[cur_row] += h_eval;
}
}
}
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
hash_point(&mut transcript, &self.f_commitment)
.expect("proof cannot contain points at infinity");
let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
// We can compute the expected f_eval from x_5
let mut f_eval = C::Scalar::zero();
for (&row, &col) in srs.meta.query_rows.iter() {
let mut eval: C::Scalar = self.q_evals[col].clone();
let mut point = x_3;
if row >= 0 {
point *= &srs.domain.get_omega().pow_vartime(&[row as u64, 0, 0, 0]);
} else {
point *= &srs
.domain
.get_omega_inv()
.pow_vartime(&[row.abs() as u64, 0, 0, 0]);
}
eval = eval - &q_evals[col];
eval = eval * &(x_6 - &point).invert().unwrap();
f_eval *= &x_5;
f_eval += &eval;
}
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);
let y: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
let mut f_commitment: C::Projective = self.f_commitment.to_projective();
for (_, &col) in srs.meta.query_rows.iter() {
f_commitment *= x_7;
f_commitment = f_commitment + &q_commitments[col].as_ref().unwrap();
f_eval *= &x_7;
f_eval += &self.q_evals[col];
}
params.verify_proof(
&self.opening,
&mut transcript,
x_6,
&f_commitment.to_affine(),
f_eval,
)
/*
let mut q_commitment = self.h_commitments[0].clone().to_projective();
let mut expected_opening = self.h_evals_x[0];
@ -102,12 +232,6 @@ impl<C: CurveAffine> Proof<C> {
return false;
}
params.verify_proof(
&self.opening,
&mut transcript,
x,
&q_commitment,
expected_opening,
)
*/
}
}