diff --git a/Cargo.toml b/Cargo.toml index 5c62eac..761c6c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,5 @@ rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" [features] default = [] + +singlecore = [] diff --git a/src/domain.rs b/src/domain.rs index c3d011c..85c8a26 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -24,7 +24,12 @@ use super::{ SynthesisError }; + use super::multicore::Worker; +pub use super::group::*; + +#[feature(not(singlecore))] +use super::parallel_fft::*; pub struct EvaluationDomain> { coeffs: Vec, @@ -149,6 +154,7 @@ impl> EvaluationDomain { }) } + pub fn fft(&mut self, worker: &Worker) { best_fft(&mut self.coeffs, worker, &self.omega, self.exp); @@ -258,190 +264,6 @@ impl> EvaluationDomain { } } -pub trait Group: Sized + Copy + Clone + Send + Sync { - fn group_zero() -> Self; - fn group_mul_assign(&mut self, by: &E::Fr); - fn group_add_assign(&mut self, other: &Self); - fn group_sub_assign(&mut self, other: &Self); -} - -pub struct Point(pub G); - -impl PartialEq for Point { - fn eq(&self, other: &Point) -> bool { - self.0 == other.0 - } -} - -impl Copy for Point { } - -impl Clone for Point { - fn clone(&self) -> Point { - *self - } -} - -impl Group for Point { - fn group_zero() -> Self { - Point(G::zero()) - } - fn group_mul_assign(&mut self, by: &G::Scalar) { - self.0.mul_assign(by.into_repr()); - } - fn group_add_assign(&mut self, other: &Self) { - self.0.add_assign(&other.0); - } - fn group_sub_assign(&mut self, other: &Self) { - self.0.sub_assign(&other.0); - } -} - -pub struct Scalar(pub E::Fr); - -impl PartialEq for Scalar { - fn eq(&self, other: &Scalar) -> bool { - self.0 == other.0 - } -} - -impl Copy for Scalar { } - -impl Clone for Scalar { - fn clone(&self) -> Scalar { - *self - } -} - -impl Group for Scalar { - fn group_zero() -> Self { - Scalar(E::Fr::zero()) - } - fn group_mul_assign(&mut self, by: &E::Fr) { - self.0.mul_assign(by); - } - fn group_add_assign(&mut self, other: &Self) { - self.0.add_assign(&other.0); - } - fn group_sub_assign(&mut self, other: &Self) { - self.0.sub_assign(&other.0); - } -} - -pub(crate) fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) -{ - let log_cpus = worker.log_num_cpus(); - - if log_n <= log_cpus { - serial_fft(a, omega, log_n); - } else { - parallel_fft(a, worker, omega, log_n, log_cpus); - } -} - -pub(crate) fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) -{ - fn bitreverse(mut n: u32, l: u32) -> u32 { - let mut r = 0; - for _ in 0..l { - r = (r << 1) | (n & 1); - n >>= 1; - } - r - } - - let n = a.len() as u32; - assert_eq!(n, 1 << log_n); - - for k in 0..n { - let rk = bitreverse(k, log_n); - if k < rk { - a.swap(rk as usize, k as usize); - } - } - - let mut m = 1; - for _ in 0..log_n { - let w_m = omega.pow(&[(n / (2*m)) as u64]); - - let mut k = 0; - while k < n { - let mut w = E::Fr::one(); - for j in 0..m { - let mut t = a[(k+j+m) as usize]; - t.group_mul_assign(&w); - let mut tmp = a[(k+j) as usize]; - tmp.group_sub_assign(&t); - a[(k+j+m) as usize] = tmp; - a[(k+j) as usize].group_add_assign(&t); - w.mul_assign(&w_m); - } - - k += 2*m; - } - - m *= 2; - } -} - -pub(crate) fn parallel_fft>( - a: &mut [T], - worker: &Worker, - omega: &E::Fr, - log_n: u32, - log_cpus: u32 -) -{ - assert!(log_n >= log_cpus); - - let num_cpus = 1 << log_cpus; - let log_new_n = log_n - log_cpus; - let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; - let new_omega = omega.pow(&[num_cpus as u64]); - - worker.scope(0, |scope, _| { - let a = &*a; - - for (j, tmp) in tmp.iter_mut().enumerate() { - scope.spawn(move |_| { - // Shuffle into a sub-FFT - let omega_j = omega.pow(&[j as u64]); - let omega_step = omega.pow(&[(j as u64) << log_new_n]); - - let mut elt = E::Fr::one(); - for i in 0..(1 << log_new_n) { - for s in 0..num_cpus { - let idx = (i + (s << log_new_n)) % (1 << log_n); - let mut t = a[idx]; - t.group_mul_assign(&elt); - tmp[i].group_add_assign(&t); - elt.mul_assign(&omega_step); - } - elt.mul_assign(&omega_j); - } - - // Perform sub-FFT - serial_fft(tmp, &new_omega, log_new_n); - }); - } - }); - - // TODO: does this hurt or help? - worker.scope(a.len(), |scope, chunk| { - let tmp = &tmp; - - for (idx, a) in a.chunks_mut(chunk).enumerate() { - scope.spawn(move |_| { - let mut idx = idx * chunk; - let mask = (1 << log_cpus) - 1; - for a in a { - *a = tmp[idx & mask][idx >> log_cpus]; - idx += 1; - } - }); - } - }); -} - // Test multiplying various (low degree) polynomials together and // comparing with naive evaluations. #[test] diff --git a/src/group.rs b/src/group.rs new file mode 100644 index 0000000..7b4fe2e --- /dev/null +++ b/src/group.rs @@ -0,0 +1,82 @@ +use pairing::{ + Engine, + CurveProjective +}; + +use ff::{ + Field, + PrimeField +}; + +use super::{ + SynthesisError +}; + +pub trait Group: Sized + Copy + Clone + Send + Sync { + fn group_zero() -> Self; + fn group_mul_assign(&mut self, by: &E::Fr); + fn group_add_assign(&mut self, other: &Self); + fn group_sub_assign(&mut self, other: &Self); +} + +pub struct Point(pub G); + +impl PartialEq for Point { + fn eq(&self, other: &Point) -> bool { + self.0 == other.0 + } +} + +impl Copy for Point { } + +impl Clone for Point { + fn clone(&self) -> Point { + *self + } +} + +impl Group for Point { + fn group_zero() -> Self { + Point(G::zero()) + } + fn group_mul_assign(&mut self, by: &G::Scalar) { + self.0.mul_assign(by.into_repr()); + } + fn group_add_assign(&mut self, other: &Self) { + self.0.add_assign(&other.0); + } + fn group_sub_assign(&mut self, other: &Self) { + self.0.sub_assign(&other.0); + } +} + +pub struct Scalar(pub E::Fr); + +impl PartialEq for Scalar { + fn eq(&self, other: &Scalar) -> bool { + self.0 == other.0 + } +} + +impl Copy for Scalar { } + +impl Clone for Scalar { + fn clone(&self) -> Scalar { + *self + } +} + +impl Group for Scalar { + fn group_zero() -> Self { + Scalar(E::Fr::zero()) + } + fn group_mul_assign(&mut self, by: &E::Fr) { + self.0.mul_assign(by); + } + fn group_add_assign(&mut self, other: &Self) { + self.0.add_assign(&other.0); + } + fn group_sub_assign(&mut self, other: &Self) { + self.0.sub_assign(&other.0); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 1ef74b4..0e9c650 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,23 @@ extern crate crossbeam; extern crate byteorder; extern crate ff; -pub mod multicore; -mod multiexp; pub mod domain; pub mod groth16; pub mod gm17; pub mod sonic; +mod group; +mod source; + +#[feature(not(singlecore))] +mod parallel_fft; +mod multicore; +mod parallel_multiexp; + +#[feature(singlecore)] +mod serial_fft; +mod serial_multiexp; + #[cfg(test)] mod tests; @@ -29,6 +39,16 @@ use std::error::Error; use std::io; use std::marker::PhantomData; +pub mod multiexp { + pub use source::*; + + #[feature(not(singlecore))] + pub use parallel_multiexp::*; + + #[feature(singlecore)] + pub use serial_multiexp::*; +} + /// Computations are expressed in terms of arithmetic circuits, in particular /// rank-1 quadratic constraint systems. The `Circuit` trait represents a /// circuit that can be synthesized. The `synthesize` method is called during diff --git a/src/multicore.rs b/src/multicore.rs index 14fab7a..84055d7 100644 --- a/src/multicore.rs +++ b/src/multicore.rs @@ -35,10 +35,6 @@ impl Worker { log2_floor(self.cpus) } - pub(crate) fn num_cpus(&self) -> usize { - self.cpus - } - pub fn compute( &self, f: F ) -> WorkerFuture diff --git a/src/parallel_fft.rs b/src/parallel_fft.rs new file mode 100644 index 0000000..687d146 --- /dev/null +++ b/src/parallel_fft.rs @@ -0,0 +1,143 @@ +//! This module contains an `EvaluationDomain` abstraction for +//! performing various kinds of polynomial arithmetic on top of +//! the scalar field. +//! +//! In pairing-based SNARKs like Groth16, we need to calculate +//! a quotient polynomial over a target polynomial with roots +//! at distinct points associated with each constraint of the +//! constraint system. In order to be efficient, we choose these +//! roots to be the powers of a 2^n root of unity in the field. +//! This allows us to perform polynomial operations in O(n) +//! by performing an O(n log n) FFT over such a domain. + +use pairing::{ + Engine, + CurveProjective +}; + +use ff::{ + Field, + PrimeField +}; + +use super::{ + SynthesisError +}; + +use super::multicore::Worker; +use super::group::*; + +pub(crate) fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) +{ + let log_cpus = worker.log_num_cpus(); + + if log_n <= log_cpus { + serial_fft(a, omega, log_n); + } else { + parallel_fft(a, worker, omega, log_n, log_cpus); + } +} + +pub(crate) fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) +{ + fn bitreverse(mut n: u32, l: u32) -> u32 { + let mut r = 0; + for _ in 0..l { + r = (r << 1) | (n & 1); + n >>= 1; + } + r + } + + let n = a.len() as u32; + assert_eq!(n, 1 << log_n); + + for k in 0..n { + let rk = bitreverse(k, log_n); + if k < rk { + a.swap(rk as usize, k as usize); + } + } + + let mut m = 1; + for _ in 0..log_n { + let w_m = omega.pow(&[(n / (2*m)) as u64]); + + let mut k = 0; + while k < n { + let mut w = E::Fr::one(); + for j in 0..m { + let mut t = a[(k+j+m) as usize]; + t.group_mul_assign(&w); + let mut tmp = a[(k+j) as usize]; + tmp.group_sub_assign(&t); + a[(k+j+m) as usize] = tmp; + a[(k+j) as usize].group_add_assign(&t); + w.mul_assign(&w_m); + } + + k += 2*m; + } + + m *= 2; + } +} + +pub(crate) fn parallel_fft>( + a: &mut [T], + worker: &Worker, + omega: &E::Fr, + log_n: u32, + log_cpus: u32 +) +{ + assert!(log_n >= log_cpus); + + let num_cpus = 1 << log_cpus; + let log_new_n = log_n - log_cpus; + let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; + let new_omega = omega.pow(&[num_cpus as u64]); + + worker.scope(0, |scope, _| { + let a = &*a; + + for (j, tmp) in tmp.iter_mut().enumerate() { + scope.spawn(move |_| { + // Shuffle into a sub-FFT + let omega_j = omega.pow(&[j as u64]); + let omega_step = omega.pow(&[(j as u64) << log_new_n]); + + let mut elt = E::Fr::one(); + for i in 0..(1 << log_new_n) { + for s in 0..num_cpus { + let idx = (i + (s << log_new_n)) % (1 << log_n); + let mut t = a[idx]; + t.group_mul_assign(&elt); + tmp[i].group_add_assign(&t); + elt.mul_assign(&omega_step); + } + elt.mul_assign(&omega_j); + } + + // Perform sub-FFT + serial_fft(tmp, &new_omega, log_new_n); + }); + } + }); + + // TODO: does this hurt or help? + worker.scope(a.len(), |scope, chunk| { + let tmp = &tmp; + + for (idx, a) in a.chunks_mut(chunk).enumerate() { + scope.spawn(move |_| { + let mut idx = idx * chunk; + let mask = (1 << log_cpus) - 1; + for a in a { + *a = tmp[idx & mask][idx >> log_cpus]; + idx += 1; + } + }); + } + }); +} \ No newline at end of file diff --git a/src/multiexp.rs b/src/parallel_multiexp.rs similarity index 65% rename from src/multiexp.rs rename to src/parallel_multiexp.rs index e834f4c..60e1dc3 100644 --- a/src/multiexp.rs +++ b/src/parallel_multiexp.rs @@ -11,137 +11,12 @@ use ff::{ ScalarEngine}; use std::sync::Arc; -use std::io; -use bit_vec::{self, BitVec}; -use std::iter; +use super::source::*; use futures::{Future}; use super::multicore::Worker; use super::SynthesisError; -/// An object that builds a source of bases. -pub trait SourceBuilder: Send + Sync + 'static + Clone { - type Source: Source; - - fn new(self) -> Self::Source; -} - -/// A source of bases, like an iterator. -pub trait Source { - /// Parses the element from the source. Fails if the point is at infinity. - fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError>; - - /// Skips `amt` elements from the source, avoiding deserialization. - fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; -} - -impl SourceBuilder for (Arc>, usize) { - type Source = (Arc>, usize); - - fn new(self) -> (Arc>, usize) { - (self.0.clone(), self.1) - } -} - -impl Source for (Arc>, usize) { - fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError> { - if self.0.len() <= self.1 { - return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases when adding from source").into()); - } - - if self.0[self.1].is_zero() { - return Err(SynthesisError::UnexpectedIdentity) - } - - to.add_assign_mixed(&self.0[self.1]); - - self.1 += 1; - - Ok(()) - } - - fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { - if self.0.len() <= self.1 { - return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases skipping from source").into()); - } - - self.1 += amt; - - Ok(()) - } -} - -pub trait QueryDensity { - /// Returns whether the base exists. - type Iter: Iterator; - - fn iter(self) -> Self::Iter; - fn get_query_size(self) -> Option; -} - -#[derive(Clone)] -pub struct FullDensity; - -impl AsRef for FullDensity { - fn as_ref(&self) -> &FullDensity { - self - } -} - -impl<'a> QueryDensity for &'a FullDensity { - type Iter = iter::Repeat; - - fn iter(self) -> Self::Iter { - iter::repeat(true) - } - - fn get_query_size(self) -> Option { - None - } -} - -#[derive(Clone)] -pub struct DensityTracker { - bv: BitVec, - total_density: usize -} - -impl<'a> QueryDensity for &'a DensityTracker { - type Iter = bit_vec::Iter<'a>; - - fn iter(self) -> Self::Iter { - self.bv.iter() - } - - fn get_query_size(self) -> Option { - Some(self.bv.len()) - } -} - -impl DensityTracker { - pub fn new() -> DensityTracker { - DensityTracker { - bv: BitVec::new(), - total_density: 0 - } - } - - pub fn add_element(&mut self) { - self.bv.push(false); - } - - pub fn inc(&mut self, idx: usize) { - if !self.bv.get(idx).unwrap() { - self.bv.set(idx, true); - self.total_density += 1; - } - } - - pub fn get_total_density(&self) -> usize { - self.total_density - } -} - /// This genious piece of code works in the following way: /// - choose `c` - the bit length of the region that one thread works on /// - make `2^c - 1` buckets and initialize them with `G = infinity` (that's equivalent of zero) @@ -307,6 +182,114 @@ pub fn multiexp( multiexp_inner(pool, bases, density_map, exponents, 0, c, true) } + +/// Perform multi-exponentiation. The caller is responsible for ensuring that +/// the number of bases is the same as the number of exponents. +pub fn dense_multiexp( + pool: &Worker, + bases: & [G], + exponents: & [<::Fr as PrimeField>::Repr] +) -> Result<::Projective, SynthesisError> +{ + if exponents.len() != bases.len() { + return Err(SynthesisError::AssignmentMissing); + } + let c = if exponents.len() < 32 { + 3u32 + } else { + (f64::from(exponents.len() as u32)).ln().ceil() as u32 + }; + + dense_multiexp_inner(pool, bases, exponents, 0, c, true) +} + +fn dense_multiexp_inner( + pool: &Worker, + bases: & [G], + exponents: & [<::Fr as PrimeField>::Repr], + mut skip: u32, + c: u32, + handle_trivial: bool +) -> Result<::Projective, SynthesisError> +{ + // Perform this region of the multiexp. We use a different strategy - go over region in parallel, + // then over another region, etc. No Arc required + let this = { + let this_region = pool.scope(bases.len(), |scope, chunk| { + let mut handles = vec![]; + let mut this_acc = ::Projective::zero(); + for (base, exp) in bases.chunks(chunk).zip(exponents.chunks(chunk)) { + let handle = scope.spawn(move |_| { + let mut buckets = vec![::Projective::zero(); (1 << c) - 1]; + // Accumulate the result + let mut acc = G::Projective::zero(); + let zero = ::Fr::zero().into_repr(); + let one = ::Fr::one().into_repr(); + + for (base, &exp) in base.iter().zip(exp.iter()) { + if exp != zero { + if exp == one { + if handle_trivial { + acc.add_assign_mixed(base); + } + } else { + let mut exp = exp; + exp.shr(skip); + let exp = exp.as_ref()[0] % (1 << c); + if exp != 0 { + buckets[(exp - 1) as usize].add_assign_mixed(base); + } + } + } + } + + // buckets are filled with the corresponding accumulated value, now sum + let mut running_sum = G::Projective::zero(); + for exp in buckets.into_iter().rev() { + running_sum.add_assign(&exp); + acc.add_assign(&running_sum); + } + + // acc contains values over this region + acc + }); + + handles.push(handle); + } + + // wait for all threads to finish + for r in handles.into_iter().rev() { + let thread_result = r.join().unwrap(); + this_acc.add_assign(&thread_result); + } + + this_acc + }); + + this_region + }; + + skip += c; + + if skip >= ::Fr::NUM_BITS { + // There isn't another region, and this will be the highest region + return Ok(this); + } else { + // next region is actually higher than this one, so double it enough times + let mut next_region = dense_multiexp_inner( + pool, bases, exponents, skip, c, false).unwrap(); + for _ in 0..c { + next_region.double(); + } + + next_region.add_assign(&this); + + return Ok(next_region); + } +} + + + #[test] fn test_with_bls12() { fn naive_multiexp( @@ -378,3 +361,41 @@ fn test_speed_with_bn256() { let time_per_sample = duration_ns/(SAMPLES as f64); println!("Tested on {} samples on {} CPUs with {} ns per multiplication", SAMPLES, cpus, time_per_sample); } + + +#[test] +fn test_dense_multiexp() { + use rand::{self, Rand}; + use pairing::bn256::Bn256; + use num_cpus; + + const SAMPLES: usize = 1 << 22; + + let rng = &mut rand::thread_rng(); + let v = (0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>(); + let g = (0..SAMPLES).map(|_| ::G1::rand(rng).into_affine()).collect::>(); + + let pool = Worker::new(); + + let start = std::time::Instant::now(); + + let dense = dense_multiexp( + &pool, &g, &v).unwrap(); + + let duration_ns = start.elapsed().as_nanos() as f64; + println!("{} ns for dense for {} samples", duration_ns, SAMPLES); + + let start = std::time::Instant::now(); + + let sparse = multiexp( + &pool, + (Arc::new(g), 0), + FullDensity, + Arc::new(v) + ).wait().unwrap(); + + let duration_ns = start.elapsed().as_nanos() as f64; + println!("{} ns for sparse for {} samples", duration_ns, SAMPLES); + + assert_eq!(dense, sparse); +} \ No newline at end of file diff --git a/src/serial_fft.rs b/src/serial_fft.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/serial_multiexp.rs b/src/serial_multiexp.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/sonic/cs/mod.rs b/src/sonic/cs/mod.rs index 342d408..4860cf5 100644 --- a/src/sonic/cs/mod.rs +++ b/src/sonic/cs/mod.rs @@ -217,3 +217,111 @@ impl SynthesisDriver for Basic { Ok(()) } } + +pub struct Nonassigning; + +impl SynthesisDriver for Nonassigning { + fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { + struct NonassigningSynthesizer> { + backend: B, + current_variable: Option, + _marker: PhantomData, + q: usize, + n: usize, + } + + impl> ConstraintSystem for NonassigningSynthesizer { + const ONE: Variable = Variable::A(1); + + fn alloc(&mut self, _value: F) -> Result + where + F: FnOnce() -> Result + { + match self.current_variable.take() { + Some(index) => { + let var_b = Variable::B(index); + + self.current_variable = None; + + Ok(var_b) + }, + None => { + self.n += 1; + let index = self.n; + self.backend.new_multiplication_gate(); + + let var_a = Variable::A(index); + + self.current_variable = Some(index); + + Ok(var_a) + } + } + } + + fn alloc_input(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + let input_var = self.alloc(value)?; + + self.enforce_zero(LinearCombination::zero() + input_var); + self.backend.new_k_power(self.q); + + Ok(input_var) + } + + fn enforce_zero(&mut self, lc: LinearCombination) + { + self.q += 1; + self.backend.new_linear_constraint(); + + for (var, coeff) in lc.as_ref() { + self.backend.insert_coefficient(*var, *coeff); + } + } + + fn multiply(&mut self, _values: F) -> Result<(Variable, Variable, Variable), SynthesisError> + where + F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError> + { + self.n += 1; + let index = self.n; + self.backend.new_multiplication_gate(); + + let a = Variable::A(index); + let b = Variable::B(index); + let c = Variable::C(index); + + Ok((a, b, c)) + } + + fn get_value(&self, var: Variable) -> Result { + self.backend.get_var(var).ok_or(()) + } + } + + let mut tmp: NonassigningSynthesizer = NonassigningSynthesizer { + backend: backend, + current_variable: None, + _marker: PhantomData, + q: 0, + n: 0, + }; + + let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, as ConstraintSystem>::ONE) { + (Variable::A(1), Variable::A(1)) => {}, + _ => panic!("one variable is incorrect") + } + + circuit.synthesize(&mut tmp)?; + + // TODO: add blinding factors so we actually get zero-knowledge + + // println!("n = {}", tmp.n); + + Ok(()) + } +} \ No newline at end of file diff --git a/src/sonic/helped/adapted_prover.rs b/src/sonic/helped/adapted_prover.rs new file mode 100644 index 0000000..2811db4 --- /dev/null +++ b/src/sonic/helped/adapted_prover.rs @@ -0,0 +1,146 @@ +use ff::{Field}; +use pairing::{Engine, CurveProjective}; +use std::marker::PhantomData; + +use super::{Proof, SxyAdvice}; +use super::batch::Batch; +use super::poly::{SxEval, SyEval}; +use super::parameters::{Parameters}; + +use crate::SynthesisError; + +use crate::sonic::transcript::{Transcript, TranscriptProtocol}; +use crate::sonic::util::*; +use crate::sonic::cs::{Backend, SynthesisDriver}; +use crate::{Circuit}; +use crate::sonic::sonic::AdaptorCircuit; +use crate::sonic::srs::SRS; +use crate::sonic::cs::Basic; +use super::prover::create_advice as create_advice_sonic_circuit; +use super::prover::create_advice_on_information_and_srs as create_advice_on_information_and_srs_sonic_circuit; +use super::prover::create_proof_on_srs as create_proof_on_srs_sonic_circuit; + +// pub fn create_advice_on_information_and_srs + Clone, S: SynthesisDriver>( +pub fn create_advice_on_information_and_srs + Clone>( + circuit: C, + proof: &Proof, + srs: &SRS, + n: usize +) -> Result, SynthesisError> +{ + let adapted_circuit = AdaptorCircuit(circuit); + + create_advice_on_information_and_srs_sonic_circuit::<_, _, Basic>(&adapted_circuit, proof, srs, n) +} + +// pub fn create_advice + Clone, S: SynthesisDriver>( +pub fn create_advice + Clone>( + circuit: C, + proof: &Proof, + parameters: &Parameters, +) -> Result, SynthesisError> +{ + let n = parameters.vk.n; + create_advice_on_information_and_srs::(circuit, proof, ¶meters.srs, n) +} + +// pub fn create_advice_on_srs + Clone, S: SynthesisDriver>( +pub fn create_advice_on_srs + Clone>( + circuit: C, + proof: &Proof, + srs: &SRS +) -> Result, SynthesisError> +{ + use crate::sonic::cs::Nonassigning; + + let adapted_circuit = AdaptorCircuit(circuit.clone()); + // annoying, but we need n to compute s(z, y), and this isn't + // precomputed anywhere yet + let n = { + struct CountN { + n: usize + } + + impl<'a, E: Engine> Backend for &'a mut CountN { + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + } + + let mut tmp = CountN{n:0}; + Nonassigning::synthesize(&mut tmp, &adapted_circuit)?; + + tmp.n + }; + + create_advice_on_information_and_srs::(circuit, proof, srs, n) +} + +// pub fn create_proof + Clone, S: SynthesisDriver>( +pub fn create_proof + Clone>( + circuit: C, + parameters: &Parameters +) -> Result, SynthesisError> { + create_proof_on_srs::(circuit, ¶meters.srs) +} + +// pub fn create_proof_on_srs + Clone, S: SynthesisDriver>( +pub fn create_proof_on_srs + Clone>( + circuit: C, + srs: &SRS +) -> Result, SynthesisError> +{ + let adapted_circuit = AdaptorCircuit(circuit); + + create_proof_on_srs_sonic_circuit::<_, _, Basic>(&adapted_circuit, srs) +} + +// #[test] +// fn my_fun_circuit_test() { +// use ff::PrimeField; +// use pairing::bls12_381::{Bls12, Fr}; +// use super::*; +// use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; + +// struct MyCircuit; + +// impl Circuit for MyCircuit { +// fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { +// let (a, b, _) = cs.multiply(|| { +// Ok(( +// E::Fr::from_str("10").unwrap(), +// E::Fr::from_str("20").unwrap(), +// E::Fr::from_str("200").unwrap(), +// )) +// })?; + +// cs.enforce_zero(LinearCombination::from(a) + a - b); + +// //let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?; + +// //cs.enforce_zero(LinearCombination::from(b) - multiplier); + +// Ok(()) +// } +// } + +// let srs = SRS::::new( +// 20, +// Fr::from_str("22222").unwrap(), +// Fr::from_str("33333333").unwrap(), +// ); +// let proof = create_proof_on_srs::(&MyCircuit, &srs).unwrap(); + +// use std::time::{Instant}; +// let start = Instant::now(); +// let mut batch = MultiVerifier::::new(MyCircuit, &srs).unwrap(); + +// for _ in 0..1 { +// batch.add_proof(&proof, &[/*Fr::from_str("20").unwrap()*/], |_, _| None); +// } + +// assert!(batch.check_all()); + +// let elapsed = start.elapsed(); +// println!("time to verify: {:?}", elapsed); +// } diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index 230ff89..ce7cea9 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -688,104 +688,4 @@ pub fn generate_srs( h_positive_x_alpha: h_positive_x_alpha, } ) -} - -#[test] -fn parameters_generation() { - use pairing::bls12_381::{Bls12, Fr}; - struct MySillyCircuit { - a: Option, - b: Option - } - - impl Circuit for MySillyCircuit { - fn synthesize>( - self, - cs: &mut CS - ) -> Result<(), SynthesisError> - { - let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; - let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; - let c = cs.alloc_input(|| "c", || { - let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; - let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; - - a.mul_assign(&b); - Ok(a) - })?; - - cs.enforce( - || "a*b=c", - |lc| lc + a, - |lc| lc + b, - |lc| lc + c - ); - - Ok(()) - } - } - - use rand::{Rand, thread_rng}; - - let info = get_circuit_parameters::(MySillyCircuit { a: None, b: None }).expect("Must get circuit info"); - println!("{:?}", info); - let rng = &mut thread_rng(); - - let x: Fr = rng.gen(); - let alpha: Fr = rng.gen(); - - let params = generate_parameters::(MySillyCircuit { a: None, b: None }, alpha, x).unwrap(); - let srs = generate_srs::(alpha, x, info.n * 100).unwrap(); - let naive_srs = SRS::::new( - info.n * 100, - x, - alpha, - ); - - assert!(srs == naive_srs); - - let params_on_srs = generate_parameters_on_srs_and_information::(&srs, info.clone()).unwrap(); - - assert!(params == params_on_srs); - - { - let mut v = vec![]; - - params.write(&mut v).unwrap(); - - let de_params = Parameters::read(&v[..], true).unwrap(); - assert!(params == de_params); - - let de_params = Parameters::read(&v[..], false).unwrap(); - assert!(params == de_params); - } - - // let pvk = prepare_verifying_key::(¶ms.vk); - - // for _ in 0..100 { - // let a = Fr::rand(rng); - // let b = Fr::rand(rng); - // let mut c = a; - // c.mul_assign(&b); - - // let proof = create_random_proof( - // MySillyCircuit { - // a: Some(a), - // b: Some(b) - // }, - // ¶ms, - // rng - // ).unwrap(); - - // let mut v = vec![]; - // proof.write(&mut v).unwrap(); - - // assert_eq!(v.len(), 192); - - // let de_proof = Proof::read(&v[..]).unwrap(); - // assert!(proof == de_proof); - - // assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); - // assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); - // } } \ No newline at end of file diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index 8cfda80..86ca823 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -6,22 +6,18 @@ use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; mod verifier; -mod prover; mod batch; mod poly; mod helper; mod parameters; mod generator; +mod adapted_prover; + +pub mod prover; pub use self::batch::{Batch}; pub use self::helper::{Aggregate, create_aggregate}; pub use self::verifier::{MultiVerifier}; -pub use self::prover::{ - create_advice, - create_advice_on_information_and_srs, - create_advice_on_srs, - create_proof -}; pub use self::generator::{ CircuitParameters, @@ -32,4 +28,11 @@ pub use self::generator::{ generate_srs, get_circuit_parameters }; -pub use self::parameters::{Proof, SxyAdvice, Parameters, VerifyingKey, PreparedVerifyingKey}; \ No newline at end of file +pub use self::parameters::{Proof, SxyAdvice, Parameters, VerifyingKey, PreparedVerifyingKey}; +pub use self::adapted_prover::{ + create_advice, + create_advice_on_srs, + create_advice_on_information_and_srs, + create_proof, + create_proof_on_srs, +}; \ No newline at end of file diff --git a/src/sonic/helped/parameters.rs b/src/sonic/helped/parameters.rs index 255899c..dfc27c2 100644 --- a/src/sonic/helped/parameters.rs +++ b/src/sonic/helped/parameters.rs @@ -19,15 +19,23 @@ use std::io::{self, Read, Write}; use std::sync::Arc; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Eq)] pub struct SxyAdvice { pub s: E::G1Affine, pub opening: E::G1Affine, pub szy: E::Fr, } +impl PartialEq for SxyAdvice { + fn eq(&self, other: &SxyAdvice) -> bool { + self.s == other.s && + self.opening == other.opening && + self.szy == other.szy + } +} -#[derive(Clone, Debug, PartialEq, Eq)] + +#[derive(Clone, Debug, Eq)] pub struct Proof { pub r: E::G1Affine, pub t: E::G1Affine, @@ -37,6 +45,17 @@ pub struct Proof { pub zy_opening: E::G1Affine } +impl PartialEq for Proof { + fn eq(&self, other: &Proof) -> bool { + self.r == other.r && + self.t == other.t && + self.rz == other.rz && + self.rzy == other.rzy && + self.z_opening == other.z_opening && + self.zy_opening == other.zy_opening + } +} + impl Proof { pub fn write( &self, @@ -49,6 +68,7 @@ impl Proof { let mut buffer = vec![]; self.rz.into_repr().write_be(&mut buffer)?; writer.write_all(&buffer[..])?; + let mut buffer = vec![]; self.rzy.into_repr().write_be(&mut buffer)?; writer.write_all(&buffer[..])?; writer.write_all(self.z_opening.into_compressed().as_ref())?; @@ -362,185 +382,105 @@ impl Parameters { } } -// pub trait ParameterSource { -// type G1Builder: SourceBuilder; -// type G2Builder: SourceBuilder; +#[test] +fn parameters_generation() { + use crate::{ConstraintSystem, Circuit}; -// fn get_vk( -// &mut self, -// num_ic: usize -// ) -> Result, SynthesisError>; -// fn get_h( -// &mut self, -// num_h: usize -// ) -> Result; -// fn get_l( -// &mut self, -// num_l: usize -// ) -> Result; -// fn get_a( -// &mut self, -// num_inputs: usize, -// num_aux: usize -// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; -// fn get_b_g1( -// &mut self, -// num_inputs: usize, -// num_aux: usize -// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; -// fn get_b_g2( -// &mut self, -// num_inputs: usize, -// num_aux: usize -// ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>; -// } + use pairing::bls12_381::{Bls12, Fr}; -// impl<'a, E: Engine> ParameterSource for &'a Parameters { -// type G1Builder = (Arc>, usize); -// type G2Builder = (Arc>, usize); + #[derive(Clone)] + struct MySillyCircuit { + a: Option, + b: Option + } -// fn get_vk( -// &mut self, -// _: usize -// ) -> Result, SynthesisError> -// { -// Ok(self.vk.clone()) -// } + impl Circuit for MySillyCircuit { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError> + { + let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; + let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; + let c = cs.alloc_input(|| "c", || { + let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; + let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; -// fn get_h( -// &mut self, -// _: usize -// ) -> Result -// { -// Ok((self.h.clone(), 0)) -// } + a.mul_assign(&b); + Ok(a) + })?; -// fn get_l( -// &mut self, -// _: usize -// ) -> Result -// { -// Ok((self.l.clone(), 0)) -// } + cs.enforce( + || "a*b=c", + |lc| lc + a, + |lc| lc + b, + |lc| lc + c + ); -// fn get_a( -// &mut self, -// num_inputs: usize, -// _: usize -// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> -// { -// Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs))) -// } + Ok(()) + } + } -// fn get_b_g1( -// &mut self, -// num_inputs: usize, -// _: usize -// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> -// { -// Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs))) -// } + use rand::{Rng, Rand, thread_rng}; + use super::{generate_parameters, get_circuit_parameters, generate_srs, generate_parameters_on_srs_and_information}; + use super::adapted_prover::create_proof; -// fn get_b_g2( -// &mut self, -// num_inputs: usize, -// _: usize -// ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> -// { -// Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs))) -// } -// } + let info = get_circuit_parameters::(MySillyCircuit { a: None, b: None }).expect("Must get circuit info"); + println!("{:?}", info); + let rng = &mut thread_rng(); -// #[cfg(test)] -// mod test_with_bls12_381 { -// use super::*; -// use {Circuit, SynthesisError, ConstraintSystem}; + let x: Fr = rng.gen(); + let alpha: Fr = rng.gen(); -// use rand::{Rand, thread_rng}; -// use ff::{Field}; -// use pairing::bls12_381::{Bls12, Fr}; + let params = generate_parameters::(MySillyCircuit { a: None, b: None }, alpha, x).unwrap(); + let srs = generate_srs::(alpha, x, info.n * 100).unwrap(); + let naive_srs = SRS::::new( + info.n * 100, + x, + alpha, + ); -// #[test] -// fn serialization() { -// struct MySillyCircuit { -// a: Option, -// b: Option -// } + assert!(srs == naive_srs); -// impl Circuit for MySillyCircuit { -// fn synthesize>( -// self, -// cs: &mut CS -// ) -> Result<(), SynthesisError> -// { -// let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; -// let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; -// let c = cs.alloc_input(|| "c", || { -// let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; -// let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; + let params_on_srs = generate_parameters_on_srs_and_information::(&srs, info.clone()).unwrap(); -// a.mul_assign(&b); -// Ok(a) -// })?; + assert!(params == params_on_srs); -// cs.enforce( -// || "a*b=c", -// |lc| lc + a, -// |lc| lc + b, -// |lc| lc + c -// ); + { + let mut v = vec![]; -// Ok(()) -// } -// } + params.write(&mut v).unwrap(); -// let rng = &mut thread_rng(); + let de_params = Parameters::read(&v[..], true).unwrap(); + assert!(params == de_params); -// let params = generate_random_parameters::( -// MySillyCircuit { a: None, b: None }, -// rng -// ).unwrap(); + let de_params = Parameters::read(&v[..], false).unwrap(); + assert!(params == de_params); + } -// { -// let mut v = vec![]; + for _ in 0..100 { + let a = Fr::rand(rng); + let b = Fr::rand(rng); + let mut c = a; + c.mul_assign(&b); -// params.write(&mut v).unwrap(); -// assert_eq!(v.len(), 2136); + let proof = create_proof ( + MySillyCircuit { + a: Some(a), + b: Some(b) + }, + ¶ms, + ).unwrap(); -// let de_params = Parameters::read(&v[..], true).unwrap(); -// assert!(params == de_params); + let mut v = vec![]; + proof.write(&mut v).unwrap(); -// let de_params = Parameters::read(&v[..], false).unwrap(); -// assert!(params == de_params); -// } + assert_eq!(v.len(), 256); -// let pvk = prepare_verifying_key::(¶ms.vk); + let de_proof = Proof::read(&v[..]).unwrap(); + assert!(proof == de_proof); -// for _ in 0..100 { -// let a = Fr::rand(rng); -// let b = Fr::rand(rng); -// let mut c = a; -// c.mul_assign(&b); - -// let proof = create_random_proof( -// MySillyCircuit { -// a: Some(a), -// b: Some(b) -// }, -// ¶ms, -// rng -// ).unwrap(); - -// let mut v = vec![]; -// proof.write(&mut v).unwrap(); - -// assert_eq!(v.len(), 192); - -// let de_proof = Proof::read(&v[..]).unwrap(); -// assert!(proof == de_proof); - -// assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); -// assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); -// } -// } -// } \ No newline at end of file + // assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); + // assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); + } +} \ No newline at end of file diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 43355d6..613aa3a 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -133,6 +133,13 @@ pub fn create_advice_on_srs, S: SynthesisDriver>( } pub fn create_proof, S: SynthesisDriver>( + circuit: &C, + parameters: &Parameters +) -> Result, SynthesisError> { + create_proof_on_srs::(circuit, ¶meters.srs) +} + +pub fn create_proof_on_srs, S: SynthesisDriver>( circuit: &C, srs: &SRS ) -> Result, SynthesisError> @@ -209,6 +216,10 @@ pub fn create_proof, S: SynthesisDriver>( let y: E::Fr = transcript.get_challenge_scalar(); + // create r(X, 1) by observation that it's just a series of coefficients. + // Used representation is for powers X^{-2n}...X^{-n-1}, X^{-n}...X^{-1}, X^{1}...X^{n} + // Same representation is ok for r(X, Y) too cause powers always match + // TODO: add blindings c_{n+1}*X^{-2n - 1}, c_{n+2}*X^{-2n - 2}, c_{n+3}*X^{-2n - 3}, c_{n+4}*X^{-2n - 4} let mut rx1 = wires.b; rx1.extend(wires.c); rx1.reverse(); @@ -219,11 +230,13 @@ pub fn create_proof, S: SynthesisDriver>( let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; let mut tmp = y.pow(&[n as u64]); + // evaluate r(X, y) for rxy in rxy.iter_mut().rev() { rxy.mul_assign(&tmp); tmp.mul_assign(&y_inv); } + // negative powers [-n, -1], positive [1, 2n] let (s_poly_negative, s_poly_positive) = { let mut tmp = SxEval::new(y, n); S::synthesize(&mut tmp, circuit)?; @@ -231,10 +244,11 @@ pub fn create_proof, S: SynthesisDriver>( tmp.poly() }; + // r'(X, y) = r(X, y) + s(X, y). Note `y` - those are evaluated at the point already let mut rxy_prime = rxy.clone(); { rxy_prime.resize(4 * n + 1, E::Fr::zero()); - // Add s(x, y) + // add coefficients in front of X^{-2n}...X^{-n-1}, X^{-n}...X^{-1} for (r, s) in rxy_prime[0..(2 * n)] .iter_mut() .rev() @@ -242,14 +256,18 @@ pub fn create_proof, S: SynthesisDriver>( { r.add_assign(&s); } + // add coefficients in front of X^{1}...X^{n}, X^{n+1}...X^{2*n} for (r, s) in rxy_prime[(2 * n + 1)..].iter_mut().zip(s_poly_positive) { r.add_assign(&s); } } + // t(X, y) = r'(X, y)*r(X, 1) and will be later evaluated at z + // contained degree in respect to X are from -4*n to 3*n including X^0 let mut txy = multiply_polynomials::(rx1.clone(), rxy_prime); txy[4 * n] = E::Fr::zero(); // -k(y) + // commit to t(X, y) to later open at z let t = multiexp( srs.g_positive_x_alpha[0..(3 * n)] .iter() @@ -392,7 +410,7 @@ fn my_fun_circuit_test() { Fr::from_str("22222").unwrap(), Fr::from_str("33333333").unwrap(), ); - let proof = create_proof::(&MyCircuit, &srs).unwrap(); + let proof = self::create_proof_on_srs::(&MyCircuit, &srs).unwrap(); use std::time::{Instant}; let start = Instant::now(); diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 808cd2f..f6fada4 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -226,6 +226,7 @@ impl, S: SynthesisDriver> MultiVerifier { }); // Finally, compute t(z, y) + // t(z, y) = (r(z, y) + s(z,y))*r(z, 1) - k(y) let mut tzy = proof.rzy; tzy.add_assign(&szy); tzy.mul_assign(&proof.rz); diff --git a/src/sonic/sonic/adaptor.rs b/src/sonic/sonic/adaptor.rs index cf96240..f38eaed 100644 --- a/src/sonic/sonic/adaptor.rs +++ b/src/sonic/sonic/adaptor.rs @@ -111,7 +111,7 @@ impl<'a, E: Engine, CS: SonicConstraintSystem + 'a> crate::ConstraintSystem + 'a> crate::ConstraintSystem(pub T); impl<'a, E: Engine, C: crate::Circuit + Clone> SonicCircuit for AdaptorCircuit { diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 2bc2850..bc12159 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -202,20 +202,18 @@ where use pairing::CurveAffine; use crate::multicore::Worker; - use crate::multiexp; - use crate::multiexp::FullDensity; + use crate::multiexp::dense_multiexp; - let s: Arc::Repr>> = Arc::new(s.into_iter().map(|e| e.into_repr()).collect::>()); - let g: Arc> = Arc::new(g.into_iter().map(|e| *e).collect::>()); + let s: Vec<::Repr> = s.into_iter().map(|e| e.into_repr()).collect::>(); + let g: Vec = g.into_iter().map(|e| *e).collect::>(); let pool = Worker::new(); - let result = multiexp::multiexp( + let result = dense_multiexp( &pool, - (g, 0), - FullDensity, - s - ).wait().unwrap(); + &g, + &s + ).unwrap(); result } @@ -379,7 +377,6 @@ fn laurent_division() { } pub fn multiply_polynomials(a: Vec, b: Vec) -> Vec { - println!("Multiplying polynomails of degrees {} and {}", a.len(), b.len()); let result_len = a.len() + b.len() - 1; use crate::multicore::Worker; diff --git a/src/source.rs b/src/source.rs new file mode 100644 index 0000000..5a77d8f --- /dev/null +++ b/src/source.rs @@ -0,0 +1,141 @@ +use pairing::{ + CurveAffine, + CurveProjective, + Engine +}; + +use ff::{ + PrimeField, + Field, + PrimeFieldRepr, + ScalarEngine}; + +use std::sync::Arc; +use std::io; +use bit_vec::{self, BitVec}; +use std::iter; + +use super::SynthesisError; + +/// An object that builds a source of bases. +pub trait SourceBuilder: Send + Sync + 'static + Clone { + type Source: Source; + + fn new(self) -> Self::Source; +} + +/// A source of bases, like an iterator. +pub trait Source { + /// Parses the element from the source. Fails if the point is at infinity. + fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError>; + + /// Skips `amt` elements from the source, avoiding deserialization. + fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; +} + +impl SourceBuilder for (Arc>, usize) { + type Source = (Arc>, usize); + + fn new(self) -> (Arc>, usize) { + (self.0.clone(), self.1) + } +} + +impl Source for (Arc>, usize) { + fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError> { + if self.0.len() <= self.1 { + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases when adding from source").into()); + } + + if self.0[self.1].is_zero() { + return Err(SynthesisError::UnexpectedIdentity) + } + + to.add_assign_mixed(&self.0[self.1]); + + self.1 += 1; + + Ok(()) + } + + fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { + if self.0.len() <= self.1 { + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases skipping from source").into()); + } + + self.1 += amt; + + Ok(()) + } +} + +pub trait QueryDensity { + /// Returns whether the base exists. + type Iter: Iterator; + + fn iter(self) -> Self::Iter; + fn get_query_size(self) -> Option; +} + +#[derive(Clone)] +pub struct FullDensity; + +impl AsRef for FullDensity { + fn as_ref(&self) -> &FullDensity { + self + } +} + +impl<'a> QueryDensity for &'a FullDensity { + type Iter = iter::Repeat; + + fn iter(self) -> Self::Iter { + iter::repeat(true) + } + + fn get_query_size(self) -> Option { + None + } +} + +#[derive(Clone)] +pub struct DensityTracker { + bv: BitVec, + total_density: usize +} + +impl<'a> QueryDensity for &'a DensityTracker { + type Iter = bit_vec::Iter<'a>; + + fn iter(self) -> Self::Iter { + self.bv.iter() + } + + fn get_query_size(self) -> Option { + Some(self.bv.len()) + } +} + +impl DensityTracker { + pub fn new() -> DensityTracker { + DensityTracker { + bv: BitVec::new(), + total_density: 0 + } + } + + pub fn add_element(&mut self) { + self.bv.push(false); + } + + pub fn inc(&mut self, idx: usize) { + if !self.bv.get(idx).unwrap() { + self.bv.set(idx, true); + self.total_density += 1; + } + } + + pub fn get_total_density(&self) -> usize { + self.total_density + } +} \ No newline at end of file diff --git a/tests/mimc.rs b/tests/mimc.rs index 7d569a4..7a37a7f 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -479,16 +479,17 @@ fn test_sonic_mimc() { use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; - use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier, create_advice_on_srs}; + use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; + use bellman::sonic::helped::{create_aggregate, MultiVerifier}; println!("creating proof"); let start = Instant::now(); - let proof = create_proof::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating advice"); let start = Instant::now(); - let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs); + let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating aggregate for {} proofs", samples); @@ -578,19 +579,20 @@ fn test_inputs_into_sonic_mimc() { use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; - use bellman::sonic::helped::{create_proof, get_circuit_parameters, create_advice, create_aggregate, MultiVerifier, create_advice_on_srs}; + use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; + use bellman::sonic::helped::{create_aggregate, MultiVerifier, get_circuit_parameters}; let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); println!("{:?}", info); println!("creating proof"); let start = Instant::now(); - let proof = create_proof::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating advice"); let start = Instant::now(); - let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs); + let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating aggregate for {} proofs", samples); @@ -624,32 +626,6 @@ fn test_inputs_into_sonic_mimc() { } println!("done in {:?}", start.elapsed()); } - - { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); - println!("verifying 1 proof with advice"); - let start = Instant::now(); - { - for _ in 0..1 { - verifier.add_proof_with_advice(&proof, &[image], &advice); - } - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } - - { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); - println!("verifying 100 proofs with advice"); - let start = Instant::now(); - { - for _ in 0..samples { - verifier.add_proof_with_advice(&proof, &[image], &advice); - } - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } { let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap();