integrate initial part of SONIC. Use original code, restructure, mock Transcript

This commit is contained in:
Alex Vlasov 2019-02-04 13:18:44 +03:00
parent ff6c46240b
commit f06f92a9cc
23 changed files with 3082 additions and 16 deletions

@ -17,7 +17,7 @@ bit-vec = "0.4.4"
futures = "0.1"
futures-cpupool = "0.1"
num_cpus = "1"
crossbeam = "0.3"
crossbeam = "0.7.1"
pairing = { git = 'https://github.com/matterinc/pairing' }
byteorder = "1"
ff = { git = 'https://github.com/matterinc/ff', features = ["derive"] }

@ -110,7 +110,7 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
let minv = self.minv;
for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || {
scope.spawn(move |_| {
for v in v {
v.group_mul_assign(&minv);
}
@ -123,7 +123,7 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
{
worker.scope(self.coeffs.len(), |scope, chunk| {
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
scope.spawn(move || {
scope.spawn(move |_| {
let mut u = g.pow(&[(i * chunk) as u64]);
for v in v.iter_mut() {
v.group_mul_assign(&u);
@ -166,7 +166,7 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
worker.scope(self.coeffs.len(), |scope, chunk| {
for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || {
scope.spawn(move |_| {
for v in v {
v.group_mul_assign(&i);
}
@ -181,7 +181,7 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
scope.spawn(move || {
scope.spawn(move |_| {
for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_mul_assign(&b.0);
}
@ -196,7 +196,7 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
scope.spawn(move || {
scope.spawn(move |_| {
for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_sub_assign(&b);
}
@ -350,7 +350,7 @@ fn parallel_fft<E: Engine, T: Group<E>>(
let a = &*a;
for (j, tmp) in tmp.iter_mut().enumerate() {
scope.spawn(move || {
scope.spawn(move |_| {
// Shuffle into a sub-FFT
let omega_j = omega.pow(&[j as u64]);
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
@ -378,7 +378,7 @@ fn parallel_fft<E: Engine, T: Group<E>>(
let tmp = &tmp;
for (idx, a) in a.chunks_mut(chunk).enumerate() {
scope.spawn(move || {
scope.spawn(move |_| {
let mut idx = idx * chunk;
let mask = (1 << log_cpus) - 1;
for a in a {

@ -391,7 +391,7 @@ pub fn generate_parameters<E, C>(
worker.scope(domain.len(), |scope, chunk| {
for (i, subdomain) in domain.chunks_mut(chunk).enumerate()
{
scope.spawn(move || {
scope.spawn(move |_| {
let mut current_power = tau.pow(&[(i*chunk) as u64]);
for p in subdomain {
@ -420,7 +420,7 @@ pub fn generate_parameters<E, C>(
for (gamma2_z_t_g1, p) in gamma2_z_t_g1.chunks_mut(chunk).zip(domain.as_ref().chunks(chunk))
{
let mut g1_wnaf = g1_wnaf.shared();
scope.spawn(move || {
scope.spawn(move |_| {
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
for (gamma2_z_t_g1, p) in gamma2_z_t_g1.iter_mut().zip(p.iter())
{
@ -554,7 +554,7 @@ pub fn generate_parameters<E, C>(
let mut g1_wnaf = g1_wnaf.shared();
let mut g2_wnaf = g2_wnaf.shared();
scope.spawn(move || {
scope.spawn(move |_| {
for (((((a_g1, a_g2), c_1_g1), c_2_g1), at), ct) in a_g1.iter_mut()
.zip(a_g2.iter_mut())
.zip(c_1_g1.iter_mut())

@ -259,7 +259,7 @@ pub fn generate_parameters<E, C>(
worker.scope(powers_of_tau.len(), |scope, chunk| {
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate()
{
scope.spawn(move || {
scope.spawn(move |_| {
let mut current_tau_power = tau.pow(&[(i*chunk) as u64]);
for p in powers_of_tau {
@ -285,7 +285,7 @@ pub fn generate_parameters<E, C>(
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
{
let mut g1_wnaf = g1_wnaf.shared();
scope.spawn(move || {
scope.spawn(move |_| {
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
for (h, p) in h.iter_mut().zip(p.iter())
{
@ -376,7 +376,7 @@ pub fn generate_parameters<E, C>(
let mut g1_wnaf = g1_wnaf.shared();
let mut g2_wnaf = g2_wnaf.shared();
scope.spawn(move || {
scope.spawn(move |_| {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut()
.zip(b_g1.iter_mut())
.zip(b_g2.iter_mut())

@ -15,6 +15,7 @@ mod multiexp;
pub mod domain;
pub mod groth16;
pub mod gm17;
pub mod sonic;
#[cfg(test)]
mod tests;

@ -7,7 +7,8 @@
use num_cpus;
use futures::{Future, IntoFuture, Poll};
use futures_cpupool::{CpuPool, CpuFuture};
use crossbeam::{self, Scope};
use crossbeam::{self};
use crossbeam::thread::{Scope};
#[derive(Clone)]
pub struct Worker {
@ -63,7 +64,7 @@ impl Worker {
crossbeam::scope(|scope| {
f(scope, chunk_size)
})
}).expect("must run")
}
}

11
src/sonic/README.md Normal file

@ -0,0 +1,11 @@
# Description
Initial SONIC proof system integration using the code from the [original implementation](https://github.com/zknuckles/sonic.git). It's here for experimental reasons and evaluation of the following properties:
- How applicable is "helped" procedure for a case of Ethereum
- What is a final verification cost for "helped" and "unhelped" procedures
- Prover efficiency in both cases
- Implementation of a memory constrained prover and helper
- Smart-contract implementation of verifiers
- Code cleanup
- Migration for smart-contract compatible transcripts

139
src/sonic/cs/lc.rs Normal file

@ -0,0 +1,139 @@
use ff::{Field};
use pairing::{Engine};
use std::ops::{Add, Sub, Neg};
/// This represents a linear combination of some variables, with coefficients
/// in the scalar field of a pairing-friendly elliptic curve group.
#[derive(Clone)]
pub struct LinearCombination<E: Engine>(Vec<(Variable, Coeff<E>)>);
impl<E: Engine> From<Variable> for LinearCombination<E> {
fn from(var: Variable) -> LinearCombination<E> {
LinearCombination::<E>::zero() + var
}
}
impl<E: Engine> AsRef<[(Variable, Coeff<E>)]> for LinearCombination<E> {
fn as_ref(&self) -> &[(Variable, Coeff<E>)] {
&self.0
}
}
impl<E: Engine> LinearCombination<E> {
pub fn zero() -> LinearCombination<E> {
LinearCombination(vec![])
}
}
impl<E: Engine> Add<(Coeff<E>, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(mut self, (coeff, var): (Coeff<E>, Variable)) -> LinearCombination<E> {
self.0.push((var, coeff));
self
}
}
impl<E: Engine> Sub<(Coeff<E>, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(self, (coeff, var): (Coeff<E>, Variable)) -> LinearCombination<E> {
self + (-coeff, var)
}
}
impl<E: Engine> Add<Variable> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(self, other: Variable) -> LinearCombination<E> {
self + (Coeff::One, other)
}
}
impl<E: Engine> Sub<Variable> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(self, other: Variable) -> LinearCombination<E> {
self - (Coeff::One, other)
}
}
impl<'a, E: Engine> Add<&'a LinearCombination<E>> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
for s in &other.0 {
self = self + (s.1, s.0);
}
self
}
}
impl<'a, E: Engine> Sub<&'a LinearCombination<E>> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
for s in &other.0 {
self = self - (s.1, s.0);
}
self
}
}
#[derive(Copy, Clone, Debug)]
pub enum Variable {
A(usize),
B(usize),
C(usize),
}
#[derive(Debug)]
pub enum Coeff<E: Engine> {
Zero,
One,
NegativeOne,
Full(E::Fr),
}
impl<E: Engine> Coeff<E> {
pub fn multiply(&self, with: &mut E::Fr) {
match self {
Coeff::Zero => {
*with = E::Fr::zero();
},
Coeff::One => {},
Coeff::NegativeOne => {
with.negate();
},
Coeff::Full(val) => {
with.mul_assign(val);
}
}
}
}
impl<E: Engine> Copy for Coeff<E> {}
impl<E: Engine> Clone for Coeff<E> {
fn clone(&self) -> Self {
*self
}
}
impl<E: Engine> Neg for Coeff<E> {
type Output = Coeff<E>;
fn neg(self) -> Self {
match self {
Coeff::Zero => Coeff::Zero,
Coeff::One => Coeff::NegativeOne,
Coeff::NegativeOne => Coeff::One,
Coeff::Full(mut a) => {
a.negate();
Coeff::Full(a)
}
}
}
}

220
src/sonic/cs/mod.rs Normal file

@ -0,0 +1,220 @@
extern crate ff;
extern crate pairing;
use ff::{Field};
use pairing::{Engine};
use crate::{SynthesisError};
use std::marker::PhantomData;
mod lc;
pub use self::lc::{Coeff, Variable, LinearCombination};
pub trait Circuit<E: Engine> {
fn synthesize<CS: ConstraintSystem<E>>(&self, cs: &mut CS) -> Result<(), SynthesisError>;
}
pub trait ConstraintSystem<E: Engine> {
const ONE: Variable;
fn alloc<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>;
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>;
fn enforce_zero(&mut self, lc: LinearCombination<E>);
fn multiply<F>(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
where
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>;
// TODO: get rid of this
fn get_value(&self, _var: Variable) -> Result<E::Fr, ()> {
Err(())
}
}
/// This is a backend for the `SynthesisDriver` to relay information about
/// the concrete circuit. One backend might just collect basic information
/// about the circuit for verification, while another actually constructs
/// a witness.
pub trait Backend<E: Engine> {
/// Get the value of a variable. Can return None if we don't know.
fn get_var(&self, _variable: Variable) -> Option<E::Fr> { None }
/// Set the value of a variable. Might error if this backend expects to know it.
fn set_var<F>(&mut self, _variable: Variable, _value: F) -> Result<(), SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError> { Ok(()) }
/// Create a new multiplication gate.
fn new_multiplication_gate(&mut self) { }
/// Create a new linear constraint.
fn new_linear_constraint(&mut self) { }
/// Insert a term into a linear constraint. TODO: bad name of function
fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff<E>) { }
/// Mark y^{_index} as the power of y cooresponding to the public input
/// coefficient for the next public input, in the k(Y) polynomial.
fn new_k_power(&mut self, _index: usize) { }
}
/// This is an abstraction which synthesizes circuits.
pub trait SynthesisDriver {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError>;
}
pub struct Basic;
impl SynthesisDriver for Basic {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
struct Synthesizer<E: Engine, B: Backend<E>> {
backend: B,
current_variable: Option<usize>,
_marker: PhantomData<E>,
q: usize,
n: usize,
}
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for Synthesizer<E, B> {
const ONE: Variable = Variable::A(1);
fn alloc<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
match self.current_variable.take() {
Some(index) => {
let var_a = Variable::A(index);
let var_b = Variable::B(index);
let var_c = Variable::C(index);
let mut product = None;
let value_a = self.backend.get_var(var_a);
self.backend.set_var(var_b, || {
let value_b = value()?;
product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
product.as_mut().map(|product| product.mul_assign(&value_b));
Ok(value_b)
})?;
self.backend.set_var(var_c, || {
product.ok_or(SynthesisError::AssignmentMissing)
})?;
self.current_variable = None;
Ok(var_b)
},
None => {
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let var_a = Variable::A(index);
self.backend.set_var(var_a, value)?;
self.current_variable = Some(index);
Ok(var_a)
}
}
}
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
let input_var = self.alloc(value)?;
self.enforce_zero(LinearCombination::zero() + input_var);
self.backend.new_k_power(self.q);
Ok(input_var)
}
fn enforce_zero(&mut self, lc: LinearCombination<E>)
{
self.q += 1;
self.backend.new_linear_constraint();
for (var, coeff) in lc.as_ref() {
self.backend.insert_coefficient(*var, *coeff);
}
}
fn multiply<F>(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
where
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
{
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let a = Variable::A(index);
let b = Variable::B(index);
let c = Variable::C(index);
let mut b_val = None;
let mut c_val = None;
self.backend.set_var(a, || {
let (a, b, c) = values()?;
b_val = Some(b);
c_val = Some(c);
Ok(a)
})?;
self.backend.set_var(b, || {
b_val.ok_or(SynthesisError::AssignmentMissing)
})?;
self.backend.set_var(c, || {
c_val.ok_or(SynthesisError::AssignmentMissing)
})?;
Ok((a, b, c))
}
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
self.backend.get_var(var).ok_or(())
}
}
let mut tmp: Synthesizer<E, B> = Synthesizer {
backend: backend,
current_variable: None,
_marker: PhantomData,
q: 0,
n: 0,
};
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <Synthesizer<E, B> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut tmp)?;
// TODO: add blinding factors so we actually get zero-knowledge
// println!("n = {}", tmp.n);
Ok(())
}
}

128
src/sonic/helped/batch.rs Normal file

@ -0,0 +1,128 @@
//! Our protocol allows the verification of multiple proofs and even
//! of individual proofs to batch the pairing operations such that
//! only a smaller, fixed number of pairings must occur for an entire
//! batch of proofs. This is possible because G2 elements are fixed
//! in our protocol and never appear in proofs; everything can be
//! combined probabilistically.
//!
//! This submodule contains the `Batch` abstraction for creating a
//! context for batch verification.
use ff::{Field};
use pairing::{Engine, CurveAffine, CurveProjective};
use crate::sonic::srs::SRS;
use crate::sonic::util::multiexp;
// One of the primary functions of the `Batch` abstraction is handling
// Kate commitment openings:
//
// e(P', [\alpha(x - z)] H) = e(P, H) e([-v] G, [\alpha] H)
// ==> e(P', [\alpha x] H) e([-z] P', [\alpha] H) = e(P, H) e([-v] G, [\alpha] H)
//
// Many of these can be opened simultaneously by sampling random `r` and
// accumulating...
//
// e([r] P', [\alpha x] H)
// e([-rz] P', [\alpha] H)
// e([r] P, -H)
// e([rv] G, [\alpha] H)
//
// ... and checking that the result is the identity in the target group.
pub struct Batch<E: Engine> {
alpha_x: Vec<(E::G1Affine, E::Fr)>,
alpha_x_precomp: <E::G2Affine as CurveAffine>::Prepared,
alpha: Vec<(E::G1Affine, E::Fr)>,
alpha_precomp: <E::G2Affine as CurveAffine>::Prepared,
neg_h: Vec<(E::G1Affine, E::Fr)>,
neg_h_precomp: <E::G2Affine as CurveAffine>::Prepared,
neg_x_n_minus_d: Vec<(E::G1Affine, E::Fr)>,
neg_x_n_minus_d_precomp: <E::G2Affine as CurveAffine>::Prepared,
// The value paired with [\alpha] H, accumulated in the field
// to save group operations.
value: E::Fr,
g: E::G1Affine,
}
impl<E: Engine> Batch<E> {
pub fn new(srs: &SRS<E>, n: usize) -> Self {
Batch {
alpha_x: vec![],
alpha_x_precomp: srs.h_positive_x_alpha[1].prepare(),
alpha: vec![],
alpha_precomp: srs.h_positive_x_alpha[0].prepare(),
neg_h: vec![],
neg_h_precomp: {
let mut tmp = srs.h_negative_x[0];
tmp.negate();
tmp.prepare()
},
neg_x_n_minus_d: vec![],
neg_x_n_minus_d_precomp: {
let mut tmp = srs.h_negative_x[srs.d - n];
tmp.negate();
tmp.prepare()
},
value: E::Fr::zero(),
g: srs.g_positive_x[0],
}
}
pub fn add_opening(&mut self, p: E::G1Affine, mut r: E::Fr, point: E::Fr) {
self.alpha_x.push((p, r));
r.mul_assign(&point);
r.negate();
self.alpha.push((p, r));
}
pub fn add_commitment(&mut self, p: E::G1Affine, r: E::Fr) {
self.neg_h.push((p, r));
}
pub fn add_commitment_max_n(&mut self, p: E::G1Affine, r: E::Fr) {
self.neg_x_n_minus_d.push((p, r));
}
pub fn add_opening_value(&mut self, mut r: E::Fr, point: E::Fr) {
r.mul_assign(&point);
self.value.add_assign(&r);
}
pub fn check_all(mut self) -> bool {
self.alpha.push((self.g, self.value));
let alpha_x = multiexp(
self.alpha_x.iter().map(|x| &x.0),
self.alpha_x.iter().map(|x| &x.1),
).into_affine().prepare();
let alpha = multiexp(
self.alpha.iter().map(|x| &x.0),
self.alpha.iter().map(|x| &x.1),
).into_affine().prepare();
let neg_h = multiexp(
self.neg_h.iter().map(|x| &x.0),
self.neg_h.iter().map(|x| &x.1),
).into_affine().prepare();
let neg_x_n_minus_d = multiexp(
self.neg_x_n_minus_d.iter().map(|x| &x.0),
self.neg_x_n_minus_d.iter().map(|x| &x.1),
).into_affine().prepare();
E::final_exponentiation(&E::miller_loop(&[
(&alpha_x, &self.alpha_x_precomp),
(&alpha, &self.alpha_precomp),
(&neg_h, &self.neg_h_precomp),
(&neg_x_n_minus_d, &self.neg_x_n_minus_d_precomp),
])).unwrap() == E::Fqk::one()
}
}

39
src/sonic/helped/mod.rs Normal file

@ -0,0 +1,39 @@
extern crate ff;
extern crate pairing;
// extern crate merlin;
use ff::{Field};
use pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
// use merlin::{Transcript};
mod verifier;
mod prover;
mod batch;
mod poly;
pub use self::verifier::{MultiVerifier, create_aggregate};
pub use self::prover::{Aggregate, create_proof, create_advice};
// use super::super::util::*;
// pub use super::batch::Batch;
// use crate::synthesis::{Backend, SynthesisDriver};
// use crate::{Circuit, SynthesisError, Variable, Coeff};
// use crate::srs::SRS;
#[derive(Clone)]
pub struct SxyAdvice<E: Engine> {
s: E::G1Affine,
opening: E::G1Affine,
szy: E::Fr,
}
#[derive(Clone)]
pub struct Proof<E: Engine> {
r: E::G1Affine,
t: E::G1Affine,
rz: E::Fr,
rzy: E::Fr,
z_opening: E::G1Affine,
zy_opening: E::G1Affine
}

264
src/sonic/helped/poly.rs Normal file

@ -0,0 +1,264 @@
use ff::{Field};
use pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use crate::sonic::cs::{Backend};
use crate::sonic::cs::{Coeff, Variable, LinearCombination};
/*
s(X, Y) = \sum\limits_{i=1}^N u_i(Y) X^{-i}
+ \sum\limits_{i=1}^N v_i(Y) X^{i}
+ \sum\limits_{i=1}^N w_i(Y) X^{i+N}
where
u_i(Y) = \sum\limits_{q=1}^Q Y^{q+N} u_{i,q}
v_i(Y) = \sum\limits_{q=1}^Q Y^{q+N} v_{i,q}
w_i(Y) = -Y^i + -Y^{-i} + \sum\limits_{q=1}^Q Y^{q+N} w_{i,q}
*/
#[derive(Clone)]
pub struct SxEval<E: Engine> {
y: E::Fr,
// current value of y^{q+N}
yqn: E::Fr,
// x^{-i} (\sum\limits_{q=1}^Q y^{q+N} u_{q,i})
u: Vec<E::Fr>,
// x^{i} (\sum\limits_{q=1}^Q y^{q+N} v_{q,i})
v: Vec<E::Fr>,
// x^{i+N} (-y^i -y^{-i} + \sum\limits_{q=1}^Q y^{q+N} w_{q,i})
w: Vec<E::Fr>,
}
impl<E: Engine> SxEval<E> {
pub fn new(y: E::Fr, n: usize) -> Self {
let y_inv = y.inverse().unwrap(); // TODO
let yqn = y.pow(&[n as u64]);
let u = vec![E::Fr::zero(); n];
let v = vec![E::Fr::zero(); n];
let mut w = vec![E::Fr::zero(); n];
let mut tmp1 = y;
let mut tmp2 = y_inv;
for w in &mut w {
let mut new = tmp1;
new.add_assign(&tmp2);
new.negate();
*w = new;
tmp1.mul_assign(&y);
tmp2.mul_assign(&y_inv);
}
SxEval {
y,
yqn,
u,
v,
w,
}
}
pub fn poly(mut self) -> (Vec<E::Fr>, Vec<E::Fr>) {
self.v.extend(self.w);
(self.u, self.v)
}
pub fn finalize(self, x: E::Fr) -> E::Fr {
let x_inv = x.inverse().unwrap(); // TODO
let mut tmp = x_inv;
let mut acc = E::Fr::zero();
for mut u in self.u {
u.mul_assign(&tmp);
acc.add_assign(&u);
tmp.mul_assign(&x_inv);
}
let mut tmp = x;
for mut v in self.v {
v.mul_assign(&tmp);
acc.add_assign(&v);
tmp.mul_assign(&x);
}
for mut w in self.w {
w.mul_assign(&tmp);
acc.add_assign(&w);
tmp.mul_assign(&x);
}
acc
}
}
impl<'a, E: Engine> Backend<E> for &'a mut SxEval<E> {
fn new_linear_constraint(&mut self) {
self.yqn.mul_assign(&self.y);
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
let acc = match var {
Variable::A(index) => {
&mut self.u[index - 1]
}
Variable::B(index) => {
&mut self.v[index - 1]
}
Variable::C(index) => {
&mut self.w[index - 1]
}
};
match coeff {
Coeff::Zero => { },
Coeff::One => {
acc.add_assign(&self.yqn);
},
Coeff::NegativeOne => {
acc.sub_assign(&self.yqn);
},
Coeff::Full(mut val) => {
val.mul_assign(&self.yqn);
acc.add_assign(&val);
}
}
}
}
/*
s(X, Y) = \sum\limits_{i=1}^N \sum\limits_{q=1}^Q Y^{q+N} u_{i,q} X^{-i}
+ \sum\limits_{i=1}^N \sum\limits_{q=1}^Q Y^{q+N} v_{i,q} X^{i}
+ \sum\limits_{i=1}^N \sum\limits_{q=1}^Q Y^{q+N} w_{i,q} X^{i+N}
- \sum\limits_{i=1}^N Y^i X^{i+N}
- \sum\limits_{i=1}^N Y^{-i} X^{i+N}
*/
pub struct SyEval<E: Engine> {
max_n: usize,
current_q: usize,
// x^{-1}, ..., x^{-N}
a: Vec<E::Fr>,
// x^1, ..., x^{N}
b: Vec<E::Fr>,
// x^{N+1}, ..., x^{2*N}
c: Vec<E::Fr>,
// coeffs for y^1, ..., y^{N+Q}
positive_coeffs: Vec<E::Fr>,
// coeffs for y^{-1}, y^{-2}, ..., y^{-N}
negative_coeffs: Vec<E::Fr>,
}
impl<E: Engine> SyEval<E> {
pub fn new(x: E::Fr, n: usize, q: usize) -> Self {
let xinv = x.inverse().unwrap();
let mut tmp = E::Fr::one();
let mut a = vec![E::Fr::zero(); n];
for a in &mut a {
tmp.mul_assign(&xinv); // tmp = x^{-i}
*a = tmp;
}
let mut tmp = E::Fr::one();
let mut b = vec![E::Fr::zero(); n];
for b in &mut b {
tmp.mul_assign(&x); // tmp = x^{i}
*b = tmp;
}
let mut positive_coeffs = vec![E::Fr::zero(); n + q];
let mut negative_coeffs = vec![E::Fr::zero(); n];
let mut c = vec![E::Fr::zero(); n];
for ((c, positive_coeff), negative_coeff) in c.iter_mut().zip(&mut positive_coeffs).zip(&mut negative_coeffs) {
tmp.mul_assign(&x); // tmp = x^{i+N}
*c = tmp;
// - \sum\limits_{i=1}^N Y^i X^{i+N}
let mut tmp = tmp;
tmp.negate();
*positive_coeff = tmp;
// - \sum\limits_{i=1}^N Y^{-i} X^{i+N}
*negative_coeff = tmp;
}
SyEval {
a,
b,
c,
positive_coeffs,
negative_coeffs,
current_q: 0,
max_n: n,
}
}
pub fn poly(self) -> (Vec<E::Fr>, Vec<E::Fr>) {
(self.negative_coeffs, self.positive_coeffs)
}
pub fn finalize(self, y: E::Fr) -> E::Fr {
let mut acc = E::Fr::zero();
let mut tmp = y;
for mut coeff in self.positive_coeffs {
coeff.mul_assign(&tmp);
acc.add_assign(&coeff);
tmp.mul_assign(&y);
}
let yinv = y.inverse().unwrap(); // TODO
let mut tmp = yinv;
for mut coeff in self.negative_coeffs {
coeff.mul_assign(&tmp);
acc.add_assign(&coeff);
tmp.mul_assign(&yinv);
}
acc
}
}
impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
fn new_linear_constraint(&mut self) {
self.current_q += 1;
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
match var {
Variable::A(index) => {
let index = index - 1;
// Y^{q+N} += X^{-i} * coeff
let mut tmp = self.a[index];
coeff.multiply(&mut tmp);
let yindex = self.current_q + self.max_n;
self.positive_coeffs[yindex - 1].add_assign(&tmp);
}
Variable::B(index) => {
let index = index - 1;
// Y^{q+N} += X^{i} * coeff
let mut tmp = self.b[index];
coeff.multiply(&mut tmp);
let yindex = self.current_q + self.max_n;
self.positive_coeffs[yindex - 1].add_assign(&tmp);
}
Variable::C(index) => {
let index = index - 1;
// Y^{q+N} += X^{i+N} * coeff
let mut tmp = self.c[index];
coeff.multiply(&mut tmp);
let yindex = self.current_q + self.max_n;
self.positive_coeffs[yindex - 1].add_assign(&tmp);
}
};
}
}

599
src/sonic/helped/prover.rs Normal file

@ -0,0 +1,599 @@
use ff::{Field};
use pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use super::{Proof, SxyAdvice};
use super::batch::Batch;
use super::poly::{SxEval, SyEval};
use crate::SynthesisError;
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
#[derive(Clone)]
pub struct Aggregate<E: Engine> {
// Commitment to s(z, Y)
c: E::G1Affine,
// We have to open each of the S commitments to a random point `z`
s_opening: E::G1Affine,
// We have to open C to each constituent `y`
c_openings: Vec<(E::G1Affine, E::Fr)>,
// Then we have to finally open C
opening: E::G1Affine,
}
pub fn create_aggregate<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
inputs: &[(Proof<E>, SxyAdvice<E>)],
srs: &SRS<E>,
) -> Aggregate<E>
{
// TODO: precompute this?
let (n, q) = {
struct CountN {
n: usize,
q: usize
}
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
fn new_linear_constraint(&mut self) {
self.q += 1;
}
}
let mut tmp = CountN{n:0,q:0};
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
(tmp.n, tmp.q)
};
let mut transcript = Transcript::new(&[]);
let mut y_values: Vec<E::Fr> = Vec::with_capacity(inputs.len());
for &(ref proof, ref sxyadvice) in inputs {
{
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&proof.r);
y_values.push(transcript.get_challenge_scalar());
}
transcript.commit_point(&sxyadvice.s);
}
let z: E::Fr = transcript.get_challenge_scalar();
// Compute s(z, Y)
let (s_poly_negative, s_poly_positive) = {
let mut tmp = SyEval::new(z, n, q);
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.poly()
};
// Compute C = g^{s(z, x)}
let c = multiexp(
srs.g_positive_x_alpha[0..(n + q)]
.iter()
.chain_ext(srs.g_negative_x_alpha[0..n].iter()),
s_poly_positive.iter().chain_ext(s_poly_negative.iter())
).into_affine();
transcript.commit_point(&c);
// Open C at w
let w: E::Fr = transcript.get_challenge_scalar();
let value = compute_value::<E>(&w, &s_poly_positive, &s_poly_negative);
let opening = {
let mut value = value;
value.negate();
let poly = kate_divison(
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
w,
);
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
// Let's open up C to every y.
fn compute_value<E: Engine>(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr {
let mut value = E::Fr::zero();
let yinv = y.inverse().unwrap(); // TODO
let mut tmp = yinv;
for &coeff in poly_negative {
let mut coeff = coeff;
coeff.mul_assign(&tmp);
value.add_assign(&coeff);
tmp.mul_assign(&yinv);
}
let mut tmp = *y;
for &coeff in poly_positive {
let mut coeff = coeff;
coeff.mul_assign(&tmp);
value.add_assign(&coeff);
tmp.mul_assign(&y);
}
value
}
let mut c_openings = vec![];
for y in &y_values {
let value = compute_value::<E>(y, &s_poly_positive, &s_poly_negative);
let opening = {
let mut value = value;
value.negate();
let poly = kate_divison(
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
*y,
);
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
c_openings.push((opening, value));
}
// Okay, great. Now we need to open up each S at the same point z to the same value.
// Since we're opening up all the S's at the same point, we create a bunch of random
// challenges instead and open up a random linear combination.
let mut poly_negative = vec![E::Fr::zero(); n];
let mut poly_positive = vec![E::Fr::zero(); 2*n];
let mut expected_value = E::Fr::zero();
for (y, c_opening) in y_values.iter().zip(c_openings.iter()) {
// Compute s(X, y_i)
let (s_poly_negative, s_poly_positive) = {
let mut tmp = SxEval::new(*y, n);
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.poly()
};
let mut value = c_opening.1;
let r: E::Fr = transcript.get_challenge_scalar();
value.mul_assign(&r);
expected_value.add_assign(&value);
for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) {
coeff.mul_assign(&r);
target.add_assign(&coeff);
}
for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) {
coeff.mul_assign(&r);
target.add_assign(&coeff);
}
}
let s_opening = {
let mut value = expected_value;
value.negate();
let poly = kate_divison(
poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()),
z,
);
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
Aggregate {
// Commitment to s(z, Y)
c,
// We have to open each of the S commitments to a random point `z`
s_opening,
// We have to open C to each constituent `y`
c_openings,
// Then we have to finally open C
opening,
}
}
pub fn create_advice<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
proof: &Proof<E>,
srs: &SRS<E>
) -> SxyAdvice<E>
{
// annoying, but we need n to compute s(z, y), and this isn't
// precomputed anywhere yet
let n = {
struct CountN {
n: usize
}
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
}
let mut tmp = CountN{n:0};
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.n
};
let z: E::Fr;
let y: E::Fr;
{
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&proof.r);
y = transcript.get_challenge_scalar();
transcript.commit_point(&proof.t);
z = transcript.get_challenge_scalar();
}
let z_inv = z.inverse().unwrap(); // TODO
let (s_poly_negative, s_poly_positive) = {
let mut tmp = SxEval::new(y, n);
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.poly()
};
// Compute S commitment
let s = multiexp(
srs.g_positive_x_alpha[0..(2 * n)]
.iter()
.chain_ext(srs.g_negative_x_alpha[0..(n)].iter()),
s_poly_positive.iter().chain_ext(s_poly_negative.iter())
).into_affine();
// Compute s(z, y)
let mut szy = E::Fr::zero();
{
let mut tmp = z;
for &p in &s_poly_positive {
let mut p = p;
p.mul_assign(&tmp);
szy.add_assign(&p);
tmp.mul_assign(&z);
}
let mut tmp = z_inv;
for &p in &s_poly_negative {
let mut p = p;
p.mul_assign(&tmp);
szy.add_assign(&p);
tmp.mul_assign(&z_inv);
}
}
// Compute kate opening
let opening = {
let mut open = szy;
open.negate();
let poly = kate_divison(
s_poly_negative.iter().rev().chain_ext(Some(open).iter()).chain_ext(s_poly_positive.iter()),
z,
);
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
SxyAdvice {
s,
szy,
opening
}
}
pub fn create_proof<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
srs: &SRS<E>
) -> Result<Proof<E>, SynthesisError>
{
struct Wires<E: Engine> {
a: Vec<E::Fr>,
b: Vec<E::Fr>,
c: Vec<E::Fr>
}
impl<'a, E: Engine> Backend<E> for &'a mut Wires<E> {
fn new_multiplication_gate(&mut self) {
self.a.push(E::Fr::zero());
self.b.push(E::Fr::zero());
self.c.push(E::Fr::zero());
}
fn get_var(&self, variable: Variable) -> Option<E::Fr> {
Some(match variable {
Variable::A(index) => {
self.a[index - 1]
},
Variable::B(index) => {
self.b[index - 1]
},
Variable::C(index) => {
self.c[index - 1]
}
})
}
fn set_var<F>(&mut self, variable: Variable, value: F) -> Result<(), SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>
{
let value = value()?;
match variable {
Variable::A(index) => {
self.a[index - 1] = value;
},
Variable::B(index) => {
self.b[index - 1] = value;
},
Variable::C(index) => {
self.c[index - 1] = value;
}
}
Ok(())
}
}
let mut wires = Wires {
a: vec![],
b: vec![],
c: vec![],
};
S::synthesize(&mut wires, circuit)?;
let n = wires.a.len();
let mut transcript = Transcript::new(&[]);
let r = multiexp(
srs.g_positive_x_alpha[(srs.d - 3*n - 1)..].iter(),
wires.c.iter().rev()
.chain_ext(wires.b.iter().rev())
.chain_ext(Some(E::Fr::zero()).iter())
.chain_ext(wires.a.iter()),
).into_affine();
transcript.commit_point(&r);
let y: E::Fr = transcript.get_challenge_scalar();
let mut rx1 = wires.b;
rx1.extend(wires.c);
rx1.reverse();
rx1.push(E::Fr::zero());
rx1.extend(wires.a);
let mut rxy = rx1.clone();
let y_inv = y.inverse().unwrap(); // TODO
let mut tmp = y.pow(&[n as u64]);
for rxy in rxy.iter_mut().rev() {
rxy.mul_assign(&tmp);
tmp.mul_assign(&y_inv);
}
let (s_poly_negative, s_poly_positive) = {
let mut tmp = SxEval::new(y, n);
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.poly()
};
let mut rxy_prime = rxy.clone();
{
rxy_prime.resize(4 * n + 1, E::Fr::zero());
// Add s(x, y)
for (r, s) in rxy_prime[0..(2 * n)]
.iter_mut()
.rev()
.zip(s_poly_negative)
{
r.add_assign(&s);
}
for (r, s) in rxy_prime[(2 * n + 1)..].iter_mut().zip(s_poly_positive) {
r.add_assign(&s);
}
}
let mut txy = multiply_polynomials::<E>(rx1.clone(), rxy_prime);
txy[4 * n] = E::Fr::zero(); // -k(y)
let t = multiexp(
srs.g_positive_x_alpha[0..(3 * n)]
.iter()
.chain_ext(srs.g_negative_x_alpha[0..(4 * n)].iter()),
txy[(4 * n + 1)..]
.iter()
.chain_ext(txy[0..4 * n].iter().rev()),
).into_affine();
transcript.commit_point(&t);
let z: E::Fr = transcript.get_challenge_scalar();
let z_inv = z.inverse().unwrap(); // TODO
// TODO: use the faster way to evaluate the polynomials
let mut rz = E::Fr::zero();
{
let mut tmp = z.pow(&[n as u64]);
for coeff in rx1.iter().rev() {
let mut coeff = *coeff;
coeff.mul_assign(&tmp);
rz.add_assign(&coeff);
tmp.mul_assign(&z_inv);
}
}
let mut rzy = E::Fr::zero();
{
let mut tmp = z.pow(&[n as u64]);
for mut coeff in rxy.into_iter().rev() {
coeff.mul_assign(&tmp);
rzy.add_assign(&coeff);
tmp.mul_assign(&z_inv);
}
}
transcript.commit_scalar(&rz);
transcript.commit_scalar(&rzy);
let r1: E::Fr = transcript.get_challenge_scalar();
let zy_opening = {
// r(X, 1) - r(z, y)
rx1[2 * n].sub_assign(&rzy);
let mut point = y;
point.mul_assign(&z);
let poly = kate_divison(
rx1.iter(),
point,
);
let negative_poly = poly[0..2*n].iter().rev();
let positive_poly = poly[2*n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
let z_opening = {
rx1[2 * n].add_assign(&rzy); // restore
for (t, &r) in txy[2 * n..].iter_mut().zip(rx1.iter()) {
let mut r = r;
r.mul_assign(&r1);
t.add_assign(&r);
}
let mut val = E::Fr::zero();
{
assert_eq!(txy.len(), 3*n + 1 + 4*n);
let mut tmp = z.pow(&[(3*n) as u64]);
for coeff in txy.iter().rev() {
let mut coeff = *coeff;
coeff.mul_assign(&tmp);
val.add_assign(&coeff);
tmp.mul_assign(&z_inv);
}
}
txy[4 * n].sub_assign(&val);
let poly = kate_divison(
txy.iter(),
z,
);
let negative_poly = poly[0..4*n].iter().rev();
let positive_poly = poly[4*n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
Ok(Proof {
r, rz, rzy, t, z_opening, zy_opening
})
}
#[test]
fn my_fun_circuit_test() {
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr};
use super::*;
use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination};
struct MyCircuit;
impl<E: Engine> Circuit<E> for MyCircuit {
fn synthesize<CS: ConstraintSystem<E>>(&self, cs: &mut CS) -> Result<(), SynthesisError> {
let (a, b, _) = cs.multiply(|| {
Ok((
E::Fr::from_str("10").unwrap(),
E::Fr::from_str("20").unwrap(),
E::Fr::from_str("200").unwrap(),
))
})?;
cs.enforce_zero(LinearCombination::from(a) + a - b);
//let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?;
//cs.enforce_zero(LinearCombination::from(b) - multiplier);
Ok(())
}
}
let srs = SRS::<Bls12>::new(
20,
Fr::from_str("22222").unwrap(),
Fr::from_str("33333333").unwrap(),
);
let proof = create_proof::<Bls12, _, Basic>(&MyCircuit, &srs).unwrap();
use std::time::{Instant};
let start = Instant::now();
let mut batch = MultiVerifier::<Bls12, _, Basic>::new(MyCircuit, &srs).unwrap();
for _ in 0..1 {
batch.add_proof(&proof, &[/*Fr::from_str("20").unwrap()*/], |_, _| None);
}
assert!(batch.check_all());
let elapsed = start.elapsed();
println!("time to verify: {:?}", elapsed);
}

@ -0,0 +1,463 @@
use ff::{Field};
use pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use super::{Proof, SxyAdvice};
use super::batch::Batch;
use super::poly::{SxEval, SyEval};
use crate::SynthesisError;
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
pub struct MultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver> {
circuit: C,
batch: Batch<E>,
k_map: Vec<usize>,
n: usize,
q: usize,
_marker: PhantomData<(E, S)>
}
impl<E: Engine, C: Circuit<E>, S: SynthesisDriver> MultiVerifier<E, C, S> {
pub fn new(circuit: C, srs: &SRS<E>) -> Result<Self, SynthesisError> {
struct Preprocess<E: Engine> {
k_map: Vec<usize>,
n: usize,
q: usize,
_marker: PhantomData<E>
}
impl<'a, E: Engine> Backend<E> for &'a mut Preprocess<E> {
fn new_k_power(&mut self, index: usize) {
self.k_map.push(index);
}
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
fn new_linear_constraint(&mut self) {
self.q += 1;
}
}
let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData };
S::synthesize(&mut preprocess, &circuit)?;
Ok(MultiVerifier {
circuit,
batch: Batch::new(srs, preprocess.n),
k_map: preprocess.k_map,
n: preprocess.n,
q: preprocess.q,
_marker: PhantomData
})
}
pub fn add_aggregate(
&mut self,
proofs: &[(Proof<E>, SxyAdvice<E>)],
aggregate: &Aggregate<E>,
)
{
let mut transcript = Transcript::new(&[]);
let mut y_values: Vec<E::Fr> = Vec::with_capacity(proofs.len());
for &(ref proof, ref sxyadvice) in proofs {
{
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&proof.r);
y_values.push(transcript.get_challenge_scalar());
}
transcript.commit_point(&sxyadvice.s);
}
let z: E::Fr = transcript.get_challenge_scalar();
transcript.commit_point(&aggregate.c);
let w: E::Fr = transcript.get_challenge_scalar();
let szw = {
let mut tmp = SxEval::new(w, self.n);
S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO
tmp.finalize(z)
};
{
// TODO: like everything else doing this, this isn't really random
let random: E::Fr;
let mut transcript = transcript.clone();
random = transcript.get_challenge_scalar();
self.batch.add_opening(aggregate.opening, random, w);
self.batch.add_commitment(aggregate.c, random);
self.batch.add_opening_value(szw, random);
}
for ((opening, value), &y) in aggregate.c_openings.iter().zip(y_values.iter()) {
let random: E::Fr;
let mut transcript = transcript.clone();
random = transcript.get_challenge_scalar();
self.batch.add_opening(*opening, random, y);
self.batch.add_commitment(aggregate.c, random);
self.batch.add_opening_value(*value, random);
}
let random: E::Fr;
{
let mut transcript = transcript.clone();
random = transcript.get_challenge_scalar();
}
let mut expected_value = E::Fr::zero();
for ((_, advice), c_opening) in proofs.iter().zip(aggregate.c_openings.iter()) {
let mut r: E::Fr = transcript.get_challenge_scalar();
// expected value of the later opening
{
let mut tmp = c_opening.1;
tmp.mul_assign(&r);
expected_value.add_assign(&tmp);
}
r.mul_assign(&random);
self.batch.add_commitment(advice.s, r);
}
self.batch.add_opening_value(expected_value, random);
self.batch.add_opening(aggregate.s_opening, random, z);
}
pub fn add_proof_with_advice(
&mut self,
proof: &Proof<E>,
inputs: &[E::Fr],
advice: &SxyAdvice<E>,
)
{
let mut z = None;
self.add_proof(proof, inputs, |_z, _y| {
z = Some(_z);
Some(advice.szy)
});
let z = z.unwrap();
// We need to open up SxyAdvice.s at z using SxyAdvice.opening
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&advice.opening);
transcript.commit_point(&advice.s);
transcript.commit_scalar(&advice.szy);
let random: E::Fr = transcript.get_challenge_scalar();
self.batch.add_opening(advice.opening, random, z);
self.batch.add_commitment(advice.s, random);
self.batch.add_opening_value(advice.szy, random);
}
pub fn add_proof<F>(
&mut self,
proof: &Proof<E>,
inputs: &[E::Fr],
sxy: F
)
where F: FnOnce(E::Fr, E::Fr) -> Option<E::Fr>
{
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&proof.r);
let y: E::Fr = transcript.get_challenge_scalar();
transcript.commit_point(&proof.t);
let z: E::Fr = transcript.get_challenge_scalar();
transcript.commit_scalar(&proof.rz);
transcript.commit_scalar(&proof.rzy);
let r1: E::Fr = transcript.get_challenge_scalar();
transcript.commit_point(&proof.z_opening);
transcript.commit_point(&proof.zy_opening);
// First, the easy one. Let's open up proof.r at zy, using proof.zy_opening
// as the evidence and proof.rzy as the opening.
{
let random = transcript.get_challenge_scalar();
let mut zy = z;
zy.mul_assign(&y);
self.batch.add_opening(proof.zy_opening, random, zy);
self.batch.add_commitment_max_n(proof.r, random);
self.batch.add_opening_value(proof.rzy, random);
}
// Now we need to compute t(z, y) with what we have. Let's compute k(y).
let mut ky = E::Fr::zero();
for (exp, input) in self.k_map.iter().zip(Some(E::Fr::one()).iter().chain(inputs.iter())) {
let mut term = y.pow(&[(*exp + self.n) as u64]);
term.mul_assign(input);
ky.add_assign(&term);
}
// Compute s(z, y)
let szy = sxy(z, y).unwrap_or_else(|| {
let mut tmp = SxEval::new(y, self.n);
S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO
tmp.finalize(z)
// let mut tmp = SyEval::new(z, self.n, self.q);
// S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO
// tmp.finalize(y)
});
// Finally, compute t(z, y)
let mut tzy = proof.rzy;
tzy.add_assign(&szy);
tzy.mul_assign(&proof.rz);
tzy.sub_assign(&ky);
// We open these both at the same time by keeping their commitments
// linearly independent (using r1).
{
let mut random = transcript.get_challenge_scalar();
self.batch.add_opening(proof.z_opening, random, z);
self.batch.add_opening_value(tzy, random);
self.batch.add_commitment(proof.t, random);
random.mul_assign(&r1);
self.batch.add_opening_value(proof.rz, random);
self.batch.add_commitment_max_n(proof.r, random);
}
}
pub fn check_all(self) -> bool {
self.batch.check_all()
}
}
#[derive(Clone)]
pub struct Aggregate<E: Engine> {
// Commitment to s(z, Y)
c: E::G1Affine,
// We have to open each of the S commitments to a random point `z`
s_opening: E::G1Affine,
// We have to open C to each constituent `y`
c_openings: Vec<(E::G1Affine, E::Fr)>,
// Then we have to finally open C
opening: E::G1Affine,
}
pub fn create_aggregate<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
inputs: &[(Proof<E>, SxyAdvice<E>)],
srs: &SRS<E>,
) -> Aggregate<E>
{
// TODO: precompute this?
let (n, q) = {
struct CountN {
n: usize,
q: usize
}
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
fn new_linear_constraint(&mut self) {
self.q += 1;
}
}
let mut tmp = CountN{n:0,q:0};
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
(tmp.n, tmp.q)
};
let mut transcript = Transcript::new(&[]);
let mut y_values: Vec<E::Fr> = Vec::with_capacity(inputs.len());
for &(ref proof, ref sxyadvice) in inputs {
{
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&proof.r);
y_values.push(transcript.get_challenge_scalar());
}
transcript.commit_point(&sxyadvice.s);
}
let z: E::Fr = transcript.get_challenge_scalar();
// Compute s(z, Y)
let (s_poly_negative, s_poly_positive) = {
let mut tmp = SyEval::new(z, n, q);
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.poly()
};
// Compute C = g^{s(z, x)}
let c = multiexp(
srs.g_positive_x_alpha[0..(n + q)]
.iter()
.chain_ext(srs.g_negative_x_alpha[0..n].iter()),
s_poly_positive.iter().chain_ext(s_poly_negative.iter())
).into_affine();
transcript.commit_point(&c);
// Open C at w
let w: E::Fr = transcript.get_challenge_scalar();
let value = compute_value::<E>(&w, &s_poly_positive, &s_poly_negative);
let opening = {
let mut value = value;
value.negate();
let poly = kate_divison(
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
w,
);
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
// Let's open up C to every y.
fn compute_value<E: Engine>(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr {
let mut value = E::Fr::zero();
let yinv = y.inverse().unwrap(); // TODO
let mut tmp = yinv;
for &coeff in poly_negative {
let mut coeff = coeff;
coeff.mul_assign(&tmp);
value.add_assign(&coeff);
tmp.mul_assign(&yinv);
}
let mut tmp = *y;
for &coeff in poly_positive {
let mut coeff = coeff;
coeff.mul_assign(&tmp);
value.add_assign(&coeff);
tmp.mul_assign(&y);
}
value
}
let mut c_openings = vec![];
for y in &y_values {
let value = compute_value::<E>(y, &s_poly_positive, &s_poly_negative);
let opening = {
let mut value = value;
value.negate();
let poly = kate_divison(
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
*y,
);
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
c_openings.push((opening, value));
}
// Okay, great. Now we need to open up each S at the same point z to the same value.
// Since we're opening up all the S's at the same point, we create a bunch of random
// challenges instead and open up a random linear combination.
let mut poly_negative = vec![E::Fr::zero(); n];
let mut poly_positive = vec![E::Fr::zero(); 2*n];
let mut expected_value = E::Fr::zero();
for (y, c_opening) in y_values.iter().zip(c_openings.iter()) {
// Compute s(X, y_i)
let (s_poly_negative, s_poly_positive) = {
let mut tmp = SxEval::new(*y, n);
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.poly()
};
let mut value = c_opening.1;
let r: E::Fr = transcript.get_challenge_scalar();
value.mul_assign(&r);
expected_value.add_assign(&value);
for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) {
coeff.mul_assign(&r);
target.add_assign(&coeff);
}
for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) {
coeff.mul_assign(&r);
target.add_assign(&coeff);
}
}
let s_opening = {
let mut value = expected_value;
value.negate();
let poly = kate_divison(
poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()),
z,
);
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
Aggregate {
// Commitment to s(z, Y)
c,
// We have to open each of the S commitments to a random point `z`
s_opening,
// We have to open C to each constituent `y`
c_openings,
// Then we have to finally open C
opening,
}
}

15
src/sonic/mod.rs Normal file

@ -0,0 +1,15 @@
extern crate ff;
extern crate pairing;
pub use crate::{SynthesisError};
pub mod sonic;
pub mod srs;
pub mod util;
pub mod helped;
pub mod cs;
mod transcript;

310
src/sonic/paper.rs Normal file

@ -0,0 +1,310 @@
#[test]
fn test_paper_results() {
use pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
println!("making srs");
let start = Instant::now();
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
println!("done in {:?}", start.elapsed());
struct PedersenHashPreimageCircuit<'a, E: sapling_crypto::jubjub::JubjubEngine + 'a> {
preimage: Vec<Option<bool>>,
params: &'a E::Params,
}
impl<'a, E: sapling_crypto::jubjub::JubjubEngine + 'a> Clone for PedersenHashPreimageCircuit<'a, E> {
fn clone(&self) -> Self {
PedersenHashPreimageCircuit {
preimage: self.preimage.clone(),
params: self.params
}
}
}
impl<'a, E: sapling_crypto::jubjub::JubjubEngine> bellman::Circuit<E> for PedersenHashPreimageCircuit<'a, E> {
fn synthesize<CS: bellman::ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), bellman::SynthesisError>
{
//use bellman::ConstraintSystem;
use sapling_crypto::circuit::boolean::{AllocatedBit, Boolean};
use sapling_crypto::circuit::pedersen_hash;
let mut preimage = vec![];
for &bit in self.preimage.iter() {
preimage.push(Boolean::from(AllocatedBit::alloc(&mut* cs, bit)?));
}
pedersen_hash::pedersen_hash(
&mut* cs, pedersen_hash::Personalization::NoteCommitment, &preimage, self.params)?;
Ok(())
}
}
#[derive(Clone)]
struct SHA256PreimageCircuit {
preimage: Vec<Option<bool>>,
}
impl<E: Engine> bellman::Circuit<E> for SHA256PreimageCircuit {
fn synthesize<CS: bellman::ConstraintSystem<E>>(
self,
cs: &mut CS,
) -> Result<(), bellman::SynthesisError> {
//use bellman::ConstraintSystem;
use sapling_crypto::circuit::boolean::{AllocatedBit, Boolean};
use sapling_crypto::circuit::sha256::sha256_block_no_padding;
let mut preimage = vec![];
for &bit in self.preimage.iter() {
preimage.push(Boolean::from(AllocatedBit::alloc(&mut *cs, bit)?));
}
sha256_block_no_padding(&mut *cs, &preimage)?;
sha256_block_no_padding(&mut *cs, &preimage)?;
sha256_block_no_padding(&mut *cs, &preimage)?;
// sha256_block_no_padding(&mut *cs, &preimage)?;
Ok(())
}
}
{
use pairing::{CurveAffine};
use pairing::bls12_381::{G1Affine, G2Affine};
let a = G1Affine::one();
let b = G2Affine::one();
let c = G1Affine::one();
let alpha = G1Affine::one();
let beta = G2Affine::one();
let iv = G1Affine::one();
let gamma = G2Affine::one().prepare();
let delta = G2Affine::one().prepare();
let alphabeta = <Bls12 as Engine>::pairing(alpha, beta);
println!("verifying an idealized groth16 proof");
let start = Instant::now();
assert!(<Bls12 as Engine>::final_exponentiation(
&<Bls12 as Engine>::miller_loop([
(&a.prepare(), &b.prepare()),
(&iv.prepare(), &gamma),
(&c.prepare(), &delta),
].into_iter())
).unwrap() != alphabeta);
println!("done in {:?}", start.elapsed());
}
{
use sonic::util::multiexp;
use pairing::{CurveAffine};
use pairing::bls12_381::{G1Affine, G2Affine};
// e([\alpha G], [\beta H]) = e(A, B) e(IV, [\gamma] H) e(C, [\delta] H)
let a = G1Affine::one();
let b = G2Affine::one();
let c = vec![G1Affine::one(); 100];
let mut tmp = Fr::one();
tmp.double();
tmp = tmp.inverse().unwrap();
let cscalars = (0..100).map(|_| {tmp.square(); tmp}).collect::<Vec<_>>();
let alpha = G1Affine::one();
let beta = G2Affine::one();
let iv = G1Affine::one();
let gamma = G2Affine::one().prepare();
let delta = G2Affine::one().prepare();
let alphabeta = <Bls12 as Engine>::pairing(alpha, beta);
println!("verifying 100 idealized groth16 proofs");
let start = Instant::now();
let c = multiexp(
c.iter(),
cscalars.iter(),
).into_affine();
assert!(<Bls12 as Engine>::final_exponentiation(
&<Bls12 as Engine>::miller_loop([
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&a.prepare(), &b.prepare()),
(&iv.prepare(), &gamma),
(&c.prepare(), &delta),
].into_iter())
).unwrap() != alphabeta);
println!("done in {:?}", start.elapsed());
}
{
let samples: usize = 100;
const NUM_BITS: usize = 384;
let params = sapling_crypto::jubjub::JubjubBls12::new();
let circuit = PedersenHashPreimageCircuit {
preimage: vec![Some(true); NUM_BITS],
params: &params
};
println!("creating proof");
let start = Instant::now();
let proof = create_proof::<Bls12, _, Basic>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("done in {:?}", start.elapsed());
println!("creating advice");
let start = Instant::now();
let advice = create_advice::<Bls12, _, Basic>(&AdaptorCircuit(circuit.clone()), &proof, &srs);
println!("done in {:?}", start.elapsed());
println!("creating aggregate for {} proofs", samples);
let start = Instant::now();
let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect();
let aggregate = create_aggregate::<Bls12, _, Basic>(&AdaptorCircuit(circuit.clone()), &proofs, &srs);
println!("done in {:?}", start.elapsed());
{
let mut verifier = MultiVerifier::<Bls12, _, Basic>::new(AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("verifying 1 proof without advice");
let start = Instant::now();
{
for _ in 0..1 {
verifier.add_proof(&proof, &[], |_, _| None);
}
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
{
let mut verifier = MultiVerifier::<Bls12, _, Basic>::new(AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("verifying {} proofs without advice", samples);
let start = Instant::now();
{
for _ in 0..samples {
verifier.add_proof(&proof, &[], |_, _| None);
}
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
{
let mut verifier = MultiVerifier::<Bls12, _, Basic>::new(AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("verifying 100 proofs with advice");
let start = Instant::now();
{
for (ref proof, ref advice) in &proofs {
verifier.add_proof_with_advice(proof, &[], advice);
}
verifier.add_aggregate(&proofs, &aggregate);
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
}
}

166
src/sonic/sonic/adaptor.rs Normal file

@ -0,0 +1,166 @@
extern crate ff;
extern crate pairing;
extern crate rand;
use ff::{Field, PrimeField};
use pairing::{Engine, CurveProjective};
// this one is for all external interfaces
// use crate::{LinearCombination, ConstraintSystem, Circuit, Variable};
use crate::SynthesisError;
use crate::sonic::srs::SRS;
use crate::sonic::cs::LinearCombination as SonicLinearCombination;
use crate::sonic::cs::Circuit as SonicCircuit;
use crate::sonic::cs::ConstraintSystem as SonicConstraintSystem;
use crate::sonic::cs::Variable as SonicVariable;
use crate::sonic::cs::Coeff;
// use crate::sonic::cs::synthesis::*;
use std::marker::PhantomData;
struct Adaptor<'a, E: Engine, CS: SonicConstraintSystem<E> + 'a> {
cs: &'a mut CS,
_marker: PhantomData<E>,
}
impl<'a, E: Engine, CS: SonicConstraintSystem<E> + 'a> crate::ConstraintSystem<E>
for Adaptor<'a, E, CS>
{
type Root = Self;
// this is an important change
fn one() -> crate::Variable {
crate::Variable::new_unchecked(crate::Index::Input(1))
}
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<crate::Variable, crate::SynthesisError>
where
F: FnOnce() -> Result<E::Fr, crate::SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
let var = self.cs.alloc(|| {
f().map_err(|_| crate::SynthesisError::AssignmentMissing)
}).map_err(|_| crate::SynthesisError::AssignmentMissing)?;
Ok(match var {
SonicVariable::A(index) => crate::Variable::new_unchecked(crate::Index::Input(index)),
SonicVariable::B(index) => crate::Variable::new_unchecked(crate::Index::Aux(index)),
_ => unreachable!(),
})
}
fn alloc_input<F, A, AR>(
&mut self,
_: A,
f: F,
) -> Result<crate::Variable, crate::SynthesisError>
where
F: FnOnce() -> Result<E::Fr, crate::SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
let var = self.cs.alloc_input(|| {
f().map_err(|_| crate::SynthesisError::AssignmentMissing)
}).map_err(|_| crate::SynthesisError::AssignmentMissing)?;
Ok(match var {
SonicVariable::A(index) => crate::Variable::new_unchecked(crate::Index::Input(index)),
SonicVariable::B(index) => crate::Variable::new_unchecked(crate::Index::Aux(index)),
_ => unreachable!(),
})
}
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(crate::LinearCombination<E>) -> crate::LinearCombination<E>,
LB: FnOnce(crate::LinearCombination<E>) -> crate::LinearCombination<E>,
LC: FnOnce(crate::LinearCombination<E>) -> crate::LinearCombination<E>,
{
fn convert<E: Engine>(lc: crate::LinearCombination<E>) -> SonicLinearCombination<E> {
let mut ret = SonicLinearCombination::zero();
for &(v, coeff) in lc.as_ref().iter() {
let var = match v.get_unchecked() {
crate::Index::Input(i) => SonicVariable::A(i),
crate::Index::Aux(i) => SonicVariable::B(i),
};
ret = ret + (Coeff::Full(coeff), var);
}
ret
}
fn eval<E: Engine, CS: SonicConstraintSystem<E>>(
lc: &SonicLinearCombination<E>,
cs: &CS,
) -> Option<E::Fr> {
let mut ret = E::Fr::zero();
for &(v, coeff) in lc.as_ref().iter() {
let mut tmp = match cs.get_value(v) {
Ok(tmp) => tmp,
Err(_) => return None,
};
coeff.multiply(&mut tmp);
ret.add_assign(&tmp);
}
Some(ret)
}
let a_lc = convert(a(crate::LinearCombination::zero()));
let a_value = eval(&a_lc, &*self.cs);
let b_lc = convert(b(crate::LinearCombination::zero()));
let b_value = eval(&b_lc, &*self.cs);
let c_lc = convert(c(crate::LinearCombination::zero()));
let c_value = eval(&c_lc, &*self.cs);
let (a, b, c) = self
.cs
.multiply(|| Ok((a_value.unwrap(), b_value.unwrap(), c_value.unwrap())))
.unwrap();
self.cs.enforce_zero(a_lc - a);
self.cs.enforce_zero(b_lc - b);
self.cs.enforce_zero(c_lc - c);
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
// Do nothing; we don't care about namespaces in this context.
}
fn pop_namespace(&mut self) {
// Do nothing; we don't care about namespaces in this context.
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
pub struct AdaptorCircuit<T>(pub T);
impl<'a, E: Engine, C: crate::Circuit<E> + Clone> SonicCircuit<E> for AdaptorCircuit<C> {
fn synthesize<CS: SonicConstraintSystem<E>>(&self, cs: &mut CS) -> Result<(), SynthesisError> {
let mut adaptor = Adaptor {
cs: cs,
_marker: PhantomData,
};
match self.0.clone().synthesize(&mut adaptor) {
Err(_) => return Err(SynthesisError::AssignmentMissing),
Ok(_) => {}
};
Ok(())
}
}

6
src/sonic/sonic/mod.rs Normal file

@ -0,0 +1,6 @@
extern crate ff;
extern crate pairing;
mod adaptor;
pub use self::adaptor::{AdaptorCircuit};

5
src/sonic/srs/mod.rs Normal file

@ -0,0 +1,5 @@
extern crate ff;
extern crate pairing;
mod srs;
pub use self::srs::SRS;

95
src/sonic/srs/srs.rs Normal file

@ -0,0 +1,95 @@
use ff::{Field, PrimeField};
use pairing::{CurveAffine, CurveProjective, Engine, Wnaf};
pub struct SRS<E: Engine> {
pub d: usize,
// g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}
pub g_negative_x: Vec<E::G1Affine>,
// g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}
pub g_positive_x: Vec<E::G1Affine>,
// g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}
pub h_negative_x: Vec<E::G2Affine>,
// g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}
pub h_positive_x: Vec<E::G2Affine>,
// alpha*(g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}})
pub g_negative_x_alpha: Vec<E::G1Affine>,
// alpha*(g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}})
pub g_positive_x_alpha: Vec<E::G1Affine>,
// alpha*(h^{x^0}, h^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}})
pub h_negative_x_alpha: Vec<E::G2Affine>,
// alpha*(h^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}})
pub h_positive_x_alpha: Vec<E::G2Affine>,
}
impl<E: Engine> SRS<E> {
pub fn dummy(d: usize, _: E::Fr, _: E::Fr) -> Self {
SRS {
d: d,
g_negative_x: vec![E::G1Affine::one(); d + 1],
g_positive_x: vec![E::G1Affine::one(); d + 1],
h_negative_x: vec![E::G2Affine::one(); d + 1],
h_positive_x: vec![E::G2Affine::one(); d + 1],
g_negative_x_alpha: vec![E::G1Affine::one(); d],
g_positive_x_alpha: vec![E::G1Affine::one(); d],
h_negative_x_alpha: vec![E::G2Affine::one(); d + 1],
h_positive_x_alpha: vec![E::G2Affine::one(); d + 1],
}
}
pub fn new(d: usize, x: E::Fr, alpha: E::Fr) -> Self {
let mut g1 = Wnaf::new();
let mut g1 = g1.base(E::G1::one(), d * 4);
let mut g2 = Wnaf::new();
let mut g2 = g2.base(E::G2::one(), d * 4);
fn table<C: CurveAffine>(
mut cur: C::Scalar,
step: C::Scalar,
num: usize,
table: &mut Wnaf<usize, &[C::Projective], &mut Vec<i64>>,
) -> Vec<C> {
let mut v = vec![];
for _ in 0..num {
v.push(table.scalar(cur.into_repr()));
cur.mul_assign(&step);
}
C::Projective::batch_normalization(&mut v);
let v = v.into_iter().map(|e| e.into_affine()).collect();
v
}
let x_inv = x.inverse().unwrap();
let mut x_alpha = x;
x_alpha.mul_assign(&alpha);
let mut inv_x_alpha = x_inv;
inv_x_alpha.mul_assign(&alpha);
SRS {
d: d,
g_negative_x: table(E::Fr::one(), x_inv, d + 1, &mut g1),
g_positive_x: table(E::Fr::one(), x, d + 1, &mut g1),
h_negative_x: table(E::Fr::one(), x_inv, d + 1, &mut g2),
h_positive_x: table(E::Fr::one(), x, d + 1, &mut g2),
g_negative_x_alpha: table(inv_x_alpha, x_inv, d, &mut g1),
g_positive_x_alpha: table(x_alpha, x, d, &mut g1),
h_negative_x_alpha: table(alpha, x_inv, d + 1, &mut g2),
h_positive_x_alpha: table(alpha, x, d + 1, &mut g2),
}
}
}

@ -0,0 +1,69 @@
extern crate ff;
extern crate pairing;
use ff::{Field, PrimeField, PrimeFieldRepr};
use pairing::{CurveAffine, CurveProjective, Engine};
use std::io;
// transcript is mocked for now
#[derive(Clone)]
pub struct Transcript {
buffer: Vec<u8>
}
impl Transcript {
pub fn new(personalization: &[u8]) -> Self {
Self {
buffer: vec![]
}
}
pub fn commit_bytes(&mut self, personalization: &[u8], bytes: &[u8]) {
}
pub fn challenge_bytes(&mut self, personalization: &[u8], bytes: &[u8]) {
}
}
pub trait TranscriptProtocol {
fn commit_point<G: CurveAffine>(&mut self, point: &G);
fn commit_scalar<F: PrimeField>(&mut self, scalar: &F);
fn get_challenge_scalar<F: PrimeField>(&mut self) -> F;
}
impl TranscriptProtocol for Transcript {
fn commit_point<G: CurveAffine>(&mut self, point: &G) {
self.commit_bytes(b"point", point.into_compressed().as_ref());
}
fn commit_scalar<F: PrimeField>(&mut self, scalar: &F) {
let mut v = vec![];
scalar.into_repr().write_le(&mut v).unwrap();
self.commit_bytes(b"scalar", &v);
}
fn get_challenge_scalar<F: PrimeField>(&mut self) -> F {
return F::one();
// loop {
// let mut repr: F::Repr = Default::default();
// repr.read_be(TranscriptReader(self)).unwrap();
// if let Ok(result) = F::from_repr(repr) {
// return result;
// }
// }
}
}
struct TranscriptReader<'a>(&'a mut Transcript);
impl<'a> io::Read for TranscriptReader<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.challenge_bytes(b"read", buf);
Ok(buf.len())
}
}

337
src/sonic/util.rs Normal file

@ -0,0 +1,337 @@
use crate::SynthesisError;
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use pairing::{CurveAffine, CurveProjective, Engine};
pub trait ChainExt: Iterator {
fn chain_ext<U>(self, other: U) -> Chain<Self, U::IntoIter>
where
Self: Sized,
U: IntoIterator<Item = Self::Item>,
{
Chain {
t: self,
u: other.into_iter(),
}
}
}
impl<I: Iterator> ChainExt for I {}
#[derive(Clone)]
pub struct Chain<T, U> {
t: T,
u: U,
}
impl<T, U> Iterator for Chain<T, U>
where
T: Iterator,
U: Iterator<Item = T::Item>,
{
type Item = T::Item;
fn next(&mut self) -> Option<T::Item> {
match self.t.next() {
Some(v) => Some(v),
None => match self.u.next() {
Some(v) => Some(v),
None => None,
},
}
}
}
impl<T, U> ExactSizeIterator for Chain<T, U>
where
T: Iterator,
U: Iterator<Item = T::Item>,
T: ExactSizeIterator,
U: ExactSizeIterator,
{
fn len(&self) -> usize {
self.t.len() + self.u.len()
}
}
impl<T, U> DoubleEndedIterator for Chain<T, U>
where
T: Iterator,
U: Iterator<Item = T::Item>,
T: DoubleEndedIterator,
U: DoubleEndedIterator,
{
fn next_back(&mut self) -> Option<T::Item> {
match self.u.next_back() {
Some(v) => Some(v),
None => match self.t.next_back() {
Some(v) => Some(v),
None => None,
},
}
}
}
pub fn multiexp<
'a,
G: CurveAffine,
IB: IntoIterator<Item = &'a G>,
IS: IntoIterator<Item = &'a G::Scalar>,
>(
g: IB,
s: IS,
) -> G::Projective
where
IB::IntoIter: ExactSizeIterator + Clone,
IS::IntoIter: ExactSizeIterator,
{
let g = g.into_iter();
let s = s.into_iter();
assert_eq!(g.len(), s.len());
let c = if s.len() < 32 {
3u32
} else {
(f64::from(s.len() as u32)).ln().ceil() as u32
};
// Convert all of the scalars into representations
let mut s = s.map(|s| s.into_repr()).collect::<Vec<_>>();
let mut windows = vec![];
let mut buckets = vec![];
let mask = (1u64 << c) - 1u64;
let mut cur = 0;
let num_bits = <G::Engine as ScalarEngine>::Fr::NUM_BITS;
while cur <= num_bits {
let mut acc = G::Projective::zero();
buckets.truncate(0);
buckets.resize((1 << c) - 1, G::Projective::zero());
let g = g.clone();
for (s, g) in s.iter_mut().zip(g) {
let index = (s.as_ref()[0] & mask) as usize;
if index != 0 {
buckets[index - 1].add_assign_mixed(g);
}
s.shr(c as u32);
}
let mut running_sum = G::Projective::zero();
for exp in buckets.iter().rev() {
running_sum.add_assign(exp);
acc.add_assign(&running_sum);
}
windows.push(acc);
cur += c;
}
let mut acc = G::Projective::zero();
for window in windows.into_iter().rev() {
for _ in 0..c {
acc.double();
}
acc.add_assign(&window);
}
acc
}
/// Divides polynomial `a` in `x` by `x - b` with
/// no remainder.
pub fn kate_divison<'a, F: Field, I: IntoIterator<Item = &'a F>>(a: I, mut b: F) -> Vec<F>
where
I::IntoIter: DoubleEndedIterator + ExactSizeIterator,
{
b.negate();
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
}
#[test]
fn laurent_division() {
use ff::PrimeField;
use pairing::bls12_381::{Fr};
let mut poly = vec![
Fr::from_str("328947234").unwrap(),
Fr::from_str("3545623451111").unwrap(),
Fr::from_str("112").unwrap(),
Fr::from_str("55555").unwrap(),
Fr::from_str("1235685").unwrap(),
];
fn eval(poly: &[Fr], point: Fr) -> Fr {
let point_inv = point.inverse().unwrap();
let mut acc = Fr::zero();
let mut tmp = Fr::one();
for p in &poly[2..] {
let mut t = *p;
t.mul_assign(&tmp);
acc.add_assign(&t);
tmp.mul_assign(&point);
}
let mut tmp = point_inv;
for p in poly[0..2].iter().rev() {
let mut t = *p;
t.mul_assign(&tmp);
acc.add_assign(&t);
tmp.mul_assign(&point_inv);
}
acc
}
let x = Fr::from_str("23").unwrap();
let z = Fr::from_str("2000").unwrap();
let p_at_x = eval(&poly, x);
let p_at_z = eval(&poly, z);
// poly = poly(X) - poly(z)
poly[2].sub_assign(&p_at_z);
let quotient_poly = kate_divison(&poly, z);
let quotient = eval(&quotient_poly, x);
// check that
// quotient * (x - z) = p_at_x - p_at_z
let mut lhs = x;
lhs.sub_assign(&z);
lhs.mul_assign(&quotient);
let mut rhs = p_at_x;
rhs.sub_assign(&p_at_z);
assert_eq!(lhs, rhs);
}
pub fn multiply_polynomials<E: Engine>(mut a: Vec<E::Fr>, mut b: Vec<E::Fr>) -> Vec<E::Fr> {
let result_len = a.len() + b.len() - 1;
// Compute the size of our evaluation domain
let mut m = 1;
let mut exp = 0;
while m < result_len {
m *= 2;
exp += 1;
// The pairing-friendly curve may not be able to support
// large enough (radix2) evaluation domains.
if exp >= E::Fr::S {
panic!("polynomial too large")
}
}
// Compute omega, the 2^exp primitive root of unity
let mut omega = E::Fr::root_of_unity();
for _ in exp..E::Fr::S {
omega.square();
}
// Extend with zeroes
a.resize(m, E::Fr::zero());
b.resize(m, E::Fr::zero());
serial_fft::<E>(&mut a[..], &omega, exp);
serial_fft::<E>(&mut b[..], &omega, exp);
for (a, b) in a.iter_mut().zip(b.iter()) {
a.mul_assign(b);
}
serial_fft::<E>(&mut a[..], &omega.inverse().unwrap(), exp);
a.truncate(result_len);
let minv = E::Fr::from_str(&format!("{}", m))
.unwrap()
.inverse()
.unwrap();
for a in a.iter_mut() {
a.mul_assign(&minv);
}
a
}
fn serial_fft<E: Engine>(a: &mut [E::Fr], omega: &E::Fr, log_n: u32) {
fn bitreverse(mut n: u32, l: u32) -> u32 {
let mut r = 0;
for _ in 0..l {
r = (r << 1) | (n & 1);
n >>= 1;
}
r
}
let n = a.len() as u32;
assert_eq!(n, 1 << log_n);
for k in 0..n {
let rk = bitreverse(k, log_n);
if k < rk {
a.swap(rk as usize, k as usize);
}
}
let mut m = 1;
for _ in 0..log_n {
let w_m = omega.pow(&[(n / (2 * m)) as u64]);
let mut k = 0;
while k < n {
let mut w = E::Fr::one();
for j in 0..m {
let mut t = a[(k + j + m) as usize];
t.mul_assign(&w);
let mut tmp = a[(k + j) as usize];
tmp.sub_assign(&t);
a[(k + j + m) as usize] = tmp;
a[(k + j) as usize].add_assign(&t);
w.mul_assign(&w_m);
}
k += 2 * m;
}
m *= 2;
}
}
pub trait OptionExt<T> {
fn get(self) -> Result<T, SynthesisError>;
}
impl<T> OptionExt<T> for Option<T> {
fn get(self) -> Result<T, SynthesisError> {
match self {
Some(t) => Ok(t),
None => Err(SynthesisError::AssignmentMissing),
}
}
}

@ -82,6 +82,7 @@ fn mimc<E: Engine>(
/// This is our demo circuit for proving knowledge of the
/// preimage of a MiMC hash invocation.
#[derive(Clone)]
struct MiMCDemo<'a, E: Engine> {
xl: Option<E::Fr>,
xr: Option<E::Fr>,
@ -342,3 +343,200 @@ fn test_mimc_bn256() {
println!("Average verifying time: {:?} seconds", verifying_avg);
}
/// This is our demo circuit for proving knowledge of the
/// preimage of a MiMC hash invocation.
#[derive(Clone)]
struct MiMCDemoNoInputs<'a, E: Engine> {
xl: Option<E::Fr>,
xr: Option<E::Fr>,
image: Option<E::Fr>,
constants: &'a [E::Fr]
}
/// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to
/// synthesize the constraint system.
impl<'a, E: Engine> Circuit<E> for MiMCDemoNoInputs<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
assert_eq!(self.constants.len(), MIMC_ROUNDS);
// Allocate the first component of the preimage.
let mut xl_value = self.xl;
let mut xl = cs.alloc(|| "preimage xl", || {
xl_value.ok_or(SynthesisError::AssignmentMissing)
})?;
// Allocate the second component of the preimage.
let mut xr_value = self.xr;
let mut xr = cs.alloc(|| "preimage xr", || {
xr_value.ok_or(SynthesisError::AssignmentMissing)
})?;
for i in 0..MIMC_ROUNDS {
// xL, xR := xR + (xL + Ci)^3, xL
let cs = &mut cs.namespace(|| format!("round {}", i));
// tmp = (xL + Ci)^2
let mut tmp_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.square();
e
});
let mut tmp = cs.alloc(|| "tmp", || {
tmp_value.ok_or(SynthesisError::AssignmentMissing)
})?;
cs.enforce(
|| "tmp = (xL + Ci)^2",
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + tmp
);
// new_xL = xR + (xL + Ci)^3
// new_xL = xR + tmp * (xL + Ci)
// new_xL - xR = tmp * (xL + Ci)
let mut new_xl_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.mul_assign(&tmp_value.unwrap());
e.add_assign(&xr_value.unwrap());
e
});
let mut new_xl = if i == (MIMC_ROUNDS-1) {
// This is the last round, xL is our image and so
// we use the image
let image_value = self.image;
cs.alloc(|| "image", || {
image_value.ok_or(SynthesisError::AssignmentMissing)
})?
} else {
cs.alloc(|| "new_xl", || {
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
})?
};
cs.enforce(
|| "new_xL = xR + (xL + Ci)^3",
|lc| lc + tmp,
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + new_xl - xr
);
// xR = xL
xr = xl;
xr_value = xl_value;
// xL = new_xL
xl = new_xl;
xl_value = new_xl_value;
}
Ok(())
}
}
#[test]
fn test_sonic_mimc() {
use ff::{Field, PrimeField};
use pairing::{Engine, CurveAffine, CurveProjective};
use pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use bellman::sonic::srs::SRS;
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
println!("making srs");
let start = Instant::now();
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
println!("done in {:?}", start.elapsed());
{
// This may not be cryptographically safe, use
// `OsRng` (for example) in production software.
let rng = &mut thread_rng();
// Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
let samples: usize = 100;
const NUM_BITS: usize = 384;
let xl = rng.gen();
let xr = rng.gen();
let image = mimc::<Bls12>(xl, xr, &constants);
// Create an instance of our circuit (with the
// witness)
let circuit = MiMCDemoNoInputs {
xl: Some(xl),
xr: Some(xr),
image: Some(image),
constants: &constants
};
use bellman::sonic::cs::Basic;
use bellman::sonic::sonic::AdaptorCircuit;
use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier};
println!("creating proof");
let start = Instant::now();
let proof = create_proof::<Bls12, _, Basic>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("done in {:?}", start.elapsed());
println!("creating advice");
let start = Instant::now();
let advice = create_advice::<Bls12, _, Basic>(&AdaptorCircuit(circuit.clone()), &proof, &srs);
println!("done in {:?}", start.elapsed());
println!("creating aggregate for {} proofs", samples);
let start = Instant::now();
let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect();
let aggregate = create_aggregate::<Bls12, _, Basic>(&AdaptorCircuit(circuit.clone()), &proofs, &srs);
println!("done in {:?}", start.elapsed());
{
let mut verifier = MultiVerifier::<Bls12, _, Basic>::new(AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("verifying 1 proof without advice");
let start = Instant::now();
{
for _ in 0..1 {
verifier.add_proof(&proof, &[], |_, _| None);
}
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
{
let mut verifier = MultiVerifier::<Bls12, _, Basic>::new(AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("verifying {} proofs without advice", samples);
let start = Instant::now();
{
for _ in 0..samples {
verifier.add_proof(&proof, &[], |_, _| None);
}
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
{
let mut verifier = MultiVerifier::<Bls12, _, Basic>::new(AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("verifying 100 proofs with advice");
let start = Instant::now();
{
for (ref proof, ref advice) in &proofs {
verifier.add_proof_with_advice(proof, &[], advice);
}
verifier.add_aggregate(&proofs, &aggregate);
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
}
}