diff --git a/Cargo.toml b/Cargo.toml index 6a73d80..208769c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/sonic/cs/lc.rs b/src/sonic/cs/lc.rs index 971eca4..0bb32e5 100644 --- a/src/sonic/cs/lc.rs +++ b/src/sonic/cs/lc.rs @@ -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 { Zero, diff --git a/src/sonic/cs/mod.rs b/src/sonic/cs/mod.rs index 4a87c5e..9049731 100644 --- a/src/sonic/cs/mod.rs +++ b/src/sonic/cs/mod.rs @@ -41,6 +41,8 @@ pub trait ConstraintSystem { /// about the circuit for verification, while another actually constructs /// a witness. pub trait Backend { + type LinearConstraintIndex; + /// Get the value of a variable. Can return None if we don't know. fn get_var(&self, _variable: Variable) -> Option { None } @@ -51,11 +53,14 @@ pub trait Backend { /// 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) { } + fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff, 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. @@ -65,252 +70,4 @@ pub trait Backend { /// This is an abstraction which synthesizes circuits. pub trait SynthesisDriver { fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError>; -} - -pub struct Basic; - -impl SynthesisDriver for Basic { - fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { - struct Synthesizer> { - backend: B, - current_variable: Option, - _marker: PhantomData, - q: usize, - n: usize, - } - - impl> ConstraintSystem for Synthesizer { - const ONE: Variable = Variable::A(1); - - fn alloc(&mut self, value: F) -> Result - where - F: FnOnce() -> Result - { - 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(&mut self, value: F) -> Result - where - F: FnOnce() -> Result - { - 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) - { - self.q += 1; - self.backend.new_linear_constraint(); - - for (var, coeff) in lc.as_ref() { - self.backend.insert_coefficient(*var, *coeff); - } - } - - fn multiply(&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 { - self.backend.get_var(var).ok_or(()) - } - } - - let mut tmp: Synthesizer = 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, as ConstraintSystem>::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, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { - struct NonassigningSynthesizer> { - backend: B, - current_variable: Option, - _marker: PhantomData, - q: usize, - n: usize, - } - - impl> ConstraintSystem for NonassigningSynthesizer { - const ONE: Variable = Variable::A(1); - - fn alloc(&mut self, _value: F) -> Result - where - F: FnOnce() -> Result - { - 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(&mut self, value: F) -> Result - where - F: FnOnce() -> Result - { - 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) - { - self.q += 1; - self.backend.new_linear_constraint(); - - for (var, coeff) in lc.as_ref() { - self.backend.insert_coefficient(*var, *coeff); - } - } - - fn multiply(&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 { - self.backend.get_var(var).ok_or(()) - } - } - - let mut tmp: NonassigningSynthesizer = 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, as ConstraintSystem>::ONE) { - (Variable::A(1), Variable::A(1)) => {}, - _ => panic!("one variable is incorrect") - } - - circuit.synthesize(&mut tmp)?; - - Ok(()) - } } \ No newline at end of file diff --git a/src/sonic/helped/adapted_helper.rs b/src/sonic/helped/adapted_helper.rs index 22f3a90..197f1b1 100644 --- a/src/sonic/helped/adapted_helper.rs +++ b/src/sonic/helped/adapted_helper.rs @@ -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 + Clone>( diff --git a/src/sonic/helped/adapted_prover.rs b/src/sonic/helped/adapted_prover.rs index 26034de..62b6b12 100644 --- a/src/sonic/helped/adapted_prover.rs +++ b/src/sonic/helped/adapted_prover.rs @@ -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 + Clone, S: SynthesisDriver>( pub fn create_advice_on_information_and_srs + Clone>( @@ -51,23 +52,13 @@ pub fn create_advice_on_srs + Clone>( srs: &SRS ) -> Result, 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 for &'a mut CountN { - fn new_multiplication_gate(&mut self) { - self.n += 1; - } - } - - let mut tmp = CountN{n:0}; + let mut tmp = CountN::::new(); Nonassigning::synthesize(&mut tmp, &adapted_circuit)?; tmp.n diff --git a/src/sonic/helped/adapted_verifier.rs b/src/sonic/helped/adapted_verifier.rs index 6085ca3..c796506 100644 --- a/src/sonic/helped/adapted_verifier.rs +++ b/src/sonic/helped/adapted_verifier.rs @@ -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; diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index e7c0406..6ef9efc 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -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( where E: Engine, C: Circuit { - struct NonassigningSynthesizer> { - backend: B, - current_variable: Option, - _marker: PhantomData, - q: usize, - n: usize, - } - - impl> SonicConstraintSystem for NonassigningSynthesizer { - const ONE: SonicVariable = SonicVariable::A(1); - - fn alloc(&mut self, _value: F) -> Result - where - F: FnOnce() -> Result - { - 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(&mut self, value: F) -> Result - where - F: FnOnce() -> Result - { - 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) - { - self.q += 1; - self.backend.new_linear_constraint(); - - for (var, coeff) in lc.as_ref() { - self.backend.insert_coefficient(*var, *coeff); - } - } - - fn multiply(&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 { - self.backend.get_var(var).ok_or(()) - } - } - - struct Preprocess { - k_map: Vec, - n: usize, - q: usize, - _marker: PhantomData - } - - impl<'a, E: Engine> Backend for &'a mut Preprocess { - 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> = NonassigningSynthesizer { - backend: &mut preprocess, - current_variable: None, - _marker: PhantomData, - q: 0, - n: 0, - }; + let mut cs: NonassigningSynthesizer> = 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( }) } +/// 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( + circuit: C, +) -> Result, SynthesisError> + where E: Engine, C: Circuit + +{ + let mut preprocess = Preprocess::new(); + + let (num_inputs, num_aux, num_constraints) = { + + let mut cs: PermutationSynthesizer> = PermutationSynthesizer::new(&mut preprocess); + + let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, > as SonicConstraintSystem>::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( circuit: C, alpha: E::Fr, diff --git a/src/sonic/helped/helper.rs b/src/sonic/helped/helper.rs index 43b1995..72e83af 100644 --- a/src/sonic/helped/helper.rs +++ b/src/sonic/helped/helper.rs @@ -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 { @@ -47,22 +48,8 @@ pub fn create_aggregate_on_srs, S: SynthesisDriver>( { // TODO: precompute this? let (n, q) = { - struct CountN { - n: usize, - q: usize - } + let mut tmp = CountNandQ::::new(); - impl<'a, E: Engine> Backend 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) diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index c1d136b..c73396a 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -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, diff --git a/src/sonic/helped/parameters.rs b/src/sonic/helped/parameters.rs index d08ed29..cdf6e0f 100644 --- a/src/sonic/helped/parameters.rs +++ b/src/sonic/helped/parameters.rs @@ -250,35 +250,16 @@ impl VerifyingKey { } } -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 VerifyingKey { pub fn new, S: SynthesisDriver>(circuit: C, srs: &SRS) -> Result { - struct Preprocess { - k_map: Vec, - n: usize, - q: usize, - _marker: PhantomData - } - - impl<'a, E: Engine> Backend for &'a mut Preprocess { - 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)?; diff --git a/src/sonic/helped/poly.rs b/src/sonic/helped/poly.rs index 023791b..e54b276 100644 --- a/src/sonic/helped/poly.rs +++ b/src/sonic/helped/poly.rs @@ -31,6 +31,8 @@ pub struct SxEval { v: Vec, // x^{i+N} (-y^i -y^{-i} + \sum\limits_{q=1}^Q y^{q+N} w_{q,i}) w: Vec, + + max_n: usize, } impl SxEval { @@ -69,6 +71,7 @@ impl SxEval { u, v, w, + max_n: n } } @@ -114,11 +117,19 @@ impl SxEval { } impl<'a, E: Engine> Backend for &'a mut SxEval { - 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) { + 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, y: &E::Fr) { let acc = match var { Variable::A(index) => { &mut self.u[index - 1] @@ -134,13 +145,13 @@ impl<'a, E: Engine> Backend for &'a mut SxEval { 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 SyEval { } impl<'a, E: Engine> Backend for &'a mut SyEval { - 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) { + fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { + q + } + + fn insert_coefficient(&mut self, var: Variable, coeff: Coeff, 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 for &'a mut SyEval { // 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 for &'a mut SyEval { // 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); } }; diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 58a4278..7ece0c2 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -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, S: SynthesisDriver>( circuit: &C, @@ -119,17 +120,7 @@ pub fn create_advice_on_srs, 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 for &'a mut CountN { - fn new_multiplication_gate(&mut self) { - self.n += 1; - } - } - - let mut tmp = CountN{n:0}; + let mut tmp = CountN::::new(); S::synthesize(&mut tmp, circuit)?; tmp.n @@ -147,65 +138,14 @@ pub fn create_proof, S: SynthesisDriver>( extern crate rand; use self::rand::{Rand, Rng, thread_rng}; +use crate::sonic::sonic::Wires; pub fn create_proof_on_srs, S: SynthesisDriver>( circuit: &C, srs: &SRS ) -> Result, SynthesisError> { - struct Wires { - a: Vec, - b: Vec, - c: Vec - } - - impl<'a, E: Engine> Backend for &'a mut Wires { - 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 { - 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(&mut self, variable: Variable, value: F) -> Result<(), SynthesisError> - where F: FnOnce() -> Result - { - 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}; diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 141bc17..da150e7 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -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, S: SynthesisDriver, R: Rng> { circuit: C, @@ -30,28 +31,7 @@ pub struct MultiVerifier, S: SynthesisDriver, R: Rng> { impl, S: SynthesisDriver, R: Rng> MultiVerifier { // This constructor consumes randomness source cause it's later used internally pub fn new(circuit: C, srs: &SRS, rng: R) -> Result { - struct Preprocess { - k_map: Vec, - n: usize, - q: usize, - _marker: PhantomData - } - - impl<'a, E: Engine> Backend for &'a mut Preprocess { - 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)?; diff --git a/src/sonic/sonic/backends.rs b/src/sonic/sonic/backends.rs new file mode 100644 index 0000000..175d5e8 --- /dev/null +++ b/src/sonic/sonic/backends.rs @@ -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 { + pub k_map: Vec, + pub n: usize, + pub q: usize, + _marker: PhantomData +} + +impl<'a, E: Engine> Backend for &'a mut Preprocess { + 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 Preprocess { + pub fn new() -> Self { + Preprocess { + k_map: vec![], + n: 0, + q: 0, + _marker: PhantomData + } + } +} + +pub struct Wires { + pub a: Vec, + pub b: Vec, + pub c: Vec +} + +impl<'a, E: Engine> Backend for &'a mut Wires { + 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 { + 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(&mut self, variable: Variable, value: F) -> Result<(), SynthesisError> + where F: FnOnce() -> Result + { + 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 Wires { + pub fn new() -> Self { + Wires { + a: vec![], + b: vec![], + c: vec![], + } + } +} + +pub struct CountNandQ { + pub n: usize, + pub q: usize, + _marker: std::marker::PhantomData +} + +impl<'a, E: Engine, S: SynthesisDriver> Backend for &'a mut CountNandQ { + 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 CountNandQ { + pub fn new() -> Self { + Self { + n: 0, + q: 0, + _marker: std::marker::PhantomData + } + } +} + +pub struct CountN { + pub n: usize, + _marker: std::marker::PhantomData +} + +impl<'a, E: Engine, S: SynthesisDriver> Backend for &'a mut CountN { + 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 CountN { + pub fn new() -> Self { + Self { + n: 0, + _marker: std::marker::PhantomData + } + } +} + diff --git a/src/sonic/sonic/constraint_systems.rs b/src/sonic/sonic/constraint_systems.rs new file mode 100644 index 0000000..1bdf4f7 --- /dev/null +++ b/src/sonic/sonic/constraint_systems.rs @@ -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> { + backend: B, + current_variable: Option, + _marker: PhantomData, + q: usize, + n: usize, +} + +impl>NonassigningSynthesizer { + pub fn new(backend: B) -> Self { + Self { + backend: backend, + current_variable: None, + _marker: PhantomData, + q: 0, + n: 0, + } + } +} + +impl> ConstraintSystem for NonassigningSynthesizer { + const ONE: Variable = Variable::A(1); + + fn alloc(&mut self, _value: F) -> Result + where + F: FnOnce() -> Result + { + 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(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + 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) + { + 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(&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 { + self.backend.get_var(var).ok_or(()) + } +} + +pub struct Synthesizer> { + backend: B, + current_variable: Option, + _marker: PhantomData, + q: usize, + n: usize, +} + +impl> ConstraintSystem for Synthesizer { + const ONE: Variable = Variable::A(1); + + fn alloc(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + 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(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + 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) + { + 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(&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 { + self.backend.get_var(var).ok_or(()) + } +} + +impl>Synthesizer { + pub fn new(backend: B) -> Self { + Self { + backend: backend, + current_variable: None, + _marker: PhantomData, + q: 0, + n: 0, + } + } +} + +pub struct PermutationSynthesizer> { + backend: B, + current_variable: Option, + _marker: PhantomData, + 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 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, usize)>; M]>, + pub b: Vec<[Option<(Coeff, usize)>; M]>, + pub c: Vec<[Option<(Coeff, usize)>; M]>, +} + +impl> ConstraintSystem for PermutationSynthesizer { + const ONE: Variable = Variable::A(1); + + fn alloc(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + 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(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + 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) + { + // 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 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)> = &mut lc.into_iter(); + let lc = lc.peekable(); + + self.enforce_equals(lc, None); + } + + fn multiply(&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 { + self.backend.get_var(var).ok_or(()) + } +} + +impl> PermutationSynthesizer { + 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)>>, + rhs: Option + ) -> Option + { + // 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)> = &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, 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)> = &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, 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"); + } +} \ No newline at end of file diff --git a/src/sonic/sonic/mod.rs b/src/sonic/sonic/mod.rs index b3b4353..00f32c4 100644 --- a/src/sonic/sonic/mod.rs +++ b/src/sonic/sonic/mod.rs @@ -1,3 +1,11 @@ mod adaptor; +mod synthesis_drivers; +mod backends; +mod constraint_systems; -pub use self::adaptor::{Adaptor, AdaptorCircuit}; \ No newline at end of file +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; \ No newline at end of file diff --git a/src/sonic/sonic/synthesis_drivers.rs b/src/sonic/sonic/synthesis_drivers.rs new file mode 100644 index 0000000..1ca7e5c --- /dev/null +++ b/src/sonic/sonic/synthesis_drivers.rs @@ -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, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { + let mut tmp: Synthesizer = Synthesizer::new(backend); + + let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, as ConstraintSystem>::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, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { + let mut tmp: NonassigningSynthesizer = NonassigningSynthesizer::new(backend); + + let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, as ConstraintSystem>::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, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { + let mut tmp: PermutationSynthesizer = PermutationSynthesizer::new(backend); + + let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, as ConstraintSystem>::ONE) { + (Variable::A(1), Variable::A(1)) => {}, + _ => panic!("one variable is incorrect") + } + + circuit.synthesize(&mut tmp)?; + + Ok(()) + } +} diff --git a/src/sonic/tests/sonics.rs b/src/sonic/tests/sonics.rs index 7464c89..210789f 100644 --- a/src/sonic/tests/sonics.rs +++ b/src/sonic/tests/sonics.rs @@ -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::::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::>(); + let samples: usize = 100; + + let xl = rng.gen(); + let xr = rng.gen(); + let image = mimc::(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::(&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::(circuit.clone()).expect("Must get circuit info"); + // println!("{:?}", info); + + // println!("creating proof"); + // let start = Instant::now(); + // let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + // println!("done in {:?}", start.elapsed()); + + // println!("creating advice"); + // let start = Instant::now(); + // let advice = create_advice_on_srs::(&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::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); + // println!("done in {:?}", start.elapsed()); + + // { + // let rng = thread_rng(); + // let mut verifier = MultiVerifier::::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::::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::::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::>(); +// // Generate the MiMC round constants +// let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); - let xl = rng.gen(); - let xr = rng.gen(); - let image = mimc::(xl, xr, &constants); +// let xl = rng.gen(); +// let xr = rng.gen(); +// let image = mimc::(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::(circuit.clone()); - } -} +// constraints_info::(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::::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::::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::>(); - let samples: usize = 100; +// // Generate the MiMC round constants +// let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); +// let samples: usize = 100; - let xl = rng.gen(); - let xr = rng.gen(); - let image = mimc::(xl, xr, &constants); +// let xl = rng.gen(); +// let xr = rng.gen(); +// let image = mimc::(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::(circuit.clone()).expect("Must get circuit info"); - println!("{:?}", info); +// let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); +// println!("{:?}", info); - println!("creating proof"); - let start = Instant::now(); - let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); - println!("done in {:?}", start.elapsed()); +// println!("creating proof"); +// let start = Instant::now(); +// let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); +// println!("done in {:?}", start.elapsed()); - { - let rng = thread_rng(); - let mut verifier = MultiVerifier::::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()); - } - } -} \ No newline at end of file +// { +// let rng = thread_rng(); +// let mut verifier = MultiVerifier::::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()); +// } +// } +// } \ No newline at end of file diff --git a/src/sonic/unhelped/helper.rs b/src/sonic/unhelped/helper.rs new file mode 100644 index 0000000..7fd5e4d --- /dev/null +++ b/src/sonic/unhelped/helper.rs @@ -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 { + pub permutations: [] + pub a: Vec<[Option<(Coeff, usize)>; M]>, + pub b: Vec<[Option<(Coeff, usize)>; M]>, + pub c: Vec<[Option<(Coeff, usize)>; M]>, +} + +pub fn create_aggregate, S: SynthesisDriver>( + circuit: &C, + inputs: &[(Proof, SxyAdvice)], + params: &Parameters, +) -> SuccinctAggregate +{ + let n = params.vk.n; + let q = params.vk.q; + + create_aggregate_on_srs_using_information::(circuit, inputs, ¶ms.srs, n, q) +} + +pub fn create_aggregate_on_srs, S: SynthesisDriver>( + circuit: &C, + inputs: &[(Proof, SxyAdvice)], + srs: &SRS, +) -> SuccinctAggregate +{ + // TODO: precompute this? + let (n, q) = { + let mut tmp = CountNandQ::::new(); + + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + (tmp.n, tmp.q) + }; + + create_aggregate_on_srs_using_information::(circuit, inputs, srs, n, q) +} + +pub fn create_aggregate_on_srs_using_information, S: SynthesisDriver>( + circuit: &C, + inputs: &[(Proof, SxyAdvice)], + srs: &SRS, + n: usize, + q: usize, +) -> SuccinctAggregate +{ + let mut transcript = Transcript::new(&[]); + let mut y_values: Vec = 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 = PermutationSynthesizer::new(backend); + + let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, as ConstraintSystem>::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::(&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(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::(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, + } +} \ No newline at end of file diff --git a/src/sonic/unhelped/mod.rs b/src/sonic/unhelped/mod.rs index 7da3f6a..0efebdb 100644 --- a/src/sonic/unhelped/mod.rs +++ b/src/sonic/unhelped/mod.rs @@ -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}; \ No newline at end of file +pub use self::permutation_argument::{PermutationArgument, PermutationProof, Proof}; +pub use self::verifier::SuccinctMultiVerifier; \ No newline at end of file diff --git a/src/sonic/unhelped/permutation_argument.rs b/src/sonic/unhelped/permutation_argument.rs index 549c1c1..2662009 100644 --- a/src/sonic/unhelped/permutation_argument.rs +++ b/src/sonic/unhelped/permutation_argument.rs @@ -47,6 +47,11 @@ fn permute(coeffs: &[F], permutation: & [usize]) -> Vec{ assert_eq!(coeffs.len(), permutation.len()); let mut result: Vec = 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 PermutationArgument { 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 PermutationArgument { // 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 PermutationArgument { 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::::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::>(); - // let mut permutation = (0..n).collect::>(); - // 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::>(); + coeffs[2] = Fr::zero(); // edge case + let mut permutation = (1..=n).collect::>(); + 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::>(); - - let challenges = vec![Fr::one()]; + let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); 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); diff --git a/src/sonic/unhelped/permutation_structure.rs b/src/sonic/unhelped/permutation_structure.rs new file mode 100644 index 0000000..cfe5bff --- /dev/null +++ b/src/sonic/unhelped/permutation_structure.rs @@ -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 { + pub n: usize, + pub q: usize, + pub a: Vec<[Option<(Coeff, usize)>; M]>, + pub b: Vec<[Option<(Coeff, usize)>; M]>, + pub c: Vec<[Option<(Coeff, usize)>; M]>, +} + +pub fn create_permutation_structure>( + circuit: &C, +) -> PermutationStructure +{ + let mut backend: Preprocess = Preprocess::new(); + + let (a, b, c) = { + + let mut cs: PermutationSynthesizer> = PermutationSynthesizer::new(&mut backend); + + let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, > as ConstraintSystem>::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:: { + n: n, + q: q, + a: a, + b: b, + c: c + } +} + +use rand::{Rng, Rand}; + +impl PermutationStructure { + pub fn calculate_s2_commitment_value(&self, srs: &SRS) -> E::G1Affine { + S2Eval::calculate_commitment_element(self.n, srs) + } + + pub fn calculate_s2_proof(&self, x: E::Fr, y: E::Fr, srs: &SRS) -> S2Proof { + let s2_eval = S2Eval::new(self.n); + + s2_eval.evaluate(x, y, &srs) + } + + pub fn create_permutation_arguments(&self, y: E::Fr, z: E::Fr, rng: &mut R, srs: &SRS) + // -> PermutationProof + { + // 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 = (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::>(); + + 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::>(); + + 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::>(); + let wellformed_challenges = (0..(2*m)).map(|_| E::Fr::rand(rng)).collect::>(); + + 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 Circuit for MyCircuit { + fn synthesize>(&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::::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::(&MyCircuit); + perm_structure.create_permutation_arguments(Fr::one(), Fr::one(), rng, &srs); + + println!("N = {}, Q = {}", perm_structure.n, perm_structure.q); + } +} \ No newline at end of file diff --git a/src/sonic/unhelped/prover.rs b/src/sonic/unhelped/prover.rs new file mode 100644 index 0000000..970e411 --- /dev/null +++ b/src/sonic/unhelped/prover.rs @@ -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, S: SynthesisDriver>( + circuit: &C, + proof: &Proof, + srs: &SRS, + n: usize +) -> Result, 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, S: SynthesisDriver>( + circuit: &C, + proof: &Proof, + parameters: &Parameters, +) -> Result, SynthesisError> +{ + let n = parameters.vk.n; + create_advice_on_information_and_srs::(circuit, proof, ¶meters.srs, n) +} + +pub fn create_advice_on_srs, S: SynthesisDriver>( + circuit: &C, + proof: &Proof, + srs: &SRS +) -> Result, SynthesisError> +{ + // annoying, but we need n to compute s(z, y), and this isn't + // precomputed anywhere yet + let n = { + let mut tmp = CountN::::new(); + S::synthesize(&mut tmp, circuit)?; + + tmp.n + }; + + create_advice_on_information_and_srs::(circuit, proof, srs, n) +} + +pub fn create_proof, S: SynthesisDriver>( + circuit: &C, + parameters: &Parameters +) -> Result, SynthesisError> { + create_proof_on_srs::(circuit, ¶meters.srs) +} + +extern crate rand; +use self::rand::{Rand, Rng, thread_rng}; +use crate::sonic::sonic::Wires; + +pub fn create_proof_on_srs, S: SynthesisDriver>( + circuit: &C, + srs: &SRS +) -> Result, 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 = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect(); + + // r is a commitment to r(X, 1) + let r = polynomial_commitment::( + 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::(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 + }) +} diff --git a/src/sonic/unhelped/verifier.rs b/src/sonic/unhelped/verifier.rs new file mode 100644 index 0000000..6962ab5 --- /dev/null +++ b/src/sonic/unhelped/verifier.rs @@ -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, S: SynthesisDriver, R: Rng> { + circuit: C, + pub(crate) batch: Batch, + k_map: Vec, + n: usize, + q: usize, + randomness_source: R, + _marker: PhantomData<(E, S)> +} + +impl, S: SynthesisDriver, R: Rng> SuccinctMultiVerifier { + // This constructor consumes randomness source cause it's later used internally + pub fn new(circuit: C, srs: &SRS, rng: R) -> Result { + 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, SxyAdvice)], + aggregate: &Aggregate, + szw: E::Fr + ) + { + let mut transcript = Transcript::new(&[]); + let mut y_values: Vec = 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, + inputs: &[E::Fr], + advice: &SxyAdvice, + ) + { + 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( + &mut self, + proof: &Proof, + inputs: &[E::Fr], + sxy: F + ) + where F: FnOnce(E::Fr, E::Fr) -> Option + { + 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 { + 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, S: SynthesisDriver, R: Rng>( +// proofs: &[Proof], +// inputs: &[Vec], +// circuit: C, +// rng: R, +// params: &Parameters, +// ) -> Result { +// verify_proofs_on_srs::(proofs, inputs, circuit, rng, ¶ms.srs) +// } + +// /// Check multiple proofs without aggregation. Verifier's work is +// /// not succint due to `S(X, Y)` evaluation +// pub fn verify_proofs_on_srs, S: SynthesisDriver, R: Rng>( +// proofs: &[Proof], +// inputs: &[Vec], +// circuit: C, +// rng: R, +// srs: &SRS, +// ) -> Result { +// let mut verifier = MultiVerifier::::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, S: SynthesisDriver,R: Rng>( +// proofs: &[(Proof, SxyAdvice)], +// aggregate: &Aggregate, +// inputs: &[Vec], +// circuit: C, +// rng: R, +// params: &Parameters, +// ) -> Result { +// verify_aggregate_on_srs::(proofs, aggregate, inputs, circuit, rng, ¶ms.srs) +// } + +// /// Check multiple proofs with aggregation. Verifier's work is +// /// not succint due to `S(X, Y)` evaluation +// pub fn verify_aggregate_on_srs, S: SynthesisDriver, R: Rng>( +// proofs: &[(Proof, SxyAdvice)], +// aggregate: &Aggregate, +// inputs: &[Vec], +// circuit: C, +// rng: R, +// srs: &SRS, +// ) -> Result { +// let mut verifier = MultiVerifier::::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()) +// } +