├── src ├── unhelped │ ├── mod.rs │ ├── permutation.rs │ ├── s_perm.rs │ ├── well_formed.rs │ └── grand_product.rs ├── lib.rs ├── helped │ ├── mod.rs │ ├── generator.rs │ ├── helper.rs │ ├── sig_of_correct_comp.rs │ ├── adaptor.rs │ ├── verifier.rs │ └── prover.rs ├── traits.rs ├── polynomials │ ├── operations.rs │ ├── commitment.rs │ ├── s_eval.rs │ └── mod.rs ├── transcript.rs ├── srs.rs ├── cs │ ├── lc.rs │ ├── mod.rs │ └── permutation.rs └── utils.rs ├── .gitignore ├── Cargo.toml ├── README.md ├── benches └── sonic_benchmark.rs ├── LICENSE └── tests └── sonic_test.rs /src/unhelped/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod grand_product; 2 | pub mod permutation; 3 | pub mod well_formed; 4 | pub mod s_perm; 5 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod srs; 2 | pub mod cs; 3 | pub mod helped; 4 | pub mod unhelped; 5 | pub mod transcript; 6 | pub mod polynomials; 7 | pub mod utils; 8 | pub mod traits; 9 | -------------------------------------------------------------------------------- /src/helped/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod adaptor; 2 | pub mod generator; 3 | pub mod prover; 4 | pub mod sig_of_correct_comp; 5 | pub mod verifier; 6 | pub mod helper; 7 | 8 | pub use self::prover::Proof; 9 | pub use self::verifier::MultiVerifier; 10 | 11 | pub use self::generator::{ 12 | generate_srs, 13 | CircuitParameters, 14 | }; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lx-sonic" 3 | version = "0.1.0" 4 | authors = ["Osuke Sudo "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | pairing = { git = "https://github.com/LayerXcom/librustzcash", branch = "zero-chain" } 9 | bellman = { git = "https://github.com/LayerXcom/librustzcash", branch = "zero-chain" } 10 | rand = "0.4" 11 | merlin = "1.0" 12 | futures = "0.1" 13 | clear_on_drop = "0.2" 14 | crossbeam = "0.7" 15 | 16 | [dev-dependencies] 17 | criterion = "0.2" 18 | 19 | [[bench]] 20 | name = "sonic_benchmark" 21 | harness = false 22 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | use pairing::{CurveAffine, Engine}; 2 | use std::fmt; 3 | 4 | pub trait PolyEngine { 5 | type Commitment: Commitment::G1Affine>; 6 | type Opening: Opening; 7 | type Pairing: Engine; // TODO: Make default generics of this trait 8 | } 9 | 10 | pub trait Commitment: 11 | Clone + Copy + Sized + Send + Sync + 'static 12 | { 13 | type Point: CurveAffine; 14 | 15 | fn from_point(point: &Self::Point) -> Self; 16 | 17 | fn into_point(&self) -> Self::Point; 18 | 19 | fn into_bytes(&self) -> Vec; 20 | } 21 | 22 | pub trait Opening {} 23 | 24 | pub trait Challenge {} 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sonic proving system 2 | This library is an implementation of Sonic. 3 | 4 | - [Sonic: Zero-Knowledge SNARKs from Linear-Size Universal and 5 | Updatable Structured Reference Strings](https://eprint.iacr.org/2019/099.pdf) 6 | - Mary Maller, [Sean Bowe](https://github.com/ebfull), Markulf Kohlweiss, Sarah Meiklejohn 7 | 8 | Unhelpder mode is not fully implemented yet. 9 | 10 | ## Setup 11 | ``` 12 | git clone git@github.com:LayerXcom/lx-sonic.git 13 | cd lx-sonic 14 | cargo build --release 15 | ``` 16 | 17 | ## Test 18 | ``` 19 | cargo test --release 20 | ``` 21 | 22 | ## Acknowledgements 23 | Original implementation for paper is in [sonic crate](https://github.com/ebfull/sonic), developed by [Sean Bowe](https://github.com/ebfull). This implementation have been adapted from it. 24 | -------------------------------------------------------------------------------- /src/helped/generator.rs: -------------------------------------------------------------------------------- 1 | use pairing::Engine; 2 | use bellman::{ConstraintSystem, SynthesisError}; 3 | use std::marker::PhantomData; 4 | use crate::cs::{ConstraintSystem as SonicCS, Circuit}; 5 | use crate::srs::SRS; 6 | 7 | /// This is our assembly structure that we will use to synthesize the circuit into 8 | #[derive(Clone, Debug)] 9 | pub struct CircuitParameters { 10 | pub num_inputs: usize, 11 | pub num_aux: usize, 12 | pub num_constraints: usize, 13 | pub k_map: Vec, 14 | pub n: usize, 15 | pub q: usize, 16 | _marker: PhantomData 17 | } 18 | 19 | /// This is our assembly structure that we will use to syhthesize the circuit into 20 | #[derive(Debug)] 21 | pub struct GeneratorAssembly<'a, E: Engine, CS: SonicCS + 'a> { 22 | cs: &'a mut CS, 23 | num_inputs: usize, 24 | num_aux: usize, 25 | num_constraints: usize, 26 | _maker: PhantomData, 27 | } 28 | 29 | // impl<'a, E: Engine, CS: SonicCS + 'a> ConstraintSystem 30 | // for GeneratorAssembly<'a, E, CS> 31 | // { 32 | // type Root = Self; 33 | 34 | 35 | // } 36 | 37 | /// Get circuit information such as number of input, variables, constraints, 38 | /// and the corresponding SONIC parameters k_map, n, q 39 | pub fn get_circuit_parameters(circuit: C) -> Result, SynthesisError> 40 | where E: Engine, C: Circuit, 41 | { 42 | unimplemented!(); 43 | } 44 | 45 | // pub fn generate_parameters(circuit: C, alpha: E::Fr, x: E::Fr) 46 | // -> Result, SynthesisError> 47 | // where E: Engine, C: Circuit 48 | // { 49 | // unimplemented!(); 50 | // } 51 | 52 | pub fn generate_srs(alpha: E::Fr, x: E::Fr, d: usize) 53 | -> Result, SynthesisError> 54 | { 55 | unimplemented!(); 56 | } -------------------------------------------------------------------------------- /src/polynomials/operations.rs: -------------------------------------------------------------------------------- 1 | use bellman::multicore::Worker; 2 | use bellman::SynthesisError; 3 | use bellman::domain::{EvaluationDomain, Scalar}; 4 | use pairing::{Engine, Field}; 5 | use clear_on_drop::clear::Clear; 6 | 7 | pub fn add_polynomials(a: &mut [E::Fr], b: &[E::Fr]) { 8 | assert_eq!(a.len(), b.len()); 9 | 10 | let worker = Worker::new(); 11 | 12 | worker.scope(a.len(), |scope, chunk| { 13 | for (a, b) in a.chunks_mut(chunk).zip(b.chunks(chunk)) { 14 | scope.spawn(move |_| { 15 | for (a, b) in a.iter_mut().zip(b.iter()) { 16 | a.add_assign(b); 17 | } 18 | }); 19 | } 20 | }); 21 | } 22 | 23 | pub fn mul_polynomials(a: &[E::Fr], b: &[E::Fr]) -> Result, SynthesisError> { 24 | let res_len = a.len() + b.len() - 1; 25 | 26 | let worker = Worker::new(); 27 | let scalars_a: Vec> = a.into_iter().map(|e| Scalar::(*e)).collect(); 28 | // the size of evaluation domain is polynomial's multiplied by other. 29 | let mut domain_a = EvaluationDomain::from_coeffs_into_sized(scalars_a, res_len)?; 30 | 31 | let scalars_b: Vec> = b.into_iter().map(|e| Scalar::(*e)).collect(); 32 | let mut domain_b = EvaluationDomain::from_coeffs_into_sized(scalars_b, res_len)?; 33 | 34 | // Convert to point-value representations 35 | domain_a.fft(&worker); 36 | domain_b.fft(&worker); 37 | 38 | // Perform O(n) multiplication of two polynomials in the domain. 39 | domain_a.mul_assign(&worker, &domain_b); 40 | drop(domain_b); 41 | 42 | // Convert back to point-value representations 43 | domain_a.ifft(&worker); 44 | 45 | let mut mul_res: Vec = domain_a.into_coeffs().iter().map(|e| e.0).collect(); 46 | mul_res.truncate(res_len); 47 | 48 | Ok(mul_res) 49 | } 50 | -------------------------------------------------------------------------------- /src/transcript.rs: -------------------------------------------------------------------------------- 1 | use merlin::Transcript; 2 | use pairing::{CurveAffine, PrimeField, Field, PrimeFieldRepr}; 3 | use crate::traits::{Commitment, PolyEngine}; 4 | 5 | /// In this trait, we provide an interface for Fiat-Shamir transformation 6 | /// which takes an interactive argument and replaces the verifier challenges. 7 | pub trait ProvingTranscript { 8 | /// Extend the transcript with an affine representation of an elliptic curve point 9 | /// guaranteed to be in the correct prime order subgroup. 10 | fn commit_point(&mut self, point: &PE::Commitment); 11 | 12 | /// Extend the transcript with scalar 13 | fn commit_scalar(&mut self, scalar: &F); 14 | 15 | /// Produce the public challenge scalar. 16 | fn challenge_scalar(&mut self) -> F; 17 | } 18 | 19 | /// The transcript trait is compatible with `merlin::Transcript`. 20 | impl ProvingTranscript for Transcript { 21 | fn commit_point(&mut self, point: &PE::Commitment) { 22 | self.commit_bytes(b"point", &point.into_bytes()[..]); 23 | } 24 | 25 | fn commit_scalar(&mut self, scalar: &F) { 26 | let mut v = vec![]; 27 | scalar.into_repr().write_le(&mut v).unwrap(); 28 | self.commit_bytes(b"scalar", &v); 29 | } 30 | 31 | // TODO: Avoid infinite loop 32 | fn challenge_scalar(&mut self) -> F { 33 | loop { 34 | let mut repr: F::Repr = Default::default(); 35 | repr.read_be(TranscriptReader(self)).unwrap(); 36 | 37 | if let Ok(res) = F::from_repr(repr) { 38 | return res; 39 | } 40 | } 41 | } 42 | } 43 | 44 | /// A reader for transcript 45 | struct TranscriptReader<'a>(&'a mut Transcript); 46 | 47 | impl<'a> std::io::Read for TranscriptReader<'a> { 48 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result { 49 | self.0.challenge_bytes(b"read", buf); 50 | Ok(buf.len()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/polynomials/commitment.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Field, Engine, CurveAffine, CurveProjective, PrimeField}; 2 | use crate::srs::SRS; 3 | use crate::utils::{ChainExt, multiexp}; 4 | use crate::traits::{Commitment, PolyEngine}; 5 | 6 | /// Commit a polynomial `F`. 7 | /// F \from g^{\alpha * x^{(d - max)}*f(x)} 8 | /// See: Section 5 SYSTEM OF CONSTRAINTS 9 | pub fn poly_comm<'a, E: Engine, I: IntoIterator, PE: PolyEngine>( 10 | max: usize, // a maximum degree 11 | largest_neg_power: usize, // largest negative power 12 | largest_pos_power: usize, // largest positive power 13 | srs: &'a SRS, 14 | poly_coeffs: I 15 | ) -> PE::Commitment 16 | where 17 | I::IntoIter: ExactSizeIterator, 18 | { 19 | let d = srs.d; 20 | assert!(max >= largest_pos_power); 21 | 22 | // smallest power is `|(srs.d - max) - largest_neg_power|`. (See Figure.3) 23 | // If the smallest power is negative, use both positive and negative powers for commitment, 24 | // otherwise use only positive powers. 25 | if d < max + largest_neg_power + 1 { 26 | let min_power = largest_neg_power + max - d; 27 | let max_power = largest_pos_power + d - max; 28 | 29 | let point = multiexp( 30 | srs.g_neg_x_alpha[0..min_power].iter().rev() // Reverse to permute for negative powers 31 | .chain_ext(srs.g_pos_x_alpha[..max_power].iter()), 32 | poly_coeffs 33 | ).into_affine(); 34 | 35 | return PE::Commitment::from_point(&point) 36 | } else { 37 | let _max_power = srs.d - max - largest_neg_power + 1; 38 | 39 | let point = multiexp( 40 | // srs.g_pos_x_alpha[..max_power].iter(), // TODO: Ensure the range is correct 41 | srs.g_pos_x_alpha[(srs.d - max - largest_neg_power - 1)..].iter(), 42 | poly_coeffs 43 | ).into_affine(); 44 | 45 | return PE::Commitment::from_point(&point) 46 | } 47 | } 48 | 49 | /// Opening a polynomial commitment 50 | pub fn poly_comm_opening<'a, E: Engine, I: IntoIterator>( 51 | largest_neg_power: usize, 52 | largest_pos_power: usize, 53 | srs: &'a SRS, 54 | poly_coeffs: I, 55 | point: E::Fr, 56 | ) -> E::G1Affine 57 | where 58 | I::IntoIter: DoubleEndedIterator + ExactSizeIterator 59 | { 60 | let quotient_poly = kate_division( 61 | poly_coeffs, 62 | point 63 | ); 64 | 65 | let neg_poly = quotient_poly[0..largest_neg_power].iter().rev(); // -n,...,-1 66 | // let pos_poly = quotient_poly[largest_pos_power..].iter(); // n,...,1,0 67 | let pos_poly = quotient_poly[largest_neg_power..].iter(); // n,...,1,0 68 | 69 | multiexp( 70 | srs.g_neg_x[1..(neg_poly.len() + 1)].iter().chain_ext( 71 | srs.g_pos_x[..pos_poly.len()].iter() 72 | ), 73 | neg_poly.chain_ext(pos_poly) 74 | ).into_affine() 75 | } 76 | 77 | // TODO: Parallelization 78 | /// Divides polynomial `a` in `x` by `x-b` with no remainder. 79 | pub fn kate_division<'a, F: Field, I: IntoIterator>(a: I, mut b: F) -> Vec 80 | where 81 | I::IntoIter: DoubleEndedIterator + ExactSizeIterator, 82 | { 83 | b.negate(); 84 | let a_poly = a.into_iter(); 85 | 86 | let mut quotient_poly = vec![F::zero(); a_poly.len() - 1]; 87 | 88 | let mut tmp = F::zero(); 89 | for (q, r) in quotient_poly.iter_mut().rev().zip(a_poly.rev()) { 90 | let mut lead_coeff = *r; 91 | lead_coeff.sub_assign(&tmp); 92 | *q = lead_coeff; 93 | tmp = lead_coeff; 94 | tmp.mul_assign(&b) 95 | } 96 | 97 | quotient_poly 98 | } 99 | -------------------------------------------------------------------------------- /src/srs.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Wnaf, CurveAffine, CurveProjective, Field, PrimeField}; 2 | 3 | /// Defined in Section 4.3: Structured Reference String 4 | /// Pre-processing exponents 5 | #[derive(Clone, Eq, Debug)] 6 | pub struct SRS { 7 | pub d: usize, 8 | 9 | /// g^{x^{0}}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} 10 | pub g_neg_x: Vec, 11 | /// g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} 12 | pub g_pos_x: Vec, 13 | 14 | /// alpha*(g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) 15 | pub g_neg_x_alpha: Vec, 16 | /// alpha*(g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) 17 | pub g_pos_x_alpha: Vec, 18 | 19 | /// h^{x^0}, h^{x^{-1}}, h^{x^{-2}}, ..., h^{x^{-d}} 20 | pub h_neg_x: Vec, 21 | /// h^{x^0}, h^{x^{1}}, h^{x^{2}}, ..., h^{x^{d}} 22 | pub h_pos_x: Vec, 23 | 24 | /// alpha*(h^{x^0}, h^{x^{-1}}, h^{x^{-2}}, ..., h^{x^{-d}}) 25 | pub h_neg_x_alpha: Vec, 26 | /// alpha*(h^{x^0}, h^{x^{1}}, h^{x^{2}}, ..., h^{x^{d}}) 27 | pub h_pos_x_alpha: Vec, 28 | } 29 | 30 | impl PartialEq for SRS { 31 | fn eq(&self, other: &SRS) -> bool { 32 | self.d == other.d && 33 | self.g_neg_x == other.g_neg_x && 34 | self.g_pos_x == other.g_pos_x && 35 | self.h_neg_x == other.h_neg_x && 36 | self.h_pos_x == other.h_pos_x && 37 | self.g_neg_x_alpha == other.g_neg_x_alpha && 38 | self.g_pos_x_alpha == other.g_pos_x_alpha && 39 | self.h_neg_x_alpha == other.h_neg_x_alpha && 40 | self.h_pos_x_alpha == other.h_pos_x_alpha 41 | } 42 | } 43 | 44 | 45 | impl SRS { 46 | pub fn new(d: usize, x: E::Fr, alpha: E::Fr) -> Self { 47 | let mut g1 = Wnaf::new(); 48 | let mut g1 = g1.base(E::G1::one(), d * 4); 49 | let mut g2 = Wnaf::new(); 50 | let mut g2 = g2.base(E::G2::one(), d * 4); 51 | 52 | // Generate the Affine point Vec to get SRS elements. 53 | fn table( 54 | mut cur: C::Scalar, 55 | step: C::Scalar, 56 | num: usize, 57 | // W-ary Non-Adjacent Form 58 | table: &mut Wnaf>, 59 | ) -> Vec { 60 | let mut v = vec![]; 61 | for _ in 0..num { 62 | // Push x^i to v as a wnaf(the base is Projective representation) 63 | v.push(table.scalar(cur.into_repr())); 64 | cur.mul_assign(&step); 65 | } 66 | // Normalizes a slice of projective elements so that conversion to affine is cheap 67 | C::Projective::batch_normalization(&mut v); 68 | let v = v.into_iter().map(|e| e.into_affine()).collect(); 69 | v 70 | } 71 | 72 | // Get parameters to construct SRS 73 | let x_inv = x.inverse().unwrap(); 74 | let mut x_alpha = x; 75 | x_alpha.mul_assign(&alpha); 76 | 77 | let mut inv_x_alpha = x_inv; 78 | inv_x_alpha.mul_assign(&alpha); 79 | 80 | SRS { 81 | d: d, 82 | 83 | g_neg_x: table(E::Fr::one(), x_inv, d + 1, &mut g1), 84 | g_pos_x: table(E::Fr::one(), x, d + 1, &mut g1), 85 | 86 | g_neg_x_alpha: table(inv_x_alpha, x_inv, d, &mut g1), 87 | g_pos_x_alpha: table(x_alpha, x, d, &mut g1), 88 | 89 | h_neg_x: table(E::Fr::one(), x_inv, d + 1, &mut g2), 90 | h_pos_x: table(E::Fr::one(), x, d + 1, &mut g2), 91 | 92 | h_neg_x_alpha: table(alpha, x_inv, d + 1, &mut g2), 93 | h_pos_x_alpha: table(alpha, x, d + 1, &mut g2), 94 | } 95 | } 96 | 97 | pub fn dummy(d: usize, _x: E::Fr, _alpha: E::Fr) -> Self { 98 | SRS { 99 | d: d, 100 | 101 | g_neg_x: vec![E::G1Affine::one(); d + 1], 102 | g_pos_x: vec![E::G1Affine::one(); d + 1], 103 | 104 | g_neg_x_alpha: vec![E::G1Affine::one(); d], 105 | g_pos_x_alpha: vec![E::G1Affine::one(); d], 106 | 107 | h_neg_x: vec![E::G2Affine::one(); d + 1], 108 | h_pos_x: vec![E::G2Affine::one(); d + 1], 109 | 110 | h_neg_x_alpha: vec![E::G2Affine::one(); d + 1], 111 | h_pos_x_alpha: vec![E::G2Affine::one(); d + 1], 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/unhelped/permutation.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Field, PrimeField, CurveAffine, CurveProjective}; 2 | use crate::srs::SRS; 3 | use crate::utils::*; 4 | 5 | /// An additional elements in our shared information 6 | pub struct SrsPerm { 7 | n: usize, 8 | p_1: E::G1Affine, 9 | p_2: Vec, 10 | p_3: E::G1Affine, 11 | p_4: Vec, 12 | } 13 | 14 | impl SrsPerm { 15 | pub fn gen_from_coeffs( 16 | non_permuted_coeffs: &Vec>, 17 | perms: &Vec>, 18 | srs: &SRS, 19 | ) -> Self 20 | { 21 | assert!(!non_permuted_coeffs.is_empty()); 22 | assert!(non_permuted_coeffs.len() == perms.len()); 23 | 24 | let n = non_permuted_coeffs[0].len(); 25 | 26 | // A commitment to the powers of x which is the srs's randomness 27 | let p_1 = multiexp( 28 | srs.g_pos_x_alpha[..n].iter(), 29 | vec![E::Fr::one(); n].iter() 30 | ).into_affine(); 31 | 32 | let p_3 = { 33 | let vals: Vec = (1..=n).map(|e| { 34 | let mut repr = <::Fr as PrimeField>::Repr::default(); 35 | repr.as_mut()[0] = e as u64; 36 | let re = E::Fr::from_repr(repr).unwrap(); 37 | 38 | re 39 | }).collect(); 40 | 41 | multiexp( 42 | srs.g_pos_x_alpha[0..n].iter(), 43 | vals.iter() 44 | ).into_affine() 45 | }; 46 | 47 | let mut p_2 = vec![]; 48 | let mut p_4 = vec![]; 49 | 50 | for (coeff, perm) in non_permuted_coeffs.iter().zip(perms.iter()) { 51 | assert_eq!(coeff.len(), perm.len()); 52 | 53 | let p2_el = multiexp( 54 | srs.g_pos_x_alpha[..n].iter(), 55 | coeff.iter() 56 | ).into_affine(); 57 | p_2.push(p2_el); 58 | 59 | let vals: Vec = perm.iter().map(|e| { 60 | let mut repr = <::Fr as PrimeField>::Repr::default(); 61 | repr.as_mut()[0] = *e as u64; 62 | let re = E::Fr::from_repr(repr).unwrap(); 63 | 64 | re 65 | }).collect(); 66 | 67 | let p4_el = multiexp( 68 | srs.g_pos_x_alpha[..n].iter(), 69 | vals.iter() 70 | ).into_affine(); 71 | p_4.push(p4_el); 72 | } 73 | 74 | SrsPerm { 75 | n, 76 | p_1, 77 | p_2, 78 | p_3, 79 | p_4, 80 | } 81 | } 82 | } 83 | 84 | pub struct ProofSCC { 85 | j: usize, 86 | s_opening: E::G1Affine, 87 | s_zy: E::G1Affine, 88 | } 89 | 90 | pub struct PermutationArgument { 91 | n: usize, 92 | non_permuted_coeffs: Vec>, 93 | permutated_coeffs: Vec>, 94 | permutated_y_coeffs: Vec>, 95 | perms: Vec>, 96 | } 97 | 98 | impl PermutationArgument { 99 | pub fn new(coeffs: Vec>, perms: Vec>) -> Self { 100 | assert!(!coeffs.is_empty()); 101 | assert_eq!(coeffs.len(), perms.len()); 102 | 103 | 104 | unimplemented!(); 105 | } 106 | 107 | pub fn commit(&mut self, y: E::Fr, srs: &SRS) -> Vec<(E::G1Affine, E::G1Affine)> { 108 | // let acc = vec![]; 109 | // let mut permutated_coeffs = vec![]; 110 | // let mut permutated_y_coeffs = vec![]; 111 | 112 | // for (coeffs, perm) for self.non_permuted_coeffs.iter().zip(self.perms.iter()) { 113 | 114 | // eval_bivar_poly(coeffs: &mut [E::Fr], first_power: E::Fr, base: E::Fr) 115 | // } 116 | 117 | unimplemented!(); 118 | } 119 | 120 | pub fn gen_perm_arg( 121 | &self, 122 | beta: E::Fr, 123 | gamma: E::Fr, 124 | srs: &SRS, 125 | y: E::Fr, 126 | z: E::Fr, 127 | srs_perm: &SrsPerm, 128 | ) -> ProofSCC 129 | { 130 | unimplemented!(); 131 | } 132 | } 133 | 134 | fn permute(coeffs: &[E::Fr], perm: &[usize]) -> Vec { 135 | assert_eq!(coeffs.len(), perm.len()); 136 | let mut res = vec![E::Fr::zero(); coeffs.len()]; 137 | 138 | for (i, j) in perm.iter().enumerate() { 139 | res[*j - 1] = coeffs[i]; 140 | } 141 | res 142 | } 143 | -------------------------------------------------------------------------------- /src/unhelped/s_perm.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Field}; 2 | use bellman::SynthesisError; 3 | use crate::cs::{Backend, Variable, Coeff}; 4 | 5 | pub struct SxPerm { 6 | y: E::Fr, 7 | 8 | max_n: usize, 9 | 10 | current_q: usize, 11 | 12 | /// Current value of y^{q} 13 | yq: E::Fr, 14 | 15 | /// Coefficients of X^{-i+n+1} term 16 | u: Vec, 17 | u_q: Vec, 18 | 19 | /// Coefficients of X^{i+n+1} term 20 | v: Vec, 21 | v_q: Vec, 22 | 23 | /// Coefficients of X^{i+2n+1} term 24 | w: Vec, 25 | w_q: Vec, 26 | } 27 | 28 | impl SxPerm { 29 | pub fn new(y: E::Fr, n: usize) -> Self { 30 | // because of u_{q,i} and q is zero 31 | let u = vec![E::Fr::zero(); n]; 32 | let u_q = Vec::with_capacity(n); 33 | 34 | // because of v_{q,i} and q is zero 35 | let v = vec![E::Fr::zero(); n]; 36 | let v_q = Vec::with_capacity(n); 37 | 38 | // because of w_{q,i} and q is zero 39 | let w = vec![E::Fr::zero(); n]; 40 | let w_q = Vec::with_capacity(n); 41 | 42 | SxPerm { 43 | y, 44 | max_n: n, 45 | current_q: 0, 46 | yq: E::Fr::one(), 47 | u, 48 | u_q, 49 | v, 50 | v_q, 51 | w, 52 | w_q, 53 | } 54 | } 55 | 56 | pub fn poly(self) -> Vec> { 57 | vec![self.u, self.v, self.w] 58 | } 59 | 60 | pub fn perm(self) -> Vec> { 61 | vec![self.u_q, self.v_q, self.w_q] 62 | } 63 | } 64 | 65 | impl<'a, E: Engine> Backend for &'a mut SxPerm { 66 | fn new_linear_constraint(&mut self) { 67 | self.yq.mul_assign(&self.y); 68 | self.current_q += 1; 69 | } 70 | 71 | fn insert_coefficient(&mut self, var: Variable, coeff: Coeff) { 72 | let mut yq = self.yq; 73 | 74 | match var { 75 | Variable::A(index) => { 76 | coeff.multiply(&mut yq); 77 | 78 | let u = &mut self.u[index - 1]; 79 | u.add_assign(&yq); 80 | 81 | // self.u_q.push(self.current_q); 82 | 83 | let u_q = &mut self.u_q[index - 1]; 84 | *u_q += self.current_q; 85 | }, 86 | Variable::B(index) => { 87 | coeff.multiply(&mut yq); 88 | 89 | let v = &mut self.v[index - 1]; 90 | v.add_assign(&yq); 91 | 92 | let v_q = &mut self.v_q[index - 1]; 93 | *v_q += self.current_q; 94 | }, 95 | Variable::C(index) => { 96 | coeff.multiply(&mut yq); 97 | 98 | let w = &mut self.w[index - 1]; 99 | w.add_assign(&yq); 100 | 101 | let w_q = &mut self.w_q[index - 1]; 102 | *w_q += self.current_q; 103 | } 104 | } 105 | } 106 | } 107 | 108 | #[cfg(test)] 109 | mod tests { 110 | use super::*; 111 | use pairing::bls12_381::{Bls12, Fr}; 112 | use pairing::{PrimeField, CurveAffine, CurveProjective}; 113 | use crate::cs::{Basic, ConstraintSystem, LinearCombination, Circuit, SynthesisDriver}; 114 | use rand::{thread_rng}; 115 | use crate::polynomials::{PolyComm, Polynomial}; 116 | use crate::srs::SRS; 117 | 118 | struct SimpleCircuit; 119 | 120 | impl Circuit for SimpleCircuit { 121 | fn synthesize>(&self, cs: &mut CS) 122 | -> Result<(), SynthesisError> 123 | { 124 | let (a, b, _) = cs.multiply(|| { 125 | Ok(( 126 | E::Fr::from_str("10").unwrap(), 127 | E::Fr::from_str("20").unwrap(), 128 | E::Fr::from_str("200").unwrap(), 129 | )) 130 | })?; 131 | 132 | cs.enforce_zero(LinearCombination::from(a) + a - b); 133 | 134 | Ok(()) 135 | } 136 | } 137 | 138 | fn dummy_s_prove, S: SynthesisDriver>( 139 | circuit: &C, 140 | n: usize, 141 | ) -> Vec> { 142 | let y = E::Fr::from_str("2").unwrap(); 143 | 144 | let perm = { 145 | let mut s_1 = SxPerm::new(y, n); 146 | S::synthesize(&mut s_1, circuit); 147 | 148 | s_1.perm() 149 | }; 150 | 151 | perm 152 | } 153 | 154 | #[test] 155 | fn test_perm_s1() { 156 | // let perm = dummy_s_prove::(&SimpleCircuit, 1 << 4); 157 | // println!("perm: {:?}", perm); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/helped/helper.rs: -------------------------------------------------------------------------------- 1 | //! Our protocol allows the verification of multiple proofs and even 2 | //! of individual proofs to batch the pairing operations such that 3 | //! only a smaller, fixed number of pairings must occur for an entire 4 | //! batch of proofs. This is possible because G2 elements are fixed 5 | //! in our protocol and never appear in proofs; everything can be 6 | //! combined probabilistically. 7 | //! 8 | //! The helper protocol for computing aggregated signatures 9 | //! of correct computation to ensure that an element `s` is equal to `s(z, y)` for 10 | //! known polynomial. 11 | //! The helper algorithm is run on a batch of proofs. 12 | //! 13 | //! This submodule contains the `Batch` abstraction for creating a 14 | //! context for batch verification. 15 | 16 | use pairing::{Engine, CurveAffine, CurveProjective, Field}; 17 | use crate::srs::SRS; 18 | use crate::utils::multiexp; 19 | use crate::traits::{PolyEngine, Commitment}; 20 | 21 | pub struct Batch { 22 | /// Context of openings of polynomial commitment 23 | alpha_x: Vec<(E::G1Affine, E::Fr)>, 24 | alpha_x_precomp: ::Prepared, 25 | 26 | /// Context of openings of polynomial commitment 27 | alpha: Vec<(E::G1Affine, E::Fr)>, 28 | alpha_precomp: ::Prepared, 29 | 30 | /// Context of polynomial commitment and randomness 31 | neg_h: Vec<(E::G1Affine, E::Fr)>, 32 | neg_h_precomp: ::Prepared, 33 | 34 | neg_x_n_minus_d: Vec<(E::G1Affine, E::Fr)>, 35 | neg_x_n_minus_d_precomp: ::Prepared, 36 | 37 | value: E::Fr, 38 | g: E::G1Affine, 39 | } 40 | 41 | impl Batch 42 | { 43 | pub fn new(srs: &SRS, n: usize) -> Self { 44 | Batch { 45 | alpha_x: vec![], 46 | // Prepares `alpha * h^{x^{1}}` for pairing purposes. 47 | alpha_x_precomp: srs.h_pos_x_alpha[1].prepare(), 48 | 49 | alpha: vec![], 50 | // Prepares `alpha * h^{x^{0}}` for pairing purposes. 51 | alpha_precomp: srs.h_pos_x_alpha[0].prepare(), 52 | 53 | neg_h: vec![], 54 | // Prepares `-h^{x^0}` for pairing purposes. 55 | neg_h_precomp: { 56 | let mut tmp = srs.h_neg_x[0]; 57 | tmp.negate(); 58 | tmp.prepare() 59 | }, 60 | 61 | neg_x_n_minus_d: vec![], 62 | // Prepares `-h^{x^{d-n}}` for pairing purposes. 63 | neg_x_n_minus_d_precomp: { 64 | let mut tmp = srs.h_neg_x[srs.d - n]; 65 | tmp.negate(); 66 | tmp.prepare() 67 | }, 68 | 69 | value: E::Fr::zero(), 70 | g: srs.g_pos_x[0], // g^{x^0} 71 | } 72 | } 73 | 74 | pub fn add_comm>(&mut self, comm: PE::Commitment, random: E::Fr) { 75 | self.neg_h.push((comm.into_point(), random)); 76 | } 77 | 78 | pub fn add_comm_max_n>(&mut self, comm: PE::Commitment, random: E::Fr) { 79 | self.neg_x_n_minus_d.push((comm.into_point(), random)); 80 | } 81 | 82 | pub fn add_opening(&mut self, opening: E::G1Affine, mut random: E::Fr, point: E::Fr) { 83 | self.alpha_x.push((opening, random)); 84 | 85 | random.mul_assign(&point); 86 | random.negate(); 87 | self.alpha.push((opening, random)); 88 | } 89 | 90 | pub fn add_opening_value(&mut self, eval_val: E::Fr, mut random: E::Fr) { 91 | random.mul_assign(&eval_val); 92 | self.value.add_assign(&random); 93 | } 94 | 95 | pub fn check_all(mut self) -> bool { 96 | self.alpha.push((self.g, self.value)); 97 | 98 | let alpha_x = multiexp( 99 | self.alpha_x.iter().map(|x| &x.0), 100 | self.alpha_x.iter().map(|x| &x.1), 101 | ).into_affine(); 102 | 103 | let alpha_x = alpha_x.prepare(); 104 | 105 | let alpha = multiexp( 106 | self.alpha.iter().map(|x| &x.0), 107 | self.alpha.iter().map(|x| &x.1), 108 | ).into_affine(); 109 | 110 | let alpha = alpha.prepare(); 111 | 112 | let neg_h = multiexp( 113 | self.neg_h.iter().map(|x| &x.0), 114 | self.neg_h.iter().map(|x| &x.1), 115 | ).into_affine(); 116 | 117 | let neg_h = neg_h.prepare(); 118 | 119 | let neg_x_n_minus_d = multiexp( 120 | self.neg_x_n_minus_d.iter().map(|x| &x.0), 121 | self.neg_x_n_minus_d.iter().map(|x| &x.1), 122 | ).into_affine(); 123 | 124 | let neg_x_n_minus_d = neg_x_n_minus_d.prepare(); 125 | 126 | E::final_exponentiation(&E::miller_loop(&[ 127 | (&alpha_x, &self.alpha_x_precomp), 128 | (&alpha, &self.alpha_precomp), 129 | (&neg_h, &self.neg_h_precomp), 130 | (&neg_x_n_minus_d, &self.neg_x_n_minus_d_precomp), 131 | ])).unwrap() == E::Fqk::one() 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/helped/sig_of_correct_comp.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the helper protocol for computing aggregated signatures 2 | //! of correct computation to ensure that an element `s` is equal to `s(z, y)` for 3 | //! known polynomial. 4 | //! The helper algorithm is run on a batch of proofs. 5 | 6 | use pairing::{Engine, Field}; 7 | use bellman::SynthesisError; 8 | use merlin::Transcript; 9 | use crate::cs::{SynthesisDriver, Circuit}; 10 | use crate::srs::SRS; 11 | use crate::transcript::ProvingTranscript; 12 | use crate::polynomials::{SyEval, SxEval, poly_comm, poly_comm_opening}; 13 | use crate::utils::*; 14 | use crate::traits::{PolyEngine, Commitment}; 15 | use super::prover::{Proof, SxyAdvice}; 16 | 17 | 18 | #[derive(Clone)] 19 | pub struct Aggregate { 20 | /// Commitment to s(z, Y) 21 | pub c_comm: PE::Commitment, 22 | 23 | pub s_opening: E::G1Affine, 24 | 25 | pub c_openings: Vec<(E::G1Affine, E::Fr)>, 26 | 27 | pub opening: E::G1Affine, 28 | 29 | } 30 | 31 | impl> Aggregate { 32 | pub fn create_aggregate, S: SynthesisDriver>( 33 | circuit: &C, 34 | inputs: &[(Proof, SxyAdvice)], 35 | srs: &SRS, 36 | n: usize, 37 | q: usize, 38 | ) -> Result { 39 | let mut transcript = Transcript::new(&[]); 40 | let mut y_values: Vec = Vec::with_capacity(inputs.len()); 41 | 42 | for &(ref proof, ref s_xy_advice) in inputs { 43 | { 44 | let mut transcript = Transcript::new(&[]); 45 | transcript.commit_point::(&proof.r_comm); 46 | y_values.push(transcript.challenge_scalar()); 47 | } 48 | 49 | transcript.commit_point::(&s_xy_advice.s_comm); 50 | } 51 | 52 | let z: E::Fr = transcript.challenge_scalar(); 53 | 54 | // Evaluate s(X, Y) at X=z 55 | let (s_neg_poly, s_pos_poly) = { 56 | let mut tmp = SyEval::new(z, n, q)?; 57 | S::synthesize(&mut tmp, circuit)?; 58 | 59 | tmp.neg_pos_poly() 60 | }; 61 | 62 | // max = srs.d 63 | let c_comm = poly_comm::<_, _, PE>( 64 | srs.d, 65 | n, 66 | n + q, 67 | srs, 68 | s_neg_poly.iter() 69 | .chain_ext(s_pos_poly.iter()) 70 | ); 71 | 72 | transcript.commit_point::(&c_comm); 73 | 74 | let w: E::Fr = transcript.challenge_scalar(); 75 | let w_inv = w.inverse().ok_or(SynthesisError::DivisionByZero)?; 76 | 77 | // Evaluate s(z, Y) at w 78 | let mut s_zw = E::Fr::zero(); 79 | s_zw.add_assign(&eval_univar_poly::(&s_neg_poly[..], w_inv, w_inv)); 80 | s_zw.add_assign(&eval_univar_poly::(&s_pos_poly[..], w, w)); 81 | 82 | let opening = { 83 | s_zw.negate(); 84 | poly_comm_opening( 85 | n, 86 | 0, 87 | srs, 88 | s_neg_poly.iter().rev() 89 | .chain_ext(Some(s_zw).iter()) 90 | .chain_ext(s_pos_poly.iter()), 91 | w 92 | ) 93 | }; 94 | 95 | let mut c_openings = vec![]; 96 | for y in &y_values { 97 | // Evaluate s(z, Y) at y_i 98 | let mut s_zy = E::Fr::zero(); 99 | let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; 100 | 101 | s_zy.add_assign(&eval_univar_poly::(&s_neg_poly[..], y_inv, y_inv)); 102 | s_zy.add_assign(&eval_univar_poly::(&s_pos_poly[..], *y, *y)); 103 | 104 | let opening = { 105 | s_zy.negate(); 106 | poly_comm_opening( 107 | n, 108 | 0, 109 | srs, 110 | s_neg_poly.iter().rev() 111 | .chain_ext(Some(s_zy).iter()) 112 | .chain_ext(s_pos_poly.iter()), 113 | *y 114 | ) 115 | }; 116 | 117 | c_openings.push((opening, s_zy)); 118 | } 119 | 120 | let mut neg_poly = vec![E::Fr::zero(); n]; 121 | let mut pos_poly = vec![E::Fr::zero(); n]; 122 | let mut expected_value = E::Fr::zero(); 123 | 124 | for (y, c_opening) in y_values.iter().zip(c_openings.iter()) { 125 | // Evaluate s(X, Y) at Y=y_i 126 | let (s_neg_poly, s_pos_poly) = { 127 | let mut sx_poly = SxEval::new(*y, n)?; 128 | S::synthesize(&mut sx_poly, circuit)?; 129 | 130 | sx_poly.neg_pos_poly() 131 | }; 132 | 133 | let mut s_zy = c_opening.1; 134 | let r: E::Fr = transcript.challenge_scalar(); 135 | s_zy.mul_assign(&r); 136 | expected_value.add_assign(&s_zy); 137 | 138 | mul_add_poly::(&mut neg_poly[..], &s_neg_poly[..], r); 139 | mul_add_poly::(&mut pos_poly[..], &s_pos_poly[..], r); 140 | } 141 | 142 | let s_opening = { 143 | let mut value = expected_value; 144 | value.negate(); 145 | 146 | poly_comm_opening( 147 | n, 148 | 0, 149 | srs, 150 | neg_poly.iter().rev() 151 | .chain_ext(Some(value).iter()) 152 | .chain_ext(pos_poly.iter()), 153 | z 154 | ) 155 | }; 156 | 157 | Ok(Aggregate { 158 | c_comm, 159 | s_opening, 160 | c_openings, 161 | opening, 162 | }) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/cs/lc.rs: -------------------------------------------------------------------------------- 1 | //! This module contains some type difinitions like `LinearCombination`, `Variable`, and `Coeff` 2 | //! and implementation of some operator overloadings for those types. 3 | use pairing::{Engine, Field}; 4 | use std::ops::{Add, Sub, Neg}; 5 | 6 | 7 | /// This represents a linear combination of some variables, with coefficients 8 | /// in the scalar field of a pairing-friendly elliptic curve group. 9 | #[derive(Clone)] 10 | pub struct LinearCombination(Vec<(Variable, Coeff)>); 11 | 12 | impl From for LinearCombination { 13 | fn from(var: Variable) -> LinearCombination { 14 | LinearCombination::::zero() + var 15 | } 16 | } 17 | 18 | impl AsRef<[(Variable, Coeff)]> for LinearCombination { 19 | fn as_ref(&self) -> &[(Variable, Coeff)] { 20 | &self.0 21 | } 22 | } 23 | 24 | /// Return an empty linear combination 25 | impl LinearCombination { 26 | pub fn zero() -> LinearCombination { 27 | LinearCombination(vec![]) 28 | } 29 | } 30 | 31 | impl Add> for LinearCombination { 32 | type Output = LinearCombination; 33 | 34 | fn add(mut self, lc: LinearCombination) -> LinearCombination { 35 | for (var, coeff) in lc.as_ref() { 36 | self.0.push((*var, *coeff)); 37 | } 38 | 39 | self 40 | } 41 | } 42 | 43 | /// Operetor overloading for linear combination 44 | /// `LinearCombination` + `(Coeff, Variable)` = `LinearCombination` 45 | impl Add<(Coeff, Variable)> for LinearCombination { 46 | type Output = LinearCombination; 47 | 48 | fn add(mut self, (coeff, var): (Coeff, Variable)) -> LinearCombination { 49 | self.0.push((var, coeff)); 50 | self 51 | } 52 | } 53 | 54 | /// Operetor overloading for linear combination 55 | /// `LinearCombination` - `(Coeff, Variable)` = `LinearCombination` 56 | impl Sub<(Coeff, Variable)> for LinearCombination { 57 | type Output = LinearCombination; 58 | 59 | fn sub(self, (coeff, var): (Coeff, Variable)) -> LinearCombination { 60 | self + (-coeff, var) 61 | } 62 | } 63 | 64 | /// Operetor overloading for linear combination 65 | /// `LinearCombination` + `(Coeff::One, Variable)` = `LinearCombination` 66 | impl Add for LinearCombination { 67 | type Output = LinearCombination; 68 | 69 | fn add(self, var: Variable) -> LinearCombination { 70 | self + (Coeff::One, var) 71 | } 72 | } 73 | 74 | /// Operetor overloading for linear combination 75 | /// `LinearCombination` - `(Coeff::one, Variable) = `LinearCombination` 76 | impl Sub for LinearCombination { 77 | type Output = LinearCombination; 78 | 79 | fn sub(self, var: Variable) -> LinearCombination { 80 | self - (Coeff::One, var) 81 | } 82 | } 83 | 84 | /// Operetor overloading for linear combination 85 | /// `LinearCombination` + `&LinearCombination` = `LinearCombination` 86 | impl<'a, E: Engine> Add<&'a LinearCombination> for LinearCombination { 87 | type Output = LinearCombination; 88 | 89 | fn add(mut self, other: &'a LinearCombination) -> LinearCombination { 90 | for s in &other.0 { 91 | // `LinearCombination` = `LinearCombination` + `(Coeff, Variable)` 92 | self = self + (s.1, s.0); 93 | } 94 | 95 | self 96 | } 97 | } 98 | 99 | /// Operetor overloading for linear combination 100 | /// `LinearCombination` - `&LinearCombination` = `LinearCombination` 101 | impl<'a, E: Engine> Sub<&'a LinearCombination> for LinearCombination { 102 | type Output = LinearCombination; 103 | 104 | fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { 105 | for s in &other.0 { 106 | // `LinearCombination` = `LinearCombination` - `(Coeff, Variable)` 107 | self = self - (s.1, s.0); 108 | } 109 | 110 | self 111 | } 112 | } 113 | 114 | /// A difinition of Variable for linear combination used in our constraint system. 115 | #[derive(Copy, Clone, Debug)] 116 | pub enum Variable { 117 | A(usize), // input variable in r1cs 118 | B(usize), // Auxillary variable in r1cs 119 | C(usize), 120 | } 121 | 122 | // like DensityTracker 123 | /// A difinition of Coefficient for linear combination used in our constraint system. 124 | #[derive(Debug)] 125 | pub enum Coeff { 126 | Zero, 127 | One, 128 | NegativeOne, 129 | Full(E::Fr), 130 | } 131 | 132 | impl Copy for Coeff {} 133 | impl Clone for Coeff { 134 | fn clone(&self) -> Self { 135 | *self 136 | } 137 | } 138 | 139 | /// Multiply the coefficient with a given variable. 140 | impl Coeff { 141 | pub fn multiply(&self, with: &mut E::Fr) { 142 | match self { 143 | Coeff::Zero => { 144 | *with = E::Fr::zero(); 145 | }, 146 | Coeff::One => {}, 147 | Coeff::NegativeOne => { 148 | with.negate(); 149 | }, 150 | Coeff::Full(val) => { 151 | with.mul_assign(val); 152 | } 153 | } 154 | } 155 | } 156 | 157 | /// Operetor overloading for Coefficient 158 | /// used for Substraction overloading for linear combination 159 | impl Neg for Coeff { 160 | type Output = Coeff; 161 | 162 | fn neg(self) -> Self { 163 | match self { 164 | Coeff::Zero => Coeff::Zero, 165 | Coeff::One => Coeff::NegativeOne, 166 | Coeff::NegativeOne => Coeff::One, 167 | Coeff::Full(mut a) => { 168 | a.negate(); 169 | Coeff::Full(a) 170 | } 171 | } 172 | } 173 | } -------------------------------------------------------------------------------- /src/helped/adaptor.rs: -------------------------------------------------------------------------------- 1 | //! This module contains an adaptor which translates circuits written in the form of 2 | //! quadratic "rank-1 constraint systems"(R1CS) into the system of constraints natural to 3 | //! sonic's proving system. 4 | //! R1CS is a constraint system which is widely deployed NP language currently undergoing 5 | //! standardisation. 6 | 7 | use std::marker::PhantomData; 8 | use pairing::{Engine, CurveProjective, Field}; 9 | use bellman::{ConstraintSystem, Variable, Index, SynthesisError, LinearCombination, Circuit}; 10 | use crate::cs::{ 11 | ConstraintSystem as SonicCS, 12 | Variable as SonicVar, 13 | Coeff as SonicCoeff, 14 | LinearCombination as SonicLC, 15 | Circuit as SonicCircuit, 16 | }; 17 | 18 | /// Define an adaptor type which translates R1CS to Sonic's constraint system. 19 | pub struct Adaptor<'a, E: Engine, CS: SonicCS + 'a> { 20 | cs: &'a mut CS, 21 | _maker: PhantomData, 22 | } 23 | 24 | /// Apply R1CS trait to Adaptor type 25 | impl <'a, E: Engine, CS: SonicCS + 'a> ConstraintSystem 26 | for Adaptor<'a, E, CS> 27 | { 28 | /// Represents the type of the "root" of this constraint system 29 | /// so that nested namespaces can minimize indirection. 30 | type Root = Self; 31 | 32 | /// Return the "one" input variable 33 | fn one() -> Variable { 34 | Variable::new_unchecked(Index::Input(1)) 35 | } 36 | 37 | /// Allocate a private variable in the constraint system. The provided function is used to 38 | /// determine the assignment of the variable. 39 | fn alloc(&mut self, _: A, f: F) -> Result 40 | where 41 | F: FnOnce() -> Result, 42 | A: FnOnce() -> AR, 43 | AR: Into, 44 | { 45 | // Get a allocated private variable in the sonic's constraint system. 46 | let var = self.cs.alloc(|| { 47 | f().map_err(|_| SynthesisError::AssignmentMissing) 48 | }).map_err(|_| SynthesisError::AssignmentMissing)?; 49 | 50 | Ok(match var { 51 | SonicVar::A(index) => Variable::new_unchecked(Index::Input(index)), 52 | SonicVar::B(index) => Variable::new_unchecked(Index::Aux(index)), 53 | _ => unreachable!(), 54 | }) 55 | } 56 | 57 | /// Allocate a public variable in the constraint system. The provided function is used to 58 | /// determine the assignment of the variable. 59 | fn alloc_input(&mut self, _: A, f: F) -> Result 60 | where 61 | F: FnOnce() -> Result, 62 | A: FnOnce() -> AR, 63 | AR: Into, 64 | { 65 | let var = self.cs.alloc_input(|| { 66 | f().map_err(|_| SynthesisError::AssignmentMissing) 67 | }).map_err(|_| SynthesisError::AssignmentMissing)?; 68 | 69 | Ok(match var { 70 | SonicVar::A(index) => Variable::new_unchecked(Index::Input(index)), 71 | SonicVar::B(index) => Variable::new_unchecked(Index::Aux(index)), 72 | _ => unreachable!(), 73 | }) 74 | } 75 | 76 | 77 | 78 | /// Enforce that `A` * `B` = `C`. 79 | fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) 80 | where 81 | A: FnOnce() -> AR, 82 | AR: Into, 83 | LA: FnOnce(LinearCombination) -> LinearCombination, 84 | LB: FnOnce(LinearCombination) -> LinearCombination, 85 | LC: FnOnce(LinearCombination) -> LinearCombination, 86 | { 87 | /// Convert r1cs's linear combination to sonic's one. 88 | fn convert(lc: LinearCombination) -> SonicLC { 89 | let mut ret = SonicLC::zero(); 90 | 91 | for &(v, coeff) in lc.as_ref().iter() { 92 | let var = match v.get_unchecked() { 93 | Index::Input(i) => SonicVar::A(i), 94 | Index::Aux(i) => SonicVar::B(i), 95 | }; 96 | 97 | ret = ret + (SonicCoeff::Full(coeff), var); 98 | } 99 | 100 | ret 101 | } 102 | 103 | /// Get an evaluation of a linear combination. 104 | fn eval>( 105 | lc: &SonicLC, 106 | cs: &CS, 107 | ) -> Option { 108 | let mut ret = E::Fr::zero(); 109 | 110 | for &(v, coeff) in lc.as_ref().iter() { 111 | let mut tmp = match cs.get_value(v) { 112 | Ok(tmp) => tmp, 113 | Err(_) => return None, 114 | }; 115 | coeff.multiply(&mut tmp); 116 | ret.add_assign(&tmp); 117 | } 118 | 119 | Some(ret) 120 | } 121 | 122 | // Get each sonic's linear combination and evaluated value 123 | let a_lc = convert(a(LinearCombination::zero())); 124 | let a_value = eval(&a_lc, &*self.cs); 125 | let b_lc = convert(b(LinearCombination::zero())); 126 | let b_value = eval(&b_lc, &*self.cs); 127 | let c_lc = convert(c(LinearCombination::zero())); 128 | let c_value = eval(&c_lc, &*self.cs); 129 | 130 | // Convert scalars into variables in a multipilication gate. 131 | let (a, b, c) = self 132 | .cs 133 | .multiply(|| Ok((a_value.unwrap(), b_value.unwrap(), c_value.unwrap()))) 134 | .unwrap(); 135 | 136 | // Ensure each linear conbination is equal to evaluated value 137 | self.cs.enforce_zero(a_lc + b_lc + c_lc - a - b - c); 138 | // self.cs.enforce_zero(a_lc - a); 139 | // self.cs.enforce_zero(b_lc - b); 140 | // self.cs.enforce_zero(c_lc - c); 141 | } 142 | 143 | fn push_namespace(&mut self, _: N) 144 | where 145 | NR: Into, 146 | N: FnOnce() -> NR, 147 | { 148 | // Do nothing; we don't care about namespaces in this context. 149 | } 150 | 151 | fn pop_namespace(&mut self) { 152 | // Do nothing; we don't care about namespaces in this context. 153 | } 154 | 155 | fn get_root(&mut self) -> &mut Self::Root { 156 | self 157 | } 158 | } 159 | 160 | #[derive(Clone)] 161 | pub struct AdaptorCircuit(pub T); 162 | 163 | impl<'a, E: Engine, C: Circuit + Clone> SonicCircuit for AdaptorCircuit { 164 | fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { 165 | let mut adaptor = Adaptor { 166 | cs, 167 | _maker: PhantomData, 168 | }; 169 | 170 | self.0.clone().synthesize(&mut adaptor)?; 171 | 172 | Ok(()) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Field, PrimeField, CurveAffine}; 2 | use bellman::multicore::Worker; 3 | use crossbeam::channel::unbounded; 4 | 5 | /// Basically used for polynomials represented as separeted iterator 6 | /// (like positive and negative powers). 7 | /// It can be used nested chains. 8 | pub trait ChainExt: Iterator { 9 | fn chain_ext(self, other: U) -> Chain 10 | where 11 | Self: Sized, 12 | U: IntoIterator, 13 | { 14 | Chain { 15 | t: self, 16 | u: other.into_iter(), 17 | } 18 | } 19 | } 20 | 21 | impl ChainExt for I {} 22 | 23 | #[derive(Clone)] 24 | pub struct Chain { 25 | t: T, 26 | u: U 27 | } 28 | 29 | impl Iterator for Chain 30 | where T: Iterator, U: Iterator 31 | { 32 | type Item = T::Item; 33 | 34 | fn next(&mut self) -> Option { 35 | match self.t.next() { 36 | Some(v) => Some(v), 37 | None => match self.u.next() { 38 | Some(v) => Some(v), 39 | None => None, 40 | } 41 | } 42 | } 43 | } 44 | 45 | impl ExactSizeIterator for Chain 46 | where 47 | T: Iterator + ExactSizeIterator, 48 | U: Iterator + ExactSizeIterator, 49 | { 50 | fn len(&self) -> usize { 51 | self.t.len() + self.u.len() 52 | } 53 | } 54 | 55 | impl DoubleEndedIterator for Chain 56 | where 57 | T: Iterator + DoubleEndedIterator, 58 | U: Iterator + DoubleEndedIterator, 59 | { 60 | fn next_back(&mut self) -> Option { 61 | match self.u.next_back() { 62 | Some(v) => Some(v), 63 | None => match self.t.next_back() { 64 | Some(v) => Some(v), 65 | None => None, 66 | } 67 | } 68 | } 69 | } 70 | 71 | /// Multiply each coefficient by some power of the base in a form 72 | /// `first_power * base^{i}` 73 | /// This would be sparse, consecutive multiplication based on non-zero coefficients. 74 | /// Basically, it is for the evaluation of one of the variables of bivariate polynomials. 75 | /// For example, r(X, Y) at y. 76 | pub fn eval_bivar_poly<'a, E: Engine> ( 77 | coeffs: &mut [E::Fr], 78 | first_power: E::Fr, 79 | base: E::Fr 80 | ) { 81 | let worker = Worker::new(); 82 | 83 | worker.scope(coeffs.len(), |scope, chunk| { 84 | for (i, coeffs_chunk) in coeffs.chunks_mut(chunk).enumerate() { 85 | scope.spawn(move |_| { 86 | let mut current_power = base.pow(&[(i * chunk) as u64]); 87 | current_power.mul_assign(&first_power); 88 | 89 | for mut p in coeffs_chunk { 90 | p.mul_assign(¤t_power); 91 | 92 | current_power.mul_assign(&base); 93 | } 94 | }); 95 | } 96 | }); 97 | } 98 | 99 | /// It is for the evaluation of univariate polynomials. For example, r(X, y) at z. 100 | pub fn eval_univar_poly<'a, E: Engine> ( 101 | coeffs: &[E::Fr], 102 | first_power: E::Fr, 103 | base: E::Fr 104 | ) -> E::Fr 105 | { 106 | let (tx, rx) = unbounded(); 107 | let worker = Worker::new(); 108 | 109 | worker.scope(coeffs.len(), |scope, chunk| { 110 | for (i, coeffs) in coeffs.chunks(chunk).enumerate() { 111 | let tx = tx.clone(); 112 | 113 | scope.spawn(move |_| { 114 | let mut current_power = base.pow(&[(i * chunk) as u64]); 115 | current_power.mul_assign(&first_power); 116 | 117 | let mut acc = E::Fr::zero(); 118 | 119 | for p in coeffs { 120 | let mut tmp = *p; 121 | tmp.mul_assign(¤t_power); 122 | acc.add_assign(&tmp); 123 | 124 | current_power.mul_assign(&base); 125 | } 126 | 127 | tx.send(acc).expect("must send"); 128 | }); 129 | } 130 | }); 131 | 132 | // The sender is dropped, disconnect the channel. 133 | drop(tx); 134 | 135 | let mut res = E::Fr::zero(); 136 | 137 | loop { 138 | if rx.is_empty() { 139 | break; 140 | } 141 | 142 | let val = rx.recv().expect("must not be empty"); 143 | res.add_assign(&val); 144 | } 145 | 146 | res 147 | } 148 | 149 | /// Batching polynomial commitment, Defined in appendix C.1. 150 | /// Elementwise add coeffs of one polynomial with coeffs of other, that are 151 | /// first multiplied by a scalar 152 | /// f(x) + g(x) * scalar 153 | pub fn mul_add_poly(a: &mut [E::Fr], b: &[E::Fr], scalar: E::Fr) { 154 | let worker = Worker::new(); 155 | assert_eq!(a.len(), b.len()); 156 | 157 | worker.scope(a.len(), |scope, chunk| { 158 | for (a, b) in a.chunks_mut(chunk).zip(b.chunks(chunk)) { 159 | scope.spawn(move |_| { 160 | for (a, b) in a.iter_mut().zip(b.iter()) { 161 | let mut r = *b; 162 | r.mul_assign(&scalar); 163 | a.add_assign(&r); 164 | } 165 | }); 166 | } 167 | }); 168 | } 169 | 170 | /// Multiply coefficients of the polynomial by the given scalar. 171 | /// f(X) * scalar 172 | pub fn mul_poly_by_scalar(poly: &mut [E::Fr], scalar: E::Fr) { 173 | let worker = Worker::new(); 174 | 175 | worker.scope(poly.len(), |scope, chunk| { 176 | for p in poly.chunks_mut(chunk) { 177 | scope.spawn(move |_| { 178 | for p in p.iter_mut() { 179 | p.mul_assign(&scalar) 180 | } 181 | }); 182 | } 183 | }); 184 | } 185 | 186 | pub fn multiexp< 187 | 'a, 188 | G: CurveAffine, 189 | IE: IntoIterator, 190 | IS: IntoIterator, 191 | >( 192 | exponent: IE, 193 | scalar: IS, 194 | ) -> G::Projective 195 | where 196 | IE::IntoIter: ExactSizeIterator + Clone, 197 | IS::IntoIter: ExactSizeIterator, 198 | { 199 | use bellman::multiexp::{multiexp, FullDensity}; 200 | use std::sync::Arc; 201 | use futures::Future; 202 | 203 | let scalar: Vec<::Repr> = scalar 204 | .into_iter() 205 | .map(|e| e.into_repr()) 206 | .collect::>(); 207 | 208 | let exponent: Vec = exponent 209 | .into_iter() 210 | .map(|e| *e) 211 | .collect::>(); 212 | 213 | assert_eq!( 214 | scalar.len(), 215 | exponent.len(), 216 | "scalars and exponents must have the same length." 217 | ); 218 | 219 | let pool = Worker::new(); 220 | 221 | let result = multiexp( 222 | &pool, 223 | (Arc::new(exponent), 0), 224 | FullDensity, 225 | Arc::new(scalar) 226 | ).wait().unwrap(); 227 | 228 | result 229 | } 230 | -------------------------------------------------------------------------------- /src/unhelped/well_formed.rs: -------------------------------------------------------------------------------- 1 | /// Defined in appendix B.3 Well-formedness Argument 2 | use pairing::{Engine, Field, CurveAffine, CurveProjective}; 3 | use crate::srs::SRS; 4 | use crate::utils::*; 5 | 6 | // Nested vec because of \sum\limits_{j=1}^M, for now. 7 | #[derive(Clone)] 8 | pub struct WellformedArg(Vec>); 9 | 10 | #[derive(Clone)] 11 | pub struct WellformedComm(Vec); 12 | 13 | impl WellformedArg { 14 | /// The number of polynomials for well-formed argument 15 | pub fn len(&self) -> usize { 16 | self.0.len() 17 | } 18 | 19 | /// The degree of each polynomials for well-formed argument 20 | pub fn len_poly(&self) -> usize { 21 | self.0[0].len() 22 | } 23 | 24 | pub fn new(polys: Vec>) -> Self { 25 | assert!(!polys.is_empty()); 26 | let len_poly = polys[0].len(); 27 | 28 | // Ensure all of the polynomials have the same degree. 29 | assert!(polys.iter().all(|p| p.len() == len_poly)); 30 | 31 | WellformedArg(polys) 32 | } 33 | 34 | pub fn commit(&self, srs: &SRS) -> WellformedComm { 35 | let mut res = vec![]; 36 | let n = self.len_poly(); 37 | 38 | for poly in self.0.iter() { 39 | let c = multiexp( 40 | srs.g_pos_x_alpha[..n].iter(), 41 | poly.iter() 42 | ).into_affine(); 43 | 44 | res.push(c); 45 | } 46 | 47 | WellformedComm::(res) 48 | } 49 | 50 | /// The prover sends a well-formedness proof to the verifier. 51 | pub fn prove(&self, challenges: &[E::Fr], srs: &SRS) -> WellformedProof { 52 | let m = self.len(); 53 | let n = self.len_poly(); 54 | let d = srs.d; 55 | 56 | assert_eq!(m, challenges.len()); 57 | assert!(n < d); 58 | 59 | let mut acc: Vec = vec![E::Fr::zero(); n]; 60 | 61 | // Batching well-formedness arguments 62 | for j in 0..m { 63 | mul_add_poly::(&mut acc[..], &self.0[j][..], challenges[j]) 64 | } 65 | 66 | // g^{x^{-d} * f(x)} where f(x) is well-formed, meaning don't have terms of negative degree and constant term. 67 | // so larget negative power is -(d - 1), smallest negative power is -(d-n) 68 | let l = multiexp( 69 | srs.g_neg_x[(d - n)..d].iter().rev(), 70 | acc.iter() 71 | ).into_affine(); 72 | 73 | // g^{x^{d-n} * f(x)} where f(x) is well-formed. 74 | // largest positive power is d, smallet positive power is d - n + 1 75 | let r = multiexp( 76 | srs.g_pos_x[(d - n + 1)..].iter(), 77 | acc.iter() 78 | ).into_affine(); 79 | 80 | WellformedProof { 81 | l, 82 | r, 83 | } 84 | } 85 | } 86 | 87 | /// A proof of Well-formedness Argument 88 | #[derive(Clone)] 89 | pub struct WellformedProof { 90 | l: E::G1Affine, 91 | r: E::G1Affine, 92 | } 93 | 94 | impl WellformedProof { 95 | /// The verifier can check with the pairings 96 | /// e(g^{alpha * f(x)}, h) = e(proof.l, h^{alpha * x^{d}}) 97 | /// e(g^{alpha * f(x)}, h) = e(proof.r, h^{alpha * x^{n-d}}) 98 | pub fn verify( 99 | &self, 100 | n: usize, 101 | challenges: &[E::Fr], 102 | commitments: &WellformedComm, 103 | srs: &SRS 104 | ) -> bool 105 | { 106 | let d = srs.d; 107 | let alpha_x_d_prep = srs.h_pos_x_alpha[d].prepare(); 108 | let alpha_x_n_minus_d_prep = srs.h_neg_x_alpha[d - n].prepare(); 109 | 110 | let mut h = srs.h_pos_x[0]; 111 | h.negate(); 112 | let h_prep = h.prepare(); 113 | 114 | let alpha_f = multiexp( 115 | commitments.0.iter(), 116 | challenges.iter() 117 | ).into_affine(); 118 | let alpha_f_prep = alpha_f.prepare(); 119 | 120 | let is_valid_l = E::final_exponentiation(&E::miller_loop(&[ 121 | (&alpha_f_prep, &h_prep), 122 | (&self.l.prepare(), &alpha_x_d_prep) 123 | ])).unwrap() == E::Fqk::one(); 124 | 125 | let is_valid_r = E::final_exponentiation(&E::miller_loop(&[ 126 | (&alpha_f_prep, &h_prep), 127 | (&self.r.prepare(), &alpha_x_n_minus_d_prep) 128 | ])).unwrap() == E::Fqk::one(); 129 | 130 | is_valid_l && is_valid_r 131 | } 132 | } 133 | 134 | #[cfg(test)] 135 | mod tests { 136 | use super::*; 137 | use pairing::bls12_381::{Bls12, Fr}; 138 | use pairing::PrimeField; 139 | use rand::{XorShiftRng, SeedableRng, Rand}; 140 | 141 | #[test] 142 | fn wellformedness_1_arg_correctness() { 143 | let srs_x = Fr::from_str("432").unwrap(); 144 | let srs_alpha = Fr::from_str("9876").unwrap(); 145 | let srs = SRS::::dummy(824562, srs_x, srs_alpha); 146 | 147 | let n: usize = 1 << 16; 148 | let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); 149 | let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); 150 | 151 | let arg = WellformedArg::new(vec![coeffs]); 152 | let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); 153 | 154 | let commitments = arg.commit(&srs); 155 | let proof = arg.prove(&challenges[..], &srs); 156 | let valid = proof.verify(n, &challenges, &commitments, &srs); 157 | 158 | assert!(valid); 159 | } 160 | 161 | #[test] 162 | fn wellformedness_3_args_correctness() { 163 | let srs_x = Fr::from_str("432").unwrap(); 164 | let srs_alpha = Fr::from_str("9876").unwrap(); 165 | let srs = SRS::::dummy(824562, srs_x, srs_alpha); 166 | 167 | let n: usize = 1 << 16; 168 | let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); 169 | let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); 170 | 171 | let arg = WellformedArg::new(vec![coeffs; 3]); 172 | let challenges = (0..3).map(|_| Fr::rand(rng)).collect::>(); 173 | 174 | let commitments = arg.commit(&srs); 175 | let proof = arg.prove(&challenges[..], &srs); 176 | let valid = proof.verify(n, &challenges, &commitments, &srs); 177 | 178 | assert!(valid); 179 | } 180 | 181 | #[test] 182 | fn wellformedness_3_args_soundness() { 183 | let srs_x = Fr::from_str("432").unwrap(); 184 | let srs_alpha = Fr::from_str("9876").unwrap(); 185 | let srs = SRS::::dummy(824562, srs_x, srs_alpha); 186 | 187 | let n: usize = 1 << 16; 188 | let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); 189 | let coeffs_1 = (0..n).map(|_| Fr::rand(rng)).collect::>(); 190 | 191 | let arg_1 = WellformedArg::new(vec![coeffs_1; 3]); 192 | let commitments_1 = arg_1.commit(&srs); 193 | 194 | let coeffs_2 = (0..n).map(|_| Fr::rand(rng)).collect::>(); 195 | let arg_2 = WellformedArg::new(vec![coeffs_2; 3]); 196 | let challenges_2 = (0..3).map(|_| Fr::rand(rng)).collect::>(); 197 | 198 | let proof = arg_2.prove(&challenges_2[..], &srs); 199 | let valid = proof.verify(n, &challenges_2, &commitments_1, &srs); 200 | 201 | assert!(!valid); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/helped/verifier.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Field}; 2 | use rand::{Rand, Rng}; 3 | use bellman::SynthesisError; 4 | use merlin::Transcript; 5 | use crate::cs::{Circuit, Backend, SynthesisDriver}; 6 | use crate::srs::SRS; 7 | use crate::transcript::ProvingTranscript; 8 | use crate::polynomials::SxEval; 9 | use crate::traits::{PolyEngine, Commitment}; 10 | use super::prover::{Proof, SxyAdvice}; 11 | use super::helper::Batch; 12 | use std::marker::PhantomData; 13 | 14 | pub struct MultiVerifier, S: SynthesisDriver, R: Rng> { 15 | circuit: C, 16 | pub(crate) batch: Batch, 17 | k_map: Vec, 18 | n: usize, 19 | q: usize, 20 | randommness: R, 21 | _marker: PhantomData<(E, S)>, 22 | } 23 | 24 | impl, S: SynthesisDriver, R: Rng> MultiVerifier { 25 | pub fn new(circuit: C, srs: &SRS, rng: R) -> Result { 26 | struct Preprocess { 27 | k_map: Vec, 28 | n: usize, 29 | q: usize, 30 | _marker: PhantomData 31 | } 32 | 33 | impl<'a, E: Engine> Backend for &'a mut Preprocess { 34 | fn new_multiplication_gate(&mut self) { 35 | self.n += 1; 36 | } 37 | 38 | fn new_linear_constraint(&mut self) { 39 | self.q += 1; 40 | } 41 | 42 | fn new_k_power(&mut self, index: usize) { 43 | self.k_map.push(index); 44 | } 45 | } 46 | 47 | let mut preprocess = Preprocess { 48 | k_map: vec![], 49 | n: 0, 50 | q: 0, 51 | _marker: PhantomData 52 | }; 53 | 54 | S::synthesize(&mut preprocess, &circuit)?; 55 | 56 | Ok(MultiVerifier { 57 | circuit, 58 | batch: Batch::new(srs, preprocess.n), 59 | k_map: preprocess.k_map, 60 | n: preprocess.n, 61 | q: preprocess.q, 62 | randommness: rng, 63 | _marker: PhantomData, 64 | }) 65 | } 66 | 67 | pub fn add_proof(&mut self, proof: &Proof, inputs: &[E::Fr], s_xy: F) 68 | where 69 | F: FnOnce(E::Fr, E::Fr) -> Option, 70 | PE: PolyEngine 71 | { 72 | let mut transcript = Transcript::new(&[]); 73 | 74 | transcript.commit_point::(&proof.r_comm); 75 | let y: E::Fr = transcript.challenge_scalar(); 76 | 77 | transcript.commit_point::(&proof.t_comm); 78 | let z: E::Fr = transcript.challenge_scalar(); 79 | 80 | transcript.commit_scalar(&proof.r_z1); 81 | transcript.commit_scalar(&proof.r_zy); 82 | let r1: E::Fr = transcript.challenge_scalar(); 83 | 84 | // transcript.commit_point::(&proof.z_opening); 85 | // transcript.commit_point::(&proof.yz_opening); 86 | 87 | 88 | // Open up proof.r_comm at zy, using proof.yz_opening 89 | // as the evidence and proof.r_zy as the opening 90 | { 91 | let random: E::Fr = self.randommness.gen(); 92 | let mut zy = z; 93 | zy.mul_assign(&y); 94 | 95 | self.batch.add_opening(proof.yz_opening, random, zy); 96 | self.batch.add_comm_max_n::(proof.r_comm, random); 97 | self.batch.add_opening_value(proof.r_zy, random); 98 | } 99 | 100 | // Compute k(y) 101 | let mut k_y = E::Fr::zero(); 102 | for (exp, input) in self.k_map.iter().zip(Some(E::Fr::one()).iter().chain(inputs.iter())) { 103 | let mut term = y.pow(&[(*exp + self.n) as u64]); 104 | term.mul_assign(input); 105 | k_y.add_assign(&term); 106 | } 107 | 108 | // Compute s(z, y) // TODO 109 | let s_zy = s_xy(z, y).unwrap_or_else(|| { 110 | let mut tmp = SxEval::new(y, self.n).unwrap(); 111 | S::synthesize(&mut tmp, &self.circuit).unwrap(); 112 | 113 | tmp.finalize(z).unwrap() 114 | }); 115 | 116 | // Compute t(z, y) 117 | let mut t_zy = proof.r_zy; 118 | t_zy.add_assign(&s_zy); 119 | t_zy.mul_assign(&proof.r_z1); 120 | t_zy.sub_assign(&k_y); 121 | 122 | // Open up proof.t_comm and proof.r_comm at z by keeping thier commitments 123 | // linearly independent. 124 | { 125 | let mut random: E::Fr = self.randommness.gen(); 126 | 127 | self.batch.add_opening(proof.z_opening, random, z); 128 | self.batch.add_opening_value(t_zy, random); 129 | self.batch.add_comm::(proof.t_comm, random); 130 | 131 | random.mul_assign(&r1); // for batching 132 | 133 | self.batch.add_opening_value(proof.r_z1, random); 134 | self.batch.add_comm_max_n::(proof.r_comm, random); 135 | } 136 | } 137 | 138 | pub fn add_proof_with_advice( 139 | &mut self, 140 | proof: &Proof, 141 | inputs: &[E::Fr], 142 | advice: &SxyAdvice, 143 | ) 144 | where PE: PolyEngine 145 | { 146 | let mut z = None; 147 | self.add_proof(proof, inputs, |_z, _y| { 148 | z = Some(_z); 149 | Some(advice.s_zy) 150 | }); 151 | 152 | let z = z.unwrap(); 153 | 154 | let mut transcript = Transcript::new(&[]); 155 | // transcript.commit_point::(&advice.s_zy_opening); 156 | transcript.commit_point::(&advice.s_comm); 157 | transcript.commit_scalar(&advice.s_zy); 158 | let random: E::Fr = self.randommness.gen(); 159 | 160 | self.batch.add_opening(advice.s_zy_opening, random, z); 161 | self.batch.add_comm::(advice.s_comm, random); 162 | self.batch.add_opening_value(advice.s_zy, random); 163 | } 164 | 165 | // pub fn add_aggregate( 166 | // &mut self, 167 | // proofs: &[(Proof, SxyAdvice)], 168 | // aggregate: &Aggregate, 169 | // ) 170 | // { 171 | // unimplemented!(); 172 | // } 173 | 174 | pub fn get_k_map(&self) -> Vec { 175 | self.k_map.clone() 176 | } 177 | 178 | pub fn get_n(&self) -> usize { 179 | self.n 180 | } 181 | 182 | pub fn get_q(&self) -> usize { 183 | self.q 184 | } 185 | 186 | pub fn check_all(self) -> bool { 187 | self.batch.check_all() 188 | } 189 | } 190 | 191 | pub fn verify_a_proof<'a, E: Engine, PE: PolyEngine>( 192 | proof: Proof, 193 | public_inputs: &[E::Fr], 194 | ) -> Result 195 | { 196 | 197 | unimplemented!(); 198 | } 199 | 200 | pub fn verify_proofs, S: SynthesisDriver, R: Rng, PE: PolyEngine>( 201 | proofs: &[Proof], 202 | inputs: &[Vec], 203 | circuit: C, 204 | rng: R, 205 | srs: &SRS, 206 | ) -> Result { 207 | let mut verifier = MultiVerifier::::new(circuit, srs, rng)?; 208 | // minus one because of the inputize ONE 209 | let expected_inputs_size = verifier.get_k_map().len() - 1; 210 | 211 | for (proof, inputs) in proofs.iter().zip(inputs.iter()) { 212 | if inputs.len() != expected_inputs_size { 213 | return Err(SynthesisError::Unsatisfiable); 214 | } 215 | verifier.add_proof(proof, &inputs, |_, _| None); 216 | } 217 | 218 | Ok(verifier.check_all()) 219 | } 220 | -------------------------------------------------------------------------------- /src/polynomials/s_eval.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Field}; 2 | use bellman::SynthesisError; 3 | use crate::utils::{eval_bivar_poly, eval_univar_poly}; 4 | use crate::cs::Backend; 5 | use crate::cs::lc::{Variable, Coeff}; 6 | use super::add_polynomials; 7 | 8 | /// Defined in Section 5: SYSTEM OF CONSTRAINTS 9 | /// Evaluation of s(X, Y) at x 10 | #[derive(Clone)] 11 | pub struct SyEval { 12 | max_n: usize, 13 | current_q: usize, 14 | 15 | /// Coefficients of u term 16 | /// x^{-1}, ..., x^{-N} 17 | a: Vec, 18 | 19 | /// Coefficients of v term 20 | /// x^1, ..., x^{N} 21 | b: Vec, 22 | 23 | /// Coefficients of w term 24 | /// x^{N+1}, ..., x^{2*N+1} 25 | c: Vec, 26 | 27 | /// Coefficients of Y^1, ..., Y^{N+Q} 28 | pos_coeffs: Vec, 29 | 30 | /// Coefficients of Y^{-1}, Y^{-2}, ..., Y^{-N} 31 | neg_coeffs: Vec, 32 | } 33 | 34 | impl SyEval { 35 | pub fn new( 36 | x: E::Fr, 37 | n: usize, // Max N 38 | q: usize, // Max Q 39 | ) -> Result 40 | { 41 | let x_inv = x.inverse().ok_or(SynthesisError::DivisionByZero)?; 42 | let x_n_plus_1 = x.pow(&[(n + 1) as u64]); 43 | 44 | let mut a = vec![E::Fr::one(); n]; 45 | let mut b = vec![E::Fr::one(); n]; 46 | let mut c = vec![E::Fr::one(); n]; 47 | 48 | // Evaluate polynomial S(X, Y) at x for each coefficients u, v ,and w. 49 | eval_bivar_poly::(&mut a[..], x_inv, x_inv); 50 | eval_bivar_poly::(&mut b[..], x, x); 51 | eval_bivar_poly::(&mut c[..], x_n_plus_1, x); 52 | 53 | let mut minus_one = E::Fr::one(); 54 | minus_one.negate(); 55 | 56 | // Coefficients of powers [-1, -n] and [1, n] are fixed to -1 57 | // because of -Y^{i}-Y^{-i} term in w_i(Y) 58 | let mut pos_coeffs = vec![minus_one; n]; 59 | eval_bivar_poly::(&mut pos_coeffs[..], x_n_plus_1, x); 60 | let neg_coeffs = pos_coeffs.clone(); 61 | 62 | // Coefficients of powers [1+n, q+n] will be assigned with u, v, and w via synthesizing. 63 | // We don't append a, b, and c as coefficients because u, v, and w haven't be determined yet. 64 | // We store the a, b, and c as separate elements, so can add those coefficients at the time of 65 | // synthesizing u,v,w. 66 | pos_coeffs.resize(q + n, E::Fr::zero()); 67 | 68 | Ok(SyEval { 69 | max_n: n, 70 | current_q: 0, 71 | a, 72 | b, 73 | c, 74 | pos_coeffs, 75 | neg_coeffs, 76 | }) 77 | } 78 | 79 | /// Return polynomials each of negative and positive powers 80 | pub fn neg_pos_poly(self) -> (Vec, Vec) { 81 | (self.neg_coeffs, self.pos_coeffs) 82 | } 83 | 84 | // Evaluate S(x, Y) at y 85 | pub fn finalize(&self, y: E::Fr) -> Result { 86 | let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; 87 | 88 | let pos_eval = eval_univar_poly::(&self.pos_coeffs[..], y, y); 89 | let neg_eval = eval_univar_poly::(&self.neg_coeffs[..], y_inv, y_inv); 90 | 91 | let mut acc = E::Fr::zero(); 92 | acc.add_assign(&pos_eval); 93 | acc.add_assign(&neg_eval); 94 | 95 | Ok(acc) 96 | } 97 | } 98 | 99 | impl<'a, E: Engine> Backend for &'a mut SyEval { 100 | fn new_linear_constraint(&mut self) { 101 | self.current_q += 1; 102 | } 103 | 104 | /// Append coefficients u, v, w to Y powers [1+n, Q+n] 105 | fn insert_coefficient( 106 | &mut self, 107 | var: Variable, // a, b, and c 108 | coeff: Coeff // u, v, and w for current q 109 | ) { 110 | let y_index = self.current_q + self.max_n; 111 | 112 | match var { 113 | Variable::A(index) => { 114 | // index starts from 1 115 | let mut a = self.a[index - 1]; 116 | coeff.multiply(&mut a); 117 | 118 | self.pos_coeffs[y_index - 1].add_assign(&a); 119 | }, 120 | Variable::B(index) => { 121 | let mut b = self.b[index - 1]; 122 | coeff.multiply(&mut b); 123 | 124 | self.pos_coeffs[y_index - 1].add_assign(&b); 125 | }, 126 | Variable::C(index) => { 127 | let mut c = self.c[index - 1]; 128 | coeff.multiply(&mut c); 129 | 130 | self.pos_coeffs[y_index - 1].add_assign(&c); 131 | } 132 | } 133 | } 134 | } 135 | 136 | /// Defined in Section 5: SYSTEM OF CONSTRAINTS 137 | /// Evaluation of s(X, Y) at y 138 | #[derive(Clone)] 139 | pub struct SxEval { 140 | y: E::Fr, 141 | 142 | /// Current value of y^{q+n} 143 | yqn: E::Fr, 144 | 145 | /// Coefficients of X^{-i} term 146 | /// Y^{q+n} * u_{q,1}, Y^{q+n} * u_{q,2},... , Y^{q+n} * u_{q,n} 147 | u: Vec, 148 | 149 | /// Coefficients of X^{i} term 150 | /// Y^{q+n} * v_{q,1}, Y^{q+n} * v_{q,2},... , Y^{q+n} * v_{q,n} 151 | v: Vec, 152 | 153 | /// Coefficients of X^{i+n} term 154 | /// -Y^{1}-Y^{-1} + Y^{q+n}*w_{q,1}, -Y^{2}-Y^{-2} + Y^{q+n}*w_{q,2},... , -Y^{n}-Y^{-n} + Y^{q+n}*w_{q,n} 155 | w: Vec, 156 | } 157 | 158 | impl SxEval { 159 | pub fn new(y: E::Fr, n: usize) -> Result { 160 | let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; 161 | let yqn = y.pow(&[n as u64]); 162 | 163 | // because of u_{q,i} and q is zero 164 | let u = vec![E::Fr::zero(); n]; 165 | 166 | // because of v_{q,i} and q is zero 167 | let v = vec![E::Fr::zero(); n]; 168 | 169 | let mut minus_one = E::Fr::one(); 170 | minus_one.negate(); 171 | 172 | let mut w = vec![minus_one; n]; 173 | let mut inv_w = vec![minus_one; n]; 174 | 175 | eval_bivar_poly::(&mut w[..], y, y); 176 | eval_bivar_poly::(&mut inv_w[..], y_inv, y_inv); 177 | add_polynomials::(&mut w[..], &inv_w[..]); 178 | 179 | Ok(SxEval { 180 | y, 181 | yqn, 182 | u, 183 | v, 184 | w, 185 | }) 186 | } 187 | 188 | /// Return polynomials each of negative and positive powers 189 | pub fn neg_pos_poly(mut self) -> (Vec, Vec) { 190 | self.v.extend(self.w); 191 | 192 | (self.u, self.v) 193 | } 194 | 195 | /// Evaluation of s(X, y) at x 196 | pub fn finalize(self, x: E::Fr) -> Result { 197 | let x_inv = x.inverse().ok_or(SynthesisError::DivisionByZero)?; 198 | let x_n_plus_1 = x.pow(&[(self.v.len() + 1) as u64]); 199 | let mut acc = E::Fr::zero(); 200 | 201 | acc.add_assign(&eval_univar_poly::(&self.u, x_inv, x_inv)); 202 | acc.add_assign(&eval_univar_poly::(&self.v, x, x)); 203 | acc.add_assign(&eval_univar_poly::(&self.w, x_n_plus_1, x)); 204 | 205 | Ok(acc) 206 | } 207 | } 208 | 209 | impl<'a, E: Engine> Backend for &'a mut SxEval { 210 | /// One step further of q-th linear constraint 211 | fn new_linear_constraint(&mut self) { 212 | self.yqn.mul_assign(&self.y); 213 | } 214 | 215 | /// Add coefficients u, v, and w terms. 216 | fn insert_coefficient(&mut self, var: Variable, coeff: Coeff) { 217 | let mut yqn = self.yqn; 218 | 219 | match var { 220 | Variable::A(index) => { 221 | coeff.multiply(&mut yqn); 222 | 223 | let u = &mut self.u[index - 1]; 224 | u.add_assign(&yqn); 225 | }, 226 | Variable::B(index) => { 227 | coeff.multiply(&mut yqn); 228 | 229 | let v = &mut self.v[index - 1]; 230 | v.add_assign(&yqn); 231 | }, 232 | Variable::C(index) => { 233 | coeff.multiply(&mut yqn); 234 | 235 | let w = &mut self.w[index - 1]; 236 | w.add_assign(&mut yqn); 237 | } 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/cs/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains an implementtion of sonic's constraint system. 2 | //! The form is based on (Linear-Time Zero-Knowledge Proofs for 3 | //! Arithmetic Circuit Satisfiability)[https://eprint.iacr.org/2017/872.pdf], 4 | //! but made several modifications as defined in section 5: SYSTEM OF CONSTRAINTS. 5 | 6 | use pairing::{Engine, Field}; 7 | use std::marker::PhantomData; 8 | use bellman::SynthesisError; 9 | 10 | pub mod lc; 11 | pub mod permutation; 12 | pub use lc::{Variable, Coeff, LinearCombination}; 13 | 14 | pub trait Circuit { 15 | fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError>; 16 | } 17 | 18 | /// Represents a sonic's constraint system which can have new variables 19 | /// allocated and constrains between them formed. 20 | pub trait ConstraintSystem: Sized { 21 | const ONE: Variable; 22 | 23 | /// Allocate a private variable in the constraint system. 24 | /// The provided function is used to determin the assignment of the variable. 25 | fn alloc( 26 | &mut self, 27 | f: F 28 | ) -> Result 29 | where F: FnOnce() -> Result; 30 | 31 | /// Allocate a public variable in the constraint system. 32 | /// The provided function is used to determine the assignment of the variable. 33 | fn alloc_input(&mut self, value: F) -> Result 34 | where F: FnOnce() -> Result; 35 | 36 | /// Constrain a linear combination to zero. 37 | fn enforce_zero(&mut self, lc: LinearCombination); 38 | 39 | /// Constrain each varible to multiplication gate. 40 | fn multiply(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError> 41 | where F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>; 42 | 43 | /// Get a value corresponding to the given variable 44 | fn get_value(&self, _var: Variable) -> Result { Err(()) } 45 | } 46 | 47 | /// This is a backend for the `SynthesisDriver` to replay information abount 48 | /// the concrete circuit. One backend might just collect basic information 49 | /// about the circuit for verification, while another actually constructs 50 | /// a witness. 51 | pub trait Backend { 52 | /// Get the value of a variable. Can return None if we don't know. 53 | fn get_var(&self, _variable: Variable) -> Option { None } 54 | 55 | /// Set the value of a variable. Might error if this backend expects to know it. 56 | fn set_var(&mut self, _variable: Variable, _value: F) -> Result<(), SynthesisError> 57 | where F: FnOnce() -> Result { Ok(()) } 58 | 59 | /// Create a new multiplication gate. 60 | fn new_multiplication_gate(&mut self) { } 61 | 62 | /// Create a new linear constraint. 63 | fn new_linear_constraint(&mut self) { } 64 | 65 | /// Insert a term into a linear constraint. 66 | fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff) { } 67 | 68 | /// Mark y^{_index} as the power of y cooresponding to the public input 69 | /// coeefficient for the next public input, in the k(Y) polynomial. 70 | fn new_k_power(&mut self, _index: usize) { } 71 | } 72 | 73 | /// This is an abstraction which synthesizes circuits. 74 | /// Synthesize circuits to the backend object. 75 | pub trait SynthesisDriver { 76 | fn synthesize, B: Backend> (backend: B, circuit: &C) 77 | -> Result<(), SynthesisError>; 78 | } 79 | 80 | pub struct Basic; 81 | 82 | impl SynthesisDriver for Basic { 83 | fn synthesize, B: Backend>(backend: B, circuit: &C) 84 | -> Result<(), SynthesisError> 85 | { 86 | struct Synthesizer> { 87 | backend: B, 88 | current_variable: Option, // Index of the current variable 89 | q: usize, // q-th linear constraint 90 | n: usize, // Degree of the current synthesizing step 91 | _marker: PhantomData, 92 | } 93 | 94 | impl> ConstraintSystem for Synthesizer { 95 | // Variable starts from index 1 96 | const ONE: Variable = Variable::A(1); 97 | 98 | fn alloc(&mut self, value: F) -> Result 99 | where 100 | F: FnOnce() -> Result 101 | { 102 | match self.current_variable.take() { 103 | Some(index) => { 104 | let var_a = Variable::A(index); 105 | let var_b = Variable::B(index); 106 | let var_c = Variable::C(index); 107 | 108 | let mut product = None; 109 | 110 | let value_a = self.backend.get_var(var_a); 111 | 112 | // Set the value_b from the argument to the variable B 113 | // and then calculate a product of value_a and value_b. 114 | self.backend.set_var(var_b, || { 115 | let value_b = value()?; 116 | product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?); 117 | product.as_mut().map(|product| product.mul_assign(&value_b)); 118 | 119 | Ok(value_b) 120 | })?; 121 | 122 | // Set the product to the variable C 123 | self.backend.set_var(var_c, || { 124 | product.ok_or(SynthesisError::AssignmentMissing) 125 | })?; 126 | 127 | self.current_variable = None; 128 | 129 | // Return the Variable 130 | Ok(var_b) 131 | }, 132 | None => { 133 | // One step further because there's not variable in the degree 134 | self.n += 1; 135 | let index = self.n; 136 | 137 | self.backend.new_multiplication_gate(); 138 | let var_a = Variable::A(index); 139 | 140 | self.backend.set_var(var_a, value)?; 141 | self.current_variable = Some(index); 142 | 143 | Ok(var_a) 144 | } 145 | } 146 | } 147 | 148 | fn alloc_input(&mut self, value: F) -> Result 149 | where 150 | F: FnOnce() -> Result 151 | { 152 | let input_var = self.alloc(value)?; 153 | 154 | self.enforce_zero(LinearCombination::zero() + input_var); 155 | self.backend.new_k_power(self.q); 156 | 157 | Ok(input_var) 158 | } 159 | 160 | fn enforce_zero(&mut self, lc: LinearCombination) { 161 | self.q += 1; 162 | self.backend.new_linear_constraint(); 163 | 164 | for (var, coeff) in lc.as_ref() { 165 | self.backend.insert_coefficient(*var, *coeff); 166 | } 167 | } 168 | 169 | fn multiply(&mut self, values: F) 170 | -> Result<(Variable, Variable, Variable), SynthesisError> 171 | where 172 | F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError> 173 | { 174 | self.n += 1; 175 | let index = self.n; 176 | self.backend.new_multiplication_gate(); 177 | 178 | let var_a = Variable::A(index); 179 | let var_b = Variable::B(index); 180 | let var_c = Variable::C(index); 181 | 182 | let mut value_b = None; 183 | let mut value_c = None; 184 | 185 | self.backend.set_var(var_a, || { 186 | let (a, b, c) = values()?; 187 | 188 | value_b = Some(b); 189 | value_c = Some(c); 190 | 191 | Ok(a) 192 | })?; 193 | 194 | self.backend.set_var(var_b, || { 195 | value_b.ok_or(SynthesisError::AssignmentMissing) 196 | })?; 197 | 198 | self.backend.set_var(var_c, || { 199 | value_c.ok_or(SynthesisError::AssignmentMissing) 200 | })?; 201 | 202 | Ok((var_a, var_b, var_c)) 203 | } 204 | 205 | fn get_value(&self, var: Variable) -> Result { 206 | self.backend.get_var(var).ok_or(()) 207 | } 208 | } 209 | 210 | let mut instance: Synthesizer = Synthesizer { 211 | backend: backend, 212 | current_variable: None, 213 | q: 0, 214 | n: 0, 215 | _marker: PhantomData, 216 | }; 217 | 218 | let one_var = instance.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues."); 219 | 220 | match (one_var, as ConstraintSystem>::ONE) { 221 | (Variable::A(1), Variable::A(1)) => {}, 222 | _ => panic!("one variable is incorrect.") 223 | } 224 | 225 | circuit.synthesize(&mut instance)?; 226 | 227 | Ok(()) 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /benches/sonic_benchmark.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | use criterion::Criterion; 4 | use pairing::{Engine, Field, PrimeField, CurveAffine, CurveProjective}; 5 | use pairing::bls12_381::{Bls12, Fr}; 6 | use bellman::{Circuit, ConstraintSystem, SynthesisError}; 7 | // use sonic::cs::{Circuit, ConstraintSystem}; 8 | use rand::{thread_rng, Rng}; 9 | use sonic::srs::SRS; 10 | use sonic::cs::Basic; 11 | use sonic::helped::adaptor::AdaptorCircuit; 12 | use sonic::helped::{Proof, MultiVerifier}; 13 | 14 | pub const MIMC_ROUNDS: usize = 322; 15 | 16 | /// This is an implementation of MiMC, specifically a 17 | /// variant named `LongsightF322p3` for BLS12-381. 18 | /// See http://eprint.iacr.org/2016/492 for more 19 | /// information about this construction. 20 | /// 21 | /// ``` 22 | /// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) { 23 | /// for i from 0 up to 321 { 24 | /// xL, xR := xR + (xL + Ci)^3, xL 25 | /// } 26 | /// return xL 27 | /// } 28 | /// ``` 29 | pub fn mimc( 30 | mut xl: E::Fr, 31 | mut xr: E::Fr, 32 | constants: &[E::Fr] 33 | ) -> E::Fr 34 | { 35 | assert_eq!(constants.len(), MIMC_ROUNDS); 36 | 37 | for i in 0..MIMC_ROUNDS { 38 | let mut tmp1 = xl; 39 | tmp1.add_assign(&constants[i]); 40 | let mut tmp2 = tmp1; 41 | tmp2.square(); 42 | tmp2.mul_assign(&tmp1); 43 | tmp2.add_assign(&xr); 44 | xr = xl; 45 | xl = tmp2; 46 | } 47 | 48 | xl 49 | } 50 | 51 | /// This is our demo circuit for proving knowledge of the 52 | /// preimage of a MiMC hash invocation. 53 | pub struct MiMCDemo<'a, E: Engine> { 54 | xl: Option, 55 | xr: Option, 56 | constants: &'a [E::Fr] 57 | } 58 | 59 | /// Our demo circuit implements this `Circuit` trait which 60 | /// is used during paramgen and proving in order to 61 | /// synthesize the constraint system. 62 | impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { 63 | fn synthesize>( 64 | self, 65 | cs: &mut CS 66 | ) -> Result<(), SynthesisError> 67 | { 68 | assert_eq!(self.constants.len(), MIMC_ROUNDS); 69 | 70 | // Allocate the first component of the preimage. 71 | let mut xl_value = self.xl; 72 | let mut xl = cs.alloc(|| "preimage xl", || { 73 | xl_value.ok_or(SynthesisError::AssignmentMissing) 74 | })?; 75 | 76 | // Allocate the second component of the preimage. 77 | let mut xr_value = self.xr; 78 | let mut xr = cs.alloc(|| "preimage xr", || { 79 | xr_value.ok_or(SynthesisError::AssignmentMissing) 80 | })?; 81 | 82 | for i in 0..MIMC_ROUNDS { 83 | // xL, xR := xR + (xL + Ci)^3, xL 84 | let cs = &mut cs.namespace(|| format!("round {}", i)); 85 | 86 | // tmp = (xL + Ci)^2 87 | let mut tmp_value = xl_value.map(|mut e| { 88 | e.add_assign(&self.constants[i]); 89 | e.square(); 90 | e 91 | }); 92 | let mut tmp = cs.alloc(|| "tmp", || { 93 | tmp_value.ok_or(SynthesisError::AssignmentMissing) 94 | })?; 95 | 96 | cs.enforce( 97 | || "tmp = (xL + Ci)^2", 98 | |lc| lc + xl + (self.constants[i], CS::one()), 99 | |lc| lc + xl + (self.constants[i], CS::one()), 100 | |lc| lc + tmp 101 | ); 102 | 103 | // new_xL = xR + (xL + Ci)^3 104 | // new_xL = xR + tmp * (xL + Ci) 105 | // new_xL - xR = tmp * (xL + Ci) 106 | let mut new_xl_value = xl_value.map(|mut e| { 107 | e.add_assign(&self.constants[i]); 108 | e.mul_assign(&tmp_value.unwrap()); 109 | e.add_assign(&xr_value.unwrap()); 110 | e 111 | }); 112 | 113 | let mut new_xl = if i == (MIMC_ROUNDS-1) { 114 | // This is the last round, xL is our image and so 115 | // we allocate a public input. 116 | cs.alloc_input(|| "image", || { 117 | new_xl_value.ok_or(SynthesisError::AssignmentMissing) 118 | })? 119 | } else { 120 | cs.alloc(|| "new_xl", || { 121 | new_xl_value.ok_or(SynthesisError::AssignmentMissing) 122 | })? 123 | }; 124 | 125 | cs.enforce( 126 | || "new_xL = xR + (xL + Ci)^3", 127 | |lc| lc + tmp, 128 | |lc| lc + xl + (self.constants[i], CS::one()), 129 | |lc| lc + new_xl - xr 130 | ); 131 | 132 | // xR = xL 133 | xr = xl; 134 | xr_value = xl_value; 135 | 136 | // xL = new_xL 137 | xl = new_xl; 138 | xl_value = new_xl_value; 139 | } 140 | 141 | Ok(()) 142 | } 143 | } 144 | 145 | /// This is our demo circuit for proving knowledge of the 146 | /// preimage of a MiMC hash invocation. 147 | #[derive(Clone)] 148 | struct MiMCDemoNoInputs<'a, E: Engine> { 149 | xl: Option, 150 | xr: Option, 151 | image: Option, 152 | constants: &'a [E::Fr] 153 | } 154 | 155 | /// Our demo circuit implements this `Circuit` trait which 156 | /// is used during paramgen and proving in order to 157 | /// synthesize the constraint system. 158 | impl<'a, E: Engine> Circuit for MiMCDemoNoInputs<'a, E> { 159 | fn synthesize>( 160 | self, 161 | cs: &mut CS 162 | ) -> Result<(), SynthesisError> 163 | { 164 | assert_eq!(self.constants.len(), MIMC_ROUNDS); 165 | 166 | // Allocate the first component of the preimage. 167 | let mut xl_value = self.xl; 168 | let mut xl = cs.alloc(|| "preimage xl", || { 169 | xl_value.ok_or(SynthesisError::AssignmentMissing) 170 | })?; 171 | 172 | // Allocate the second component of the preimage. 173 | let mut xr_value = self.xr; 174 | let mut xr = cs.alloc(|| "preimage xr", || { 175 | xr_value.ok_or(SynthesisError::AssignmentMissing) 176 | })?; 177 | 178 | for i in 0..MIMC_ROUNDS { 179 | // xL, xR := xR + (xL + Ci)^3, xL 180 | let cs = &mut cs.namespace(|| format!("round {}", i)); 181 | 182 | // tmp = (xL + Ci)^2 183 | let tmp_value = xl_value.map(|mut e| { 184 | e.add_assign(&self.constants[i]); 185 | e.square(); 186 | e 187 | }); 188 | let tmp = cs.alloc(|| "tmp", || { 189 | tmp_value.ok_or(SynthesisError::AssignmentMissing) 190 | })?; 191 | 192 | cs.enforce( 193 | || "tmp = (xL + Ci)^2", 194 | |lc| lc + xl + (self.constants[i], CS::one()), 195 | |lc| lc + xl + (self.constants[i], CS::one()), 196 | |lc| lc + tmp 197 | ); 198 | 199 | // new_xL = xR + (xL + Ci)^3 200 | // new_xL = xR + tmp * (xL + Ci) 201 | // new_xL - xR = tmp * (xL + Ci) 202 | let new_xl_value = xl_value.map(|mut e| { 203 | e.add_assign(&self.constants[i]); 204 | e.mul_assign(&tmp_value.unwrap()); 205 | e.add_assign(&xr_value.unwrap()); 206 | e 207 | }); 208 | 209 | let new_xl = if i == (MIMC_ROUNDS-1) { 210 | // This is the last round, xL is our image and so 211 | // we use the image 212 | let image_value = self.image; 213 | cs.alloc(|| "image", || { 214 | image_value.ok_or(SynthesisError::AssignmentMissing) 215 | })? 216 | } else { 217 | cs.alloc(|| "new_xl", || { 218 | new_xl_value.ok_or(SynthesisError::AssignmentMissing) 219 | })? 220 | }; 221 | 222 | cs.enforce( 223 | || "new_xL = xR + (xL + Ci)^3", 224 | |lc| lc + tmp, 225 | |lc| lc + xl + (self.constants[i], CS::one()), 226 | |lc| lc + new_xl - xr 227 | ); 228 | 229 | // xR = xL 230 | xr = xl; 231 | xr_value = xl_value; 232 | 233 | // xL = new_xL 234 | xl = new_xl; 235 | xl_value = new_xl_value; 236 | } 237 | 238 | Ok(()) 239 | } 240 | } 241 | 242 | fn mimc_1_prove_wo_advice(c: &mut Criterion) { 243 | let srs_x = Fr::from_str("23923").unwrap(); 244 | let srs_alpha = Fr::from_str("23728792").unwrap(); 245 | 246 | let srs = SRS::::dummy(830564, srs_x, srs_alpha); 247 | 248 | c.bench_function("create proof", move |b| { 249 | // This may not be cryptographically safe, use 250 | // `OsRng` (for example) in production software. 251 | let rng = &mut thread_rng(); 252 | 253 | // Generate the MiMC round constants 254 | let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); 255 | 256 | let xl = rng.gen(); 257 | let xr = rng.gen(); 258 | let image = mimc::(xl, xr, &constants); 259 | 260 | // Create an instance of our circuit (with the 261 | // witness) 262 | let circuit = MiMCDemoNoInputs { 263 | xl: Some(xl), 264 | xr: Some(xr), 265 | image: Some(image), 266 | constants: &constants 267 | }; 268 | 269 | b.iter(|| { 270 | Proof::::create_proof::< _, Basic>(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); 271 | }) 272 | }); 273 | } 274 | 275 | criterion_group! { 276 | name = sonic_prove; 277 | config = Criterion::default().sample_size(10); 278 | targets = 279 | mimc_1_prove_wo_advice, 280 | } 281 | 282 | fn mimc_1_verify_wo_advice(c: &mut Criterion) { 283 | let srs_x = Fr::from_str("23923").unwrap(); 284 | let srs_alpha = Fr::from_str("23728792").unwrap(); 285 | 286 | let srs = SRS::::dummy(830564, srs_x, srs_alpha); 287 | 288 | c.bench_function("Verify proof", move |b| { 289 | // This may not be cryptographically safe, use 290 | // `OsRng` (for example) in production software. 291 | let rng = &mut thread_rng(); 292 | 293 | // Generate the MiMC round constants 294 | let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); 295 | 296 | let xl = rng.gen(); 297 | let xr = rng.gen(); 298 | let image = mimc::(xl, xr, &constants); 299 | 300 | // Create an instance of our circuit (with the 301 | // witness) 302 | let circuit = MiMCDemoNoInputs { 303 | xl: Some(xl), 304 | xr: Some(xr), 305 | image: Some(image), 306 | constants: &constants 307 | }; 308 | 309 | let proof = Proof::::create_proof::< _, Basic>(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); 310 | 311 | b.iter(|| { 312 | let rng = thread_rng(); 313 | let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); 314 | 315 | for _ in 0..1 { 316 | verifier.add_proof(&proof, &[], |_, _| None); 317 | } 318 | assert_eq!(verifier.check_all(), true); 319 | }) 320 | }); 321 | } 322 | 323 | criterion_group! { 324 | name = sonic_verify; 325 | config = Criterion::default().sample_size(10); 326 | targets = 327 | mimc_1_verify_wo_advice, 328 | } 329 | 330 | criterion_main!(sonic_prove, sonic_verify); 331 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/polynomials/mod.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Field, Engine, CurveAffine, CurveProjective, PrimeField}; 2 | use bellman::multicore::Worker; 3 | use bellman::domain::{EvaluationDomain, Scalar}; 4 | use crossbeam::channel::unbounded; 5 | pub mod commitment; 6 | pub mod s_eval; 7 | pub mod operations; 8 | 9 | pub use operations::*; 10 | pub use commitment::*; 11 | pub use s_eval::*; 12 | use crate::srs::SRS; 13 | use crate::utils::ChainExt; 14 | use crate::traits::*; 15 | use std::borrow::Borrow; 16 | use std::ops::{Add, Mul, Index, IndexMut, Range}; 17 | 18 | pub struct Polynomial(Vec); 19 | 20 | impl PolyEngine for Polynomial { 21 | type Commitment = PolyComm; 22 | type Opening = PolyCommOpening; 23 | type Pairing = E; 24 | } 25 | 26 | impl IntoIterator for Polynomial { 27 | type Item = as IntoIterator>::Item; 28 | type IntoIter = as IntoIterator>::IntoIter; 29 | 30 | fn into_iter(self) -> Self::IntoIter { 31 | self.0.into_iter() 32 | } 33 | } 34 | 35 | impl Index for Polynomial { 36 | type Output = E::Fr; 37 | 38 | fn index(&self, id: usize) -> &Self::Output { 39 | &self.0[id] 40 | } 41 | } 42 | 43 | impl IndexMut for Polynomial { 44 | fn index_mut(&mut self, id: usize) -> &mut Self::Output { 45 | &mut self.0[id] 46 | } 47 | } 48 | 49 | impl Add> for Polynomial { 50 | type Output = Polynomial; 51 | 52 | fn add(mut self, other: Polynomial) -> Polynomial { 53 | assert_eq!(self.0.len(), other.0.len()); 54 | 55 | let worker = Worker::new(); 56 | 57 | worker.scope(self.0.len(), |scope, chunk| { 58 | for (a, b) in self.0.chunks_mut(chunk).zip(other.0.chunks(chunk)) { 59 | scope.spawn(move |_| { 60 | for (a, b) in a.iter_mut().zip(b.iter()) { 61 | a.add_assign(b); 62 | } 63 | }); 64 | } 65 | }); 66 | 67 | self 68 | } 69 | } 70 | 71 | impl Mul> for Polynomial { 72 | type Output = Vec; // TODO 73 | 74 | fn mul(self, other: Polynomial) -> Vec { 75 | let res_len = self.0.len() + other.0.len() - 1; 76 | 77 | let worker = Worker::new(); 78 | 79 | let scalars_a = self.0.iter().map(|e| Scalar::(*e)).collect(); 80 | // the size of evaluation domain is polynomial's multiplied by other. 81 | let mut domain_a = EvaluationDomain::from_coeffs_into_sized(scalars_a, res_len) 82 | .expect("The degree of polynomial should be under the rational size"); 83 | 84 | let scalars_b = other.0.iter().map(|e| Scalar::(*e)).collect(); 85 | let mut domain_b = EvaluationDomain::from_coeffs_into_sized(scalars_b, res_len) 86 | .expect("The degree of polynomial should be under the rational size"); 87 | 88 | // Convert to point-value representations 89 | domain_a.fft(&worker); 90 | domain_b.fft(&worker); 91 | 92 | // Perform O(n) multiplication of two polynomials in the domain. 93 | domain_a.mul_assign(&worker, &domain_b); 94 | drop(domain_b); 95 | 96 | // Convert back to point-value representations 97 | domain_a.ifft(&worker); 98 | 99 | let mut mul_res: Vec = domain_a.into_coeffs().iter().map(|e| e.0).collect(); 100 | mul_res.truncate(res_len); 101 | 102 | mul_res 103 | } 104 | } 105 | 106 | impl Polynomial { 107 | pub fn from_slice(s: &mut [E::Fr]) -> Self { 108 | Polynomial(s.to_vec()) 109 | } 110 | 111 | /// Commit a polynomial `F`. 112 | /// F \from g^{\alpha * x^{(d - max)}*f(x)} 113 | /// See: Section 5 SYSTEM OF CONSTRAINTS 114 | pub fn commit>( 115 | &self, 116 | max: usize, // a maximum degree 117 | largest_neg_power: usize, // largest negative power 118 | largest_pos_power: usize, // largest positive power 119 | srs: &SRS, 120 | ) -> PE::Commitment 121 | { 122 | let d = srs.d; 123 | assert!(max >= largest_pos_power); 124 | 125 | // smallest power is `|(srs.d - max) - largest_neg_power|`. (See Figure.3) 126 | // If the smallest power is negative, use both positive and negative powers for commitment, 127 | // otherwise use only positive powers. 128 | if d < max + largest_neg_power + 1 { 129 | let min_power = largest_neg_power + max - d; 130 | let max_power = largest_pos_power + d - max; 131 | 132 | let point = multiexp( 133 | srs.g_neg_x_alpha[0..min_power].iter().rev() // Reverse to permute for negative powers 134 | .chain_ext(srs.g_pos_x_alpha[..max_power].iter()), 135 | self.0.iter() 136 | ).into_affine(); 137 | 138 | PE::Commitment::from_point(&point) 139 | } else { 140 | let _max_power = srs.d - max - largest_neg_power + 1; 141 | 142 | let point = multiexp( 143 | // srs.g_pos_x_alpha[..max_power].iter(), // TODO: Ensure the range is correct 144 | srs.g_pos_x_alpha[(srs.d - max - largest_neg_power - 1)..].iter(), 145 | self.0.iter() 146 | ).into_affine(); 147 | 148 | PE::Commitment::from_point(&point) 149 | } 150 | } 151 | 152 | /// Opening a polynomial commitment 153 | pub fn open( 154 | &self, 155 | largest_neg_power: usize, 156 | largest_pos_power: usize, 157 | srs: &SRS, 158 | mut point: E::Fr, 159 | ) -> E::G1Affine 160 | { 161 | // let quotient_poly = self.kate_division(point); 162 | 163 | // kate division 164 | point.negate(); 165 | let a_poly = self.0.iter(); 166 | 167 | let mut quotient_poly = vec![E::Fr::zero(); a_poly.len() - 1]; 168 | 169 | let mut tmp = E::Fr::zero(); 170 | for (q, r) in quotient_poly.iter_mut().rev().zip(a_poly.rev()) { 171 | let mut lead_coeff = *r; 172 | lead_coeff.sub_assign(&tmp); 173 | *q = lead_coeff; 174 | tmp = lead_coeff; 175 | tmp.mul_assign(&point) 176 | } 177 | 178 | let neg_poly = quotient_poly[..largest_neg_power].iter().rev(); // -n,...,-1 179 | let pos_poly = quotient_poly[largest_pos_power..].iter(); // n,...,1,0 180 | 181 | multiexp( 182 | srs.g_neg_x[1..(neg_poly.len() + 1)].iter().chain_ext( 183 | srs.g_pos_x[..pos_poly.len()].iter() 184 | ), 185 | neg_poly.chain_ext(pos_poly) 186 | ).into_affine() 187 | } 188 | 189 | // TODO: Parallelization 190 | // Divides polynomial `a` in `x` by `x-b` with no remainder. 191 | // pub fn kate_division(self, mut b: E::Fr) -> Self 192 | // { 193 | // b.negate(); 194 | // let a_poly = self.0.into_iter(); 195 | 196 | // let mut quotient_poly = vec![E::Fr::zero(); a_poly.len() - 1]; 197 | 198 | // let mut tmp = E::Fr::zero(); 199 | // for (q, r) in quotient_poly.iter_mut().rev().zip(a_poly.rev()) { 200 | // let mut lead_coeff = *r; 201 | // lead_coeff.sub_assign(&tmp); 202 | // *q = lead_coeff; 203 | // tmp = lead_coeff; 204 | // tmp.mul_assign(&b) 205 | // } 206 | 207 | // Polynomial("ient_poly[..]) 208 | // } 209 | 210 | /// Multiply each coefficient by some power of the base in a form 211 | /// `first_power * base^{i}` 212 | /// This would be sparse, consecutive multiplication based on non-zero coefficients. 213 | /// Basically, it is for the evaluation of one of the variables of bivariate polynomials. 214 | /// For example, r(X, Y) at y. 215 | pub fn eval_bivar_poly( 216 | &mut self, 217 | first_power: E::Fr, 218 | base: E::Fr 219 | ) { 220 | let worker = Worker::new(); 221 | 222 | worker.scope(self.0.len(), |scope, chunk| { 223 | for (i, coeffs_chunk) in self.0.chunks_mut(chunk).enumerate() { 224 | scope.spawn(move |_| { 225 | let mut current_power = base.pow(&[(i * chunk) as u64]); 226 | current_power.mul_assign(&first_power); 227 | 228 | for mut p in coeffs_chunk { 229 | p.mul_assign(¤t_power); 230 | 231 | current_power.mul_assign(&base); 232 | } 233 | }); 234 | } 235 | }); 236 | } 237 | 238 | /// It is for the evaluation of univariate polynomials. For example, r(X, y) at z. 239 | pub fn eval_univar_poly( 240 | &self, 241 | first_power: E::Fr, 242 | base: E::Fr 243 | ) -> E::Fr 244 | { 245 | let (tx, rx) = unbounded(); 246 | let worker = Worker::new(); 247 | 248 | worker.scope(self.0.len(), |scope, chunk| { 249 | for (i, coeffs_chunk) in self.0.chunks(chunk).enumerate() { 250 | let tx = tx.clone(); 251 | 252 | scope.spawn(move |_| { 253 | let mut current_power = base.pow(&[(i * chunk) as u64]); 254 | current_power.mul_assign(&first_power); 255 | 256 | let mut acc = E::Fr::zero(); 257 | 258 | for p in coeffs_chunk { 259 | let mut tmp = *p; 260 | tmp.mul_assign(¤t_power); 261 | acc.add_assign(&tmp); 262 | 263 | current_power.mul_assign(&base); 264 | } 265 | 266 | tx.send(acc).expect("must send"); 267 | }); 268 | } 269 | }); 270 | 271 | // The sender is dropped, disconnect the channel. 272 | drop(tx); 273 | 274 | let mut res = E::Fr::zero(); 275 | 276 | loop { 277 | if rx.is_empty() { 278 | break; 279 | } 280 | 281 | let val = rx.recv().expect("must not be empty"); 282 | res.add_assign(&val); 283 | } 284 | 285 | res 286 | } 287 | } 288 | 289 | #[derive(Clone)] 290 | pub struct PolyComm(pub E::G1Affine); 291 | 292 | impl Commitment for PolyComm { 293 | type Point = E::G1Affine; 294 | 295 | fn from_point(point: &Self::Point) -> Self { 296 | PolyComm(*point) 297 | } 298 | 299 | fn into_point(&self) -> Self::Point { 300 | self.0 301 | } 302 | 303 | fn into_bytes(&self) -> Vec { // TODO 304 | self.0.into_compressed().as_ref().to_vec() 305 | } 306 | } 307 | 308 | impl Copy for PolyComm {} 309 | 310 | pub struct PolyCommOpening(E::G1Affine); 311 | 312 | impl Opening for PolyCommOpening {} 313 | 314 | pub fn multiexp< 315 | 'a, 316 | G: CurveAffine, 317 | IE: IntoIterator, 318 | IS: IntoIterator, 319 | >( 320 | exponent: IE, 321 | scalar: IS, 322 | ) -> G::Projective 323 | where 324 | IE::IntoIter: ExactSizeIterator + Clone, 325 | IS::IntoIter: ExactSizeIterator, 326 | { 327 | use bellman::multiexp::{multiexp, FullDensity}; 328 | use std::sync::Arc; 329 | use futures::Future; 330 | 331 | let scalar: Vec<::Repr> = scalar 332 | .into_iter() 333 | .map(|e| e.into_repr()) 334 | .collect::>(); 335 | 336 | let exponent: Vec = exponent 337 | .into_iter() 338 | .map(|e| *e) 339 | .collect::>(); 340 | 341 | assert_eq!( 342 | scalar.len(), 343 | exponent.len(), 344 | "scalars and exponents must have the same length." 345 | ); 346 | 347 | let pool = Worker::new(); 348 | 349 | let result = multiexp( 350 | &pool, 351 | (Arc::new(exponent), 0), 352 | FullDensity, 353 | Arc::new(scalar) 354 | ).wait().unwrap(); 355 | 356 | result 357 | } 358 | 359 | pub fn multiexp_mut< 360 | 'a, 361 | G: CurveAffine, 362 | IE: IntoIterator, 363 | IS: IntoIterator, 364 | >( 365 | exponent: IE, 366 | scalar: IS, 367 | ) -> G::Projective 368 | where 369 | IE::IntoIter: ExactSizeIterator + Clone, 370 | IS::IntoIter: ExactSizeIterator, 371 | { 372 | use bellman::multiexp::{multiexp, FullDensity}; 373 | use std::sync::Arc; 374 | use futures::Future; 375 | 376 | let scalar: Vec<::Repr> = scalar 377 | .into_iter() 378 | .map(|e| e.into_repr()) 379 | .collect::>(); 380 | 381 | let exponent: Vec = exponent 382 | .into_iter() 383 | .map(|e| *e) 384 | .collect::>(); 385 | 386 | assert_eq!( 387 | scalar.len(), 388 | exponent.len(), 389 | "scalars and exponents must have the same length." 390 | ); 391 | 392 | let pool = Worker::new(); 393 | 394 | let result = multiexp( 395 | &pool, 396 | (Arc::new(exponent), 0), 397 | FullDensity, 398 | Arc::new(scalar) 399 | ).wait().unwrap(); 400 | 401 | result 402 | } 403 | -------------------------------------------------------------------------------- /tests/sonic_test.rs: -------------------------------------------------------------------------------- 1 | 2 | use pairing::{Engine, Field, PrimeField, CurveAffine, CurveProjective}; 3 | use pairing::bls12_381::{Bls12, Fr}; 4 | use bellman::{Circuit, ConstraintSystem, SynthesisError}; 5 | // use sonic::cs::{Circuit, ConstraintSystem}; 6 | use rand::{thread_rng, Rng}; 7 | // For benchmarking 8 | use std::time::{Duration, Instant}; 9 | use sonic::srs::SRS; 10 | 11 | use sonic::cs::Basic; 12 | use sonic::helped::adaptor::AdaptorCircuit; 13 | use sonic::helped::{Proof, MultiVerifier}; 14 | use sonic::polynomials::Polynomial; 15 | 16 | pub const MIMC_ROUNDS: usize = 32200; 17 | 18 | /// This is an implementation of MiMC, specifically a 19 | /// variant named `LongsightF322p3` for BLS12-381. 20 | /// See http://eprint.iacr.org/2016/492 for more 21 | /// information about this construction. 22 | /// 23 | /// ``` 24 | /// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) { 25 | /// for i from 0 up to 321 { 26 | /// xL, xR := xR + (xL + Ci)^3, xL 27 | /// } 28 | /// return xL 29 | /// } 30 | /// ``` 31 | pub fn mimc( 32 | mut xl: E::Fr, 33 | mut xr: E::Fr, 34 | constants: &[E::Fr] 35 | ) -> E::Fr 36 | { 37 | assert_eq!(constants.len(), MIMC_ROUNDS); 38 | 39 | for i in 0..MIMC_ROUNDS { 40 | let mut tmp1 = xl; 41 | tmp1.add_assign(&constants[i]); 42 | let mut tmp2 = tmp1; 43 | tmp2.square(); 44 | tmp2.mul_assign(&tmp1); 45 | tmp2.add_assign(&xr); 46 | xr = xl; 47 | xl = tmp2; 48 | } 49 | 50 | xl 51 | } 52 | 53 | /// This is our demo circuit for proving knowledge of the 54 | /// preimage of a MiMC hash invocation. 55 | #[derive(Clone)] 56 | pub struct MiMCDemo<'a, E: Engine> { 57 | xl: Option, 58 | xr: Option, 59 | constants: &'a [E::Fr] 60 | } 61 | 62 | /// Our demo circuit implements this `Circuit` trait which 63 | /// is used during paramgen and proving in order to 64 | /// synthesize the constraint system. 65 | impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { 66 | fn synthesize>( 67 | self, 68 | cs: &mut CS 69 | ) -> Result<(), SynthesisError> 70 | { 71 | assert_eq!(self.constants.len(), MIMC_ROUNDS); 72 | 73 | // Allocate the first component of the preimage. 74 | let mut xl_value = self.xl; 75 | let mut xl = cs.alloc(|| "preimage xl", || { 76 | xl_value.ok_or(SynthesisError::AssignmentMissing) 77 | })?; 78 | 79 | // Allocate the second component of the preimage. 80 | let mut xr_value = self.xr; 81 | let mut xr = cs.alloc(|| "preimage xr", || { 82 | xr_value.ok_or(SynthesisError::AssignmentMissing) 83 | })?; 84 | 85 | for i in 0..MIMC_ROUNDS { 86 | // xL, xR := xR + (xL + Ci)^3, xL 87 | let cs = &mut cs.namespace(|| format!("round {}", i)); 88 | 89 | // tmp = (xL + Ci)^2 90 | let mut tmp_value = xl_value.map(|mut e| { 91 | e.add_assign(&self.constants[i]); 92 | e.square(); 93 | e 94 | }); 95 | let mut tmp = cs.alloc(|| "tmp", || { 96 | tmp_value.ok_or(SynthesisError::AssignmentMissing) 97 | })?; 98 | 99 | cs.enforce( 100 | || "tmp = (xL + Ci)^2", 101 | |lc| lc + xl + (self.constants[i], CS::one()), 102 | |lc| lc + xl + (self.constants[i], CS::one()), 103 | |lc| lc + tmp 104 | ); 105 | 106 | // new_xL = xR + (xL + Ci)^3 107 | // new_xL = xR + tmp * (xL + Ci) 108 | // new_xL - xR = tmp * (xL + Ci) 109 | let mut new_xl_value = xl_value.map(|mut e| { 110 | e.add_assign(&self.constants[i]); 111 | e.mul_assign(&tmp_value.unwrap()); 112 | e.add_assign(&xr_value.unwrap()); 113 | e 114 | }); 115 | 116 | let mut new_xl = if i == (MIMC_ROUNDS-1) { 117 | // This is the last round, xL is our image and so 118 | // we allocate a public input. 119 | cs.alloc_input(|| "image", || { 120 | new_xl_value.ok_or(SynthesisError::AssignmentMissing) 121 | })? 122 | } else { 123 | cs.alloc(|| "new_xl", || { 124 | new_xl_value.ok_or(SynthesisError::AssignmentMissing) 125 | })? 126 | }; 127 | 128 | cs.enforce( 129 | || "new_xL = xR + (xL + Ci)^3", 130 | |lc| lc + tmp, 131 | |lc| lc + xl + (self.constants[i], CS::one()), 132 | |lc| lc + new_xl - xr 133 | ); 134 | 135 | // xR = xL 136 | xr = xl; 137 | xr_value = xl_value; 138 | 139 | // xL = new_xL 140 | xl = new_xl; 141 | xl_value = new_xl_value; 142 | } 143 | 144 | Ok(()) 145 | } 146 | } 147 | 148 | /// This is our demo circuit for proving knowledge of the 149 | /// preimage of a MiMC hash invocation. 150 | #[derive(Clone)] 151 | struct MiMCDemoNoInputs<'a, E: Engine> { 152 | xl: Option, 153 | xr: Option, 154 | image: Option, 155 | constants: &'a [E::Fr] 156 | } 157 | 158 | /// Our demo circuit implements this `Circuit` trait which 159 | /// is used during paramgen and proving in order to 160 | /// synthesize the constraint system. 161 | impl<'a, E: Engine> Circuit for MiMCDemoNoInputs<'a, E> { 162 | fn synthesize>( 163 | self, 164 | cs: &mut CS 165 | ) -> Result<(), SynthesisError> 166 | { 167 | assert_eq!(self.constants.len(), MIMC_ROUNDS); 168 | 169 | // Allocate the first component of the preimage. 170 | let mut xl_value = self.xl; 171 | let mut xl = cs.alloc(|| "preimage xl", || { 172 | xl_value.ok_or(SynthesisError::AssignmentMissing) 173 | })?; 174 | 175 | // Allocate the second component of the preimage. 176 | let mut xr_value = self.xr; 177 | let mut xr = cs.alloc(|| "preimage xr", || { 178 | xr_value.ok_or(SynthesisError::AssignmentMissing) 179 | })?; 180 | 181 | for i in 0..MIMC_ROUNDS { 182 | // xL, xR := xR + (xL + Ci)^3, xL 183 | let cs = &mut cs.namespace(|| format!("round {}", i)); 184 | 185 | // tmp = (xL + Ci)^2 186 | let tmp_value = xl_value.map(|mut e| { 187 | e.add_assign(&self.constants[i]); 188 | e.square(); 189 | e 190 | }); 191 | let tmp = cs.alloc(|| "tmp", || { 192 | tmp_value.ok_or(SynthesisError::AssignmentMissing) 193 | })?; 194 | 195 | cs.enforce( 196 | || "tmp = (xL + Ci)^2", 197 | |lc| lc + xl + (self.constants[i], CS::one()), 198 | |lc| lc + xl + (self.constants[i], CS::one()), 199 | |lc| lc + tmp 200 | ); 201 | 202 | // new_xL = xR + (xL + Ci)^3 203 | // new_xL = xR + tmp * (xL + Ci) 204 | // new_xL - xR = tmp * (xL + Ci) 205 | let new_xl_value = xl_value.map(|mut e| { 206 | e.add_assign(&self.constants[i]); 207 | e.mul_assign(&tmp_value.unwrap()); 208 | e.add_assign(&xr_value.unwrap()); 209 | e 210 | }); 211 | 212 | let new_xl = if i == (MIMC_ROUNDS-1) { 213 | // This is the last round, xL is our image and so 214 | // we use the image 215 | let image_value = self.image; 216 | cs.alloc(|| "image", || { 217 | image_value.ok_or(SynthesisError::AssignmentMissing) 218 | })? 219 | } else { 220 | cs.alloc(|| "new_xl", || { 221 | new_xl_value.ok_or(SynthesisError::AssignmentMissing) 222 | })? 223 | }; 224 | 225 | cs.enforce( 226 | || "new_xL = xR + (xL + Ci)^3", 227 | |lc| lc + tmp, 228 | |lc| lc + xl + (self.constants[i], CS::one()), 229 | |lc| lc + new_xl - xr 230 | ); 231 | 232 | // xR = xL 233 | xr = xl; 234 | xr_value = xl_value; 235 | 236 | // xL = new_xL 237 | xl = new_xl; 238 | xl_value = new_xl_value; 239 | } 240 | 241 | Ok(()) 242 | } 243 | } 244 | 245 | #[test] 246 | fn test_sonic_mimc_wo_inputs() { 247 | let srs_x = Fr::from_str("23923").unwrap(); 248 | let srs_alpha = Fr::from_str("23728792").unwrap(); 249 | 250 | // let start = Instant::now(); 251 | let srs = SRS::::dummy(830564, srs_x, srs_alpha); 252 | // println!("Done in {:?}", start.elapsed()); 253 | 254 | { 255 | // This may not be cryptographically safe, use 256 | // `OsRng` (for example) in production software. 257 | let rng = &mut thread_rng(); 258 | 259 | // Generate the MiMC round constants 260 | let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); 261 | let samples: usize = 100; 262 | 263 | let xl = rng.gen(); 264 | let xr = rng.gen(); 265 | let image = mimc::(xl, xr, &constants); 266 | 267 | // Create an instance of our circuit (with the 268 | // witness) 269 | let circuit = MiMCDemoNoInputs { 270 | xl: Some(xl), 271 | xr: Some(xr), 272 | image: Some(image), 273 | constants: &constants 274 | }; 275 | 276 | // println!("Creating proof"); 277 | let start = Instant::now(); 278 | let proof = Proof::>::create_proof::< _, Basic>(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); 279 | println!("(Proving SONIC) Done in {:?}", start.elapsed()); 280 | 281 | // println!("creating advice"); 282 | // let start = Instant::now(); 283 | // let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); 284 | // println!("done in {:?}", start.elapsed()); 285 | 286 | // println!("creating aggregate for {} proofs", samples); 287 | // let start = Instant::now(); 288 | // let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); 289 | // let aggregate = create_aggregate_on_srs::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); 290 | // println!("done in {:?}", start.elapsed()); 291 | 292 | let rng = thread_rng(); 293 | let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); 294 | // println!("Verifying 1 proof without advice"); 295 | let start = Instant::now(); 296 | { 297 | for _ in 0..1 { 298 | verifier.add_proof(&proof, &[], |_, _| None); 299 | } 300 | assert_eq!(verifier.check_all(), true); 301 | } 302 | println!("(Verifying SONIC) Done in {:?}", start.elapsed()); 303 | } 304 | } 305 | 306 | #[test] 307 | fn test_sonic_mimc_w_input() { 308 | let srs_x = Fr::from_str("23923").unwrap(); 309 | let srs_alpha = Fr::from_str("23728792").unwrap(); 310 | 311 | let srs = SRS::::dummy(830564, srs_x, srs_alpha); 312 | 313 | // This may not be cryptographically safe, use 314 | // `OsRng` (for example) in production software. 315 | let rng = &mut thread_rng(); 316 | 317 | // Generate the MiMC round constants 318 | let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); 319 | let samples: usize = 100; 320 | 321 | let xl = rng.gen(); 322 | let xr = rng.gen(); 323 | let image = mimc::(xl, xr, &constants); 324 | 325 | // Create an instance of our circuit (with the 326 | // witness) 327 | let circuit = MiMCDemo { 328 | xl: Some(xl), 329 | xr: Some(xr), 330 | constants: &constants 331 | }; 332 | 333 | let start = Instant::now(); 334 | let proof = Proof::>::create_proof::< _, Basic>(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); 335 | println!("(Proving SONIC input)done in {:?}", start.elapsed()); 336 | 337 | let rng = thread_rng(); 338 | let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); 339 | 340 | let start = Instant::now(); 341 | { 342 | for _ in 0..1 { 343 | verifier.add_proof(&proof, &[image], |_, _| None); 344 | } 345 | assert_eq!(verifier.check_all(), true); 346 | } 347 | println!("(Verifying SONIC Input)done in {:?}", start.elapsed()); 348 | } 349 | 350 | #[test] 351 | fn test_groth16_mimc() { 352 | use bellman::groth16::{generate_random_parameters, Proof, prepare_verifying_key, create_random_proof, verify_proof}; 353 | 354 | // This may not be cryptographically safe, use 355 | // `OsRng` (for example) in production software. 356 | let rng = &mut thread_rng(); 357 | 358 | // Generate the MiMC round constants 359 | let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); 360 | 361 | // println!("Creating parameters..."); 362 | 363 | let params = { 364 | let c = MiMCDemo:: { 365 | xl: None, 366 | xr: None, 367 | constants: &constants 368 | }; 369 | 370 | generate_random_parameters(c, rng).unwrap() 371 | }; 372 | 373 | let pvk = prepare_verifying_key(¶ms.vk); 374 | 375 | let xl = rng.gen(); 376 | let xr = rng.gen(); 377 | let image = mimc::(xl, xr, &constants); 378 | 379 | let c = MiMCDemo { 380 | xl: Some(xl), 381 | xr: Some(xr), 382 | constants: &constants 383 | }; 384 | 385 | // println!("Creating proofs..."); 386 | let start = Instant::now(); 387 | let proof = create_random_proof(c, ¶ms, rng).unwrap(); 388 | println!("(Proving Groth16) Done in {:?}", start.elapsed()); 389 | 390 | // println!("Verifying proof"); 391 | let start = Instant::now(); 392 | // Check the proof 393 | verify_proof( 394 | &pvk, 395 | &proof, 396 | &[image] 397 | ).unwrap(); 398 | println!("(Verifying Groth16) Done in {:?}", start.elapsed()); 399 | } 400 | -------------------------------------------------------------------------------- /src/cs/permutation.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::collections::HashMap; 3 | use pairing::{Engine, Field}; 4 | use bellman::SynthesisError; 5 | use super::{SynthesisDriver, Circuit, Backend, ConstraintSystem, Variable, LinearCombination}; 6 | 7 | pub struct Permutation; 8 | 9 | impl SynthesisDriver for Permutation { 10 | fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { 11 | struct Synthesizer> { 12 | backend: B, 13 | current_variable: Option, 14 | q: usize, 15 | n: usize, 16 | _marker: PhantomData, 17 | } 18 | 19 | impl> Synthesizer { 20 | // A * 1 = A 21 | fn remove_current_variable(&mut self) { 22 | match self.current_variable.take() { 23 | Some(index) => { 24 | let var_a = Variable::A(index); 25 | let var_b = Variable::B(index); 26 | let var_c = Variable::C(index); 27 | 28 | let mut product = None; 29 | 30 | let value_a = self.backend.get_var(var_a); 31 | 32 | self.backend.set_var(var_b, || { 33 | let value_b = E::Fr::one(); 34 | product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?); 35 | product.as_mut().map(|p| p.mul_assign(&value_b)); 36 | 37 | Ok(value_b) 38 | }).expect("should exist by now"); 39 | 40 | self.backend.set_var(var_c, || { 41 | product.ok_or(SynthesisError::AssignmentMissing) 42 | }).expect("should exist by now"); 43 | 44 | self.current_variable = None; 45 | }, 46 | _ => {} 47 | } 48 | } 49 | } 50 | 51 | impl> ConstraintSystem for Synthesizer { 52 | const ONE: Variable = Variable::A(1); 53 | 54 | fn alloc(&mut self, value: F) -> Result 55 | where 56 | F: FnOnce() -> Result 57 | { 58 | match self.current_variable.take() { 59 | Some(index) => { 60 | let var_a = Variable::A(index); 61 | let var_b = Variable::B(index); 62 | let var_c = Variable::C(index); 63 | 64 | let value_a = self.backend.get_var(var_a).ok_or(SynthesisError::AssignmentMissing)?; 65 | let mut product = None; 66 | 67 | self.backend.set_var(var_b, || { 68 | let value_b = value()?; 69 | product = Some(value_a); 70 | product.as_mut().map(|p| p.mul_assign(&value_b)); 71 | Ok(value_b) 72 | })?; 73 | 74 | self.backend.set_var(var_c, || { 75 | product.ok_or(SynthesisError::AssignmentMissing) 76 | })?; 77 | 78 | self.current_variable = None; 79 | Ok(var_b) 80 | }, 81 | None => { 82 | self.n += 1; 83 | self.backend.new_multiplication_gate(); 84 | 85 | let index = self.n; 86 | let var_a = Variable::A(index); 87 | 88 | self.backend.set_var(var_a, value)?; 89 | self.current_variable = Some(index); 90 | Ok(var_a) 91 | } 92 | } 93 | } 94 | 95 | fn alloc_input(&mut self, value: F) -> Result 96 | where 97 | F: FnOnce() -> Result 98 | { 99 | let input_var = self.alloc(value)?; 100 | self.enforce_zero(LinearCombination::zero() + input_var); 101 | 102 | self.backend.new_k_power(self.q - 2); 103 | self.backend.new_k_power(self.q - 1); 104 | self.backend.new_k_power(self.q); 105 | 106 | Ok(input_var) 107 | } 108 | 109 | fn enforce_zero(&mut self, lc: LinearCombination) { 110 | self.q += 1; 111 | self.backend.new_linear_constraint(); 112 | 113 | for (var, coeff) in lc.as_ref() { 114 | self.backend.insert_coefficient(*var, *coeff); 115 | } 116 | 117 | self.remove_current_variable(); 118 | 119 | { 120 | self.q += 1; 121 | self.backend.new_linear_constraint(); 122 | let mut alloc_map = HashMap::with_capacity(lc.as_ref().len()); 123 | let new_index = self.n + 1; 124 | 125 | for (var, _) in lc.as_ref() { 126 | match var { 127 | Variable::A(index) => { 128 | if alloc_map.get(index).is_none() && *index != 1 { 129 | alloc_map.insert(*index, new_index); 130 | } 131 | }, 132 | Variable::B(index) => { 133 | if alloc_map.get(index).is_none() && *index != 2 { 134 | alloc_map.insert(*index, new_index); 135 | } 136 | }, 137 | Variable::C(index) => { 138 | if alloc_map.get(index).is_none() && *index != 3 { 139 | alloc_map.insert(*index, new_index); 140 | } 141 | } 142 | } 143 | } 144 | 145 | for (index, new_index) in alloc_map.iter() { 146 | self.n += 1; 147 | self.backend.new_multiplication_gate(); 148 | 149 | let current_value_a = self.backend.get_var(Variable::A(*index)); 150 | let current_value_b = self.backend.get_var(Variable::B(*index)); 151 | let current_value_c = self.backend.get_var(Variable::C(*index)); 152 | 153 | self.backend.set_var(Variable::B(*new_index), || { 154 | let value = current_value_a.ok_or(SynthesisError::AssignmentMissing)?; 155 | Ok(value) 156 | }); 157 | 158 | self.backend.set_var(Variable::C(*new_index), || { 159 | let value = current_value_b.ok_or(SynthesisError::AssignmentMissing)?; 160 | Ok(value) 161 | }); 162 | 163 | self.backend.set_var(Variable::A(*new_index), || { 164 | let value = current_value_c.ok_or(SynthesisError::AssignmentMissing)?; 165 | Ok(value) 166 | }); 167 | } 168 | 169 | for (var, coeff) in lc.as_ref() { 170 | let new_var = match var { 171 | Variable::A(index) => { 172 | if *index == 1 { 173 | Variable::B(2) 174 | } else { 175 | let new_index = alloc_map.get(index).unwrap(); 176 | Variable::B(*new_index) 177 | } 178 | }, 179 | Variable::B(index) => { 180 | if *index == 2 { 181 | Variable::C(3) 182 | } else { 183 | let new_index = alloc_map.get(index).unwrap(); 184 | Variable::C(*new_index) 185 | } 186 | }, 187 | Variable::C(index) => { 188 | if *index == 3 { 189 | Variable::A(1) 190 | } else { 191 | let new_index = alloc_map.get(index).unwrap(); 192 | Variable::A(*new_index) 193 | } 194 | } 195 | }; 196 | 197 | self.backend.insert_coefficient(new_var, *coeff); 198 | } 199 | } 200 | 201 | { 202 | self.q += 1; 203 | self.backend.new_linear_constraint(); 204 | 205 | let mut alloc_map = HashMap::with_capacity(lc.as_ref().len()); 206 | let new_index = self.n + 1; 207 | 208 | for (var, _) in lc.as_ref() { 209 | match var { 210 | Variable::A(index) => { 211 | if alloc_map.get(index).is_none() && *index != 1 { 212 | alloc_map.insert(*index, new_index); 213 | } 214 | }, 215 | Variable::B(index) => { 216 | if alloc_map.get(index).is_none() && *index != 2 { 217 | alloc_map.insert(*index, new_index); 218 | } 219 | }, 220 | Variable::C(index) => { 221 | if alloc_map.get(index).is_none() && *index != 3 { 222 | alloc_map.insert(*index, new_index); 223 | } 224 | } 225 | } 226 | } 227 | 228 | for (index, new_index) in alloc_map.iter() { 229 | self.n += 1; 230 | self.backend.new_multiplication_gate(); 231 | 232 | let current_value_a = self.backend.get_var(Variable::A(*index)); 233 | let current_value_b = self.backend.get_var(Variable::B(*index)); 234 | let current_value_c = self.backend.get_var(Variable::C(*index)); 235 | 236 | self.backend.set_var(Variable::A(*new_index), || { 237 | current_value_b.ok_or(SynthesisError::AssignmentMissing) 238 | }).expect("should exist by now"); 239 | 240 | self.backend.set_var(Variable::B(*new_index), || { 241 | current_value_c.ok_or(SynthesisError::AssignmentMissing) 242 | }).expect("should exist by now"); 243 | 244 | self.backend.set_var(Variable::C(*new_index), || { 245 | current_value_a.ok_or(SynthesisError::AssignmentMissing) 246 | }).expect("should exist by now"); 247 | } 248 | 249 | for (var, coeff) in lc.as_ref() { 250 | let new_var = match var { 251 | Variable::A(index) => { 252 | if *index == 1 { 253 | Variable::C(3) 254 | } else { 255 | let new_index = alloc_map.get(index).unwrap(); 256 | Variable::C(*new_index) 257 | } 258 | }, 259 | Variable::B(index) => { 260 | if *index == 2 { 261 | Variable::A(1) 262 | } else { 263 | let new_index = alloc_map.get(index).unwrap(); 264 | Variable::B(*new_index) 265 | } 266 | }, 267 | Variable::C(index) => { 268 | if *index == 3 { 269 | Variable::B(2) 270 | } else { 271 | let new_index = alloc_map.get(index).unwrap(); 272 | Variable::C(*new_index) 273 | } 274 | } 275 | }; 276 | 277 | self.backend.insert_coefficient(new_var, *coeff); 278 | } 279 | } 280 | } 281 | 282 | fn multiply(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError> 283 | where 284 | F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError> 285 | { 286 | self.n += 1; 287 | self.backend.new_multiplication_gate(); 288 | let index = self.n; 289 | 290 | let var_a = Variable::A(index); 291 | let var_b = Variable::B(index); 292 | let var_c = Variable::C(index); 293 | 294 | let mut value_b = None; 295 | let mut value_c = None; 296 | 297 | self.backend.set_var(var_a, || { 298 | let (a, b, c) = values()?; 299 | 300 | value_b = Some(b); 301 | value_c = Some(c); 302 | 303 | Ok(a) 304 | })?; 305 | 306 | self.backend.set_var(var_b, || { 307 | value_b.ok_or(SynthesisError::AssignmentMissing) 308 | })?; 309 | 310 | self.backend.set_var(var_c, || { 311 | value_c.ok_or(SynthesisError::AssignmentMissing) 312 | })?; 313 | 314 | Ok((var_a, var_b, var_c)) 315 | } 316 | 317 | fn get_value(&self, var: Variable) -> Result { 318 | self.backend.get_var(var).ok_or(()) 319 | } 320 | } 321 | 322 | let mut instance = Synthesizer { 323 | backend, 324 | current_variable: None, 325 | q: 0, 326 | n: 0, 327 | _marker: PhantomData, 328 | }; 329 | 330 | let one = instance.alloc_input(|| { 331 | Ok(E::Fr::one()) 332 | }).expect("should have no issues"); 333 | 334 | match (one, as ConstraintSystem>::ONE) { 335 | (Variable::A(1), Variable::A(1)) => {}, 336 | _ => panic!("one variable is incorrect") 337 | } 338 | 339 | circuit.synthesize(&mut instance)?; 340 | 341 | Ok(()) 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /src/unhelped/grand_product.rs: -------------------------------------------------------------------------------- 1 | /// Defined in appendix B: THE GRAND PRODUCT ARGUMENT 2 | use pairing::{Engine, Field, CurveAffine, CurveProjective}; 3 | use bellman::SynthesisError; 4 | use merlin::Transcript; 5 | use crate::{traits, transcript::ProvingTranscript}; 6 | use crate::srs::SRS; 7 | use crate::utils::*; 8 | use crate::polynomials::operations::mul_polynomials; 9 | use crate::polynomials::commitment::poly_comm_opening; 10 | use crate::traits::*; 11 | use super::well_formed; 12 | 13 | #[derive(Clone)] 14 | pub struct ProductPolys{ 15 | u_poly: Vec, 16 | v_poly: Vec 17 | } 18 | 19 | impl ProductPolys { 20 | pub fn new(u_poly: Vec, v_poly: Vec) -> Self { 21 | ProductPolys { 22 | u_poly, 23 | v_poly, 24 | } 25 | } 26 | 27 | /// Create the Grand product arguments from the given two polynomials 28 | pub fn gen_arg>(&self, srs: &SRS) -> GprodArg { 29 | // let (u_poly, v_poly) = polys; 30 | let n = self.u_poly.len(); 31 | assert!(self.u_poly.len() == self.v_poly.len()); 32 | 33 | let mut a_poly = Vec::with_capacity(2 * n + 1); 34 | let mut c_poly = Vec::with_capacity(2 * n + 1); 35 | let mut c_coeff = E::Fr::one(); 36 | 37 | // c_1 = a_1 * b_1(=1) 38 | // c_2 = a_2 * b_2(=c_1) = a_2 * a_1 * 1 39 | // c_3 = a_3 * b_3(=c_2) = a_3 * a_2 * a_1 * 1 40 | // ... 41 | // c_n = a_n + c_{n-1} = \prod a_i 42 | for a in self.u_poly.iter() { 43 | c_coeff.mul_assign(a); 44 | c_poly.push(c_coeff); 45 | } 46 | 47 | // c_n_inv = a_{n+1} = c_{n}^-1 48 | let c_n_inv = c_poly[n - 1].inverse().unwrap(); 49 | 50 | let mut c_coeff = E::Fr::one(); // re-set to one 51 | 52 | // (3) c_{n+1} = 1 53 | c_poly.push(c_coeff); 54 | 55 | for b in self.v_poly.iter() { 56 | c_coeff.mul_assign(b); 57 | c_poly.push(c_coeff); 58 | } 59 | assert_eq!(c_poly.len(), 2 * n + 1); 60 | 61 | // (4) c_{2n+1} == c_{n} 62 | assert_eq!(c_poly[2 * n], c_poly[n - 1]); 63 | 64 | // Define the a_i arguments 65 | // a_1, a_2, ..., a_n from U 66 | a_poly.extend(&self.u_poly); 67 | // a_{n+1} = 0 68 | a_poly.push(E::Fr::zero()); 69 | // a_{n+2}, a_{n+3}, ..., a_{2n+1} from V 70 | a_poly.extend(&self.v_poly); 71 | 72 | let c_point = multiexp( 73 | srs.g_pos_x_alpha[0..c_poly.len()].iter(), 74 | c_poly.iter() 75 | ).into_affine(); 76 | 77 | let c_comm = PE::Commitment::from_point(&c_point); 78 | 79 | GprodArg { 80 | a_poly, 81 | c_poly, 82 | // a_comm, 83 | c_comm, 84 | c_n_inv, 85 | n, 86 | } 87 | } 88 | } 89 | 90 | #[derive(Clone)] 91 | pub struct GprodArg { 92 | /// the coeffientis of two commitments U and V, 93 | /// where U and V are fully well-formed commitments to n-degree polynomials. 94 | /// U = g^{alpha * \sum\limits_{i=1}^n a_i x^i, V = g^{alpha * \sum\limits_{i=1}^n a_{i+n+1} x^i, 95 | a_poly: Vec, 96 | 97 | /// g^{alpha * \sum\limits_{i=1}^{2n+1} c_i x^i 98 | /// Following the requirements. 99 | /// (1) a \cdot b = c 100 | /// (2) b = (1, c_1, ..., c_{2n+1}) 101 | /// (3) c_{n+1} = 1 102 | /// (4) c_{2n+1} = c_n 103 | c_poly: Vec, 104 | 105 | // a_comm: PE::Commitment, 106 | c_comm: PE::Commitment, 107 | 108 | /// c_n^{-1} 109 | c_n_inv: E::Fr, 110 | 111 | n: usize, 112 | } 113 | 114 | impl> GprodArg { 115 | pub fn commit_t_poly( 116 | &mut self, 117 | y: E::Fr, // challenge 118 | srs: &SRS 119 | ) -> Result<(Vec, PE::Commitment), SynthesisError> { 120 | let n = self.n; 121 | 122 | let mut a_xy = self.a_poly.clone(); 123 | 124 | // r(X, y) + s(X, y) with powers [1, 2n+2] 125 | let r_plus_s = { 126 | 127 | // (y * \sum\limits_{i=1}^{2n+1} a_i y^i) * X^i 128 | let mut tmp = y; 129 | tmp.square(); 130 | eval_bivar_poly::(&mut a_xy[..], tmp, y); 131 | 132 | // (x_n^{-1}*y^{n+2} + y) * X^{n+1} 133 | let y_n_plus_two = y.pow(&[(n+2) as u64]); 134 | let mut x_n_plus_one = self.c_n_inv; 135 | x_n_plus_one.mul_assign(&y_n_plus_two); 136 | x_n_plus_one.add_assign(&y); 137 | a_xy[n].add_assign(&x_n_plus_one); 138 | 139 | // 1 * X^{n+2} 140 | a_xy[n+1].add_assign(&E::Fr::one()); 141 | 142 | // (-y) * X^{2n+2} 143 | let mut neg_y = y; 144 | neg_y.negate(); 145 | a_xy.push(neg_y); 146 | 147 | // Padding for negative powers 148 | let mut a_prime = vec![E::Fr::zero(); 2 * n + 3]; 149 | a_prime.extend(a_xy); 150 | 151 | a_prime 152 | }; 153 | 154 | // r'(X, y) with powers [-2n-3, -1] 155 | let r_prime = { 156 | let mut cx = self.c_poly.iter().rev().map(|e| *e).collect::>(); 157 | cx.push(E::Fr::one()); 158 | cx.push(E::Fr::zero()); 159 | 160 | cx 161 | }; 162 | 163 | let mut t_xy = mul_polynomials::(&r_plus_s, &r_prime)?; 164 | 165 | // (4n+5) + (2n+3) - 1 166 | assert_eq!(t_xy.len(), 6 * n + 7); 167 | 168 | // Remove the first powers due to the padding. 169 | t_xy.drain(0..(2*n+3)); 170 | let last = t_xy.pop(); 171 | assert_eq!(last.unwrap(), E::Fr::zero(), "last element should be zero"); 172 | assert_eq!(t_xy.len(), 4 * n + 3); 173 | 174 | // k(y) 175 | let mut k_y = { 176 | let mut y_sq = y; 177 | y_sq.square(); 178 | eval_univar_poly::(&self.c_poly[..], y_sq, y) 179 | }; 180 | k_y.add_assign(&E::Fr::one()); 181 | 182 | // (r(X, y) + s(X, y))r'(X, y) - k(y) 183 | t_xy[2 * n + 1].sub_assign(&k_y); 184 | 185 | // mul_add_poly::(&mut self.t_polys[..], &t_xy, *challenge); 186 | 187 | let t_comm = multiexp( 188 | srs.g_neg_x_alpha[..(2*n+1)].iter().rev() 189 | .chain_ext(srs.g_pos_x_alpha[..(2*n+1)].iter()), 190 | t_xy[..(2*n+1)].iter() 191 | .chain_ext(t_xy[(2*n+2)..].iter()) 192 | ).into_affine(); 193 | 194 | Ok((t_xy, PE::Commitment::from_point(&t_comm))) 195 | } 196 | 197 | pub fn open_a(&self, y: E::Fr, z: E::Fr, srs: &SRS) -> (E::Fr, E::G1Affine) { 198 | let n = self.n; 199 | let mut yz = y; 200 | yz.mul_assign(&z); 201 | 202 | let u = &self.a_poly[..n]; 203 | let v = &self.a_poly[(n+1)..]; 204 | assert_eq!(u.len(), v.len()); 205 | 206 | let mut val = eval_univar_poly::(u, yz, yz); 207 | let fp = yz.pow([(n+2) as u64]); 208 | let val_v = eval_univar_poly::(v, fp, yz); 209 | val.add_assign(&val_v); 210 | 211 | let mut constant_term = val; 212 | constant_term.negate(); 213 | 214 | let opening = poly_comm_opening( 215 | 0, 216 | 2 * n + 1, 217 | srs, 218 | Some(constant_term).iter() // f(x)-f(yz) 219 | .chain_ext(u.iter()) 220 | .chain_ext(Some(E::Fr::zero()).iter()) 221 | .chain_ext(v.iter()), 222 | yz, 223 | ); 224 | 225 | (val, opening) 226 | } 227 | } 228 | 229 | pub fn create_gprod_proof>( 230 | polys: &ProductPolys, 231 | srs: &SRS 232 | ) -> Result, SynthesisError> { 233 | let mut transcript = Transcript::new(&[]); 234 | 235 | // gprodP_1 236 | let mut args = polys.gen_arg::(srs); 237 | let n = args.n; 238 | let c_comm = args.c_comm; 239 | transcript.commit_point::(&c_comm); 240 | 241 | // gprodV -> gprodP: 242 | let y: E::Fr = transcript.challenge_scalar(); 243 | 244 | // gprod_2(y) -> T: 245 | let (mut t_xy, t_comm) = args.commit_t_poly(y, srs)?; 246 | transcript.commit_point::(&t_comm); 247 | 248 | // gprodV -> gprodP: 249 | let z: E::Fr = transcript.challenge_scalar(); 250 | let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?; 251 | 252 | let mut yz = y; 253 | yz.mul_assign(&z); 254 | 255 | // gprod_3(z) -> T: 256 | let (a_yz, a_opening) = args.open_a(y, z, srs); 257 | 258 | let mut c_z_inv = eval_univar_poly::(&args.c_poly[..], z_inv, z_inv); 259 | c_z_inv.negate(); 260 | 261 | let c_opening = poly_comm_opening( 262 | 0, 263 | 2 * n + 1, 264 | srs, 265 | Some(c_z_inv).iter() 266 | .chain_ext(args.c_poly.iter()), 267 | z_inv 268 | ); 269 | c_z_inv.negate(); 270 | 271 | let mut k_y = eval_univar_poly::(&args.c_poly[..], y, y); 272 | k_y.negate(); 273 | 274 | let k_opening = poly_comm_opening( 275 | 0, 276 | 2 * n + 1, 277 | srs, 278 | Some(k_y).iter() 279 | .chain_ext(args.c_poly.iter()), 280 | y 281 | ); 282 | k_y.negate(); 283 | 284 | let t_zy = { 285 | let first_power = z_inv.pow([(2 * n + 1) as u64]); 286 | eval_univar_poly::(&t_xy, first_power, z) 287 | }; 288 | t_xy[2 * n + 1].sub_assign(&t_zy); 289 | 290 | let t_opening = poly_comm_opening( 291 | 2 * n + 1, 292 | 2 * n + 1, 293 | srs, 294 | t_xy.iter(), 295 | z 296 | ); 297 | 298 | Ok(GrandProductProof:: { 299 | a_yz, 300 | a_opening, 301 | c_z_inv, 302 | c_opening, 303 | k_y, 304 | k_opening, 305 | t_opening, 306 | c_comm, 307 | t_comm, 308 | }) 309 | } 310 | 311 | #[derive(Clone)] 312 | pub struct GrandProductProof { 313 | a_yz: E::Fr, 314 | a_opening: E::G1Affine, 315 | c_z_inv: E::Fr, 316 | c_opening: E::G1Affine, 317 | k_y: E::Fr, 318 | k_opening: E::G1Affine, 319 | t_opening: E::G1Affine, 320 | c_comm: PE::Commitment, 321 | t_comm: PE::Commitment, 322 | 323 | } 324 | 325 | impl> GrandProductProof { 326 | pub fn verify( 327 | &self, 328 | n: usize, 329 | randomness: &Vec, 330 | a_yz: E::Fr, 331 | y: E::Fr, 332 | z: E::Fr, 333 | srs: &SRS 334 | ) -> Result { 335 | assert_eq!(randomness.len(), 3); 336 | 337 | let c_z_inv = self.c_z_inv; 338 | let k_y = self.k_y; 339 | let g = srs.g_pos_x[0]; 340 | 341 | // Prepare the G2 elements for pairing 342 | let g = srs.g_pos_x[0]; 343 | let alpha_x_prep = srs.h_pos_x_alpha[1].prepare(); 344 | let alpha_prep = srs.h_pos_x_alpha[0].prepare(); 345 | let mut neg_h = srs.h_pos_x[0]; 346 | neg_h.negate(); 347 | let neg_h_prep = neg_h.prepare(); 348 | 349 | // Re-calculate t(z, y) 350 | // r <- y * v_a 351 | let mut r = y; 352 | r.mul_assign(&a_yz); 353 | 354 | // s <- z^{n+2} + z^{n+1}*y - z{2*n+2}*y 355 | let mut s = E::Fr::zero(); 356 | let mut z_n_plus_1 = z.pow([(n + 1) as u64]); 357 | 358 | // z^{n+2} 359 | let mut z_n_plus_2 = z_n_plus_1; 360 | z_n_plus_2.mul_assign(&z); 361 | 362 | // z{2*n+2}*y 363 | let mut z_2n_plus_2_y = z_n_plus_1; 364 | z_2n_plus_2_y.square(); 365 | z_2n_plus_2_y.mul_assign(&y); 366 | 367 | // z^{n+1}*y 368 | z_n_plus_1.mul_assign(&y); 369 | 370 | s.add_assign(&z_n_plus_2); 371 | s.add_assign(&z_n_plus_1); 372 | s.sub_assign(&z_2n_plus_2_y); 373 | 374 | // r' <- v_c * z^{-1} 375 | let mut r_prime = c_z_inv; 376 | let z_inv = z; 377 | z_inv.inverse().ok_or(SynthesisError::DivisionByZero)?; 378 | r_prime.mul_assign(&z_inv); 379 | 380 | // k <- v_k * y + 1 381 | let mut k = k_y; 382 | k.mul_assign(&y); 383 | k.add_assign(&E::Fr::one()); 384 | 385 | // t <- (r + s) * r' - k 386 | let mut t = r; 387 | t.add_assign(&s); 388 | t.mul_assign(&r_prime); 389 | t.sub_assign(&k); 390 | 391 | // g^{c_z_inv} * c_opening^{-z^{-1}} 392 | let mut minus_z_inv = z_inv; 393 | minus_z_inv.negate(); 394 | let mut g_c_term = self.c_opening.mul(minus_z_inv); 395 | let g_c = g.mul(self.c_z_inv); 396 | g_c_term.add_assign(&g_c); 397 | 398 | // g^{k_y} * k_opening^{-y} 399 | let mut minus_y = y; 400 | minus_y.negate(); 401 | let mut g_k_term = self.k_opening.mul(minus_y); 402 | let g_k = g.mul(self.k_y); 403 | g_k_term.add_assign(&g_k); 404 | 405 | // g^{t} * t_opening^{-z} 406 | let mut minus_z = z; 407 | minus_z.negate(); 408 | let mut g_t_term = self.t_opening.mul(minus_z); 409 | let g_t = g.mul(t); 410 | g_t_term.add_assign(&g_t); 411 | 412 | let alpha_x = multiexp( 413 | Some(self.c_opening).iter() 414 | .chain_ext(Some(self.k_opening).iter()) 415 | .chain_ext(Some(self.t_opening).iter()), 416 | randomness.iter() 417 | ).into_affine(); 418 | 419 | let alpha = multiexp( 420 | Some(g_c_term.into_affine()).iter() 421 | .chain_ext(Some(g_k_term.into_affine()).iter()) 422 | .chain_ext(Some(g_t_term.into_affine()).iter()), 423 | randomness.iter() 424 | ).into_affine(); 425 | 426 | let neg_h = multiexp( 427 | Some(self.c_comm.into_point()).iter() 428 | .chain_ext(Some(self.c_comm.into_point()).iter()) 429 | .chain_ext(Some(self.t_comm.into_point()).iter()), 430 | randomness.iter() 431 | ).into_affine(); 432 | 433 | let is_valid = E::final_exponentiation(&E::miller_loop(&[ 434 | (&alpha_x.prepare(), &alpha_prep), 435 | (&alpha.prepare(), &alpha_x_prep), 436 | (&neg_h.prepare(), &neg_h_prep), 437 | ])).unwrap() == E::Fqk::one(); 438 | 439 | Ok(is_valid) 440 | } 441 | } 442 | 443 | #[cfg(test)] 444 | mod tests { 445 | use super::*; 446 | use rand::{XorShiftRng, SeedableRng, Rand, Rng}; 447 | use pairing::bls12_381::{Bls12, Fr}; 448 | use pairing::PrimeField; 449 | use crate::polynomials::Polynomial; 450 | 451 | #[test] 452 | fn grand_product_argument_corecctness() { 453 | let srs_x = Fr::from_str("432").unwrap(); 454 | let srs_alpha = Fr::from_str("9876").unwrap(); 455 | let srs = &SRS::::dummy(824562, srs_x, srs_alpha); 456 | 457 | let n: usize = 1 << 16; 458 | let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); 459 | let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); 460 | let mut perm = coeffs.clone(); 461 | rng.shuffle(&mut perm); 462 | 463 | let prod_polys = ProductPolys::new(coeffs, perm); 464 | let proof = create_gprod_proof::>(&prod_polys, srs).unwrap(); 465 | 466 | let y: Fr = rng.gen(); 467 | let z: Fr = rng.gen(); 468 | let randomness = (0..3).map(|_| Fr::rand(rng)).collect::>(); 469 | 470 | let is_valid = proof.verify( 471 | n, 472 | &randomness, 473 | proof.a_yz, 474 | y, 475 | z, 476 | srs 477 | ).unwrap(); 478 | 479 | // assert!(is_valid); // TODO 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /src/helped/prover.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Field}; 2 | use bellman::SynthesisError; 3 | use rand::{Rand, Rng, thread_rng}; 4 | use merlin::Transcript; 5 | use crate::cs::{SynthesisDriver, Circuit, Backend, Variable, Coeff}; 6 | use crate::srs::SRS; 7 | use crate::transcript::ProvingTranscript; 8 | use crate::polynomials::{Polynomial, poly_comm, poly_comm_opening, SxEval, add_polynomials, mul_polynomials}; 9 | use crate::utils::*; 10 | use crate::traits::{Commitment, PolyEngine}; 11 | 12 | pub const NUM_BLINDINGS: usize = 4; 13 | 14 | #[derive(Clone, Debug, Eq, PartialEq)] 15 | pub struct Proof { 16 | /// A commitment of `r(X, 1)` 17 | pub r_comm: PE::Commitment, 18 | 19 | /// A commitment of `t(X, y)`. `y` represents a random challenge from the verifier. 20 | pub t_comm: PE::Commitment, 21 | 22 | /// An evaluation `r(z, 1)`. `z` represents a random challenge from the verifier. 23 | pub r_z1: E::Fr, 24 | 25 | /// An evaluation `r(z, y)`. `y` and `z` represent a random challenge from the verifier. 26 | pub r_zy: E::Fr, 27 | 28 | /// An opening of `t(z, y)` and `r(z, 1)` which are evaluated at `X = z`. 29 | pub z_opening: E::G1Affine, 30 | 31 | /// An opening of `r(z, y)` which are evaluated at `X = yz`. 32 | pub yz_opening: E::G1Affine, 33 | } 34 | 35 | impl> Proof { 36 | pub fn create_proof, S: SynthesisDriver>( 37 | circuit: &C, 38 | srs: &SRS 39 | ) -> Result 40 | { 41 | let mut wires = Wires { 42 | a: vec![], 43 | b: vec![], 44 | c: vec![], 45 | }; 46 | 47 | // Synthesize polynomial coefficients from circuit 48 | S::synthesize(&mut wires, circuit)?; 49 | 50 | let n = wires.a.len(); 51 | // TODO: Make better entropy 52 | let rng = &mut thread_rng(); 53 | let mut transcript = Transcript::new(&[]); 54 | 55 | 56 | // ------------------------------------------------------ 57 | // zkP_1(info, a, b, c) -> R: 58 | // ------------------------------------------------------ 59 | 60 | // c_{n+1}, c_{n+2}, c_{n+3}, c_{n+4} <- F_p 61 | let blindings: Vec = (0..NUM_BLINDINGS) 62 | .into_iter() 63 | .map(|_| E::Fr::rand(rng)) 64 | .collect(); 65 | 66 | // A coefficients vector which can be used in common with polynomials r and r' 67 | // associated with powers for X. 68 | let mut r_x1 = wires.b; // X^{-n}...X^{-1} 69 | r_x1.extend(wires.c); // X^{-2n}...X^{-n-1} 70 | r_x1.extend(blindings); // X^{-2n-4}...X^{-2n-1} 71 | r_x1.reverse(); 72 | r_x1.push(E::Fr::zero()); 73 | r_x1.extend(wires.a); // X^{1}...X^{n} 74 | 75 | let r_comm = Polynomial::from_slice(&mut r_x1[..]).commit::( 76 | n, 77 | 2*n + NUM_BLINDINGS, 78 | n, 79 | &srs 80 | ); 81 | 82 | // A prover commits polynomial 83 | transcript.commit_point::(&r_comm); 84 | 85 | 86 | // ------------------------------------------------------ 87 | // zkV -> zkP: Send y <- F_p to prover 88 | // ------------------------------------------------------ 89 | 90 | // A varifier send to challenge scalar to prover 91 | let y: E::Fr = transcript.challenge_scalar(); 92 | let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; 93 | let y_first_power = y_inv.pow(&[(2 * n + NUM_BLINDINGS) as u64]); 94 | 95 | 96 | // ------------------------------------------------------ 97 | // zkP_2(y) -> T: 98 | // ------------------------------------------------------ 99 | 100 | // powers: [-2n-4, n] 101 | let mut r_xy = r_x1.clone(); 102 | 103 | // Evaluate the polynomial r(X, Y) at y 104 | eval_bivar_poly::( 105 | &mut r_xy, 106 | y_first_power, 107 | y, 108 | ); 109 | 110 | // negative powers [-1, -n], positive [1, 2n] of Polynomial s(X, y) 111 | let (mut s_neg_poly, s_pos_poly) = { 112 | let mut sx_poly = SxEval::new(y, n)?; 113 | S::synthesize(&mut sx_poly, circuit)?; 114 | 115 | sx_poly.neg_pos_poly() 116 | }; 117 | 118 | // Evaluate the polynomial r'(X, Y) = r(X, Y) + s(X, Y) at y 119 | let mut r_xy_prime = r_xy.clone(); 120 | { 121 | // extend to have powers [n+1, 2n] for w_i(Y)X^{i+n} 122 | r_xy_prime.resize(4 * n + 1 + NUM_BLINDINGS, E::Fr::zero()); 123 | // negative powers: [-n, -1] 124 | s_neg_poly.reverse(); 125 | 126 | let neg_poly_len = s_neg_poly.len(); 127 | // Add negative powers [-n, -1] 128 | add_polynomials::(&mut r_xy_prime[(neg_poly_len + NUM_BLINDINGS)..(2 * n + NUM_BLINDINGS)], &s_neg_poly[..]); 129 | s_neg_poly.reverse(); 130 | 131 | // Add positive powers [1, 2n] 132 | add_polynomials::(&mut r_xy_prime[(2 * n + 1 + NUM_BLINDINGS)..], &s_pos_poly[..]); 133 | } 134 | 135 | // Compute t(X, y) = r(X, 1) * r'(X, y) 136 | let mut t_xy = mul_polynomials::(&r_x1[..], &r_xy_prime[..])?; 137 | // the constant term of t(X,Y) is zero 138 | t_xy[4 * n + 2 * NUM_BLINDINGS] = E::Fr::zero(); // -k(y) 139 | 140 | // commitment of t(X, y) 141 | let mut t_comm_vec = t_xy[..(4 * n + 2 * NUM_BLINDINGS)].iter() 142 | .chain_ext(t_xy[(4 * n + 2 * NUM_BLINDINGS + 1)..].iter()) 143 | .map(|e| *e) 144 | .collect::>(); 145 | 146 | let t_comm = Polynomial::from_slice(&mut t_comm_vec[..]) 147 | .commit::( 148 | srs.d, 149 | 4 * n + 2 * NUM_BLINDINGS, 150 | 3 * n, 151 | srs 152 | ); 153 | 154 | transcript.commit_point::(&t_comm); 155 | 156 | 157 | // ------------------------------------------------------ 158 | // zkV -> zkP: Send z <- F_p to prover 159 | // ------------------------------------------------------ 160 | 161 | // A varifier send to challenge scalar to prover 162 | let z: E::Fr = transcript.challenge_scalar(); 163 | let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?; 164 | let z_first_power = z_inv.pow(&[(2 * n + NUM_BLINDINGS) as u64]); 165 | 166 | // ------------------------------------------------------ 167 | // zkP_3(z) -> (a, W_a, b, W_b, W_t, s, sc): 168 | // ------------------------------------------------------ 169 | 170 | // r(X, 1) -> r(z, 1) 171 | let r_z1 = eval_univar_poly::(&r_x1, z_first_power, z); 172 | transcript.commit_scalar(&r_z1); 173 | 174 | // Ensure: r(X, 1) -> r(yz, 1) = r(z, y) 175 | // let r_zy = evaluate_poly(&r_x1, z_first_power, z*y); 176 | // r(X, y) -> r(z, y) 177 | let r_zy = eval_univar_poly::(&r_xy, z_first_power, z); 178 | transcript.commit_scalar(&r_zy); 179 | 180 | let r1: E::Fr = transcript.challenge_scalar(); 181 | 182 | // An opening of r(X, 1) at yz 183 | let yz_opening = { 184 | // r(X, 1) - r(z, y) 185 | // substract constant term from r(X, 1) 186 | r_x1[2 * n + NUM_BLINDINGS].sub_assign(&r_zy); 187 | 188 | let mut point = y; 189 | point.mul_assign(&z); 190 | 191 | poly_comm_opening( 192 | 2 * n + NUM_BLINDINGS, 193 | n, 194 | srs, 195 | &r_x1, 196 | point 197 | ) 198 | }; 199 | 200 | assert_eq!(r_x1.len(), 3*n + NUM_BLINDINGS + 1); 201 | 202 | // An opening of t(X, y) and r(X, 1) at z 203 | let z_opening = { 204 | // Add constant term 205 | r_x1[(2 * n + NUM_BLINDINGS)].add_assign(&r_zy); 206 | 207 | let r_x1_len = r_x1.len(); 208 | 209 | // Batching polynomial commitments t(X, y) and r(X, 1) 210 | // powers domain: [-2n-4, n] 211 | mul_add_poly::( 212 | &mut t_xy[(2 * n + NUM_BLINDINGS)..(2 * n + NUM_BLINDINGS + r_x1_len)], 213 | &r_x1[..], 214 | r1 215 | ); 216 | 217 | // Evaluate t(X, y) at z 218 | let t_zy = { 219 | let z4_first_power = z_inv.pow(&[(4 * n + 2 * NUM_BLINDINGS) as u64]); 220 | eval_univar_poly::(&t_xy, z4_first_power, z) 221 | }; 222 | 223 | // Sub constant term 224 | t_xy[(4 * n + 2 * NUM_BLINDINGS)].sub_assign(&t_zy); 225 | 226 | poly_comm_opening( 227 | 4 * n + 2 * NUM_BLINDINGS, 228 | 3 * n, 229 | srs, 230 | &t_xy, 231 | z 232 | ) 233 | }; 234 | 235 | Ok( 236 | Proof { 237 | r_comm, 238 | t_comm, 239 | r_z1, 240 | r_zy, 241 | z_opening, 242 | yz_opening, 243 | } 244 | ) 245 | } 246 | } 247 | 248 | /// Three vectors representing the left inputs, right inputs, and outputs of 249 | /// multiplication constraints respectively in sonic's constraint system. 250 | /// Basically, these are value of a variable. 251 | struct Wires { 252 | a: Vec, 253 | b: Vec, 254 | c: Vec 255 | } 256 | 257 | impl<'a, E: Engine> Backend for &'a mut Wires { 258 | fn new_multiplication_gate(&mut self) { 259 | self.a.push(E::Fr::zero()); 260 | self.b.push(E::Fr::zero()); 261 | self.c.push(E::Fr::zero()); 262 | } 263 | 264 | fn get_var(&self, var: Variable) -> Option { 265 | Some(match var { 266 | Variable::A(index) => { 267 | self.a[index - 1] 268 | }, 269 | Variable::B(index) => { 270 | self.b[index - 1] 271 | }, 272 | Variable::C(index) => { 273 | self.c[index - 1] 274 | } 275 | }) 276 | } 277 | 278 | fn set_var(&mut self, var: Variable, value: F) -> Result<(), SynthesisError> 279 | where F: FnOnce() -> Result 280 | { 281 | let value = value()?; 282 | 283 | match var { 284 | Variable::A(index) => { 285 | self.a[index - 1] = value; 286 | }, 287 | Variable::B(index) => { 288 | self.b[index - 1] = value; 289 | }, 290 | Variable::C(index) => { 291 | self.c[index - 1] = value; 292 | }, 293 | } 294 | 295 | Ok(()) 296 | } 297 | } 298 | 299 | 300 | pub struct SxyAdvice { 301 | pub s_comm: PE::Commitment, 302 | pub s_zy_opening: E::G1Affine, // TODO: W opening type 303 | pub s_zy: E::Fr, // s(z, y) 304 | } 305 | 306 | impl> SxyAdvice { 307 | pub fn create_advice, S: SynthesisDriver> ( 308 | circuit: &C, 309 | proof: &Proof, 310 | srs: &SRS, 311 | n: usize, 312 | ) -> Result 313 | { 314 | let y: E::Fr; 315 | let z: E::Fr; 316 | 317 | { 318 | let mut transcript = Transcript::new(&[]); 319 | 320 | transcript.commit_point::(&proof.r_comm); 321 | y = transcript.challenge_scalar(); 322 | 323 | transcript.commit_point::(&proof.t_comm); 324 | z = transcript.challenge_scalar(); 325 | } 326 | 327 | let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?; 328 | 329 | let (s_neg_poly, s_pos_poly) = { 330 | let mut sx_poly = SxEval::new(y, n)?; 331 | S::synthesize(&mut sx_poly, circuit)?; 332 | 333 | sx_poly.neg_pos_poly() 334 | }; 335 | 336 | // a commitment to s(X, y) 337 | let s_comm = poly_comm::<_, _, PE>( 338 | srs.d, 339 | n, 340 | 2 * n, 341 | srs, 342 | s_neg_poly.iter() 343 | .chain_ext(s_pos_poly.iter()) 344 | ); 345 | 346 | // Evaluate s(X, y) at z 347 | let mut s_zy = E::Fr::zero(); 348 | s_zy.add_assign(&eval_univar_poly::(&s_neg_poly[..], z_inv, z_inv)); 349 | s_zy.add_assign(&eval_univar_poly::(&s_pos_poly[..], z, z)); 350 | 351 | let s_zy_opening = { 352 | s_zy.negate(); 353 | 354 | poly_comm_opening( 355 | n, 356 | 2 * n, 357 | srs, 358 | s_neg_poly.iter().rev() 359 | .chain_ext(Some(s_zy).iter()) // f(X) - f(z) 360 | .chain_ext(s_pos_poly.iter()), 361 | z, 362 | ) 363 | }; 364 | 365 | Ok(SxyAdvice { 366 | s_comm, 367 | s_zy, 368 | s_zy_opening, 369 | }) 370 | } 371 | } 372 | 373 | #[cfg(test)] 374 | mod tests { 375 | use super::*; 376 | use pairing::bls12_381::{Bls12, Fr}; 377 | use pairing::{PrimeField, CurveAffine, CurveProjective}; 378 | use crate::cs::{Basic, ConstraintSystem, LinearCombination}; 379 | use super::super::verifier::MultiVerifier; 380 | use rand::{thread_rng}; 381 | use crate::polynomials::{PolyComm, Polynomial}; 382 | 383 | struct SimpleCircuit; 384 | 385 | impl Circuit for SimpleCircuit { 386 | fn synthesize>(&self, cs: &mut CS) 387 | -> Result<(), SynthesisError> 388 | { 389 | let (a, b, _) = cs.multiply(|| { 390 | Ok(( 391 | E::Fr::from_str("10").unwrap(), 392 | E::Fr::from_str("20").unwrap(), 393 | E::Fr::from_str("200").unwrap(), 394 | )) 395 | })?; 396 | 397 | cs.enforce_zero(LinearCombination::from(a) + a - b); 398 | 399 | Ok(()) 400 | } 401 | } 402 | 403 | #[test] 404 | fn test_create_proof() { 405 | let rng = thread_rng(); 406 | let srs = SRS::::new( 407 | 20, 408 | Fr::from_str("22222").unwrap(), 409 | Fr::from_str("33333333").unwrap(), 410 | ); 411 | 412 | let proof: Proof> = Proof::create_proof::<_, Basic>(&SimpleCircuit, &srs).unwrap(); 413 | 414 | let mut batch = MultiVerifier::::new(SimpleCircuit, &srs, rng).unwrap(); 415 | 416 | for _ in 0..1 { 417 | batch.add_proof(&proof, &[], |_, _| None); 418 | } 419 | 420 | assert!(batch.check_all()); 421 | } 422 | 423 | #[test] 424 | fn polynomial_commitment_test() { 425 | let srs = SRS::::new( 426 | 20, 427 | Fr::from_str("22222").unwrap(), 428 | Fr::from_str("33333333").unwrap(), 429 | ); 430 | 431 | // x^-4 + x^-3 + x^-2 + x^-1 + x + x^2 432 | let mut poly = vec![Fr::one(), Fr::one(), Fr::one(), Fr::one(), Fr::zero(), Fr::one(), Fr::one()]; 433 | // make commitment to the poly 434 | let commitment = poly_comm::>(2, 4, 2, &srs, poly.iter()).into_point(); 435 | 436 | let point: Fr = Fr::one(); 437 | let mut tmp = point.inverse().unwrap(); 438 | tmp.square(); 439 | let value = eval_univar_poly::(&poly, tmp, point); 440 | 441 | // evaluate f(z) 442 | poly[4] = value; 443 | poly[4].negate(); 444 | // f(x) - f(z) 445 | 446 | let opening = poly_comm_opening(4, 2, &srs, poly.iter(), point); 447 | 448 | // e(W , hα x )e(g^{v} * W{-z} , hα ) = e(F , h^{x^{−d +max}} ) 449 | 450 | let alpha_x_precomp = srs.h_pos_x_alpha[1].prepare(); 451 | let alpha_precomp = srs.h_pos_x_alpha[0].prepare(); 452 | let mut neg_x_n_minus_d_precomp = srs.h_neg_x[srs.d - 2]; 453 | neg_x_n_minus_d_precomp.negate(); 454 | let neg_x_n_minus_d_precomp = neg_x_n_minus_d_precomp.prepare(); 455 | 456 | let w = opening.prepare(); 457 | let mut gv = srs.g_pos_x[0].mul(value.into_repr()); 458 | let mut z_neg = point; 459 | z_neg.negate(); 460 | let w_minus_z = opening.mul(z_neg.into_repr()); 461 | gv.add_assign(&w_minus_z); 462 | 463 | let gv = gv.into_affine().prepare(); 464 | 465 | assert!(Bls12::final_exponentiation(&Bls12::miller_loop(&[ 466 | (&w, &alpha_x_precomp), 467 | (&gv, &alpha_precomp), 468 | (&commitment.prepare(), &neg_x_n_minus_d_precomp), 469 | ])).unwrap() == ::Fqk::one()); 470 | } 471 | } 472 | --------------------------------------------------------------------------------