make succinct transformation working, can now make full protocol

This commit is contained in:
Alex Vlasov 2019-06-06 00:50:46 +03:00
parent 8cfacca862
commit 95b91fc0b0
24 changed files with 2619 additions and 621 deletions

@ -18,7 +18,7 @@ bit-vec = "0.4.4"
futures = "0.1"
cfg-if = "0.1.7"
#pairing = { git = 'https://github.com/matterinc/pairing', tag = "0.16.2" }
#pairing_ce = { path = "../pairing" }
pairing_ce = { version = "0.17.0" }
byteorder = "1"

@ -90,6 +90,16 @@ pub enum Variable {
C(usize),
}
impl Variable {
pub(crate) fn get_index(&self) -> usize {
match *self {
Variable::A(index) => index,
Variable::B(index) => index,
Variable::C(index) => index,
}
}
}
#[derive(Debug)]
pub enum Coeff<E: Engine> {
Zero,

@ -41,6 +41,8 @@ pub trait ConstraintSystem<E: Engine> {
/// about the circuit for verification, while another actually constructs
/// a witness.
pub trait Backend<E: Engine> {
type LinearConstraintIndex;
/// Get the value of a variable. Can return None if we don't know.
fn get_var(&self, _variable: Variable) -> Option<E::Fr> { None }
@ -51,11 +53,14 @@ pub trait Backend<E: Engine> {
/// Create a new multiplication gate.
fn new_multiplication_gate(&mut self) { }
/// Create a new linear constraint.
fn new_linear_constraint(&mut self) { }
/// Create a new linear constraint, returning the power of Y for caching purposes.
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex;
/// Insert a term into a linear constraint. TODO: bad name of function
fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff<E>) { }
fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff<E>, y: &Self::LinearConstraintIndex) { }
/// Compute a `LinearConstraintIndex` from `q`.
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex;
/// Mark y^{_index} as the power of y cooresponding to the public input
/// coefficient for the next public input, in the k(Y) polynomial.
@ -66,251 +71,3 @@ pub trait Backend<E: Engine> {
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)?;
Ok(())
}
}
pub struct Nonassigning;
impl SynthesisDriver for Nonassigning {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
struct NonassigningSynthesizer<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 NonassigningSynthesizer<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_b = Variable::B(index);
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.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);
Ok((a, b, c))
}
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
self.backend.get_var(var).ok_or(())
}
}
let mut tmp: NonassigningSynthesizer<E, B> = NonassigningSynthesizer {
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, <NonassigningSynthesizer<E, B> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut tmp)?;
Ok(())
}
}

