integrate initial part of SONIC. Use original code, restructure, mock Transcript
This commit is contained in:
parent
ff6c46240b
commit
f06f92a9cc
@ -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
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
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
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
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
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
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
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);
|
||||
}
|
463
src/sonic/helped/verifier.rs
Normal file
463
src/sonic/helped/verifier.rs
Normal file
@ -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
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
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: ¶ms
|
||||
};
|
||||
|
||||
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
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
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
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
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),
|
||||
}
|
||||
}
|
||||
}
|
69
src/sonic/transcript/mod.rs
Normal file
69
src/sonic/transcript/mod.rs
Normal file
@ -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
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("ient_poly, x);
|
||||
|
||||
// check that
|
||||
// quotient * (x - z) = p_at_x - p_at_z
|
||||
|
||||
let mut lhs = x;
|
||||
lhs.sub_assign(&z);
|
||||
lhs.mul_assign("ient);
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
198
tests/mimc.rs
198
tests/mimc.rs
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user