@ -18,7 +18,7 @@ use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::{Circuit};
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::srs::SRS;
use crate::sonic::cs::Nonassigning;
use crate::sonic::sonic::Nonassigning;
use super::helper::create_aggregate as create_aggregate_sonic_circuit;
pub fn create_aggregate<E: Engine, C: Circuit<E> + Clone>(

@ -15,10 +15,11 @@ use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::{Circuit};
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::srs::SRS;
use crate::sonic::cs::Basic;
use crate::sonic::sonic::Basic;
use super::prover::create_advice as create_advice_sonic_circuit;
use super::prover::create_advice_on_information_and_srs as create_advice_on_information_and_srs_sonic_circuit;
use super::prover::create_proof_on_srs as create_proof_on_srs_sonic_circuit;
use crate::sonic::sonic::CountN;
// pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E> + Clone, S: SynthesisDriver>(
pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E> + Clone>(
@ -51,23 +52,13 @@ pub fn create_advice_on_srs<E: Engine, C: Circuit<E> + Clone>(
srs: &SRS<E>
) -> Result<SxyAdvice<E>, SynthesisError>
{
use crate::sonic::cs::Nonassigning;
use crate::sonic::sonic::Nonassigning;
let adapted_circuit = AdaptorCircuit(circuit.clone());
// 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};
let mut tmp = CountN::<Nonassigning>::new();
Nonassigning::synthesize(&mut tmp, &adapted_circuit)?;
tmp.n

@ -18,7 +18,7 @@ use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::{Circuit};
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::srs::SRS;
use crate::sonic::cs::Nonassigning;
use crate::sonic::sonic::Nonassigning;
use super::verifier::verify_aggregate_on_srs as verify_aggregate_on_srs_sonic_circuit;
use super::verifier::verify_proofs_on_srs as verify_proofs_on_srs_sonic_circuit;

@ -38,7 +38,7 @@ use crate::multicore::{
use std::marker::PhantomData;
use crate::sonic::cs::{Backend, Basic, SynthesisDriver};
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::srs::SRS;
use crate::sonic::cs::LinearCombination as SonicLinearCombination;
use crate::sonic::cs::Circuit as SonicCircuit;
@ -47,6 +47,9 @@ use crate::sonic::cs::Variable as SonicVariable;
use crate::sonic::cs::Coeff;
use crate::sonic::sonic::{AdaptorCircuit};
use super::parameters::NUM_BLINDINGS;
use crate::sonic::sonic::NonassigningSynthesizer;
use crate::sonic::sonic::PermutationSynthesizer;
use crate::sonic::sonic::{Basic, Preprocess};
use crate::verbose_flag;
@ -231,117 +234,11 @@ pub fn get_circuit_parameters<E, C>(
where E: Engine, C: Circuit<E>
{
struct NonassigningSynthesizer<E: Engine, B: Backend<E>> {
backend: B,
current_variable: Option<usize>,
_marker: PhantomData<E>,
q: usize,
n: usize,
}
impl<E: Engine, B: Backend<E>> SonicConstraintSystem<E> for NonassigningSynthesizer<E, B> {
const ONE: SonicVariable = SonicVariable::A(1);
fn alloc<F>(&mut self, _value: F) -> Result<SonicVariable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
match self.current_variable.take() {
Some(index) => {
let var_b = SonicVariable::B(index);
self.current_variable = None;
Ok(var_b)
},
None => {
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let var_a = SonicVariable::A(index);
self.current_variable = Some(index);
Ok(var_a)
}
}
}
fn alloc_input<F>(&mut self, value: F) -> Result<SonicVariable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
let input_var = self.alloc(value)?;
self.enforce_zero(SonicLinearCombination::zero() + input_var);
self.backend.new_k_power(self.q);
Ok(input_var)
}
fn enforce_zero(&mut self, lc: SonicLinearCombination<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<(SonicVariable, SonicVariable, SonicVariable), 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 = SonicVariable::A(index);
let b = SonicVariable::B(index);
let c = SonicVariable::C(index);
Ok((a, b, c))
}
fn get_value(&self, var: SonicVariable) -> Result<E::Fr, ()> {
self.backend.get_var(var).ok_or(())
}
}
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 };
let mut preprocess = Preprocess::new();
let (num_inputs, num_aux, num_constraints) = {
let mut cs: NonassigningSynthesizer<E, &'_ mut Preprocess<E>> = NonassigningSynthesizer {
backend: &mut preprocess,
current_variable: None,
_marker: PhantomData,
q: 0,
n: 0,
};
let mut cs: NonassigningSynthesizer<E, &'_ mut Preprocess<E>> = NonassigningSynthesizer::new(&mut preprocess);
let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
@ -374,6 +271,52 @@ pub fn get_circuit_parameters<E, C>(
})
}
/// Get circuit information such as number of input, variables,
/// constraints, and the corresponding SONIC parameters
/// k_map, n, q
pub fn get_circuit_parameters_for_succinct_sonic<E, C>(
circuit: C,
) -> Result<CircuitParameters<E>, SynthesisError>
where E: Engine, C: Circuit<E>
{
let mut preprocess = Preprocess::new();
let (num_inputs, num_aux, num_constraints) = {
let mut cs: PermutationSynthesizer<E, &'_ mut Preprocess<E>> = PermutationSynthesizer::new(&mut preprocess);
let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <PermutationSynthesizer<E, &'_ mut Preprocess<E>> as SonicConstraintSystem<E>>::ONE) {
(SonicVariable::A(1), SonicVariable::A(1)) => {},
_ => return Err(SynthesisError::UnconstrainedVariable)
}
let mut assembly = GeneratorAssembly::<'_, E, _> {
cs: &mut cs,
num_inputs: 0,
num_aux: 0,
num_constraints: 0,
_marker: PhantomData
};
circuit.synthesize(&mut assembly)?;
(assembly.num_inputs, assembly.num_aux, assembly.num_constraints)
};
Ok(CircuitParameters {
num_inputs: num_inputs,
num_aux: num_aux,
num_constraints: num_constraints,
k_map: preprocess.k_map,
n: preprocess.n,
q: preprocess.q,
_marker: PhantomData
})
}
pub fn generate_parameters<E, C>(
circuit: C,
alpha: E::Fr,

@ -14,6 +14,7 @@ use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::CountNandQ;
#[derive(Clone)]
pub struct Aggregate<E: Engine> {
@ -47,22 +48,8 @@ pub fn create_aggregate_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
{
// TODO: precompute this?
let (n, q) = {
struct CountN {
n: usize,
q: usize
}
let mut tmp = CountNandQ::<S>::new();
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)

@ -2,13 +2,13 @@ use crate::pairing::ff::{Field};
use crate::pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
mod batch;
mod poly;
pub mod batch;
pub mod poly;
pub mod prover;
pub mod verifier;
pub mod helper;
mod parameters;
mod generator;
pub mod parameters;
pub mod generator;
mod adapted_prover;
mod adapted_verifier;
mod adapted_helper;
@ -23,7 +23,8 @@ pub use self::generator::{
generate_parameters_on_srs_and_information,
generate_random_parameters,
generate_srs,
get_circuit_parameters
get_circuit_parameters,
get_circuit_parameters_for_succinct_sonic
};
pub use self::parameters::{
Proof,

@ -250,35 +250,16 @@ impl<E: Engine> VerifyingKey<E> {
}
}
use crate::sonic::cs::{Backend, Basic, SynthesisDriver};
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::srs::SRS;
use crate::sonic::cs::Circuit as SonicCircuit;
use crate::sonic::sonic::{Basic, Preprocess};
use std::marker::PhantomData;
impl<E: Engine> VerifyingKey<E> {
pub fn new<C: SonicCircuit<E>, S: SynthesisDriver>(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 };
let mut preprocess = Preprocess::new();
S::synthesize(&mut preprocess, &circuit)?;

@ -31,6 +31,8 @@ pub struct SxEval<E: Engine> {
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>,
max_n: usize,
}
impl<E: Engine> SxEval<E> {
@ -69,6 +71,7 @@ impl<E: Engine> SxEval<E> {
u,
v,
w,
max_n: n
}
}
@ -114,11 +117,19 @@ impl<E: Engine> SxEval<E> {
}
impl<'a, E: Engine> Backend<E> for &'a mut SxEval<E> {
fn new_linear_constraint(&mut self) {
type LinearConstraintIndex = E::Fr;
fn new_linear_constraint(&mut self) -> E::Fr {
self.yqn.mul_assign(&self.y);
self.yqn
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex {
self.y.pow(&[(self.max_n + q) as u64])
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>, y: &E::Fr) {
let acc = match var {
Variable::A(index) => {
&mut self.u[index - 1]
@ -134,13 +145,13 @@ impl<'a, E: Engine> Backend<E> for &'a mut SxEval<E> {
match coeff {
Coeff::Zero => { },
Coeff::One => {
acc.add_assign(&self.yqn);
acc.add_assign(&y);
},
Coeff::NegativeOne => {
acc.sub_assign(&self.yqn);
acc.sub_assign(&y);
},
Coeff::Full(mut val) => {
val.mul_assign(&self.yqn);
val.mul_assign(&y);
acc.add_assign(&val);
}
}
@ -270,18 +281,25 @@ impl<E: Engine> SyEval<E> {
}
impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
fn new_linear_constraint(&mut self) {
type LinearConstraintIndex = usize;
fn new_linear_constraint(&mut self) -> usize {
self.current_q += 1;
self.current_q
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex {
q
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>, q: &usize) {
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;
let yindex = *q + self.max_n;
self.positive_coeffs[yindex - 1].add_assign(&tmp);
}
Variable::B(index) => {
@ -289,7 +307,7 @@ impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
// Y^{q+N} += X^{i} * coeff
let mut tmp = self.b[index];
coeff.multiply(&mut tmp);
let yindex = self.current_q + self.max_n;
let yindex = *q + self.max_n;
self.positive_coeffs[yindex - 1].add_assign(&tmp);
}
Variable::C(index) => {
@ -297,7 +315,7 @@ impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
// 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;
let yindex = *q + self.max_n;
self.positive_coeffs[yindex - 1].add_assign(&tmp);
}
};

@ -14,6 +14,7 @@ use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::{CountN, Basic};
pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
@ -119,17 +120,7 @@ pub fn create_advice_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
// 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};
let mut tmp = CountN::<S>::new();
S::synthesize(&mut tmp, circuit)?;
tmp.n
@ -147,65 +138,14 @@ pub fn create_proof<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
extern crate rand;
use self::rand::{Rand, Rng, thread_rng};
use crate::sonic::sonic::Wires;
pub fn create_proof_on_srs<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![],
};
let mut wires = Wires::new();
S::synthesize(&mut wires, circuit)?;
@ -392,7 +332,8 @@ fn my_fun_circuit_test() {
use crate::pairing::ff::PrimeField;
use crate::pairing::bls12_381::{Bls12, Fr};
use super::*;
use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination};
use crate::sonic::cs::{ConstraintSystem, LinearCombination};
use crate::sonic::sonic::Basic;
use rand::{thread_rng};
struct MyCircuit;
@ -445,7 +386,8 @@ fn polynomial_commitment_test() {
use crate::pairing::ff::PrimeFieldRepr;
use crate::pairing::bls12_381::{Bls12, Fr};
use super::*;
use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination};
use crate::sonic::cs::{ConstraintSystem, LinearCombination};
use crate::sonic::sonic::Basic;
use rand::{thread_rng};
use crate::pairing::{CurveAffine};

@ -16,6 +16,7 @@ use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::Preprocess;
pub struct MultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
circuit: C,
@ -30,28 +31,7 @@ pub struct MultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
impl<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> MultiVerifier<E, C, S, R> {
// This constructor consumes randomness source cause it's later used internally
pub fn new(circuit: C, srs: &SRS<E>, rng: R) -> 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 };
let mut preprocess = Preprocess::new();
S::synthesize(&mut preprocess, &circuit)?;

169
src/sonic/sonic/backends.rs Normal file

@ -0,0 +1,169 @@
use crate::pairing::{Engine};
use crate::sonic::cs::Backend;
use std::marker::PhantomData;
use crate::SynthesisError;
use crate::sonic::cs::SynthesisDriver;
use crate::sonic::cs::{Circuit, ConstraintSystem, Variable, LinearCombination};
use crate::pairing::ff::Field;
pub struct Preprocess<E: Engine> {
pub k_map: Vec<usize>,
pub n: usize,
pub q: usize,
_marker: PhantomData<E>
}
impl<'a, E: Engine> Backend<E> for &'a mut Preprocess<E> {
type LinearConstraintIndex = ();
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () }
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;
()
}
}
impl<E: Engine> Preprocess<E> {
pub fn new() -> Self {
Preprocess {
k_map: vec![],
n: 0,
q: 0,
_marker: PhantomData
}
}
}
pub struct Wires<E: Engine> {
pub a: Vec<E::Fr>,
pub b: Vec<E::Fr>,
pub c: Vec<E::Fr>
}
impl<'a, E: Engine> Backend<E> for &'a mut Wires<E> {
type LinearConstraintIndex = ();
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex { () }
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () }
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(())
}
}
impl<E: Engine> Wires<E> {
pub fn new() -> Self {
Wires {
a: vec![],
b: vec![],
c: vec![],
}
}
}
pub struct CountNandQ<S: SynthesisDriver> {
pub n: usize,
pub q: usize,
_marker: std::marker::PhantomData<S>
}
impl<'a, E: Engine, S: SynthesisDriver> Backend<E> for &'a mut CountNandQ<S> {
type LinearConstraintIndex = ();
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () }
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex {
self.q += 1;
()
}
}
impl<S: SynthesisDriver> CountNandQ<S> {
pub fn new() -> Self {
Self {
n: 0,
q: 0,
_marker: std::marker::PhantomData
}
}
}
pub struct CountN<S: SynthesisDriver> {
pub n: usize,
_marker: std::marker::PhantomData<S>
}
impl<'a, E: Engine, S: SynthesisDriver> Backend<E> for &'a mut CountN<S> {
type LinearConstraintIndex = ();
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex { () }
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () }
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
}
impl<S: SynthesisDriver> CountN<S> {
pub fn new() -> Self {
Self {
n: 0,
_marker: std::marker::PhantomData
}
}
}

@ -0,0 +1,638 @@
use crate::pairing::{Engine};
use crate::sonic::cs::Backend;
use std::marker::PhantomData;
use std::iter::Peekable;
use crate::SynthesisError;
use crate::sonic::cs::SynthesisDriver;
use crate::sonic::cs::{Circuit, ConstraintSystem, Variable, LinearCombination, Coeff};
use crate::pairing::ff::Field;
use super::M;
pub struct NonassigningSynthesizer<E: Engine, B: Backend<E>> {
backend: B,
current_variable: Option<usize>,
_marker: PhantomData<E>,
q: usize,
n: usize,
}
impl<E: Engine, B: Backend<E>>NonassigningSynthesizer<E, B> {
pub fn new(backend: B) -> Self {
Self {
backend: backend,
current_variable: None,
_marker: PhantomData,
q: 0,
n: 0,
}
}
}
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for NonassigningSynthesizer<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_b = Variable::B(index);
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.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;
let y = self.backend.new_linear_constraint();
for (var, coeff) in lc.as_ref() {
self.backend.insert_coefficient(*var, *coeff, &y);
}
}
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);
Ok((a, b, c))
}
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
self.backend.get_var(var).ok_or(())
}
}
pub 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;
let y = self.backend.new_linear_constraint();
for (var, coeff) in lc.as_ref() {
self.backend.insert_coefficient(*var, *coeff, &y);
}
}
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(())
}
}
impl<E: Engine, B: Backend<E>>Synthesizer<E, B> {
pub fn new(backend: B) -> Self {
Self {
backend: backend,
current_variable: None,
_marker: PhantomData,
q: 0,
n: 0,
}
}
}
pub struct PermutationSynthesizer<E: Engine, B: Backend<E>> {
backend: B,
current_variable: Option<usize>,
_marker: PhantomData<E>,
q: usize,
n: usize,
// These vectors will hold, for all of the wires, the terms related to these
// wires for each of the M permutation polynomials. The Coeff<E> is the
// coefficient, and the usize is q, the index of the linear constraint and is
// related to the power of Y in the s_1(X, Y) polynomial.
pub a: Vec<[Option<(Coeff<E>, usize)>; M]>,
pub b: Vec<[Option<(Coeff<E>, usize)>; M]>,
pub c: Vec<[Option<(Coeff<E>, usize)>; M]>,
}
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for PermutationSynthesizer<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();
// Create slots for the new wires.
self.a.push([None; M]);
self.b.push([None; M]);
self.c.push([None; M]);
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);
// The new variable has all free slots, so this shouldn't create
// more than one linear combination.
self.backend.new_k_power(self.q);
Ok(input_var)
}
fn enforce_zero(&mut self, lc: LinearCombination<E>)
{
// We just redirect things into the (recursing) enforce_equals method which
// does the actual work. Annoyingly, we need to use dynamic dispatch on the
// underlying iterator because once you've taken a Peekable<I> you can't get
// the underlying iterator (since .next() may have been called on it) so
// at each depth of recursion we'd end up with a new type, which is
// impossible for the compiler to reason about.
let lc = lc.as_ref();
let lc: &mut Iterator<Item=&(Variable, Coeff<E>)> = &mut lc.into_iter();
let lc = lc.peekable();
self.enforce_equals(lc, None);
}
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();
// Create slots for the new wires.
self.a.push([None; M]);
self.b.push([None; M]);
self.c.push([None; M]);
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(())
}
}
impl<E: Engine, B: Backend<E>> PermutationSynthesizer<E, B> {
pub fn new(backend: B) -> Self {
Self {
backend: backend,
current_variable: None,
_marker: PhantomData,
q: 0,
n: 0,
a: vec![],
b: vec![],
c: vec![],
}
}
// Enforces that the value of `lhs` equals the value
// of `rhs`, returning the value of the left hand side
// as determined by the assignment. If rhs is none, it
// is interpreted to be zero.
fn enforce_equals<'a>(
&mut self,
mut lhs: Peekable<&mut Iterator<Item=&'a (Variable, Coeff<E>)>>,
rhs: Option<Variable>
) -> Option<E::Fr>
{
// First, let's create a new linear constraint. We'll save its y value
// for the backend and q as well.
self.q += 1;
let q = self.q;
let y = self.backend.new_linear_constraint();
let mut slots_available = [true; M];
let mut num_slots_available = M;
// If the caller is enforce_equals we need to return the value of the lhs
// so that rhs can be assigned properly, so we keep track of it here.
let mut current_value = if rhs.is_some() { Some(E::Fr::zero()) } else { None };
// If rhs is Some, then we _need_ to involve it in this
// linear constraint, so let's just handle it right away. (This also
// helps avoid an infinite recursion issue described later.)
if let Some(rhs) = rhs {
self.emplace_variable(&mut slots_available, &y, rhs, Coeff::NegativeOne, q);
num_slots_available -= 1;
}
// Iterate through the linear combination
loop {
if let Some(term) = lhs.next() {
assert!(num_slots_available > 0);
if num_slots_available == 1 && lhs.peek().is_some() {
// We'll be out of slots if we add this variable to the linear
// combination; instead, create an ephemeral variable to hold
// the value of the remaining terms and use that. Temporarily,
// give the variable "zero" value.
let ephemeral = self.alloc(|| Ok(E::Fr::zero())).expect("assignment is provided so this should not fail");
// One of the annoying "tricks" we have to embrace is that the ephemeral
// variable has all of its slots available, and so because it's the rhs
// when we recursively call `enforce_equals` we know that it will not trigger
// a condition in `emplace_variable` that results in the variable being
// duplicated; otherwise, the duplicate variable will have a value of zero
// and we'd have to somehow track all of the duplicates when we later assign.
let mut iter = Some(term).into_iter().chain(lhs);
let iter: &mut Iterator<Item=&(Variable, Coeff<E>)> = &mut iter;
let value = self.enforce_equals(iter.peekable(), Some(ephemeral));
// Set the correct ephemeral value right away
self.backend.set_var(ephemeral, || {
value.ok_or(SynthesisError::AssignmentMissing)
}).expect("assignment is provided so this should not fail");
// Fix the underlying assignment -- the c-wire value will change if the ephemeral
// value was a b-wire.
self.fix_variable_assignment(ephemeral);
// Now we emplace the variable into the linear combination.
self.emplace_variable(&mut slots_available, &y, ephemeral, Coeff::One, q);
num_slots_available -= 1;
match (&mut current_value, &value) {
(Some(ref mut current_value), Some(ref value)) => {
current_value.add_assign(&value);
},
_ => {
current_value = None;
}
}
assert!(num_slots_available == 0);
// We're done, so return.
return current_value;
} else {
self.emplace_variable(&mut slots_available, &y, term.0, term.1, q);
num_slots_available -= 1;
match (&mut current_value, self.backend.get_var(term.0)) {
(Some(ref mut current_value), Some(mut value)) => {
term.1.multiply(&mut value);
current_value.add_assign(&value);
},
_ => {
current_value = None;
}
}
}
} else {
// We're done, so return.
return current_value;
}
}
}
// This takes a variable and coefficient and places it into a linear combination,
// given a set of slots that are available, and updates the slot availability to
// reflect which slot was chosen.
fn emplace_variable(&mut self, slots_available: &mut [bool; M], y: &B::LinearConstraintIndex, var: Variable, coeff: Coeff<E>, q: usize)
{
// Get the slots for this wire.
let wire_slots = self.get_wire_slots(var);
// Let's handle the simple case where the linear combination and the
// variable have a slot that coincides.
let mut available_i = None;
for i in 0..M {
if slots_available[i] {
available_i = Some(i);
if wire_slots[i] {
self.emplace_slot(var, i, coeff, y, q);
slots_available[i] = false;
return;
}
}
}
let available_i = available_i.expect("there is always at least one slot open");
// available_i corresponds to a slot that is available in the linear
// combination; clearly, it is not available for the wire. In order
// to rectify this, we will create a new wire with the same value.
let ephemeral_value = self.backend.get_var(var);
let ephemeral = self.alloc(|| {
ephemeral_value.ok_or(SynthesisError::AssignmentMissing)
}).expect("assignment is provided so this should not fail");
// Now, we'll emplace the slot for _this_ variable.
self.emplace_slot(ephemeral, available_i, coeff, y, q);
slots_available[available_i] = false;
// Next, we'll free up a slot in the original wire
let free_i = (available_i + 1) % M;
// by moving the term to the ephemeral wire.
self.move_slot(free_i, var, ephemeral);
// The original wire has slot free_i available now, and
// the new wire has only available_i and (available_i + 1) % M
// occupied. As long as M>=3, this means available_i + 2 % M
// is a free wire for the ephemeral and it is distinct from
// free_i! So, we can relate the ephemeral variable to the
// original.
let iter = [(var, Coeff::One), (ephemeral, Coeff::NegativeOne)];
let mut iter = iter.into_iter();
let iter: &mut Iterator<Item=&(Variable, Coeff<E>)> = &mut iter;
self.enforce_equals(iter.peekable(), None);
}
// Move slot value from wire to another
fn move_slot(&mut self, slot: usize, from: Variable, to: Variable) {
let slot_val;
{
let from_vals = match from {
Variable::A(index) => &mut self.a[index - 1],
Variable::B(index) => &mut self.b[index - 1],
Variable::C(index) => &mut self.c[index - 1],
};
if from_vals[slot].is_none() {
// In this case, we do nothing.
return;
}
slot_val = from_vals[slot].unwrap();
from_vals[slot] = None;
}
// We need the backend to compute the cached y^q value for us,
// if it needs it.
let y = self.backend.get_for_q(slot_val.1);
self.backend.insert_coefficient(from, -slot_val.0, &y); // Negate coefficient to undo
{
let to_vals = match to {
Variable::A(index) => &mut self.a[index - 1],
Variable::B(index) => &mut self.b[index - 1],
Variable::C(index) => &mut self.c[index - 1],
};
to_vals[slot] = Some(slot_val);
self.backend.insert_coefficient(to, slot_val.0, &y);
}
}
// Place a coefficient in a slot
fn emplace_slot(&mut self, var: Variable, slot_index: usize, coeff: Coeff<E>, y: &B::LinearConstraintIndex, q: usize)
{
let vals = match var {
Variable::A(index) => &mut self.a[index - 1],
Variable::B(index) => &mut self.b[index - 1],
Variable::C(index) => &mut self.c[index - 1],
};
vals[slot_index] = Some((coeff, q));
self.backend.insert_coefficient(var, coeff, &y);
}
// Get available slots for a wire
fn get_wire_slots(&self, var: Variable) -> [bool; M] {
let vals = match var {
Variable::A(index) => &self.a[index - 1],
Variable::B(index) => &self.b[index - 1],
Variable::C(index) => &self.c[index - 1],
};
let mut slots = [true; M];
for i in 0..M {
if vals[i].is_some() {
slots[i] = false;
}
}
slots
}
// If a variable changes value, we probably need to adjust.
fn fix_variable_assignment(&mut self, var: Variable) {
let index = var.get_index();
let a_value = self.backend.get_var(Variable::A(index));
let b_value = self.backend.get_var(Variable::B(index));
let c_value = match (a_value, b_value) {
(Some(mut a), Some(b)) => {
a.mul_assign(&b);
Some(a)
},
_ => { None }
};
self.backend.set_var(Variable::C(index), || {
c_value.ok_or(SynthesisError::AssignmentMissing)
}).expect("assignment exists if the closure is called");
}
}

@ -1,3 +1,11 @@
mod adaptor;
mod synthesis_drivers;
mod backends;
mod constraint_systems;
pub use self::adaptor::{Adaptor, AdaptorCircuit};
pub use self::synthesis_drivers::{Basic, Nonassigning, Permutation3};
pub use self::backends::{CountNandQ, CountN, Preprocess, Wires};
pub use self::constraint_systems::{NonassigningSynthesizer, Synthesizer, PermutationSynthesizer};
pub const M: usize = 3;

@ -0,0 +1,126 @@
use std::marker::PhantomData;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::pairing::{Engine};
use crate::sonic::cs::{Variable, Circuit, ConstraintSystem, LinearCombination};
use crate::SynthesisError;
use crate::pairing::ff::{Field};
use super::constraint_systems::{NonassigningSynthesizer, Synthesizer, PermutationSynthesizer};
pub struct Basic;
impl SynthesisDriver for Basic {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
let mut tmp: Synthesizer<E, B> = Synthesizer::new(backend);
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)?;
Ok(())
}
}
pub struct Nonassigning;
impl SynthesisDriver for Nonassigning {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
let mut tmp: NonassigningSynthesizer<E, B> = NonassigningSynthesizer::new(backend);
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <NonassigningSynthesizer<E, B> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut tmp)?;
Ok(())
}
}
/*
In order to use the fully succinct version of Sonic, the resulting s(X, Y) polynomial
must be in a more "trivial" form
s(X, Y) = X^{-N - 1} Y^N s_1(X, Y) - X^N s_2(X, Y)
where
s_1(X, Y) = \sum\limits_{i=1}^N u'_i(Y) X^{-i + N + 1}
+ \sum\limits_{i=1}^N v'_i(Y) X^{i + N + 1}
+ \sum\limits_{i=1}^N w'_i(Y) X^{i + 2N + 1}
s_2(X, Y) = \sum\limits_{i=1}^N (Y^i + Y^{-i}) X^i
u'_i(Y) = \sum\limits_{q=1}^Q Y^q u_{q,i}
v'_i(Y) = \sum\limits_{q=1}^Q Y^q v_{q,i}
w'_i(Y) = \sum\limits_{q=1}^Q Y^q w_{q,i}
such that s_1(X, Y) can be expressed as the sum of M permutation polynomials.
It is trivial for the verifier to evaluate s_2(X, Y), since polynomials of the form
x + x^2 + x^3 + ... can be evaluated with a logarithmic number of field operations.
In order to get s_1(X, Y) into the form needed, each constituent permutation polynomial
is effectively of the form
s_j(X, Y) = \sum\limits_{i=1}^{3N+1} c_i X^i Y^\sigma_j(i)
where \sigma_j(i) defines the permutation. The X^i corresponds to the wire, and the
Y^\sigma_j(i) corresponds to the index of the linear constraint.
This effectively means that within each polynomial there can be only one particular
X^i term, and so wires can only appear in M different linear combinations. Further,
because there is only ever a particular Y^i term in each M permutation polynomial,
linear combinations can have only M wires.
In order to synthesize a constraint system into a form that supports this wonky
arrangement, we need M>=3. The general goal is to treat each permutation polynomial
as a "slot" and, when constructing linear constraints, keep track of which slots are
"occupied" by wires, either with respect to the wires themselves or with respect to
the linear combination as it is being assembled.
If the linear combination has more than M terms, then we need to recursively
construct ephemeral wires to hold the values of the remaining terms, and relate those
wires to those terms in new linear combinations.
Once our linear combinations are small enough to fit the terms into the M slots,
we eagerly shove the terms in. The easy case is when a slot is available for both
the wire and the linear combination. The remaining cases can be addressed generally
by imagining that the wire has no available slots. We will create a new ephemeral
wire that holds the same value as the original wire and use this wire to insert the
linear combination. Then, we'll swap one of the terms from another slot into the new
ephemeral wire, freeing a slot in the original wire. Then, we trivially have that the
new wire and old wire have distinct slots free (since M>=3) and so we can now force
that they become equal.
In terms of actually implementing this, things can get tricky. We don't want to end
up in a circumstance where we are infinitely recursing, which can happen depending on
the order we create linear combinations for the ephemeral variables.
*/
pub struct Permutation3;
impl SynthesisDriver for Permutation3 {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
let mut tmp: PermutationSynthesizer<E, B> = PermutationSynthesizer::new(backend);
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <PermutationSynthesizer<E, B> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut tmp)?;
Ok(())
}
}

@ -284,7 +284,7 @@ fn test_sonic_mimc() {
constants: &constants
};
use crate::sonic::cs::Basic;
use crate::sonic::sonic::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
@ -351,6 +351,121 @@ fn test_sonic_mimc() {
}
}
#[test]
fn test_succinct_sonic_mimc() {
use crate::pairing::ff::{Field, PrimeField};
use crate::pairing::{Engine, CurveAffine, CurveProjective};
use crate::pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use crate::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());
{
use rand::{XorShiftRng, SeedableRng, Rand, Rng};
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
// Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
let samples: usize = 100;
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 crate::sonic::sonic::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters_for_succinct_sonic};
use crate::sonic::helped::helper::{create_aggregate_on_srs};
use crate::sonic::sonic::Permutation3;
use crate::sonic::unhelped::permutation_structure::*;
let perm_structure = create_permutation_structure::<Bls12, _>(&AdaptorCircuit(circuit.clone()));
perm_structure.create_permutation_arguments(Fr::one(), Fr::one(), rng, &srs);
println!("N = {}, Q = {}", perm_structure.n, perm_structure.q);
return;
// let info = get_circuit_parameters_for_succinct_sonic::<Bls12, _>(circuit.clone()).expect("Must get circuit info");
// println!("{:?}", info);
// println!("creating proof");
// let start = Instant::now();
// let proof = create_proof_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
// println!("done in {:?}", start.elapsed());
// println!("creating advice");
// let start = Instant::now();
// let advice = create_advice_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap();
// 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_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &proofs, &srs);
// println!("done in {:?}", start.elapsed());
// {
// let rng = thread_rng();
// let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).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 rng = thread_rng();
// let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).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 rng = thread_rng();
// let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).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());
// }
}
}
#[test]
fn test_inputs_into_sonic_mimc() {
use crate::pairing::ff::{Field, PrimeField};
@ -388,7 +503,7 @@ fn test_inputs_into_sonic_mimc() {
constants: &constants
};
use crate::sonic::cs::Basic;
use crate::sonic::sonic::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
@ -541,100 +656,100 @@ fn test_high_level_sonic_api() {
}
}
#[test]
fn test_constraints_info() {
use crate::pairing::bn256::{Bn256};
use std::time::{Instant};
use crate::sonic::unhelped::padding::{constraints_info};
{
// This may not be cryptographically safe, use
// `OsRng` (for example) in production software.
let mut rng = &mut thread_rng();
// #[test]
// fn test_constraints_info() {
// use crate::pairing::bn256::{Bn256};
// use std::time::{Instant};
// use crate::sonic::unhelped::padding::{constraints_info};
// {
// // This may not be cryptographically safe, use
// // `OsRng` (for example) in production software.
// let mut rng = &mut thread_rng();
// Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
// // Generate the MiMC round constants
// let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
let xl = rng.gen();
let xr = rng.gen();
let image = mimc::<Bn256>(xl, xr, &constants);
// let xl = rng.gen();
// let xr = rng.gen();
// let image = mimc::<Bn256>(xl, xr, &constants);
// Create an instance of our circuit (with the
// witness)
let circuit = MiMCDemo {
xl: Some(xl),
xr: Some(xr),
constants: &constants
};
// // Create an instance of our circuit (with the
// // witness)
// let circuit = MiMCDemo {
// xl: Some(xl),
// xr: Some(xr),
// constants: &constants
// };
constraints_info::<Bn256, _>(circuit.clone());
}
}
// constraints_info::<Bn256, _>(circuit.clone());
// }
// }
#[test]
fn test_padding_using_mimc() {
use crate::pairing::ff::{Field, PrimeField};
use crate::pairing::{Engine, CurveAffine, CurveProjective};
use crate::pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use crate::sonic::srs::SRS;
// #[test]
// fn test_padding_using_mimc() {
// use crate::pairing::ff::{Field, PrimeField};
// use crate::pairing::{Engine, CurveAffine, CurveProjective};
// use crate::pairing::bls12_381::{Bls12, Fr};
// use std::time::{Instant};
// use crate::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());
// 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();
// {
// // 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;
// // Generate the MiMC round constants
// let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
// let samples: usize = 100;
let xl = rng.gen();
let xr = rng.gen();
let image = mimc::<Bls12>(xl, xr, &constants);
// 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
};
// // Create an instance of our circuit (with the
// // witness)
// let circuit = MiMCDemoNoInputs {
// xl: Some(xl),
// xr: Some(xr),
// image: Some(image),
// constants: &constants
// };
use crate::sonic::cs::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
use crate::sonic::helped::helper::{create_aggregate_on_srs};
use crate::sonic::unhelped::padding::Padding;
// use crate::sonic::cs::Basic;
// use crate::sonic::sonic::AdaptorCircuit;
// use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
// use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
// use crate::sonic::helped::helper::{create_aggregate_on_srs};
// use crate::sonic::unhelped::padding::Padding;
let info = get_circuit_parameters::<Bls12, _>(circuit.clone()).expect("Must get circuit info");
println!("{:?}", info);
// let info = get_circuit_parameters::<Bls12, _>(circuit.clone()).expect("Must get circuit info");
// println!("{:?}", info);
println!("creating proof");
let start = Instant::now();
let proof = create_proof_on_srs::<Bls12, _, Padding>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("done in {:?}", start.elapsed());
// println!("creating proof");
// let start = Instant::now();
// let proof = create_proof_on_srs::<Bls12, _, Padding>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
// println!("done in {:?}", start.elapsed());
{
let rng = thread_rng();
let mut verifier = MultiVerifier::<Bls12, _, Padding, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
println!("K map = {:?}", verifier.get_k_map());
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 rng = thread_rng();
// let mut verifier = MultiVerifier::<Bls12, _, Padding, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
// println!("K map = {:?}", verifier.get_k_map());
// 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());
// }
// }
// }

@ -0,0 +1,291 @@
use crate::pairing::ff::{Field};
use crate::pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use crate::sonic::helped::{Proof, SxyAdvice};
use crate::sonic::helped::batch::Batch;
use crate::sonic::helped::poly::{SxEval, SyEval};
use crate::sonic::helped::Parameters;
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;
use crate::sonic::sonic::CountNandQ;
use crate::sonic::sonic::M;
#[derive(Clone)]
pub struct SuccinctAggregate<E: Engine> {
pub permutations: []
pub a: Vec<[Option<(Coeff<E>, usize)>; M]>,
pub b: Vec<[Option<(Coeff<E>, usize)>; M]>,
pub c: Vec<[Option<(Coeff<E>, usize)>; M]>,
}
pub fn create_aggregate<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
inputs: &[(Proof<E>, SxyAdvice<E>)],
params: &Parameters<E>,
) -> SuccinctAggregate<E>
{
let n = params.vk.n;
let q = params.vk.q;
create_aggregate_on_srs_using_information::<E, C, S>(circuit, inputs, &params.srs, n, q)
}
pub fn create_aggregate_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
inputs: &[(Proof<E>, SxyAdvice<E>)],
srs: &SRS<E>,
) -> SuccinctAggregate<E>
{
// TODO: precompute this?
let (n, q) = {
let mut tmp = CountNandQ::<S>::new();
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
(tmp.n, tmp.q)
};
create_aggregate_on_srs_using_information::<E, C, S>(circuit, inputs, srs, n, q)
}
pub fn create_aggregate_on_srs_using_information<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
inputs: &[(Proof<E>, SxyAdvice<E>)],
srs: &SRS<E>,
n: usize,
q: usize,
) -> SuccinctAggregate<E>
{
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();
let t = {
let mut tmp: PermutationSynthesizer<E, B> = PermutationSynthesizer::new(backend);
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <PermutationSynthesizer<E, B> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut tmp).expect("should synthesize");
tmp
};
// 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();
polynomial_commitment_opening(
n,
0,
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
w,
&srs
)
// 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 positive_powers_contrib = evaluate_at_consequitive_powers(poly_positive, *y, *y);
let negative_powers_contrib = evaluate_at_consequitive_powers(poly_negative, yinv, yinv);
value.add_assign(&positive_powers_contrib);
value.add_assign(&negative_powers_contrib);
// 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
}
use std::time::Instant;
let start = Instant::now();
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();
polynomial_commitment_opening(
n,
0,
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
*y,
&srs
)
// 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));
}
println!("Evaluation of s(z, Y) taken {:?}", start.elapsed());
// 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();
// TODO: this part can be further parallelized due to synthesis of S(X, y) being singlethreaded
let start = Instant::now();
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);
mul_add_polynomials(& mut poly_negative[..], &s_poly_negative[..], r);
mul_add_polynomials(& mut poly_positive[..], &s_poly_positive[..], r);
// 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);
// }
}
println!("Re-evaluation of {} S polynomials taken {:?}", y_values.len(), start.elapsed());
let s_opening = {
let mut value = expected_value;
value.negate();
polynomial_commitment_opening(
n,
0,
poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()),
z,
&srs
)
// 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,
}
}

@ -7,7 +7,12 @@ mod s2_proof;
mod wellformed_argument;
mod grand_product_argument;
mod permutation_argument;
pub mod padding;
mod verifier;
pub mod permutation_structure;
// mod helper;
// mod permutation;
// pub mod padding;
pub use self::wellformed_argument::{WellformednessArgument, WellformednessProof};
pub use self::permutation_argument::{PermutationArgument, PermutationProof, Proof};
pub use self::verifier::SuccinctMultiVerifier;

@ -47,6 +47,11 @@ fn permute<F: Field>(coeffs: &[F], permutation: & [usize]) -> Vec<F>{
assert_eq!(coeffs.len(), permutation.len());
let mut result: Vec<F> = vec![F::zero(); coeffs.len()];
for (i, j) in permutation.iter().enumerate() {
if *j < 1 {
// if permutation information is missing coefficient itself must be zero!
assert!(coeffs[i].is_zero());
continue;
}
result[*j - 1] = coeffs[i];
}
result
@ -79,7 +84,7 @@ impl<E: Engine> PermutationArgument<E> {
let n = non_permuted_coefficients[0].len();
// p1 is just a commitment to the powers of x
// p1 is just a commitment to the powers of x. It's indexed from 0 cause there is no g^0
let p_1 = multiexp(srs.g_positive_x_alpha[0..n].iter(), vec![E::Fr::one(); n].iter()).into_affine();
let mut p_2 = vec![];
@ -104,7 +109,6 @@ impl<E: Engine> PermutationArgument<E> {
// p2 is a commitment to the s^{perm}_i * x^i
{
// let permuted_coeffs = permute(&c[..], &p[..]);
let p2 = multiexp(srs.g_positive_x_alpha[0..n].iter(), c.iter()).into_affine();
p_2.push(p2);
}
@ -253,7 +257,7 @@ impl<E: Engine> PermutationArgument<E> {
s_polynomial = Some(c.clone());
}
}
let mut s_polynomial = s_polynomial.unwrap();
let s_polynomial = s_polynomial.unwrap();
// evaluate at z
let s_zy = evaluate_at_consequitive_powers(& s_polynomial[..], z, z);
@ -590,14 +594,12 @@ fn test_permutation_argument() {
let srs_alpha = Fr::from_str("23728792").unwrap();
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
let n: usize = 1 << 1;
let n: usize = 1 << 4;
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
// let mut permutation = (0..n).collect::<Vec<_>>();
// rng.shuffle(&mut permutation);
let coeffs = vec![Fr::from_str("3").unwrap(), Fr::from_str("4").unwrap()];
let permutation = vec![2, 1];
let mut coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
coeffs[2] = Fr::zero(); // edge case
let mut permutation = (1..=n).collect::<Vec<_>>();
rng.shuffle(&mut permutation);
let coeffs = vec![coeffs];
let permutations = vec![permutation];
@ -607,12 +609,8 @@ fn test_permutation_argument() {
let mut argument = PermutationArgument::new(coeffs, permutations);
let y : Fr = rng.gen();
let y : Fr = Fr::one();
let y : Fr = Fr::from_str("2").unwrap();
// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let challenges = vec![Fr::one()];
let challenges = (0..1).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let commitments = argument.commit(y, &srs);
let mut s_commitments = vec![];
@ -623,7 +621,6 @@ fn test_permutation_argument() {
}
let z_prime : Fr = rng.gen();
let z_prime : Fr = Fr::one();
let opening = argument.open_commitments_to_s_prime(&challenges, y, z_prime, &srs);

@ -0,0 +1,415 @@
use crate::pairing::ff::{Field};
use crate::pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use crate::sonic::helped::{Proof, SxyAdvice};
use crate::sonic::helped::batch::Batch;
use crate::sonic::helped::poly::{SxEval, SyEval};
use crate::sonic::helped::Parameters;
use crate::SynthesisError;
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver, ConstraintSystem};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::Preprocess;
use crate::sonic::sonic::M;
use crate::sonic::sonic::PermutationSynthesizer;
use super::s2_proof::*;
use super::permutation_argument::*;
#[derive(Clone)]
pub struct PermutationStructure<E: Engine> {
pub n: usize,
pub q: usize,
pub a: Vec<[Option<(Coeff<E>, usize)>; M]>,
pub b: Vec<[Option<(Coeff<E>, usize)>; M]>,
pub c: Vec<[Option<(Coeff<E>, usize)>; M]>,
}
pub fn create_permutation_structure<E: Engine, C: Circuit<E>>(
circuit: &C,
) -> PermutationStructure<E>
{
let mut backend: Preprocess<E> = Preprocess::new();
let (a, b, c) = {
let mut cs: PermutationSynthesizer<E, &'_ mut Preprocess<E>> = PermutationSynthesizer::new(&mut backend);
let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <PermutationSynthesizer<E, &'_ mut Preprocess<E>> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut cs).expect("should synthesize");
(cs.a, cs.b, cs.c)
};
let n = backend.n;
let q = backend.q;
println!("Will have {} gates and {} linear constraints", n, q);
PermutationStructure::<E> {
n: n,
q: q,
a: a,
b: b,
c: c
}
}
use rand::{Rng, Rand};
impl<E: Engine> PermutationStructure<E> {
pub fn calculate_s2_commitment_value(&self, srs: &SRS<E>) -> E::G1Affine {
S2Eval::calculate_commitment_element(self.n, srs)
}
pub fn calculate_s2_proof(&self, x: E::Fr, y: E::Fr, srs: &SRS<E>) -> S2Proof<E> {
let s2_eval = S2Eval::new(self.n);
s2_eval.evaluate(x, y, &srs)
}
pub fn create_permutation_arguments<R: Rng>(&self, y: E::Fr, z: E::Fr, rng: &mut R, srs: &SRS<E>)
// -> PermutationProof<E>
{
// we have to form non-permuted coefficients, as well as permutation structures;
let n = self.n;
let mut non_permuted_coeffs = vec![vec![E::Fr::zero(); 3*n+1]; M];
let mut permutations = vec![vec![0usize; 3*n+1]; M];
let one = E::Fr::one();
let mut minus_one = E::Fr::one();
minus_one.negate();
// println!("A");
let mut not_empty = [false; M];
// go other the permutations
for (gate_index, info) in self.a.iter().enumerate() {
let offset = n-1;
for i in 0..M {
// coefficients of A are placed at the offset = 0 from the beginning of the vector
if let Some((coeff, place)) = info[i].as_ref() {
// place it
assert!(*place != 0);
let x_power = offset - gate_index + 1;
let array_position = offset - gate_index; // special for A
let place_coeff_into = &mut non_permuted_coeffs[i];
let place_permutation_into = &mut permutations[i];
match coeff {
Coeff::Zero => {
// println!("Variable A({}) coefficient 0 at constraint {} in poly {}", gate_index+1, place, i);
// println!("Coeff 0 will be for Y^{} for X^{} for permutation {} ", place, x_power, i);
},
Coeff::One => {
not_empty[i] = true;
place_coeff_into[array_position] = one;
place_permutation_into[array_position] = *place;
// println!("Variable A({}) coefficient 1 at constraint {} in poly {}", gate_index+1, place, i);
// println!("Term 1*X^{}*Y^{} in poly {}", x_power, place,i);
// println!("Coeff 1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
},
Coeff::NegativeOne => {
not_empty[i] = true;
place_coeff_into[array_position] = minus_one;
place_permutation_into[array_position] = *place;
// println!("Variable A({}) coefficient -1 at constraint {} in poly {}", gate_index+1, place, i);
// println!("Term -1*X^{}*Y^{} in poly {}", x_power, place,i);
// println!("Coeff -1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
},
Coeff::Full(value) => {
not_empty[i] = true;
place_coeff_into[array_position] = *value;
place_permutation_into[array_position] = *place;
// println!("Variable A({}) coefficient * at constraint {} in poly {}", gate_index+1, place, i);
// println!("Term k*X^{}*Y^{} in poly {}", x_power, place,i);
// println!("Coeff {} will be at for Y^{} for X^{} for permutation {} ", value, place, x_power, i);
}
}
} else {
// println!("Empty coeff for X^{} in permutation {}", gate_index, i);
}
}
}
// println!("B");
for (gate_index, info) in self.b.iter().enumerate() {
let offset = n + 1;
for i in 0..M {
if let Some((coeff, place)) = info[i].as_ref() {
// place it
assert!(*place != 0);
let x_power = offset + gate_index + 1; // 1 indexed
let array_position = offset + gate_index;
let place_coeff_into = &mut non_permuted_coeffs[i];
let place_permutation_into = &mut permutations[i];
match coeff {
Coeff::Zero => {
// println!("Coeff 0 will be for Y^{} for X^{} for permutation {} ", place, x_power, i);
// println!("Variable B({}) coefficient 0 at constraint {} in poly {}", gate_index+1, place, i);
},
Coeff::One => {
not_empty[i] = true;
place_coeff_into[array_position] = one;
place_permutation_into[array_position] = *place;
// println!("Coeff 1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
// println!("Variable B({}) coefficient 1 at constraint {} in poly {}", gate_index+1, place, i);
// println!("Term 1*X^{}*Y^{} in poly {}", x_power, place,i);
},
Coeff::NegativeOne => {
not_empty[i] = true;
place_coeff_into[array_position] = minus_one;
place_permutation_into[array_position] = *place;
// println!("Coeff -1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
// println!("Variable B({}) coefficient -1 at constraint {} in poly {}", gate_index+1, place, i);
// println!("Term -1*X^{}*Y^{} in poly {}", x_power, place,i);
},
Coeff::Full(value) => {
not_empty[i] = true;
place_coeff_into[array_position] = *value;
place_permutation_into[array_position] = *place;
// println!("Coeff {} will be at for Y^{} for X^{} for permutation {} ", value, place, x_power, i);
// println!("Variable B({}) coefficient * at constraint {} in poly {}", gate_index+1, place, i);
// println!("Term k*X^{}*Y^{} in poly {}", x_power, place,i);
}
}
} else {
// println!("Empty coeff for X^{} in permutation {}", gate_index, i);
}
}
}
// println!("C");
for (gate_index, info) in self.c.iter().enumerate() {
let offset = 2*n + 1;
for i in 0..M {
// coefficients of A are placed at the offset = 0 from the beginning of the vector
if let Some((coeff, place)) = info[i].as_ref() {
// place it
assert!(*place != 0);
let x_power = offset + gate_index + 1; // 1 indexed
let array_position = offset + gate_index;
let place_coeff_into = &mut non_permuted_coeffs[i];
let place_permutation_into = &mut permutations[i];
match coeff {
Coeff::Zero => {
// println!("Coeff 0 will be for Y^{} for X^{} for permutation {} ", place, x_power, i);
// println!("Variable C({}) coefficient 0 at constraint {} in poly {}", gate_index+1, place, i);
},
Coeff::One => {
not_empty[i] = true;
place_coeff_into[array_position] = one;
place_permutation_into[array_position] = *place;
// println!("Coeff 1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
// println!("Variable C({}) coefficient 1 at constraint {} in poly {}", gate_index+1, place, i);
// println!("Term 1*X^{}*Y^{} in poly {}", x_power, place,i);
},
Coeff::NegativeOne => {
not_empty[i] = true;
place_coeff_into[array_position] = minus_one;
place_permutation_into[array_position] = *place;
// println!("Coeff -1 will be at for Y^{} for X^{} for permutation {} ", place, x_power, i);
// println!("Variable C({}) coefficient -1 at constraint {} in poly {}", gate_index+1, place, i);
// println!("Term -1*X^{}*Y^{} in poly {}", x_power, place,i);
},
Coeff::Full(value) => {
not_empty[i] = true;
place_coeff_into[array_position] = *value;
place_permutation_into[array_position] = *place;
// println!("Coeff {} will be at for Y^{} for X^{} for permutation {} ", value, place, x_power, i);
// println!("Variable C({}) coefficient * at constraint {} in poly {}", gate_index+1, place, i);
// println!("Term k*X^{}*Y^{} in poly {}", x_power, place,i);
}
}
} else {
// println!("Empty coeff for X^{} in permutation {}", gate_index, i);
}
}
}
// need to fill arrays with non-zero indexes just to have full permutation, even while it's just zero coefficient
// permutations[0][2] = 5;
// permutations[0][5] = 6;
// permutations[0][6] = 7;
// permutations[1][0] = 1;
// permutations[1][1] = 3;
// permutations[1][2] = 5;
// permutations[1][5] = 6;
// permutations[1][6] = 7;
println!("Not empty = {:?}", not_empty);
// TODO: fix
let mut m = M;
for i in (0..M).into_iter().rev() {
if !not_empty[i] {
non_permuted_coeffs.pop();
permutations.pop();
m -= 1;
}
}
assert!(m != 0);
// find something faster, although it's still linear
for i in 0..m {
let mut fillers: Vec<usize> = (1..=(3*n+1)).map(|el| el).collect();
for (p, c) in permutations[i].iter_mut().zip(non_permuted_coeffs[i].iter()) {
if *p == 0 {
assert!(c.is_zero());
} else {
fillers[*p - 1] = 0;
}
}
let mut fill_from = 0;
for p in permutations[i].iter_mut() {
if *p == 0 {
loop {
if fillers[fill_from] != 0 {
*p = fillers[fill_from];
fill_from += 1;
break;
} else {
fill_from += 1;
}
}
}
}
}
println!("Will need {} permutation polynomials", m);
// println!("Nonpermuted 0 (coeffs for X^1, X^2, ...) = {:?}", non_permuted_coeffs[0]);
// println!("Permutation 0 (power of Y for X^1, X^2, ...) = {:?}", permutations[0]);
// println!("Nonpermuted 1 (coeffs for X^1, X^2, ...)= {:?}", non_permuted_coeffs[1]);
// println!("Permutation 1 (power of Y for X^1, X^2, ...) = {:?}", permutations[1]);
let specialized_srs = PermutationArgument::make_specialized_srs(
&non_permuted_coeffs,
&permutations,
&srs
);
let mut argument = PermutationArgument::new(non_permuted_coeffs, permutations);
let challenges = (0..m).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
let commitments = argument.commit(y, &srs);
let mut s_commitments = vec![];
let mut s_prime_commitments = vec![];
for (s, s_prime) in commitments.into_iter() {
s_commitments.push(s);
// println!("S' = {}", s_prime);
s_prime_commitments.push(s_prime);
}
let z_prime : E::Fr = rng.gen();
let opening = argument.open_commitments_to_s_prime(&challenges, y, z_prime, &srs);
let randomness = (0..2).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
let valid = PermutationArgument::verify_s_prime_commitment(n,
&randomness,
&challenges,
&s_prime_commitments,
&opening,
y,
z_prime,
&specialized_srs,
&srs);
assert!(valid, "s' commitment must be valid");
let beta : E::Fr = rng.gen();
let gamma : E::Fr = rng.gen();
let grand_product_challenges = (0..m).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
let wellformed_challenges = (0..(2*m)).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
let proof = argument.make_argument(
beta,
gamma,
& grand_product_challenges,
& wellformed_challenges,
y,
z,
&specialized_srs, &srs);
let valid = PermutationArgument::verify(&s_commitments, &proof, z, &srs);
assert!(valid, "permutation argument must be valid");
}
}
#[test]
fn test_simple_succinct_sonic() {
use crate::pairing::ff::{Field, PrimeField};
use crate::pairing::{Engine, CurveAffine, CurveProjective};
use crate::pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use crate::sonic::srs::SRS;
use crate::sonic::cs::{Circuit, 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::zero() + (Coeff::Full(E::Fr::from_str("2").unwrap()), a) - b);
let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?;
cs.enforce_zero(LinearCombination::from(b) - multiplier);
Ok(())
}
}
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());
{
use rand::{XorShiftRng, SeedableRng, Rand, Rng};
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
use crate::sonic::sonic::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters_for_succinct_sonic};
use crate::sonic::helped::helper::{create_aggregate_on_srs};
use crate::sonic::sonic::Permutation3;
use crate::sonic::unhelped::permutation_structure::*;
let perm_structure = create_permutation_structure::<Bls12, _>(&MyCircuit);
perm_structure.create_permutation_arguments(Fr::one(), Fr::one(), rng, &srs);
println!("N = {}, Q = {}", perm_structure.n, perm_structure.q);
}
}

@ -0,0 +1,310 @@
use crate::pairing::ff::{Field};
use crate::pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use super::{Proof, SxyAdvice};
use super::batch::Batch;
use super::poly::{SxEval, SyEval};
use super::parameters::{Parameters, NUM_BLINDINGS};
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;
use crate::sonic::sonic::{CountN, Basic};
pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
proof: &Proof<E>,
srs: &SRS<E>,
n: usize
) -> Result<SxyAdvice<E>, SynthesisError>
{
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().ok_or(SynthesisError::DivisionByZero)?;
let (s_poly_negative, s_poly_positive) = {
let mut tmp = SxEval::new(y, n);
S::synthesize(&mut tmp, circuit)?;
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();
{
szy.add_assign(& evaluate_at_consequitive_powers(& s_poly_positive[..], z, z));
szy.add_assign(& evaluate_at_consequitive_powers(& s_poly_negative[..], z_inv, 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()
};
Ok(SxyAdvice {
s,
szy,
opening
})
}
pub fn create_advice<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
proof: &Proof<E>,
parameters: &Parameters<E>,
) -> Result<SxyAdvice<E>, SynthesisError>
{
let n = parameters.vk.n;
create_advice_on_information_and_srs::<E, C, S>(circuit, proof, &parameters.srs, n)
}
pub fn create_advice_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
proof: &Proof<E>,
srs: &SRS<E>
) -> Result<SxyAdvice<E>, SynthesisError>
{
// annoying, but we need n to compute s(z, y), and this isn't
// precomputed anywhere yet
let n = {
let mut tmp = CountN::<S>::new();
S::synthesize(&mut tmp, circuit)?;
tmp.n
};
create_advice_on_information_and_srs::<E, C, S>(circuit, proof, srs, n)
}
pub fn create_proof<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
parameters: &Parameters<E>
) -> Result<Proof<E>, SynthesisError> {
create_proof_on_srs::<E, C, S>(circuit, &parameters.srs)
}
extern crate rand;
use self::rand::{Rand, Rng, thread_rng};
use crate::sonic::sonic::Wires;
pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
srs: &SRS<E>
) -> Result<Proof<E>, SynthesisError>
{
let mut wires = Wires::new();
S::synthesize(&mut wires, circuit)?;
let n = wires.a.len();
let mut transcript = Transcript::new(&[]);
let rng = &mut thread_rng();
// c_{n+1}, c_{n+2}, c_{n+3}, c_{n+4}
let blindings: Vec<E::Fr> = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect();
// r is a commitment to r(X, 1)
let r = polynomial_commitment::<E, _>(
n,
2*n + NUM_BLINDINGS,
n,
&srs,
blindings.iter().rev()
.chain_ext(wires.c.iter().rev())
.chain_ext(wires.b.iter().rev())
.chain_ext(Some(E::Fr::zero()).iter())
.chain_ext(wires.a.iter()),
);
transcript.commit_point(&r);
let y: E::Fr = transcript.get_challenge_scalar();
// create r(X, 1) by observation that it's just a series of coefficients.
// Used representation is for powers X^{-2n}...X^{-n-1}, X^{-n}...X^{-1}, X^{1}...X^{n}
// Same representation is ok for r(X, Y) too cause powers always match
let mut rx1 = wires.b;
rx1.extend(wires.c);
rx1.extend(blindings.clone());
rx1.reverse();
rx1.push(E::Fr::zero());
rx1.extend(wires.a);
let mut rxy = rx1.clone();
let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?;
// y^(-2n - num blindings)
let tmp = y_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]);
mut_distribute_consequitive_powers(
&mut rxy,
tmp,
y,
);
// negative powers [-1, -2n], positive [1, n]
let (mut s_poly_negative, s_poly_positive) = {
let mut tmp = SxEval::new(y, n);
S::synthesize(&mut tmp, circuit)?;
tmp.poly()
};
// r'(X, y) = r(X, y) + s(X, y). Note `y` - those are evaluated at the point already
let mut rxy_prime = rxy.clone();
{
// extend to have powers [n+1, 2n]
rxy_prime.resize(4 * n + 1 + NUM_BLINDINGS, E::Fr::zero());
s_poly_negative.reverse();
let neg_poly_len = s_poly_negative.len();
add_polynomials(&mut rxy_prime[(NUM_BLINDINGS+neg_poly_len)..(2 * n + NUM_BLINDINGS)], &s_poly_negative[..]);
s_poly_negative.reverse();
add_polynomials(&mut rxy_prime[(2 * n + 1 + NUM_BLINDINGS)..], &s_poly_positive[..])
// // add coefficients in front of X^{-2n}...X^{-n-1}, X^{-n}...X^{-1}
// for (r, s) in rxy_prime[NUM_BLINDINGS..(2 * n + NUM_BLINDINGS)]
// .iter_mut()
// .rev()
// .zip(s_poly_negative)
// {
// r.add_assign(&s);
// }
// // add coefficients in front of X^{1}...X^{n}, X^{n+1}...X^{2*n}
// for (r, s) in rxy_prime[(2 * n + 1 + NUM_BLINDINGS)..].iter_mut().zip(s_poly_positive) {
// r.add_assign(&s);
// }
}
// by this point all R related polynomials are blinded and evaluated for Y variable
// t(X, y) = r'(X, y)*r(X, 1) and will be later evaluated at z
// contained degree in respect to X are from -4*n to 3*n including X^0
let mut txy = multiply_polynomials::<E>(rx1.clone(), rxy_prime);
txy[4 * n + 2 * NUM_BLINDINGS] = E::Fr::zero(); // -k(y)
// commit to t(X, y) to later open at z
let t = polynomial_commitment(
srs.d,
(4 * n) + 2*NUM_BLINDINGS,
3 * n,
srs,
// skip what would be zero power
txy[0..(4 * n) + 2*NUM_BLINDINGS].iter()
.chain_ext(txy[(4 * n + 2*NUM_BLINDINGS + 1)..].iter()),
);
transcript.commit_point(&t);
let z: E::Fr = transcript.get_challenge_scalar();
let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?;
let rz = {
let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]);
evaluate_at_consequitive_powers(&rx1, tmp, z)
};
// rzy is evaluation of r(X, Y) at z, y
let rzy = {
let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]);
evaluate_at_consequitive_powers(&rxy, tmp, z)
};
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)
// subtract constant term from R(X, 1)
rx1[(2 * n + NUM_BLINDINGS)].sub_assign(&rzy);
let mut point = y;
point.mul_assign(&z);
polynomial_commitment_opening(
2 * n + NUM_BLINDINGS,
n,
&rx1,
point,
srs
)
};
assert_eq!(rx1.len(), 3*n + NUM_BLINDINGS + 1);
// it's an opening of t(X, y) at z
let z_opening = {
rx1[(2 * n + NUM_BLINDINGS)].add_assign(&rzy); // restore
let rx1_len = rx1.len();
mul_add_polynomials(&mut txy[(2 * n + NUM_BLINDINGS)..(2 * n + NUM_BLINDINGS + rx1_len)], &rx1[..], r1);
// // skip powers from until reach -2n - NUM_BLINDINGS
// for (t, &r) in txy[(2 * n + NUM_BLINDINGS)..].iter_mut().zip(rx1.iter()) {
// let mut r = r;
// r.mul_assign(&r1);
// t.add_assign(&r);
// }
let val = {
let tmp = z_inv.pow(&[(4*n + 2*NUM_BLINDINGS) as u64]);
evaluate_at_consequitive_powers(&txy, tmp, z)
};
txy[(4 * n + 2*NUM_BLINDINGS)].sub_assign(&val);
polynomial_commitment_opening(
4*n + 2*NUM_BLINDINGS,
3*n,
&txy,
z,
srs)
};
Ok(Proof {
r, rz, rzy, t, z_opening, zy_opening
})
}

@ -0,0 +1,314 @@
use crate::pairing::ff::{Field};
use crate::pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use rand::{Rand, Rng};
use crate::sonic::helped::{Proof, SxyAdvice};
use crate::sonic::helped::batch::Batch;
use crate::sonic::helped::poly::{SxEval, SyEval};
use crate::sonic::helped::helper::Aggregate;
use crate::sonic::helped::parameters::{Parameters};
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;
use crate::sonic::sonic::Preprocess;
pub struct SuccinctMultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
circuit: C,
pub(crate) batch: Batch<E>,
k_map: Vec<usize>,
n: usize,
q: usize,
randomness_source: R,
_marker: PhantomData<(E, S)>
}
impl<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> SuccinctMultiVerifier<E, C, S, R> {
// This constructor consumes randomness source cause it's later used internally
pub fn new(circuit: C, srs: &SRS<E>, rng: R) -> Result<Self, SynthesisError> {
let mut preprocess = Preprocess::new();
S::synthesize(&mut preprocess, &circuit)?;
Ok(SuccinctMultiVerifier {
circuit,
batch: Batch::new(srs, preprocess.n),
k_map: preprocess.k_map,
n: preprocess.n,
q: preprocess.q,
randomness_source: rng,
_marker: PhantomData
})
}
pub fn add_aggregate(
&mut self,
proofs: &[(Proof<E>, SxyAdvice<E>)],
aggregate: &Aggregate<E>,
szw: E::Fr
)
{
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)
// };
{
let random: E::Fr = self.randomness_source.gen();
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 = self.randomness_source.gen();
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 = self.randomness_source.gen();
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);
}
/// Caller must ensure to add aggregate after adding a proof
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 = self.randomness_source.gen();
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: E::Fr = self.randomness_source.gen();
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)
// t(z, y) = (r(z, y) + s(z,y))*r(z, 1) - k(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: E::Fr = self.randomness_source.gen();
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 get_k_map(&self) -> Vec<usize> {
return self.k_map.clone();
}
pub fn get_n(&self) -> usize {
return self.n;
}
pub fn get_q(&self) -> usize {
return self.q;
}
pub fn check_all(self) -> bool {
self.batch.check_all()
}
}
// /// Check multiple proofs without aggregation. Verifier's work is
// /// not succint due to `S(X, Y)` evaluation
// pub fn verify_proofs<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng>(
// proofs: &[Proof<E>],
// inputs: &[Vec<E::Fr>],
// circuit: C,
// rng: R,
// params: &Parameters<E>,
// ) -> Result<bool, SynthesisError> {
// verify_proofs_on_srs::<E, C, S, R>(proofs, inputs, circuit, rng, &params.srs)
// }
// /// Check multiple proofs without aggregation. Verifier's work is
// /// not succint due to `S(X, Y)` evaluation
// pub fn verify_proofs_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng>(
// proofs: &[Proof<E>],
// inputs: &[Vec<E::Fr>],
// circuit: C,
// rng: R,
// srs: &SRS<E>,
// ) -> Result<bool, SynthesisError> {
// let mut verifier = MultiVerifier::<E, C, S, R>::new(circuit, srs, rng)?;
// let expected_inputs_size = verifier.get_k_map().len() - 1;
// for (proof, inputs) in proofs.iter().zip(inputs.iter()) {
// if inputs.len() != expected_inputs_size {
// return Err(SynthesisError::Unsatisfiable);
// }
// verifier.add_proof(proof, &inputs, |_, _| None);
// }
// Ok(verifier.check_all())
// }
// /// Check multiple proofs with aggregation. Verifier's work is
// /// not succint due to `S(X, Y)` evaluation
// pub fn verify_aggregate<E: Engine, C: Circuit<E>, S: SynthesisDriver,R: Rng>(
// proofs: &[(Proof<E>, SxyAdvice<E>)],
// aggregate: &Aggregate<E>,
// inputs: &[Vec<E::Fr>],
// circuit: C,
// rng: R,
// params: &Parameters<E>,
// ) -> Result<bool, SynthesisError> {
// verify_aggregate_on_srs::<E, C, S, R>(proofs, aggregate, inputs, circuit, rng, &params.srs)
// }
// /// Check multiple proofs with aggregation. Verifier's work is
// /// not succint due to `S(X, Y)` evaluation
// pub fn verify_aggregate_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng>(
// proofs: &[(Proof<E>, SxyAdvice<E>)],
// aggregate: &Aggregate<E>,
// inputs: &[Vec<E::Fr>],
// circuit: C,
// rng: R,
// srs: &SRS<E>,
// ) -> Result<bool, SynthesisError> {
// let mut verifier = MultiVerifier::<E, C, S, R>::new(circuit, srs, rng)?;
// let expected_inputs_size = verifier.get_k_map().len() - 1;
// for ((proof, advice), inputs) in proofs.iter().zip(inputs.iter()) {
// if inputs.len() != expected_inputs_size {
// return Err(SynthesisError::Unsatisfiable);
// }
// verifier.add_proof_with_advice(proof, &inputs, &advice);
// }
// verifier.add_aggregate(proofs, aggregate);
// Ok(verifier.check_all())
// }