Merge pull request #10 from matter-labs/optimizations

Optimization using prefetch, available for nightly Rust and feature
This commit is contained in:
Alexander 2019-07-13 22:01:21 +03:00 committed by GitHub
commit 1aff83c6ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 5839 additions and 1198 deletions

@ -6,7 +6,7 @@ homepage = "https://github.com/matter-labs/bellman"
license = "MIT/Apache-2.0"
name = "bellman_ce"
repository = "https://github.com/matter-labs/bellman"
version = "0.3.0"
version = "0.3.1"
edition = "2018"
[lib]
@ -18,14 +18,16 @@ bit-vec = "0.4.4"
futures = "0.1"
cfg-if = "0.1.7"
#pairing = { git = 'https://github.com/matterinc/pairing', tag = "0.16.2" }
pairing_ce = { version = "0.17.0" }
#pairing = {package = "pairing_ce", path = "../pairing" }
pairing = {package = "pairing_ce", version = "0.18.0" }
byteorder = "1"
futures-cpupool = {version = "0.1", optional = true}
num_cpus = {version = "1", optional = true}
crossbeam = {version = "0.7.1", optional = true}
prefetch = {version = "0.2", optional = true}
web-sys = {version = "0.3.17", optional = true, features = ["console", "Performance", "Window"]}
tiny-keccak = {version = "1.4.2", optional = true}
@ -33,9 +35,10 @@ blake2-rfc = {version = "0.2.18", optional = true}
[features]
default = ["multicore"]
#default = ["multicore", "gm17", "sonic"]
#default = ["multicore", "nightly"]
#default = ["wasm"]
multicore = ["futures-cpupool", "num_cpus", "crossbeam"]
sonic = ["tiny-keccak", "blake2-rfc"]
gm17 = []
wasm = ["web-sys"]
nightly = ["prefetch"]

@ -2,6 +2,38 @@
Originally developed for ZCash, with extensions from us to make it a little more pleasant. Uses our "community edition" pairing for Ethereum's BN256 curve. Now published as `bellman_ce` on `crate.io` to allow ease of use.
## Features
There are two available features to be used in production and are stable and will not be changed in terms of API. Those are Groth16 proof system implementation.
- `multicore` feature (enabled by default) is intended to be run on PC and in environments that have support of a full `std` including threading.
- `singlecore` feature is mainly intended for WASM systems, where non-compatible external crates are removed, along with all the multithreading.
Due to request to have a maintainable repo with WASM compatibility those features were implemented during the implementation of GM17 and SONIC proof systems. That's why there are two more features that are incomplete and will have breaking changes in a future. Those are for interested enthusiasts.
- `gm17` - is incomplete and most likely will get attention after putting SONIC to completeness.
- `sonic` - 90% complete. Original implementation of `helped` protocol is integrated with API similar to the Groth16, along with wrapping adapters to use existing circuits without any changes. `unhelped` version is not yet complete, but all cryptographical primitives are implemented and tested. Right now it's a priority.
## Future progress
It's intended to add `GM17` proof system and `SONIC` proof system.
## Features
There are two available features to be used in production and are stable and will not be changed in terms of API. Those are Groth16 proof system implementation.
- `multicore` feature (enabled by default) is intended to be run on PC and in environments that have support of a full `std` including threading.
- `singlecore` feature is mainly intended for WASM systems, where non-compatible external crates are removed, along with all the multithreading.
Due to request to have a maintainable repo with WASM compatibility those features were implemented during the implementation of GM17 and SONIC proof systems. That's why there are two more features that are incomplete and will have breaking changes in a future. Those are for interested enthusiasts.
- `gm17` - is incomplete and most likely will get attention after putting SONIC to completeness.
- `sonic` - 90% complete. Original implementation of `helped` protocol is integrated with API similar to the Groth16, along with wrapping adapters to use existing circuits without any changes. `unhelped` version is not yet complete, but all cryptographical primitives are implemented and tested. Right now it's a priority.
## Future progress
It's intended to add `GM17` proof system and `SONIC` proof system.
## License
Licensed under either of

@ -20,7 +20,7 @@ pub trait Circuit<E: Engine> {
}
/// Represents a variable in our constraint system.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct Variable(pub(crate) Index);
impl Variable {
@ -39,7 +39,7 @@ impl Variable {
/// Represents the index of either an input variable or
/// auxillary variable.
#[derive(Copy, Clone, PartialEq, Debug)]
#[derive(Copy, Clone, PartialEq, Debug, Hash, Eq)]
pub enum Index {
Input(usize),
Aux(usize)

@ -86,6 +86,48 @@ fn eval<E: Engine>(
acc
}
pub(crate) fn field_elements_into_representations<E: Engine>(
worker: &Worker,
scalars: Vec<E::Fr>
) -> Result<Vec<<E::Fr as PrimeField>::Repr>, SynthesisError>
{
let mut representations = vec![<E::Fr as PrimeField>::Repr::default(); scalars.len()];
worker.scope(scalars.len(), |scope, chunk| {
for (scalar, repr) in scalars.chunks(chunk)
.zip(representations.chunks_mut(chunk)) {
scope.spawn(move |_| {
for (scalar, repr) in scalar.iter()
.zip(repr.iter_mut()) {
*repr = scalar.into_repr();
}
});
}
});
Ok(representations)
}
pub(crate) fn scalars_into_representations<E: Engine>(
worker: &Worker,
scalars: Vec<Scalar<E>>
) -> Result<Vec<<E::Fr as PrimeField>::Repr>, SynthesisError>
{
let mut representations = vec![<E::Fr as PrimeField>::Repr::default(); scalars.len()];
worker.scope(scalars.len(), |scope, chunk| {
for (scalar, repr) in scalars.chunks(chunk)
.zip(representations.chunks_mut(chunk)) {
scope.spawn(move |_| {
for (scalar, repr) in scalar.iter()
.zip(repr.iter_mut()) {
*repr = scalar.0.into_repr();
}
});
}
});
Ok(representations)
}
// This is a proving assignment with densities precalculated
pub struct PreparedProver<E: Engine>{
assignment: ProvingAssignment<E>,
@ -145,7 +187,7 @@ pub fn prepare_prover<E, C>(
impl<E:Engine> PreparedProver<E> {
pub fn create_random_proof<R, P: ParameterSource<E>>(
& self,
self,
params: P,
rng: &mut R
) -> Result<Proof<E>, SynthesisError>
@ -158,16 +200,16 @@ impl<E:Engine> PreparedProver<E> {
}
pub fn create_proof<P: ParameterSource<E>>(
& self,
self,
mut params: P,
r: E::Fr,
s: E::Fr
) -> Result<Proof<E>, SynthesisError>
{
let prover = self.assignment.clone();
let prover = self.assignment;
let worker = Worker::new();
let vk = params.get_vk(self.assignment.input_assignment.len())?;
let vk = params.get_vk(prover.input_assignment.len())?;
let stopwatch = Stopwatch::new();
@ -202,7 +244,8 @@ impl<E:Engine> PreparedProver<E> {
a.truncate(a_len);
// TODO: parallelize if it's even helpful
// TODO: in large settings it may worth to parallelize
let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::<Vec<_>>());
let a = Arc::new(scalars_into_representations::<E>(&worker, a)?);
// let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::<Vec<_>>());
multiexp(&worker, params.get_h(a.len())?, FullDensity, a)
};
@ -213,13 +256,19 @@ impl<E:Engine> PreparedProver<E> {
// TODO: Check that difference in operations for different chunks is small
let input_len = prover.input_assignment.len();
let aux_len = prover.aux_assignment.len();
let input_assignment = Arc::new(field_elements_into_representations::<E>(&worker, prover.input_assignment)?);
let aux_assignment = Arc::new(field_elements_into_representations::<E>(&worker, prover.aux_assignment)?);
// TODO: parallelize if it's even helpful
// TODO: in large settings it may worth to parallelize
let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
// let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
// let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
let input_len = input_assignment.len();
let aux_len = aux_assignment.len();
// let input_len = input_assignment.len();
// let aux_len = aux_assignment.len();
elog_verbose!("H query is dense in G1,\nOther queries are {} elements in G1 and {} elements in G2",
2*(input_len + aux_len) + aux_len, input_len + aux_len);
@ -402,153 +451,13 @@ pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
pub fn create_proof<E, C, P: ParameterSource<E>>(
circuit: C,
mut params: P,
params: P,
r: E::Fr,
s: E::Fr
) -> Result<Proof<E>, SynthesisError>
where E: Engine, C: Circuit<E>
{
let mut prover = ProvingAssignment {
a_aux_density: DensityTracker::new(),
b_input_density: DensityTracker::new(),
b_aux_density: DensityTracker::new(),
a: vec![],
b: vec![],
c: vec![],
input_assignment: vec![],
aux_assignment: vec![]
};
let prover = prepare_prover(circuit)?;
prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
circuit.synthesize(&mut prover)?;
for i in 0..prover.input_assignment.len() {
prover.enforce(|| "",
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
}
let worker = Worker::new();
let vk = params.get_vk(prover.input_assignment.len())?;
let stopwatch = Stopwatch::new();
let h = {
let mut a = EvaluationDomain::from_coeffs(prover.a)?;
let mut b = EvaluationDomain::from_coeffs(prover.b)?;
let mut c = EvaluationDomain::from_coeffs(prover.c)?;
elog_verbose!("H query domain size is {}", a.as_ref().len());
// here a coset is a domain where denominator (z) does not vanish
// inverse FFT is an interpolation
a.ifft(&worker);
// evaluate in coset
a.coset_fft(&worker);
// same is for B and C
b.ifft(&worker);
b.coset_fft(&worker);
c.ifft(&worker);
c.coset_fft(&worker);
// do A*B-C in coset
a.mul_assign(&worker, &b);
drop(b);
a.sub_assign(&worker, &c);
drop(c);
// z does not vanish in coset, so we divide by non-zero
a.divide_by_z_on_coset(&worker);
// interpolate back in coset
a.icoset_fft(&worker);
let mut a = a.into_coeffs();
let a_len = a.len() - 1;
a.truncate(a_len);
// TODO: parallelize if it's even helpful
// TODO: in large settings it may worth to parallelize
let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::<Vec<_>>());
multiexp(&worker, params.get_h(a.len())?, FullDensity, a)
};
elog_verbose!("{} seconds for prover for H evaluation (mostly FFT)", stopwatch.elapsed());
let stopwatch = Stopwatch::new();
// TODO: Check that difference in operations for different chunks is small
// TODO: parallelize if it's even helpful
// TODO: in large settings it may worth to parallelize
let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
// Run a dedicated process for dense vector
let l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone());
let a_aux_density_total = prover.a_aux_density.get_total_density();
let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?;
let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone());
let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone());
let b_input_density = Arc::new(prover.b_input_density);
let b_input_density_total = b_input_density.get_total_density();
let b_aux_density = Arc::new(prover.b_aux_density);
let b_aux_density_total = b_aux_density.get_total_density();
let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?;
let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone());
let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone());
let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?;
let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment);
let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment);
if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() {
// If this element is zero, someone is trying to perform a
// subversion-CRS attack.
return Err(SynthesisError::UnexpectedIdentity);
}
let mut g_a = vk.delta_g1.mul(r);
g_a.add_assign_mixed(&vk.alpha_g1);
let mut g_b = vk.delta_g2.mul(s);
g_b.add_assign_mixed(&vk.beta_g2);
let mut g_c;
{
let mut rs = r;
rs.mul_assign(&s);
g_c = vk.delta_g1.mul(rs);
g_c.add_assign(&vk.alpha_g1.mul(s));
g_c.add_assign(&vk.beta_g1.mul(r));
}
let mut a_answer = a_inputs.wait()?;
a_answer.add_assign(&a_aux.wait()?);
g_a.add_assign(&a_answer);
a_answer.mul_assign(s);
g_c.add_assign(&a_answer);
let mut b1_answer = b_g1_inputs.wait()?;
b1_answer.add_assign(&b_g1_aux.wait()?);
let mut b2_answer = b_g2_inputs.wait()?;
b2_answer.add_assign(&b_g2_aux.wait()?);
g_b.add_assign(&b2_answer);
b1_answer.mul_assign(r);
g_c.add_assign(&b1_answer);
g_c.add_assign(&h.wait()?);
g_c.add_assign(&l.wait()?);
elog_verbose!("{} seconds for prover for point multiplication", stopwatch.elapsed());
Ok(Proof {
a: g_a.into_affine(),
b: g_b.into_affine(),
c: g_c.into_affine()
})
prover.create_proof(params, r, s)
}

@ -3,7 +3,7 @@
#[macro_use]
extern crate cfg_if;
extern crate pairing_ce as pairing_import;
pub extern crate pairing;
extern crate rand;
extern crate bit_vec;
extern crate byteorder;
@ -43,10 +43,6 @@ cfg_if! {
}
}
pub mod pairing {
pub use pairing_import::*;
}
mod cs;
pub use self::cs::*;

@ -17,6 +17,8 @@ use super::worker::Worker;
use super::SynthesisError;
use cfg_if;
/// 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)
@ -47,6 +49,7 @@ use super::SynthesisError;
/// - accumulators over each set of buckets will have an implicit factor of `(2^c)^i`, so before summing thme up
/// "higher" accumulators must be doubled `c` times
///
#[cfg(not(feature = "nightly"))]
fn multiexp_inner<Q, D, G, S>(
pool: &Worker,
bases: S,
@ -56,7 +59,7 @@ fn multiexp_inner<Q, D, G, S>(
mut skip: u32,
c: u32,
handle_trivial: bool
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
) -> Box<dyn Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine,
@ -68,7 +71,7 @@ fn multiexp_inner<Q, D, G, S>(
let exponents = exponents.clone();
let density_map = density_map.clone();
// This looks like a Pippengers algorithm
// This is a Pippengers algorithm
pool.compute(move || {
// Accumulate the result
let mut acc = G::Projective::zero();
@ -153,6 +156,175 @@ fn multiexp_inner<Q, D, G, S>(
}
}
cfg_if! {
if #[cfg(feature = "nightly")] {
#[inline(always)]
fn multiexp_inner_impl<Q, D, G, S>(
pool: &Worker,
bases: S,
density_map: D,
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
skip: u32,
c: u32,
handle_trivial: bool
) -> Box<dyn Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine,
S: SourceBuilder<G>
{
multiexp_inner_with_prefetch(pool, bases, density_map, exponents, skip, c, handle_trivial)
}
} else {
#[inline(always)]
fn multiexp_inner_impl<Q, D, G, S>(
pool: &Worker,
bases: S,
density_map: D,
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
skip: u32,
c: u32,
handle_trivial: bool
) -> Box<dyn Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine,
S: SourceBuilder<G>
{
multiexp_inner(pool, bases, density_map, exponents, skip, c, handle_trivial)
}
}
}
#[cfg(feature = "nightly")]
extern crate prefetch;
#[cfg(feature = "nightly")]
fn multiexp_inner_with_prefetch<Q, D, G, S>(
pool: &Worker,
bases: S,
density_map: D,
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
mut skip: u32,
c: u32,
handle_trivial: bool
) -> Box<dyn Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine,
S: SourceBuilder<G>
{
use prefetch::prefetch::*;
// Perform this region of the multiexp
let this = {
let bases = bases.clone();
let exponents = exponents.clone();
let density_map = density_map.clone();
// This is a Pippengers algorithm
pool.compute(move || {
// Accumulate the result
let mut acc = G::Projective::zero();
// Build a source for the bases
let mut bases = bases.new();
// Create buckets to place remainders s mod 2^c,
// it will be 2^c - 1 buckets (no bucket for zeroes)
// Create space for the buckets
let mut buckets = vec![<G as CurveAffine>::Projective::zero(); (1 << c) - 1];
let zero = <G::Engine as ScalarEngine>::Fr::zero().into_repr();
let one = <G::Engine as ScalarEngine>::Fr::one().into_repr();
let padding = Arc::new(vec![zero]);
let mask = 1 << c;
// Sort the bases into buckets
for ((&exp, &next_exp), density) in exponents.iter()
.zip(exponents.iter().skip(1).chain(padding.iter()))
.zip(density_map.as_ref().iter()) {
// no matter what happens - prefetch next bucket
if next_exp != zero && next_exp != one {
let mut next_exp = next_exp;
next_exp.shr(skip);
let next_exp = next_exp.as_ref()[0] % mask;
if next_exp != 0 {
let p: *const <G as CurveAffine>::Projective = &buckets[(next_exp - 1) as usize];
prefetch::<Write, High, Data, _>(p);
}
}
// Go over density and exponents
if density {
if exp == zero {
bases.skip(1)?;
} else if exp == one {
if handle_trivial {
bases.add_assign_mixed(&mut acc)?;
} else {
bases.skip(1)?;
}
} else {
// Place multiplication into the bucket: Separate s * P as
// (s/2^c) * P + (s mod 2^c) P
// First multiplication is c bits less, so one can do it,
// sum results from different buckets and double it c times,
// then add with (s mod 2^c) P parts
let mut exp = exp;
exp.shr(skip);
let exp = exp.as_ref()[0] % mask;
if exp != 0 {
bases.add_assign_mixed(&mut buckets[(exp - 1) as usize])?;
} else {
bases.skip(1)?;
}
}
}
}
// Summation by parts
// e.g. 3a + 2b + 1c = a +
// (a) + b +
// ((a) + b) + c
let mut running_sum = G::Projective::zero();
for exp in buckets.into_iter().rev() {
running_sum.add_assign(&exp);
acc.add_assign(&running_sum);
}
Ok(acc)
})
};
skip += c;
if skip >= <G::Engine as ScalarEngine>::Fr::NUM_BITS {
// There isn't another region.
Box::new(this)
} else {
// There's another region more significant. Calculate and join it with
// this region recursively.
Box::new(
this.join(multiexp_inner_with_prefetch(pool, bases, density_map, exponents, skip, c, false))
.map(move |(this, mut higher)| {
for _ in 0..c {
higher.double();
}
higher.add_assign(&this);
higher
})
)
}
}
/// Perform multi-exponentiation. The caller is responsible for ensuring the
/// query size is the same as the number of exponents.
pub fn multiexp<Q, D, G, S>(
@ -160,7 +332,7 @@ pub fn multiexp<Q, D, G, S>(
bases: S,
density_map: D,
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
) -> Box<dyn Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine,
@ -179,7 +351,7 @@ pub fn multiexp<Q, D, G, S>(
assert!(query_size == exponents.len());
}
multiexp_inner(pool, bases, density_map, exponents, 0, c, true)
multiexp_inner_impl(pool, bases, density_map, exponents, 0, c, true)
}
@ -415,4 +587,33 @@ fn test_dense_multiexp() {
println!("{} ns for sparse for {} samples", duration_ns, SAMPLES);
assert_eq!(dense, sparse);
}
#[test]
fn test_bench_sparse_multiexp() {
use rand::{XorShiftRng, SeedableRng, Rand, Rng};
use crate::pairing::bn256::Bn256;
use num_cpus;
const SAMPLES: usize = 1 << 22;
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v = (0..SAMPLES).map(|_| <Bn256 as ScalarEngine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>();
let g = (0..SAMPLES).map(|_| <Bn256 as Engine>::G1::rand(rng).into_affine()).collect::<Vec<_>>();
println!("Done generating test points and scalars");
let pool = Worker::new();
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!("{} ms for sparse for {} samples", duration_ns/1000.0f64, SAMPLES);
}

@ -10,18 +10,19 @@ Initial SONIC proof system integration using the code from the [original impleme
- Code cleanup
- Migration for smart-contract compatible transcripts
## TODO Plan
## Current state
- [x] Test with public inputs
- [x] Test on BN256
- [x] Parallelize using existing primitives
- [x] Implement polynomial parallelized evaluation
- [x] Make custom transcriptor that is easy to transform into the smart-contract
- [x] Basic Ethereum smart-contract
- [x] Add blinding factors
- [ ] Implement unhelped version
- [x] Implement a part of S poly precomputation (S2)
- [x] Implement a "well formed" argument
- [x] Implement a coefficients product argument
- [ ] Implement a premutation argument
- [ ] Implement synthesizer for proper form of S polynomial
Beta - fast and valid, but breaking API changes are expected
## Completed
- Basic proof modes (helped/unhelped)
- Succinct `S` polynomial evaluation using permutation argument
- High-level API for non-succinct mode that can produce "large enough" SRS from a "global" SRS
- Proving/verifying keys that have additional information about the circuit such as number of gates, linear constraints and public inputs
- Implement non-assigning backends for faster estimation of circuit parameters in un-cached cases
## TODO Plan
- [ ] Make caching proving/verifying key for succinct mode
- [ ] Fix high-level API for both modes
- [ ] Re-structure the package itself

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

@ -41,6 +41,8 @@ pub trait ConstraintSystem<E: Engine> {
/// about the circuit for verification, while another actually constructs
/// a witness.
pub trait Backend<E: Engine> {
type LinearConstraintIndex;
/// Get the value of a variable. Can return None if we don't know.
fn get_var(&self, _variable: Variable) -> Option<E::Fr> { None }
@ -51,11 +53,14 @@ pub trait Backend<E: Engine> {
/// Create a new multiplication gate.
fn new_multiplication_gate(&mut self) { }
/// Create a new linear constraint.
fn new_linear_constraint(&mut self) { }
/// Create a new linear constraint, returning the power of Y for caching purposes.
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex;
/// Insert a term into a linear constraint. TODO: bad name of function
fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff<E>) { }
fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff<E>, _y: &Self::LinearConstraintIndex) { }
/// Compute a `LinearConstraintIndex` from `q`.
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex;
/// Mark y^{_index} as the power of y cooresponding to the public input
/// coefficient for the next public input, in the k(Y) polynomial.
@ -65,300 +70,4 @@ pub trait Backend<E: Engine> {
/// This is an abstraction which synthesizes circuits.
pub trait SynthesisDriver {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError>;
}
pub struct Basic;
impl SynthesisDriver for Basic {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
struct Synthesizer<E: Engine, B: Backend<E>> {
backend: B,
current_variable: Option<usize>,
_marker: PhantomData<E>,
q: usize,
n: usize,
}
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for Synthesizer<E, B> {
const ONE: Variable = Variable::A(1);
fn alloc<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
match self.current_variable.take() {
Some(index) => {
let var_a = Variable::A(index);
let var_b = Variable::B(index);
let var_c = Variable::C(index);
let mut product = None;
let value_a = self.backend.get_var(var_a);
self.backend.set_var(var_b, || {
let value_b = value()?;
product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
product.as_mut().map(|product| product.mul_assign(&value_b));
Ok(value_b)
})?;
self.backend.set_var(var_c, || {
product.ok_or(SynthesisError::AssignmentMissing)
})?;
self.current_variable = None;
Ok(var_b)
},
None => {
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let var_a = Variable::A(index);
self.backend.set_var(var_a, value)?;
self.current_variable = Some(index);
Ok(var_a)
}
}
}
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
let input_var = self.alloc(value)?;
self.enforce_zero(LinearCombination::zero() + input_var);
self.backend.new_k_power(self.q);
Ok(input_var)
}
fn enforce_zero(&mut self, lc: LinearCombination<E>)
{
self.q += 1;
self.backend.new_linear_constraint();
for (var, coeff) in lc.as_ref() {
self.backend.insert_coefficient(*var, *coeff);
}
}
fn multiply<F>(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
where
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
{
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let a = Variable::A(index);
let b = Variable::B(index);
let c = Variable::C(index);
let mut b_val = None;
let mut c_val = None;
self.backend.set_var(a, || {
let (a, b, c) = values()?;
b_val = Some(b);
c_val = Some(c);
Ok(a)
})?;
self.backend.set_var(b, || {
b_val.ok_or(SynthesisError::AssignmentMissing)
})?;
self.backend.set_var(c, || {
c_val.ok_or(SynthesisError::AssignmentMissing)
})?;
Ok((a, b, c))
}
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
self.backend.get_var(var).ok_or(())
}
}
let mut tmp: Synthesizer<E, B> = Synthesizer {
backend: backend,
current_variable: None,
_marker: PhantomData,
q: 0,
n: 0,
};
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <Synthesizer<E, B> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut tmp)?;
// let blindings_to_add = 6;
// for i in 0..blindings_to_add {
// match tmp.current_variable.take() {
// Some(index) => {
// let var_a = Variable::A(index);
// let var_b = Variable::B(index);
// let var_c = Variable::C(index);
// let mut product = None;
// let value_a = tmp.backend.get_var(var_a);
// tmp.backend.set_var(var_b, || {
// let value_b = E::Fr::one();
// product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
// product.as_mut().map(|product| product.mul_assign(&value_b));
// Ok(value_b)
// })?;
// tmp.backend.set_var(var_c, || {
// product.ok_or(SynthesisError::AssignmentMissing)
// })?;
// tmp.current_variable = None;
// },
// None => {
// self.n += 1;
// let index = self.n ;
// tmp.backend.new_multiplication_gate();
// let var_a = Variable::A(index);
// tmp.backend.set_var(var_a, value)?;
// tmp.current_variable = Some(index);
// }
// }
// }
// TODO: add blinding factors so we actually get zero-knowledge
Ok(())
}
}
pub struct Nonassigning;
impl SynthesisDriver for Nonassigning {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
struct NonassigningSynthesizer<E: Engine, B: Backend<E>> {
backend: B,
current_variable: Option<usize>,
_marker: PhantomData<E>,
q: usize,
n: usize,
}
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for NonassigningSynthesizer<E, B> {
const ONE: Variable = Variable::A(1);
fn alloc<F>(&mut self, _value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
match self.current_variable.take() {
Some(index) => {
let var_b = Variable::B(index);
self.current_variable = None;
Ok(var_b)
},
None => {
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let var_a = Variable::A(index);
self.current_variable = Some(index);
Ok(var_a)
}
}
}
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
let input_var = self.alloc(value)?;
self.enforce_zero(LinearCombination::zero() + input_var);
self.backend.new_k_power(self.q);
Ok(input_var)
}
fn enforce_zero(&mut self, lc: LinearCombination<E>)
{
self.q += 1;
self.backend.new_linear_constraint();
for (var, coeff) in lc.as_ref() {
self.backend.insert_coefficient(*var, *coeff);
}
}
fn multiply<F>(&mut self, _values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
where
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
{
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let a = Variable::A(index);
let b = Variable::B(index);
let c = Variable::C(index);
Ok((a, b, c))
}
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
self.backend.get_var(var).ok_or(())
}
}
let mut tmp: NonassigningSynthesizer<E, B> = NonassigningSynthesizer {
backend: backend,
current_variable: None,
_marker: PhantomData,
q: 0,
n: 0,
};
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <NonassigningSynthesizer<E, B> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut tmp)?;
// TODO: add blinding factors so we actually get zero-knowledge
// println!("n = {}", tmp.n);
Ok(())
}
}

BIN
src/sonic/discussion.pdf Normal file

Binary file not shown.

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

@ -15,10 +15,11 @@ use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::{Circuit};
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::srs::SRS;
use crate::sonic::cs::Basic;
use crate::sonic::sonic::Basic;
use super::prover::create_advice as create_advice_sonic_circuit;
use super::prover::create_advice_on_information_and_srs as create_advice_on_information_and_srs_sonic_circuit;
use super::prover::create_proof_on_srs as create_proof_on_srs_sonic_circuit;
use crate::sonic::sonic::CountN;
// pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E> + Clone, S: SynthesisDriver>(
pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E> + Clone>(
@ -51,23 +52,13 @@ pub fn create_advice_on_srs<E: Engine, C: Circuit<E> + Clone>(
srs: &SRS<E>
) -> Result<SxyAdvice<E>, SynthesisError>
{
use crate::sonic::cs::Nonassigning;
use crate::sonic::sonic::Nonassigning;
let adapted_circuit = AdaptorCircuit(circuit.clone());
// annoying, but we need n to compute s(z, y), and this isn't
// precomputed anywhere yet
let n = {
struct CountN {
n: usize
}
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
}
let mut tmp = CountN{n:0};
let mut tmp = CountN::<Nonassigning>::new();
Nonassigning::synthesize(&mut tmp, &adapted_circuit)?;
tmp.n

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

@ -104,6 +104,7 @@ impl<E: Engine> Batch<E> {
}
}
/// add `(r*P) to the h^(alpha*x) terms, add -(r*point)*P to h^(alpha) terms
pub fn add_opening(&mut self, p: E::G1Affine, mut r: E::Fr, point: E::Fr) {
self.alpha_x.push((p, r));
r.mul_assign(&point);
@ -111,14 +112,17 @@ impl<E: Engine> Batch<E> {
self.alpha.push((p, r));
}
/// add (r*P) to -h^(x) terms
pub fn add_commitment(&mut self, p: E::G1Affine, r: E::Fr) {
self.neg_h.push((p, r));
}
/// add (r*P) to -h^(d-n) terms
pub fn add_commitment_max_n(&mut self, p: E::G1Affine, r: E::Fr) {
self.neg_x_n_minus_d.push((p, r));
}
/// add (r*point) to g terms for later pairing with h^(alpha)
pub fn add_opening_value(&mut self, mut r: E::Fr, point: E::Fr) {
r.mul_assign(&point);
self.value.add_assign(&r);

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

@ -14,6 +14,7 @@ use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::CountNandQ;
#[derive(Clone)]
pub struct Aggregate<E: Engine> {
@ -25,6 +26,9 @@ pub struct Aggregate<E: Engine> {
pub c_openings: Vec<(E::G1Affine, E::Fr)>,
// Then we have to finally open C
pub opening: E::G1Affine,
pub z: E::Fr,
pub w: E::Fr,
}
pub fn create_aggregate<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
@ -47,22 +51,8 @@ pub fn create_aggregate_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
{
// TODO: precompute this?
let (n, q) = {
struct CountN {
n: usize,
q: usize
}
let mut tmp = CountNandQ::<S>::new();
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
fn new_linear_constraint(&mut self) {
self.q += 1;
}
}
let mut tmp = CountN{n:0,q:0};
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
(tmp.n, tmp.q)
@ -120,46 +110,31 @@ pub fn create_aggregate_on_srs_using_information<E: Engine, C: Circuit<E>, S: Sy
let mut value = value;
value.negate();
let poly = kate_divison(
polynomial_commitment_opening(
n,
0,
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
w,
);
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
&srs
)
};
// TODO: parallelize
// Let's open up C to every y.
fn compute_value<E: Engine>(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr {
let mut value = E::Fr::zero();
let yinv = y.inverse().unwrap(); // TODO
let mut tmp = yinv;
for &coeff in poly_negative {
let mut coeff = coeff;
coeff.mul_assign(&tmp);
value.add_assign(&coeff);
tmp.mul_assign(&yinv);
}
let mut tmp = *y;
for &coeff in poly_positive {
let mut coeff = coeff;
coeff.mul_assign(&tmp);
value.add_assign(&coeff);
tmp.mul_assign(&y);
}
let positive_powers_contrib = evaluate_at_consequitive_powers(poly_positive, *y, *y);
let negative_powers_contrib = evaluate_at_consequitive_powers(poly_negative, yinv, yinv);
value.add_assign(&positive_powers_contrib);
value.add_assign(&negative_powers_contrib);
value
}
use std::time::Instant;
let start = Instant::now();
let mut c_openings = vec![];
for y in &y_values {
let value = compute_value::<E>(y, &s_poly_positive, &s_poly_negative);
@ -168,24 +143,20 @@ pub fn create_aggregate_on_srs_using_information<E: Engine, C: Circuit<E>, S: Sy
let mut value = value;
value.negate();
let poly = kate_divison(
polynomial_commitment_opening(
n,
0,
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
*y,
);
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
&srs
)
};
c_openings.push((opening, value));
}
println!("Evaluation of s(z, Y) taken {:?}", start.elapsed());
// Okay, great. Now we need to open up each S at the same point z to the same value.
// Since we're opening up all the S's at the same point, we create a bunch of random
// challenges instead and open up a random linear combination.
@ -194,6 +165,9 @@ pub fn create_aggregate_on_srs_using_information<E: Engine, C: Circuit<E>, S: Sy
let mut poly_positive = vec![E::Fr::zero(); 2*n];
let mut expected_value = E::Fr::zero();
// TODO: this part can be further parallelized due to synthesis of S(X, y) being singlethreaded
let start = Instant::now();
for (y, c_opening) in y_values.iter().zip(c_openings.iter()) {
// Compute s(X, y_i)
let (s_poly_negative, s_poly_positive) = {
@ -208,35 +182,25 @@ pub fn create_aggregate_on_srs_using_information<E: Engine, C: Circuit<E>, S: Sy
value.mul_assign(&r);
expected_value.add_assign(&value);
for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) {
coeff.mul_assign(&r);
target.add_assign(&coeff);
}
mul_add_polynomials(& mut poly_negative[..], &s_poly_negative[..], r);
mul_add_polynomials(& mut poly_positive[..], &s_poly_positive[..], r);
for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) {
coeff.mul_assign(&r);
target.add_assign(&coeff);
}
}
// TODO: parallelize
println!("Re-evaluation of {} S polynomials taken {:?}", y_values.len(), start.elapsed());
let s_opening = {
let mut value = expected_value;
value.negate();
let poly = kate_divison(
polynomial_commitment_opening(
n,
0,
poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()),
z,
);
&srs
)
let negative_poly = poly[0..n].iter().rev();
let positive_poly = poly[n..].iter();
multiexp(
srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext(
srs.g_positive_x[0..positive_poly.len()].iter()
),
negative_poly.chain_ext(positive_poly)
).into_affine()
};
Aggregate {
@ -248,5 +212,9 @@ pub fn create_aggregate_on_srs_using_information<E: Engine, C: Circuit<E>, S: Sy
c_openings,
// Then we have to finally open C
opening,
z: z,
w: w
}
}

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

@ -250,35 +250,16 @@ impl<E: Engine> VerifyingKey<E> {
}
}
use crate::sonic::cs::{Backend, Basic, SynthesisDriver};
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::srs::SRS;
use crate::sonic::cs::Circuit as SonicCircuit;
use crate::sonic::sonic::{Basic, Preprocess};
use std::marker::PhantomData;
impl<E: Engine> VerifyingKey<E> {
pub fn new<C: SonicCircuit<E>, S: SynthesisDriver>(circuit: C, srs: &SRS<E>) -> Result<Self, SynthesisError> {
struct Preprocess<E: Engine> {
k_map: Vec<usize>,
n: usize,
q: usize,
_marker: PhantomData<E>
}
impl<'a, E: Engine> Backend<E> for &'a mut Preprocess<E> {
fn new_k_power(&mut self, index: usize) {
self.k_map.push(index);
}
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
fn new_linear_constraint(&mut self) {
self.q += 1;
}
}
let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData };
let mut preprocess = Preprocess::new();
S::synthesize(&mut preprocess, &circuit)?;

@ -4,6 +4,7 @@ use std::marker::PhantomData;
use crate::sonic::cs::{Backend};
use crate::sonic::cs::{Coeff, Variable, LinearCombination};
use crate::sonic::util::*;
/*
s(X, Y) = \sum\limits_{i=1}^N u_i(Y) X^{-i}
@ -30,6 +31,8 @@ pub struct SxEval<E: Engine> {
v: Vec<E::Fr>,
// x^{i+N} (-y^i -y^{-i} + \sum\limits_{q=1}^Q y^{q+N} w_{q,i})
w: Vec<E::Fr>,
max_n: usize,
}
impl<E: Engine> SxEval<E> {
@ -40,18 +43,27 @@ impl<E: Engine> SxEval<E> {
let u = vec![E::Fr::zero(); n];
let v = vec![E::Fr::zero(); n];
let mut w = vec![E::Fr::zero(); n];
let mut tmp1 = y;
let mut tmp2 = y_inv;
for w in &mut w {
let mut new = tmp1;
new.add_assign(&tmp2);
new.negate();
*w = new;
tmp1.mul_assign(&y);
tmp2.mul_assign(&y_inv);
}
let mut minus_one = E::Fr::one();
minus_one.negate();
let mut w = vec![minus_one; n];
let mut w_neg = vec![minus_one; n];
mut_distribute_consequitive_powers(&mut w[..], y, y);
mut_distribute_consequitive_powers(&mut w_neg[..], y_inv, y_inv);
add_polynomials(&mut w[..], &w_neg[..]);
// let mut w = vec![E::Fr::zero(); n];
// let mut tmp1 = y;
// let mut tmp2 = y_inv;
// for w in &mut w {
// let mut new = tmp1;
// new.add_assign(&tmp2);
// new.negate();
// *w = new;
// tmp1.mul_assign(&y);
// tmp2.mul_assign(&y_inv);
// }
SxEval {
y,
@ -59,6 +71,7 @@ impl<E: Engine> SxEval<E> {
u,
v,
w,
max_n: n
}
}
@ -71,37 +84,52 @@ impl<E: Engine> SxEval<E> {
pub fn finalize(self, x: E::Fr) -> E::Fr {
let x_inv = x.inverse().unwrap(); // TODO
let mut tmp = x_inv;
let mut acc = E::Fr::zero();
for mut u in self.u {
u.mul_assign(&tmp);
acc.add_assign(&u);
tmp.mul_assign(&x_inv);
}
let mut tmp = x;
for mut v in self.v {
v.mul_assign(&tmp);
acc.add_assign(&v);
tmp.mul_assign(&x);
}
for mut w in self.w {
w.mul_assign(&tmp);
acc.add_assign(&w);
tmp.mul_assign(&x);
}
let tmp = x_inv;
acc.add_assign(&evaluate_at_consequitive_powers(& self.u[..], tmp, tmp));
let tmp = x;
acc.add_assign(&evaluate_at_consequitive_powers(& self.v[..], tmp, tmp));
let tmp = x.pow(&[(self.v.len()+1) as u64]);
acc.add_assign(&evaluate_at_consequitive_powers(& self.w[..], tmp, x));
// let mut tmp = x_inv;
// for mut u in self.u {
// u.mul_assign(&tmp);
// acc.add_assign(&u);
// tmp.mul_assign(&x_inv);
// }
// let mut tmp = x;
// for mut v in self.v {
// v.mul_assign(&tmp);
// acc.add_assign(&v);
// tmp.mul_assign(&x);
// }
// for mut w in self.w {
// w.mul_assign(&tmp);
// acc.add_assign(&w);
// tmp.mul_assign(&x);
// }
acc
}
}
impl<'a, E: Engine> Backend<E> for &'a mut SxEval<E> {
fn new_linear_constraint(&mut self) {
type LinearConstraintIndex = E::Fr;
fn new_linear_constraint(&mut self) -> E::Fr {
self.yqn.mul_assign(&self.y);
self.yqn
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex {
self.y.pow(&[(self.max_n + q) as u64])
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>, y: &E::Fr) {
let acc = match var {
Variable::A(index) => {
&mut self.u[index - 1]
@ -117,13 +145,13 @@ impl<'a, E: Engine> Backend<E> for &'a mut SxEval<E> {
match coeff {
Coeff::Zero => { },
Coeff::One => {
acc.add_assign(&self.yqn);
acc.add_assign(&y);
},
Coeff::NegativeOne => {
acc.sub_assign(&self.yqn);
acc.sub_assign(&y);
},
Coeff::Full(mut val) => {
val.mul_assign(&self.yqn);
val.mul_assign(&y);
acc.add_assign(&val);
}
}
@ -161,36 +189,54 @@ pub struct SyEval<E: Engine> {
impl<E: Engine> SyEval<E> {
pub fn new(x: E::Fr, n: usize, q: usize) -> Self {
let xinv = x.inverse().unwrap();
let mut tmp = E::Fr::one();
let mut a = vec![E::Fr::zero(); n];
for a in &mut a {
tmp.mul_assign(&xinv); // tmp = x^{-i}
*a = tmp;
}
let mut a = vec![E::Fr::one(); n];
let mut b = vec![E::Fr::one(); n];
let mut tmp = E::Fr::one();
let mut b = vec![E::Fr::zero(); n];
for b in &mut b {
tmp.mul_assign(&x); // tmp = x^{i}
*b = tmp;
}
mut_distribute_consequitive_powers(&mut a[..], xinv, xinv);
mut_distribute_consequitive_powers(&mut b[..], x, x);
let mut positive_coeffs = vec![E::Fr::zero(); n + q];
let mut negative_coeffs = vec![E::Fr::zero(); n];
let mut c = vec![E::Fr::one(); n];
mut_distribute_consequitive_powers(&mut c[..], x.pow(&[(n+1) as u64]), x);
let mut c = vec![E::Fr::zero(); n];
for ((c, positive_coeff), negative_coeff) in c.iter_mut().zip(&mut positive_coeffs).zip(&mut negative_coeffs) {
tmp.mul_assign(&x); // tmp = x^{i+N}
*c = tmp;
let mut minus_one = E::Fr::one();
minus_one.negate();
// - \sum\limits_{i=1}^N Y^i X^{i+N}
let mut tmp = tmp;
tmp.negate();
*positive_coeff = tmp;
let mut positive_coeffs = vec![minus_one; n];
mut_distribute_consequitive_powers(&mut positive_coeffs[..], x.pow(&[(n+1) as u64]), x);
let negative_coeffs = positive_coeffs.clone();
// - \sum\limits_{i=1}^N Y^{-i} X^{i+N}
*negative_coeff = tmp;
}
positive_coeffs.resize(n + q, E::Fr::zero());
// let mut tmp = E::Fr::one();
// let mut a = vec![E::Fr::zero(); n];
// for a in &mut a {
// tmp.mul_assign(&xinv); // tmp = x^{-i}
// *a = tmp;
// }
// let mut tmp = E::Fr::one();
// let mut b = vec![E::Fr::zero(); n];
// for b in &mut b {
// tmp.mul_assign(&x); // tmp = x^{i}
// *b = tmp;
// }
// let mut positive_coeffs = vec![E::Fr::zero(); n + q];
// let mut negative_coeffs = vec![E::Fr::zero(); n];
// let mut c = vec![E::Fr::zero(); n];
// for ((c, positive_coeff), negative_coeff) in c.iter_mut().zip(&mut positive_coeffs).zip(&mut negative_coeffs) {
// tmp.mul_assign(&x); // tmp = x^{i+N}
// *c = tmp;
// // - \sum\limits_{i=1}^N Y^i X^{i+N}
// let mut tmp = tmp;
// tmp.negate();
// *positive_coeff = tmp;
// // - \sum\limits_{i=1}^N Y^{-i} X^{i+N}
// *negative_coeff = tmp;
// }
SyEval {
a,
@ -209,38 +255,51 @@ impl<E: Engine> SyEval<E> {
pub fn finalize(self, y: E::Fr) -> E::Fr {
let mut acc = E::Fr::zero();
let mut tmp = y;
for mut coeff in self.positive_coeffs {
coeff.mul_assign(&tmp);
acc.add_assign(&coeff);
tmp.mul_assign(&y);
}
let yinv = y.inverse().unwrap(); // TODO
let mut tmp = yinv;
for mut coeff in self.negative_coeffs {
coeff.mul_assign(&tmp);
acc.add_assign(&coeff);
tmp.mul_assign(&yinv);
}
let positive_powers_contrib = evaluate_at_consequitive_powers(& self.positive_coeffs[..], y, y);
let negative_powers_contrib = evaluate_at_consequitive_powers(& self.negative_coeffs[..], yinv, yinv);
acc.add_assign(&positive_powers_contrib);
acc.add_assign(&negative_powers_contrib);
// let mut tmp = y;
// for mut coeff in self.positive_coeffs {
// coeff.mul_assign(&tmp);
// acc.add_assign(&coeff);
// tmp.mul_assign(&y);
// }
// let mut tmp = yinv;
// for mut coeff in self.negative_coeffs {
// coeff.mul_assign(&tmp);
// acc.add_assign(&coeff);
// tmp.mul_assign(&yinv);
// }
acc
}
}
impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
fn new_linear_constraint(&mut self) {
type LinearConstraintIndex = usize;
fn new_linear_constraint(&mut self) -> usize {
self.current_q += 1;
self.current_q
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex {
q
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>, q: &usize) {
match var {
Variable::A(index) => {
let index = index - 1;
// Y^{q+N} += X^{-i} * coeff
let mut tmp = self.a[index];
coeff.multiply(&mut tmp);
let yindex = self.current_q + self.max_n;
let yindex = *q + self.max_n;
self.positive_coeffs[yindex - 1].add_assign(&tmp);
}
Variable::B(index) => {
@ -248,7 +307,7 @@ impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
// Y^{q+N} += X^{i} * coeff
let mut tmp = self.b[index];
coeff.multiply(&mut tmp);
let yindex = self.current_q + self.max_n;
let yindex = *q + self.max_n;
self.positive_coeffs[yindex - 1].add_assign(&tmp);
}
Variable::C(index) => {
@ -256,7 +315,7 @@ impl<'a, E: Engine> Backend<E> for &'a mut SyEval<E> {
// Y^{q+N} += X^{i+N} * coeff
let mut tmp = self.c[index];
coeff.multiply(&mut tmp);
let yindex = self.current_q + self.max_n;
let yindex = *q + self.max_n;
self.positive_coeffs[yindex - 1].add_assign(&tmp);
}
};

@ -14,6 +14,7 @@ use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::{CountN, Basic};
pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
@ -31,6 +32,7 @@ pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E>, S: Synthes
transcript.commit_point(&proof.t);
z = transcript.get_challenge_scalar();
}
let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?;
let (s_poly_negative, s_poly_positive) = {
@ -51,22 +53,28 @@ pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E>, S: Synthes
// Compute s(z, y)
let mut szy = E::Fr::zero();
{
let mut tmp = z;
for &p in &s_poly_positive {
let mut p = p;
p.mul_assign(&tmp);
szy.add_assign(&p);
tmp.mul_assign(&z);
}
let mut tmp = z_inv;
for &p in &s_poly_negative {
let mut p = p;
p.mul_assign(&tmp);
szy.add_assign(&p);
tmp.mul_assign(&z_inv);
}
szy.add_assign(& evaluate_at_consequitive_powers(& s_poly_positive[..], z, z));
szy.add_assign(& evaluate_at_consequitive_powers(& s_poly_negative[..], z_inv, z_inv));
}
// let mut szy = E::Fr::zero();
// {
// let mut tmp = z;
// for &p in &s_poly_positive {
// let mut p = p;
// p.mul_assign(&tmp);
// szy.add_assign(&p);
// tmp.mul_assign(&z);
// }
// let mut tmp = z_inv;
// for &p in &s_poly_negative {
// let mut p = p;
// p.mul_assign(&tmp);
// szy.add_assign(&p);
// tmp.mul_assign(&z_inv);
// }
// }
// Compute kate opening
let opening = {
let mut open = szy;
@ -113,17 +121,7 @@ pub fn create_advice_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
// annoying, but we need n to compute s(z, y), and this isn't
// precomputed anywhere yet
let n = {
struct CountN {
n: usize
}
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
}
let mut tmp = CountN{n:0};
let mut tmp = CountN::<S>::new();
S::synthesize(&mut tmp, circuit)?;
tmp.n
@ -141,65 +139,14 @@ pub fn create_proof<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
extern crate rand;
use self::rand::{Rand, Rng, thread_rng};
use crate::sonic::sonic::Wires;
pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
srs: &SRS<E>
) -> Result<Proof<E>, SynthesisError>
{
struct Wires<E: Engine> {
a: Vec<E::Fr>,
b: Vec<E::Fr>,
c: Vec<E::Fr>
}
impl<'a, E: Engine> Backend<E> for &'a mut Wires<E> {
fn new_multiplication_gate(&mut self) {
self.a.push(E::Fr::zero());
self.b.push(E::Fr::zero());
self.c.push(E::Fr::zero());
}
fn get_var(&self, variable: Variable) -> Option<E::Fr> {
Some(match variable {
Variable::A(index) => {
self.a[index - 1]
},
Variable::B(index) => {
self.b[index - 1]
},
Variable::C(index) => {
self.c[index - 1]
}
})
}
fn set_var<F>(&mut self, variable: Variable, value: F) -> Result<(), SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>
{
let value = value()?;
match variable {
Variable::A(index) => {
self.a[index - 1] = value;
},
Variable::B(index) => {
self.b[index - 1] = value;
},
Variable::C(index) => {
self.c[index - 1] = value;
}
}
Ok(())
}
}
let mut wires = Wires {
a: vec![],
b: vec![],
c: vec![],
};
let mut wires = Wires::new();
S::synthesize(&mut wires, circuit)?;
@ -232,7 +179,6 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
// 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.extend(blindings.clone());
@ -240,7 +186,6 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
rx1.push(E::Fr::zero());
rx1.extend(wires.a);
let mut rxy = rx1.clone();
let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?;
@ -254,31 +199,38 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
);
// negative powers [-1, -2n], positive [1, n]
let (s_poly_negative, s_poly_positive) = {
let (mut s_poly_negative, s_poly_positive) = {
let mut tmp = SxEval::new(y, n);
S::synthesize(&mut tmp, circuit)?;
tmp.poly()
};
// TODO: Parallelize
// r'(X, y) = r(X, y) + s(X, y). Note `y` - those are evaluated at the point already
let mut rxy_prime = rxy.clone();
{
// extend to have powers [n+1, 2n]
rxy_prime.resize(4 * n + 1 + NUM_BLINDINGS, E::Fr::zero());
// add coefficients in front of X^{-2n}...X^{-n-1}, X^{-n}...X^{-1}
for (r, s) in rxy_prime[NUM_BLINDINGS..(2 * n + NUM_BLINDINGS)]
.iter_mut()
.rev()
.zip(s_poly_negative)
{
r.add_assign(&s);
}
// add coefficients in front of X^{1}...X^{n}, X^{n+1}...X^{2*n}
for (r, s) in rxy_prime[(2 * n + 1 + NUM_BLINDINGS)..].iter_mut().zip(s_poly_positive) {
r.add_assign(&s);
}
s_poly_negative.reverse();
let neg_poly_len = s_poly_negative.len();
add_polynomials(&mut rxy_prime[(NUM_BLINDINGS+neg_poly_len)..(2 * n + NUM_BLINDINGS)], &s_poly_negative[..]);
s_poly_negative.reverse();
add_polynomials(&mut rxy_prime[(2 * n + 1 + NUM_BLINDINGS)..], &s_poly_positive[..])
// // add coefficients in front of X^{-2n}...X^{-n-1}, X^{-n}...X^{-1}
// for (r, s) in rxy_prime[NUM_BLINDINGS..(2 * n + NUM_BLINDINGS)]
// .iter_mut()
// .rev()
// .zip(s_poly_negative)
// {
// r.add_assign(&s);
// }
// // add coefficients in front of X^{1}...X^{n}, X^{n+1}...X^{2*n}
// for (r, s) in rxy_prime[(2 * n + 1 + NUM_BLINDINGS)..].iter_mut().zip(s_poly_positive) {
// r.add_assign(&s);
// }
}
// by this point all R related polynomials are blinded and evaluated for Y variable
@ -310,23 +262,6 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
evaluate_at_consequitive_powers(&rx1, tmp, z)
};
// {
// rx1[(2 * n + NUM_BLINDINGS)].sub_assign(&rz);
// let opening = polynomial_commitment_opening(
// 2 * n + NUM_BLINDINGS,
// n,
// rx1.iter(),
// z,
// srs
// );
// let valid_rz_commitment = check_polynomial_commitment(&r, &z, &rz, &opening, n, &srs);
// assert!(valid_rz_commitment);
// rx1[(2 * n + NUM_BLINDINGS)].add_assign(&rz);
// }
// rzy is evaluation of r(X, Y) at z, y
let rzy = {
let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]);
@ -362,12 +297,15 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
let z_opening = {
rx1[(2 * n + NUM_BLINDINGS)].add_assign(&rzy); // restore
// skip powers from until reach -2n - NUM_BLINDINGS
for (t, &r) in txy[(2 * n + NUM_BLINDINGS)..].iter_mut().zip(rx1.iter()) {
let mut r = r;
r.mul_assign(&r1);
t.add_assign(&r);
}
let rx1_len = rx1.len();
mul_add_polynomials(&mut txy[(2 * n + NUM_BLINDINGS)..(2 * n + NUM_BLINDINGS + rx1_len)], &rx1[..], r1);
// // skip powers from until reach -2n - NUM_BLINDINGS
// for (t, &r) in txy[(2 * n + NUM_BLINDINGS)..].iter_mut().zip(rx1.iter()) {
// let mut r = r;
// r.mul_assign(&r1);
// t.add_assign(&r);
// }
let val = {
let tmp = z_inv.pow(&[(4*n + 2*NUM_BLINDINGS) as u64]);
@ -385,11 +323,6 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
srs)
};
// let mut zy = z;
// zy.mul_assign(&y);
// let valid_rzy_commitment = check_polynomial_commitment(&r, &zy, &rzy, &zy_opening, n, &srs);
// assert!(valid_rzy_commitment);
Ok(Proof {
r, rz, rzy, t, z_opening, zy_opening
})
@ -400,7 +333,8 @@ fn my_fun_circuit_test() {
use crate::pairing::ff::PrimeField;
use crate::pairing::bls12_381::{Bls12, Fr};
use super::*;
use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination};
use crate::sonic::cs::{ConstraintSystem, LinearCombination};
use crate::sonic::sonic::Basic;
use rand::{thread_rng};
struct MyCircuit;
@ -453,7 +387,8 @@ fn polynomial_commitment_test() {
use crate::pairing::ff::PrimeFieldRepr;
use crate::pairing::bls12_381::{Bls12, Fr};
use super::*;
use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination};
use crate::sonic::cs::{ConstraintSystem, LinearCombination};
use crate::sonic::sonic::Basic;
use rand::{thread_rng};
use crate::pairing::{CurveAffine};

@ -16,6 +16,7 @@ use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::Preprocess;
pub struct MultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
circuit: C,
@ -30,28 +31,7 @@ pub struct MultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
impl<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> MultiVerifier<E, C, S, R> {
// This constructor consumes randomness source cause it's later used internally
pub fn new(circuit: C, srs: &SRS<E>, rng: R) -> Result<Self, SynthesisError> {
struct Preprocess<E: Engine> {
k_map: Vec<usize>,
n: usize,
q: usize,
_marker: PhantomData<E>
}
impl<'a, E: Engine> Backend<E> for &'a mut Preprocess<E> {
fn new_k_power(&mut self, index: usize) {
self.k_map.push(index);
}
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
fn new_linear_constraint(&mut self) {
self.q += 1;
}
}
let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData };
let mut preprocess = Preprocess::new();
S::synthesize(&mut preprocess, &circuit)?;

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

@ -0,0 +1,170 @@
use crate::pairing::{Engine};
use crate::sonic::cs::Backend;
use std::marker::PhantomData;
use crate::SynthesisError;
use crate::sonic::cs::SynthesisDriver;
use crate::sonic::cs::{Circuit, ConstraintSystem, Variable, LinearCombination};
use crate::pairing::ff::Field;
pub struct Preprocess<E: Engine> {
pub k_map: Vec<usize>,
pub n: usize,
pub q: usize,
_marker: PhantomData<E>
}
impl<'a, E: Engine> Backend<E> for &'a mut Preprocess<E> {
type LinearConstraintIndex = ();
fn get_for_q(&self, _q: usize) -> Self::LinearConstraintIndex { () }
fn new_k_power(&mut self, index: usize) {
self.k_map.push(index);
}
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
fn new_linear_constraint(&mut self) {
self.q += 1;
()
}
}
impl<E: Engine> Preprocess<E> {
pub fn new() -> Self {
Preprocess {
k_map: vec![],
n: 0,
q: 0,
_marker: PhantomData
}
}
}
pub struct Wires<E: Engine> {
pub a: Vec<E::Fr>,
pub b: Vec<E::Fr>,
pub c: Vec<E::Fr>
}
impl<'a, E: Engine> Backend<E> for &'a mut Wires<E> {
type LinearConstraintIndex = ();
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex { () }
fn get_for_q(&self, _q: usize) -> Self::LinearConstraintIndex { () }
fn new_multiplication_gate(&mut self) {
self.a.push(E::Fr::zero());
self.b.push(E::Fr::zero());
self.c.push(E::Fr::zero());
}
fn get_var(&self, variable: Variable) -> Option<E::Fr> {
Some(match variable {
Variable::A(index) => {
self.a[index - 1]
},
Variable::B(index) => {
self.b[index - 1]
},
Variable::C(index) => {
self.c[index - 1]
}
})
}
fn set_var<F>(&mut self, variable: Variable, value: F) -> Result<(), SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>
{
let value = value()?;
match variable {
Variable::A(index) => {
self.a[index - 1] = value;
},
Variable::B(index) => {
self.b[index - 1] = value;
},
Variable::C(index) => {
self.c[index - 1] = value;
}
}
Ok(())
}
}
impl<E: Engine> Wires<E> {
pub fn new() -> Self {
Wires {
a: vec![],
b: vec![],
c: vec![],
}
}
}
pub struct CountNandQ<S: SynthesisDriver> {
pub n: usize,
pub q: usize,
_marker: std::marker::PhantomData<S>
}
impl<'a, E: Engine, S: SynthesisDriver> Backend<E> for &'a mut CountNandQ<S> {
type LinearConstraintIndex = ();
fn get_for_q(&self, _q: usize) -> Self::LinearConstraintIndex { () }
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex {
self.q += 1;
()
}
}
impl<S: SynthesisDriver> CountNandQ<S> {
pub fn new() -> Self {
Self {
n: 0,
q: 0,
_marker: std::marker::PhantomData
}
}
}
pub struct CountN<S: SynthesisDriver> {
pub n: usize,
_marker: std::marker::PhantomData<S>
}
impl<'a, E: Engine, S: SynthesisDriver> Backend<E> for &'a mut CountN<S> {
type LinearConstraintIndex = ();
fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex { () }
fn get_for_q(&self, _q: usize) -> Self::LinearConstraintIndex { () }
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
}
impl<S: SynthesisDriver> CountN<S> {
pub fn new() -> Self {
Self {
n: 0,
_marker: std::marker::PhantomData
}
}
}

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

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

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

1
src/sonic/tests/mod.rs Normal file

@ -0,0 +1 @@
mod sonics;

@ -1,3 +1,5 @@
extern crate rand;
// For randomness (during paramgen and proof generation)
use rand::{thread_rng, Rng};
@ -23,22 +25,132 @@ use crate::pairing::bn256::{
};
// We'll use these interfaces to construct our circuit.
use bellman::{
use crate::{
Circuit,
ConstraintSystem,
SynthesisError
};
// We're going to use the Groth16 proving system.
use bellman::groth16::{
Proof,
generate_random_parameters,
prepare_verifying_key,
create_random_proof,
verify_proof,
};
// const MIMC_ROUNDS: usize = 322;
const MIMC_ROUNDS: usize = 322;
const MIMC_ROUNDS: usize = 1000000;
fn mimc<E: Engine>(
mut xl: E::Fr,
mut xr: E::Fr,
constants: &[E::Fr]
) -> E::Fr
{
assert_eq!(constants.len(), MIMC_ROUNDS);
for i in 0..MIMC_ROUNDS {
let mut tmp1 = xl;
tmp1.add_assign(&constants[i]);
let mut tmp2 = tmp1;
tmp2.square();
tmp2.mul_assign(&tmp1);
tmp2.add_assign(&xr);
xr = xl;
xl = tmp2;
}
xl
}
/// This is our demo circuit for proving knowledge of the
/// preimage of a MiMC hash invocation.
#[derive(Clone)]
struct MiMCDemo<'a, E: Engine> {
xl: Option<E::Fr>,
xr: Option<E::Fr>,
constants: &'a [E::Fr]
}
/// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to
/// synthesize the constraint system.
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
assert_eq!(self.constants.len(), MIMC_ROUNDS);
// Allocate the first component of the preimage.
let mut xl_value = self.xl;
let mut xl = cs.alloc(|| "preimage xl", || {
xl_value.ok_or(SynthesisError::AssignmentMissing)
})?;
// Allocate the second component of the preimage.
let mut xr_value = self.xr;
let mut xr = cs.alloc(|| "preimage xr", || {
xr_value.ok_or(SynthesisError::AssignmentMissing)
})?;
for i in 0..MIMC_ROUNDS {
// xL, xR := xR + (xL + Ci)^3, xL
let cs = &mut cs.namespace(|| format!("round {}", i));
// tmp = (xL + Ci)^2
let tmp_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.square();
e
});
let tmp = cs.alloc(|| "tmp", || {
tmp_value.ok_or(SynthesisError::AssignmentMissing)
})?;
cs.enforce(
|| "tmp = (xL + Ci)^2",
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + tmp
);
// new_xL = xR + (xL + Ci)^3
// new_xL = xR + tmp * (xL + Ci)
// new_xL - xR = tmp * (xL + Ci)
let new_xl_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.mul_assign(&tmp_value.unwrap());
e.add_assign(&xr_value.unwrap());
e
});
let new_xl = if i == (MIMC_ROUNDS-1) {
// This is the last round, xL is our image and so
// we allocate a public input.
cs.alloc_input(|| "image", || {
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
})?
} else {
cs.alloc(|| "new_xl", || {
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
})?
};
cs.enforce(
|| "new_xL = xR + (xL + Ci)^3",
|lc| lc + tmp,
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + new_xl - xr
);
// xR = xL
xr = xl;
xr_value = xl_value;
// xL = new_xL
xl = new_xl;
xl_value = new_xl_value;
}
Ok(())
}
}
/// This is our demo circuit for proving knowledge of the
/// preimage of a MiMC hash invocation.
@ -143,7 +255,7 @@ fn test_sonic_mimc() {
use crate::pairing::{Engine, CurveAffine, CurveProjective};
use crate::pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use bellman::sonic::srs::SRS;
use crate::sonic::srs::SRS;
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
@ -174,11 +286,11 @@ fn test_sonic_mimc() {
constants: &constants
};
use bellman::sonic::cs::Basic;
use bellman::sonic::sonic::AdaptorCircuit;
use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters};
use bellman::sonic::helped::helper::{create_aggregate_on_srs};
use crate::sonic::sonic::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
use crate::sonic::helped::helper::{create_aggregate_on_srs};
println!("creating proof");
let start = Instant::now();
@ -241,6 +353,235 @@ fn test_sonic_mimc() {
}
}
#[test]
fn test_sonic_mimc_in_permutation_driver() {
use crate::pairing::ff::{Field, PrimeField};
use crate::pairing::{Engine, CurveAffine, CurveProjective};
use crate::pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use crate::sonic::srs::SRS;
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
println!("making srs");
let start = Instant::now();
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
println!("done in {:?}", start.elapsed());
{
// This may not be cryptographically safe, use
// `OsRng` (for example) in production software.
let rng = &mut thread_rng();
// Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
let samples: usize = 100;
let xl = rng.gen();
let xr = rng.gen();
let image = mimc::<Bls12>(xl, xr, &constants);
// Create an instance of our circuit (with the
// witness)
let circuit = MiMCDemoNoInputs {
xl: Some(xl),
xr: Some(xr),
image: Some(image),
constants: &constants
};
use crate::sonic::sonic::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
use crate::sonic::helped::helper::{create_aggregate_on_srs};
use crate::sonic::sonic::Permutation3;
println!("creating proof");
let start = Instant::now();
let proof = create_proof_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("done in {:?}", start.elapsed());
println!("creating advice");
let start = Instant::now();
let advice = create_advice_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap();
println!("done in {:?}", start.elapsed());
println!("creating aggregate for {} proofs", samples);
let start = Instant::now();
let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect();
let aggregate = create_aggregate_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &proofs, &srs);
println!("done in {:?}", start.elapsed());
{
let rng = thread_rng();
let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
println!("verifying 1 proof without advice");
let start = Instant::now();
{
for _ in 0..1 {
verifier.add_proof(&proof, &[], |_, _| None);
}
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
{
let rng = thread_rng();
let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
println!("verifying {} proofs without advice", samples);
let start = Instant::now();
{
for _ in 0..samples {
verifier.add_proof(&proof, &[], |_, _| None);
}
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
{
let rng = thread_rng();
let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
println!("verifying 100 proofs with advice");
let start = Instant::now();
{
for (ref proof, ref advice) in &proofs {
verifier.add_proof_with_advice(proof, &[], advice);
}
verifier.add_aggregate(&proofs, &aggregate);
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
}
}
#[test]
fn test_succinct_sonic_mimc() {
use crate::pairing::ff::{Field, PrimeField};
use crate::pairing::{Engine, CurveAffine, CurveProjective};
use crate::pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use crate::sonic::srs::SRS;
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
println!("making srs");
let start = Instant::now();
// let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
let srs = SRS::<Bls12>::dummy(40000000, srs_x, srs_alpha);
println!("done in {:?}", start.elapsed());
{
use rand::{XorShiftRng, SeedableRng, Rand, Rng};
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
// Generate the MiMC round constants
// let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
let constants = (0..MIMC_ROUNDS).map(|_| Fr::one()).collect::<Vec<_>>();
let samples: usize = 100;
let xl = rng.gen();
let xr = rng.gen();
let image = mimc::<Bls12>(xl, xr, &constants);
// Create an instance of our circuit (with the
// witness)
let circuit = MiMCDemoNoInputs::<Bls12> {
xl: Some(xl),
xr: Some(xr),
image: Some(image),
constants: &constants
};
use crate::sonic::sonic::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{get_circuit_parameters_for_succinct_sonic, MultiVerifier};
use crate::sonic::sonic::Permutation3;
use crate::sonic::unhelped::permutation_structure::*;
use crate::sonic::unhelped::SuccinctMultiVerifier;
use crate::sonic::unhelped::{create_aggregate_on_srs};
use crate::sonic::cs::{Circuit, ConstraintSystem, LinearCombination, Coeff};
let perm_structure = create_permutation_structure::<Bls12, _>(&AdaptorCircuit(circuit.clone()));
let s1_srs = perm_structure.create_permutation_special_reference(&srs);
// let s2_srs = perm_structure.calculate_s2_commitment_value(&srs);
let info = get_circuit_parameters_for_succinct_sonic::<Bls12, _>(circuit.clone()).expect("Must get circuit info");
println!("{:?}", info);
println!("creating proof");
let start = Instant::now();
let proof = create_proof_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("done in {:?}", start.elapsed());
println!("creating advice");
let start = Instant::now();
let advice = create_advice_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap();
println!("done in {:?}", start.elapsed());
println!("creating aggregate for {} proofs", samples);
let start = Instant::now();
let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect();
let aggregate = create_aggregate_on_srs::<Bls12, _, Permutation3>(&AdaptorCircuit(circuit.clone()), &proofs, &srs, &s1_srs);
println!("done in {:?}", start.elapsed());
// {
// let rng = thread_rng();
// let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
// println!("verifying 1 proof without advice");
// let start = Instant::now();
// {
// for _ in 0..1 {
// verifier.add_proof(&proof, &[], |_, _| None);
// }
// assert_eq!(verifier.check_all(), true); // TODO
// }
// println!("done in {:?}", start.elapsed());
// }
// {
// let rng = thread_rng();
// let mut verifier = MultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
// println!("verifying {} proofs without advice", samples);
// let start = Instant::now();
// {
// for _ in 0..samples {
// verifier.add_proof(&proof, &[], |_, _| None);
// }
// assert_eq!(verifier.check_all(), true); // TODO
// }
// println!("done in {:?}", start.elapsed());
// }
{
use rand::{XorShiftRng, SeedableRng, Rand, Rng};
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut verifier = SuccinctMultiVerifier::<Bls12, _, Permutation3, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
println!("verifying 100 proofs with succinct advice");
let start = Instant::now();
{
for (ref proof, ref advice) in &proofs {
verifier.add_proof_with_advice(proof, &[], advice);
}
verifier.add_aggregate(
&proofs,
&aggregate,
&srs,
);
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
}
}
#[test]
fn test_inputs_into_sonic_mimc() {
use crate::pairing::ff::{Field, PrimeField};
@ -248,7 +589,7 @@ fn test_inputs_into_sonic_mimc() {
use crate::pairing::bn256::{Bn256, Fr};
// use crate::pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use bellman::sonic::srs::SRS;
use crate::sonic::srs::SRS;
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
@ -278,11 +619,11 @@ fn test_inputs_into_sonic_mimc() {
constants: &constants
};
use bellman::sonic::cs::Basic;
use bellman::sonic::sonic::AdaptorCircuit;
use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters};
use bellman::sonic::helped::helper::{create_aggregate_on_srs};
use crate::sonic::sonic::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
use crate::sonic::helped::helper::{create_aggregate_on_srs};
let info = get_circuit_parameters::<Bn256, _>(circuit.clone()).expect("Must get circuit info");
println!("{:?}", info);
@ -352,7 +693,7 @@ fn test_inputs_into_sonic_mimc() {
fn test_high_level_sonic_api() {
use crate::pairing::bn256::{Bn256};
use std::time::{Instant};
use bellman::sonic::helped::{
use crate::sonic::helped::{
generate_random_parameters,
verify_aggregate,
verify_proofs,
@ -399,9 +740,9 @@ fn test_high_level_sonic_api() {
println!("done in {:?}", start.elapsed());
println!("creating aggregate for {} proofs", samples);
let start = Instant::now();
let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect();
let start = Instant::now();
let aggregate = create_aggregate::<Bn256, _>(circuit.clone(), &proofs, &params);
println!("done in {:?}", start.elapsed());
@ -429,4 +770,102 @@ fn test_high_level_sonic_api() {
println!("done in {:?}", start.elapsed());
}
}
}
}
// #[test]
// fn test_constraints_info() {
// use crate::pairing::bn256::{Bn256};
// use std::time::{Instant};
// use crate::sonic::unhelped::padding::{constraints_info};
// {
// // This may not be cryptographically safe, use
// // `OsRng` (for example) in production software.
// let mut rng = &mut thread_rng();
// // Generate the MiMC round constants
// let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
// let xl = rng.gen();
// let xr = rng.gen();
// let image = mimc::<Bn256>(xl, xr, &constants);
// // Create an instance of our circuit (with the
// // witness)
// let circuit = MiMCDemo {
// xl: Some(xl),
// xr: Some(xr),
// constants: &constants
// };
// constraints_info::<Bn256, _>(circuit.clone());
// }
// }
// #[test]
// fn test_padding_using_mimc() {
// use crate::pairing::ff::{Field, PrimeField};
// use crate::pairing::{Engine, CurveAffine, CurveProjective};
// use crate::pairing::bls12_381::{Bls12, Fr};
// use std::time::{Instant};
// use crate::sonic::srs::SRS;
// let srs_x = Fr::from_str("23923").unwrap();
// let srs_alpha = Fr::from_str("23728792").unwrap();
// println!("making srs");
// let start = Instant::now();
// let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
// println!("done in {:?}", start.elapsed());
// {
// // This may not be cryptographically safe, use
// // `OsRng` (for example) in production software.
// let rng = &mut thread_rng();
// // Generate the MiMC round constants
// let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
// let samples: usize = 100;
// let xl = rng.gen();
// let xr = rng.gen();
// let image = mimc::<Bls12>(xl, xr, &constants);
// // Create an instance of our circuit (with the
// // witness)
// let circuit = MiMCDemoNoInputs {
// xl: Some(xl),
// xr: Some(xr),
// image: Some(image),
// constants: &constants
// };
// use crate::sonic::cs::Basic;
// use crate::sonic::sonic::AdaptorCircuit;
// use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
// use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
// use crate::sonic::helped::helper::{create_aggregate_on_srs};
// use crate::sonic::unhelped::padding::Padding;
// let info = get_circuit_parameters::<Bls12, _>(circuit.clone()).expect("Must get circuit info");
// println!("{:?}", info);
// println!("creating proof");
// let start = Instant::now();
// let proof = create_proof_on_srs::<Bls12, _, Padding>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
// println!("done in {:?}", start.elapsed());
// {
// let rng = thread_rng();
// let mut verifier = MultiVerifier::<Bls12, _, Padding, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
// println!("K map = {:?}", verifier.get_k_map());
// println!("verifying 1 proof without advice");
// let start = Instant::now();
// {
// for _ in 0..1 {
// verifier.add_proof(&proof, &[], |_, _| None);
// }
// assert_eq!(verifier.check_all(), true); // TODO
// }
// println!("done in {:?}", start.elapsed());
// }
// }
// }

@ -39,6 +39,7 @@ use std::marker::PhantomData;
pub struct RollingHashTranscript<H: Hasher> {
buffer: Vec<u8>,
last_finalized_value: Vec<u8>,
repeated_request_nonce: u32,
_marker: PhantomData<H>
}
@ -50,6 +51,7 @@ impl<H: Hasher> RollingHashTranscript<H> {
Self {
buffer: buffer,
last_finalized_value: vec![],
repeated_request_nonce: 0u32,
_marker: PhantomData
}
}
@ -86,6 +88,7 @@ impl<H:Hasher> TranscriptProtocol for RollingHashTranscript<H> {
fn commit_point<G: CurveAffine>(&mut self, point: &G) {
self.commit_bytes(b"point", point.into_uncompressed().as_ref());
// self.commit_bytes(b"point", point.into_compressed().as_ref());
self.repeated_request_nonce = 0u32;
}
fn commit_scalar<F: PrimeField>(&mut self, scalar: &F) {
@ -94,11 +97,12 @@ impl<H:Hasher> TranscriptProtocol for RollingHashTranscript<H> {
// scalar.into_repr().write_le(&mut v).unwrap();
self.commit_bytes(b"scalar", &v);
self.repeated_request_nonce = 0u32;
}
fn get_challenge_scalar<F: PrimeField>(&mut self) -> F {
use byteorder::ByteOrder;
let mut nonce = 0u32;
let mut nonce = self.repeated_request_nonce;
loop {
let mut nonce_bytes = vec![0u8; 4];
byteorder::BigEndian::write_u32(&mut nonce_bytes, nonce);
@ -108,6 +112,7 @@ impl<H:Hasher> TranscriptProtocol for RollingHashTranscript<H> {
if let Ok(result) = F::from_repr(repr) {
// println!("Got a challenge {} for nonce = {}", result, nonce);
self.repeated_request_nonce = nonce + 1u32;
return result;
}
if nonce == (0xffffffff as u32) {

@ -0,0 +1,264 @@
use crate::pairing::ff::{Field};
use crate::pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use crate::sonic::helped::{Proof, SxyAdvice};
use crate::sonic::helped::batch::Batch;
use crate::sonic::helped::poly::{SxEval, SyEval};
use crate::sonic::helped::Parameters;
use crate::SynthesisError;
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::CountNandQ;
use crate::sonic::sonic::M;
use super::s2_proof::{S2Eval, S2Proof};
use super::permutation_structure::create_permutation_structure;
use super::permutation_argument::PermutationArgument;
use super::permutation_argument::SignatureOfCorrectComputation;
use super::permutation_argument::SpecializedSRS;
#[derive(Clone)]
pub struct SuccinctAggregate<E: Engine> {
pub signature: SignatureOfCorrectComputation<E>,
pub s2_proof: S2Proof<E>,
pub c: E::G1Affine,
// We have to open each of the S commitments to a random point `z`
pub s_opening: E::G1Affine,
// We have to open C to each constituent `y`
pub c_openings: Vec<(E::G1Affine, E::Fr)>,
// Then we have to finally open C
pub opening: E::G1Affine,
pub z: E::Fr,
pub w: E::Fr,
}
// pub fn create_aggregate<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
// circuit: &C,
// inputs: &[(Proof<E>, SxyAdvice<E>)],
// params: &Parameters<E>,
// ) -> SuccinctAggregate<E>
// {
// let n = params.vk.n;
// let q = params.vk.q;
// create_aggregate_on_srs_using_information::<E, C, S>(circuit, inputs, &params.srs, n, q)
// }
pub fn create_aggregate_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
inputs: &[(Proof<E>, SxyAdvice<E>)],
srs: &SRS<E>,
specialized_srs: &SpecializedSRS<E>
) -> SuccinctAggregate<E>
{
// TODO: precompute this?
let (n, q) = {
let mut tmp = CountNandQ::<S>::new();
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
(tmp.n, tmp.q)
};
create_aggregate_on_srs_using_information::<E, C, S>(circuit, inputs, srs, specialized_srs, n, q)
}
pub fn create_aggregate_on_srs_using_information<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
inputs: &[(Proof<E>, SxyAdvice<E>)],
srs: &SRS<E>,
_specialized_srs: &SpecializedSRS<E>,
n: usize,
q: usize,
) -> SuccinctAggregate<E>
{
use std::time::Instant;
let start = Instant::now();
// take few proofs that are to be evaluated at some y_i and make an aggregate from them
let mut transcript = Transcript::new(&[]);
let mut y_values: Vec<E::Fr> = Vec::with_capacity(inputs.len());
for &(ref proof, ref sxyadvice) in inputs {
{
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&proof.r);
y_values.push(transcript.get_challenge_scalar());
}
transcript.commit_point(&sxyadvice.s);
}
let z: E::Fr = transcript.get_challenge_scalar();
// Compute s(z, Y) for opening of the previous commitments at the same `z`
let (s_poly_negative, s_poly_positive) = {
let mut tmp = SyEval::new(z, n, q);
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.poly()
};
// Compute C = g^{s(z, x)}
let c = multiexp(
srs.g_positive_x_alpha[0..(n + q)]
.iter()
.chain_ext(srs.g_negative_x_alpha[0..n].iter()),
s_poly_positive.iter().chain_ext(s_poly_negative.iter())
).into_affine();
transcript.commit_point(&c);
// Open C at w
let w: E::Fr = transcript.get_challenge_scalar();
let value = compute_value::<E>(&w, &s_poly_positive, &s_poly_negative);
let opening = {
let mut value = value;
value.negate();
polynomial_commitment_opening(
n,
0,
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
w,
&srs
)
};
println!("Commit and opening of for s(z, w) taken {:?}", start.elapsed());
// now we need signature of correct computation. For this purpose
// verifier already knows specialized SRS, so we can just commit to
// s1 and s2 parts of such signature to get `w` and later open at this point!
// Commit!
// TODO: Precompute!
// this will internally synthesize a circuit and structure of permutations
let start = Instant::now();
let s2_eval = S2Eval::new(n);
let s2_proof = s2_eval.evaluate(z, w, &srs);
println!("S2 proof taken {:?}", start.elapsed());
let start = Instant::now();
let permutation_structure = create_permutation_structure(circuit);
let (non_permuted_coeffs, permutations) = permutation_structure.create_permutation_vectors();
println!("Permutation vectors synthesis taken {:?}", start.elapsed());
let start = Instant::now();
let signature = PermutationArgument::make_signature(
non_permuted_coeffs,
permutations,
w,
z,
&srs,
);
println!("Succinct signature for s(z, Y) taken {:?}", start.elapsed());
// Let's open up C to every y.
fn compute_value<E: Engine>(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr {
let mut value = E::Fr::zero();
let yinv = y.inverse().unwrap(); // TODO
let positive_powers_contrib = evaluate_at_consequitive_powers(poly_positive, *y, *y);
let negative_powers_contrib = evaluate_at_consequitive_powers(poly_negative, yinv, yinv);
value.add_assign(&positive_powers_contrib);
value.add_assign(&negative_powers_contrib);
value
}
let start = Instant::now();
// we still need to re-open previous commitments at the same new z
let mut c_openings = vec![];
for y in &y_values {
let value = compute_value::<E>(y, &s_poly_positive, &s_poly_negative);
let opening = {
let mut value = value;
value.negate();
polynomial_commitment_opening(
n,
0,
s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()),
*y,
&srs
)
};
c_openings.push((opening, value));
}
println!("Re-Evaluation and re-opening of s(z, Y) taken {:?}", start.elapsed());
// Okay, great. Now we need to open up each S at the same point z to the same value.
// Since we're opening up all the S's at the same point, we create a bunch of random
// challenges instead and open up a random linear combination.
let mut poly_negative = vec![E::Fr::zero(); n];
let mut poly_positive = vec![E::Fr::zero(); 2*n];
let mut expected_value = E::Fr::zero();
// TODO: this part can be further parallelized due to synthesis of S(X, y) being singlethreaded
let start = Instant::now();
for (y, c_opening) in y_values.iter().zip(c_openings.iter()) {
// Compute s(X, y_i)
let (s_poly_negative, s_poly_positive) = {
let mut tmp = SxEval::new(*y, n);
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.poly()
};
let mut value = c_opening.1;
let r: E::Fr = transcript.get_challenge_scalar();
value.mul_assign(&r);
expected_value.add_assign(&value);
mul_add_polynomials(& mut poly_negative[..], &s_poly_negative[..], r);
mul_add_polynomials(& mut poly_positive[..], &s_poly_positive[..], r);
}
println!("Re-evaluation of {} S polynomials taken {:?}", y_values.len(), start.elapsed());
let s_opening = {
let mut value = expected_value;
value.negate();
polynomial_commitment_opening(
n,
0,
poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()),
z,
&srs
)
};
SuccinctAggregate {
signature,
s2_proof,
c,
s_opening,
c_openings,
opening,
z: z,
w: w,
}
}

@ -8,6 +8,8 @@ use std::marker::PhantomData;
use crate::sonic::srs::SRS;
use crate::sonic::util::*;
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
use super::wellformed_argument::{WellformednessSignature, WellformednessArgument};
#[derive(Clone)]
pub struct GrandProductArgument<E: Engine> {
@ -20,14 +22,75 @@ pub struct GrandProductArgument<E: Engine> {
#[derive(Clone)]
pub struct GrandProductProof<E: Engine> {
t_opening: E::G1Affine,
e_zinv: E::Fr,
e_opening: E::G1Affine,
f_y: E::Fr,
f_opening: E::G1Affine,
pub t_opening: E::G1Affine,
pub e_zinv: E::Fr,
pub e_opening: E::G1Affine,
pub f_y: E::Fr,
pub f_opening: E::G1Affine,
}
#[derive(Clone)]
pub struct GrandProductSignature<E: Engine> {
pub c_commitments: Vec<(E::G1Affine, E::Fr)>,
pub t_commitment: E::G1Affine,
pub grand_product_openings: Vec<(E::Fr, E::G1Affine)>,
pub proof: GrandProductProof<E>,
pub wellformedness_signature: WellformednessSignature<E>,
}
impl<E: Engine> GrandProductArgument<E> {
pub fn create_signature(
transcript: &mut Transcript,
grand_products: Vec<(Vec<E::Fr>, Vec<E::Fr>)>,
y: E::Fr,
z: E::Fr,
srs: &SRS<E>,
) -> GrandProductSignature<E> {
let mut grand_product_challenges = vec![];
for _ in 0..grand_products.len() {
let c = transcript.get_challenge_scalar();
grand_product_challenges.push(c);
}
let mut all_polys = vec![];
let mut wellformed_challenges = vec![];
for _ in 0..(grand_products.len()*2) {
let c = transcript.get_challenge_scalar();
wellformed_challenges.push(c);
}
for p in grand_products.iter() {
let (a, b) = p;
all_polys.push(a.clone());
all_polys.push(b.clone());
}
let wellformedness_signature = WellformednessArgument::create_signature(
all_polys,
wellformed_challenges,
&srs
);
let mut grand_product_argument = GrandProductArgument::new(grand_products);
let c_commitments = grand_product_argument.commit_to_individual_c_polynomials(&srs);
let t_commitment = grand_product_argument.commit_to_t_polynomial(&grand_product_challenges, y, &srs);
let grand_product_openings = grand_product_argument.open_commitments_for_grand_product(y, z, &srs);
let a_zy: Vec<E::Fr> = grand_product_openings.iter().map(|el| el.0.clone()).collect();
let proof = grand_product_argument.make_argument(&a_zy, &grand_product_challenges, y, z, &srs);
GrandProductSignature {
c_commitments,
t_commitment,
grand_product_openings,
// a_zy,
proof,
wellformedness_signature
}
}
pub fn new(polynomials: Vec<(Vec<E::Fr>, Vec<E::Fr>)>) -> Self {
assert!(polynomials.len() > 0);
@ -44,7 +107,7 @@ impl<E: Engine> GrandProductArgument<E> {
// c_3 = a_3 * c_2 = a_3 * a_2 * a_1
// ...
// c_n = a_n * c_{n-1} = \prod a_i
// a_{n+1} = c_{n-1}^-1
// a_{n+1} = c_{n}^-1
// c_{n+1} = 1
// c_{n+1} = a_{n+2} * c_{n+1} = a_{n+2}
// ...
@ -67,10 +130,19 @@ impl<E: Engine> GrandProductArgument<E> {
}
assert_eq!(c_poly.len(), n);
a_poly.extend(p0);
assert_eq!(a_poly.len(), n);
// v = a_{n+1} = c_{n}^-1
let v = c_poly[n-1].inverse().unwrap();
// let v = c_poly[n-1].inverse().unwrap();
let v = c_coeff.inverse().unwrap();
// ! IMPORTANT
// This line is indeed assigning a_{n+1} to zero instead of v
// for the practical purpose later we manually evaluate T polynomial
// and assign v to the term X^{n+1}
a_poly.push(E::Fr::zero());
// a_poly.push(v);
// add c_{n+1}
let mut c_coeff = E::Fr::one();
c_poly.push(c_coeff);
@ -83,6 +155,7 @@ impl<E: Engine> GrandProductArgument<E> {
a_poly.extend(p1);
assert_eq!(c_poly[n-1], c_poly[2*n]);
assert_eq!(c_poly[n], E::Fr::one());
a_polynomials.push(a_poly);
c_polynomials.push(c_poly);
@ -98,21 +171,21 @@ impl<E: Engine> GrandProductArgument<E> {
}
}
// Make a commitment to a polynomial in a form A*B^{x+1} = [a_1...a_{n}, 0, b_1...b_{n}]
pub fn commit_for_grand_product(a: &[E::Fr], b: &[E::Fr], srs: &SRS<E>) -> E::G1Affine {
assert_eq!(a.len(), b.len());
// // Make a commitment to a polynomial in a form A*B^{x+1} = [a_1...a_{n}, 0, b_1...b_{n}]
// pub fn commit_for_grand_product(a: &[E::Fr], b: &[E::Fr], srs: &SRS<E>) -> E::G1Affine {
// assert_eq!(a.len(), b.len());
let n = a.len();
// let n = a.len();
// multiexp(
// srs.g_positive_x_alpha[0..(2*n+1)].iter(),
// a.iter()
// .chain_ext(Some(E::Fr::zero()).iter())
// .chain_ext(b.iter())
// ).into_affine()
// }
multiexp(
srs.g_positive_x_alpha[0..(2*n+1)].iter(),
a.iter()
.chain_ext(Some(E::Fr::zero()).iter())
.chain_ext(b.iter())
).into_affine()
}
// Make a commitment to a polynomial in a form A*B^{x+1} = [a_1...a_{n}, 0, b_1...b_{n}]
pub fn commit_for_individual_products(a: &[E::Fr], b: &[E::Fr], srs: &SRS<E>) -> (E::G1Affine, E::G1Affine) {
assert_eq!(a.len(), b.len());
@ -139,29 +212,41 @@ impl<E: Engine> GrandProductArgument<E> {
let mut results = vec![];
for a_poly in self.a_polynomials.iter() {
let a = & a_poly[0..n];
let b = & a_poly[(n+1)..];
assert_eq!(a.len(), n);
assert_eq!(b.len(), n);
let mut val = evaluate_at_consequitive_powers(a, yz, yz);
{
let tmp = yz.pow([(n+2) as u64]);
let v = evaluate_at_consequitive_powers(b, tmp, yz);
val.add_assign(&v);
}
assert_eq!(a_poly[n], E::Fr::zero()); // there is no term for n+1 power
let val = evaluate_at_consequitive_powers(&a_poly[..], yz, yz);
// let a = & a_poly[0..n]; // powers [1, n]
// let b = & a_poly[(n+1)..]; // there is no n+1 term (numerated as `n`), skip it and start b
// assert_eq!(a.len(), n);
// assert_eq!(b.len(), n);
// let mut val = evaluate_at_consequitive_powers(a, yz, yz);
// {
// let tmp = yz.pow([(n+2) as u64]);
// let v = evaluate_at_consequitive_powers(b, tmp, yz);
// val.add_assign(&v);
// }
let mut constant_term = val;
constant_term.negate();
let opening = polynomial_commitment_opening(
let opening = polynomial_commitment_opening(
0,
2*n + 1,
Some(constant_term).iter()
.chain_ext(a.iter())
.chain_ext(Some(E::Fr::zero()).iter())
.chain_ext(b.iter()),
.chain_ext(a_poly.iter()),
yz,
&srs);
&srs
);
// let opening = polynomial_commitment_opening(
// 0,
// 2*n + 1,
// Some(constant_term).iter()
// .chain_ext(a.iter())
// .chain_ext(Some(E::Fr::zero()).iter())
// .chain_ext(b.iter()),
// yz,
// &srs);
results.push((val, opening));
@ -175,11 +260,14 @@ impl<E: Engine> GrandProductArgument<E> {
let mut results = vec![];
let n = self.c_polynomials[0].len();
let two_n_plus_1 = self.c_polynomials[0].len();
for (p, v) in self.c_polynomials.iter().zip(self.v_elements.iter()) {
let n = self.n;
assert_eq!(p[n], E::Fr::one(), "C_(n+1) must be one");
let c = multiexp(
srs.g_positive_x_alpha[0..n].iter(),
srs.g_positive_x_alpha[0..two_n_plus_1].iter(),
p.iter()
).into_affine();
@ -203,7 +291,7 @@ impl<E: Engine> GrandProductArgument<E> {
.zip(challenges.iter())
{
let mut a_xy = a.clone();
let mut c_xy = c.clone();
let c_xy = c.clone();
let v = *v;
assert_eq!(a_xy.len(), 2*n + 1);
@ -279,7 +367,7 @@ impl<E: Engine> GrandProductArgument<E> {
val.add_assign(&E::Fr::one());
// subtract at constant term
// subtract a constant term
assert_eq!(t[2*n+1], val);
t[2*n+1].sub_assign(&val);
@ -322,6 +410,8 @@ impl<E: Engine> GrandProductArgument<E> {
let z_inv = z.inverse().unwrap();
let mut t_subcomponent = E::Fr::zero();
for (((a, c), challenge), v) in a_zy.iter()
.zip(c_polynomials.into_iter())
.zip(challenges.iter())
@ -356,6 +446,9 @@ impl<E: Engine> GrandProductArgument<E> {
let mut ry = y;
ry.mul_assign(challenge);
t_subcomponent.add_assign(&rc);
t_subcomponent.sub_assign(&challenge);
if e_polynomial.is_some() && f_polynomial.is_some() {
if let Some(e_poly) = e_polynomial.as_mut() {
if let Some(f_poly) = f_polynomial.as_mut() {
@ -403,15 +496,24 @@ impl<E: Engine> GrandProductArgument<E> {
e_val.negate();
f_val.negate();
t_subcomponent.add_assign(&e_val);
t_subcomponent.sub_assign(&f_val);
let mut t_poly = self.t_polynomial.unwrap();
assert_eq!(t_poly.len(), 4*n + 3);
assert!(t_poly[2*n + 1].is_zero());
// largest negative power of t is -2n-1
let t_zy = {
let tmp = z_inv.pow([(2*n+1) as u64]);
evaluate_at_consequitive_powers(&t_poly, tmp, z)
};
assert_eq!(t_zy, t_subcomponent);
assert!(t_poly[2*n + 1].is_zero());
t_poly[2*n + 1].sub_assign(&t_zy);
let t_opening = polynomial_commitment_opening(
@ -430,7 +532,8 @@ impl<E: Engine> GrandProductArgument<E> {
}
}
pub fn verify_ab_commitment(n: usize,
pub fn verify_ab_commitment(
n: usize,
randomness: & Vec<E::Fr>,
a_commitments: &Vec<E::G1Affine>,
b_commitments: &Vec<E::G1Affine>,
@ -438,11 +541,10 @@ impl<E: Engine> GrandProductArgument<E> {
y: E::Fr,
z: E::Fr,
srs: &SRS<E>
) -> bool {
) -> bool {
assert_eq!(randomness.len(), a_commitments.len());
assert_eq!(openings.len(), a_commitments.len());
assert_eq!(b_commitments.len(), a_commitments.len());
let d = srs.d;
// e(Dj,hαx)e(Dyz,hα) = e(Aj,h)e(Bj,hxn+1)e(gaj ,hα)
@ -452,7 +554,8 @@ impl<E: Engine> GrandProductArgument<E> {
let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare();
let mut h_x_n_plus_one_precomp = srs.h_positive_x[n];
// H^(x^(n+1)) is n+1 indexed
let mut h_x_n_plus_one_precomp = srs.h_positive_x[n+1];
h_x_n_plus_one_precomp.negate();
let h_x_n_plus_one_precomp = h_x_n_plus_one_precomp.prepare();
@ -527,8 +630,6 @@ impl<E: Engine> GrandProductArgument<E> {
assert_eq!(a_zy.len(), challenges.len());
assert_eq!(commitments.len(), challenges.len());
let d = srs.d;
let g = srs.g_positive_x[0];
let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare();
@ -664,17 +765,38 @@ fn test_grand_product_argument() {
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
// let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
let srs = SRS::<Bls12>::new(128, srs_x, srs_alpha);
let n: usize = 1 << 8;
let n: usize = 1 << 5;
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let coeffs = (1..=n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let mut permutation = coeffs.clone();
rng.shuffle(&mut permutation);
let coeffs_product = coeffs.iter().fold(Fr::one(), |mut sum, x| {
sum.mul_assign(&x);
sum
});
let permutation_product = permutation.iter().fold(Fr::one(), |mut sum, x| {
sum.mul_assign(&x);
sum
});
assert_eq!(coeffs_product, permutation_product);
assert!(!coeffs_product.is_zero());
let a_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), coeffs.iter()).into_affine();
let b_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), permutation.iter()).into_affine();
let (a, b) = GrandProductArgument::commit_for_individual_products(&coeffs[..], &permutation[..], &srs);
assert_eq!(a_commitment, a);
assert_eq!(b_commitment, b);
let mut argument = GrandProductArgument::new(vec![(coeffs, permutation)]);
let commitments_and_v_values = argument.commit_to_individual_c_polynomials(&srs);
@ -693,14 +815,16 @@ fn test_grand_product_argument() {
let randomness = (0..1).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let valid = GrandProductArgument::verify_ab_commitment(n,
let valid = GrandProductArgument::verify_ab_commitment(
n,
&randomness,
&vec![a_commitment],
&vec![b_commitment],
&grand_product_openings,
y,
z,
&srs);
&srs
);
assert!(valid, "grand product commitments should be valid");

@ -5,7 +5,13 @@
mod s2_proof;
mod wellformed_argument;
mod grand_product_argument;
pub mod grand_product_argument;
mod permutation_argument;
mod verifier;
pub mod permutation_structure;
mod aggregate;
pub use self::wellformed_argument::{WellformednessArgument, WellformednessProof};
pub use self::wellformed_argument::{WellformednessArgument, WellformednessProof};
pub use self::permutation_argument::{PermutationArgument, PermutationProof, PermutationArgumentProof};
pub use self::verifier::SuccinctMultiVerifier;
pub use self::aggregate::*;

@ -0,0 +1,686 @@
use crate::pairing::ff::{Field};
use crate::pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use crate::sonic::cs::{Backend};
use crate::sonic::cs::{Coeff, Variable, LinearCombination};
use crate::sonic::util::*;
use crate::sonic::util::*;
use crate::sonic::cs::{SynthesisDriver};
use crate::Circuit as BellmanCircuit;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::cs::Circuit;
use crate::sonic::cs::ConstraintSystem;
use crate::sonic::cs::Nonassigning;
use crate::SynthesisError;
/*
s_1(X, Y) = \sum\limits_{i=1}^N u_i(Y) X^{N + 1 - i}
+ \sum\limits_{i=1}^N v_i(Y) X^{N + 1 + i}
+ \sum\limits_{i=1}^N w_i(Y) X^{2N + 1 + i}
where
u_i(Y) = \sum\limits_{q=1}^Q Y^{q} u_{i,q}
v_i(Y) = \sum\limits_{q=1}^Q Y^{q} v_{i,q}
w_i(Y) = \sum\limits_{q=1}^Q Y^{q} w_{i,q}
s_1(X, Y) = \sum\limits_{i=1}^(3N + 1) [u_{N + 1 - i}(Y), v_{i - N - 1}(Y), w_{i - 2N - 1}(Y)] X^{i}
where [] means concatenation
if we open up both sums a little it would look like
// q = 1,
Y * ( X * u_{N, 1} + X^{N + 1} * v_{1, 1} + X^{2N + 1} * w{1, 1}) = Y * (k_0 * X + k_1 * X^{N + 1} + k_2 * X^{2N + 1})
and for permutation where should exist another term over Y that would have the same structure, but with coefficients permuted, e.g.
Y^{p_1} * (k_1 * X + k_2 * X^{N + 1} + k_0 * X^{2N + 1}) and Y^{p_2} * (k_2 * X + k_0 * X^{N + 1} + k_1 * X^{2N + 1})
that would result in a sum
X * (k_0 * Y + k_1 * Y^{p_1} + k_2 * Y^{p_2})
+ X^{N + 1} * (k_1 * Y + k_2 * Y^{p_1} + k_0 * Y^{p_2})
+ X^{2N + 1} * (k_2 * Y + k_0 * Y^{p_1} + k_1 * Y^{p_2})
and permutations would look like
[k_0, k_1, k_2]
[1 , p_1, p_2]
[k_0, k_1, k_2]
[p_2, 1 , p_1]
[k_0, k_1, k_2]
[p_1, p_2, 1 ]
that would naively mean that k_0 should appear in constraint number 1 for variable number 1
constraint number p_1 for variable number N + 1
constraint number p_2 for variable number 2N + 1
restructuring strategy:
where u_{i, q} is a coefficient in a linear constraint for an A type variable number i
that corresponds to the qth multiplication gate
to make s_1 representable as a permutation we first must synthesize all the normal constraints,
then make what would look like a cyclic shift + expansion
- imagine that there were originally N variables
- variable A(i) in linear constraint number q had a coefficient of u{i, q}
- add a variable B(i+n) that would have a number
*/
pub struct Debugging<E> {
constraint_num: usize,
u: Vec<String>,
v: Vec<String>,
w: Vec<String>,
_marker: std::marker::PhantomData<E>
}
impl<'a, E: Engine> Backend<E> for &'a mut Debugging<E> {
fn new_linear_constraint(&mut self) {
self.constraint_num += 1;
self.u.push("".to_string());
self.v.push("".to_string());
self.w.push("".to_string());
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
let one = E::Fr::one();
let mut minus_one = one;
minus_one.negate();
match var {
Variable::A(index) => {
let acc = &mut self.u[self.constraint_num - 1];
match coeff {
Coeff::Zero => { },
Coeff::One => {
acc.push_str(&format!(" + A{}", index));
},
Coeff::NegativeOne => {
acc.push_str(&format!(" - A{}", index));
},
Coeff::Full(val) => {
if val == one {
acc.push_str(&format!(" + A{}", index));
} else if val == minus_one {
acc.push_str(&format!(" - A{}", index));
} else {
acc.push_str(&format!(" + {}*A{}", val, index));
}
}
}
}
Variable::B(index) => {
let acc = &mut self.v[self.constraint_num - 1];
match coeff {
Coeff::Zero => { },
Coeff::One => {
acc.push_str(&format!(" + B{}", index));
},
Coeff::NegativeOne => {
acc.push_str(&format!(" - B{}", index));
},
Coeff::Full(val) => {
if val == one {
acc.push_str(&format!(" + B{}", index));
} else if val == minus_one {
acc.push_str(&format!(" - B{}", index));
} else {
acc.push_str(&format!(" + {}*B{}", val, index));
}
}
}
}
Variable::C(index) => {
let acc = &mut self.w[self.constraint_num - 1];
match coeff {
Coeff::Zero => { },
Coeff::One => {
acc.push_str(&format!(" + C{}", index));
},
Coeff::NegativeOne => {
acc.push_str(&format!(" - C{}", index));
},
Coeff::Full(val) => {
if val == one {
acc.push_str(&format!(" + C{}", index));
} else if val == minus_one {
acc.push_str(&format!(" - C{}", index));
} else {
acc.push_str(&format!(" + {}*C{}", val, index));
}
}
}
}
};
}
}
pub struct Padding;
impl SynthesisDriver for Padding {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
struct Synthesizer<E: Engine, B: Backend<E>> {
backend: B,
current_variable: Option<usize>,
_marker: PhantomData<E>,
q: usize,
n: usize,
}
impl<E: Engine, B: Backend<E>>Synthesizer<E, B> {
fn purge_current_var(&mut self) {
match self.current_variable.take() {
Some(index) => {
let var_a = Variable::A(index);
let var_b = Variable::B(index);
let var_c = Variable::C(index);
let mut product = None;
let value_a = self.backend.get_var(var_a);
self.backend.set_var(var_b, || {
let value_b = E::Fr::one();
product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
product.as_mut().map(|product| product.mul_assign(&value_b));
Ok(value_b)
}).expect("should exist by now");
self.backend.set_var(var_c, || {
product.ok_or(SynthesisError::AssignmentMissing)
}).expect("should exist by now");
self.current_variable = None;
},
_ => {}
}
}
fn alloc_one(&mut self) -> Variable {
self.n += 1;
let index = self.n;
assert_eq!(index, 1);
self.backend.new_multiplication_gate();
let var_a = Variable::A(1);
let var_b = Variable::B(1);
let var_c = Variable::C(1);
self.backend.set_var(var_a, || {
Ok(E::Fr::one())
}).expect("should exist by now");
self.backend.set_var(var_b, || {
Ok(E::Fr::one())
}).expect("should exist by now");
self.backend.set_var(var_c, || {
Ok(E::Fr::one())
}).expect("should exist by now");
self.q += 1;
self.backend.new_linear_constraint();
self.backend.insert_coefficient(var_a, Coeff::One);
self.backend.insert_coefficient(var_b, Coeff::One);
self.backend.insert_coefficient(var_c, Coeff::NegativeOne);
self.backend.new_k_power(self.q);
var_a
}
}
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for Synthesizer<E, B> {
const ONE: Variable = Variable::A(1);
fn alloc<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
match self.current_variable.take() {
Some(index) => {
let var_a = Variable::A(index);
let var_b = Variable::B(index);
let var_c = Variable::C(index);
let mut product = None;
let value_a = self.backend.get_var(var_a);
self.backend.set_var(var_b, || {
let value_b = value()?;
product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
product.as_mut().map(|product| product.mul_assign(&value_b));
Ok(value_b)
})?;
self.backend.set_var(var_c, || {
product.ok_or(SynthesisError::AssignmentMissing)
})?;
self.current_variable = None;
Ok(var_b)
},
None => {
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let var_a = Variable::A(index);
self.backend.set_var(var_a, value)?;
self.current_variable = Some(index);
Ok(var_a)
}
}
}
// TODO: allocate input without spawning extra constraints
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
// self.purge_current_var();
// self.n += 1;
// self.backend.new_multiplication_gate();
// let index = self.n;
// let var = Variable::A::(index);
// self.q += 1;
// self.backend.new_k_power(self.q);
// self.backend.self.backend.insert_coefficient(new_var, Coeff::One);
// it's always going to be
let input_var = self.alloc(value)?;
self.enforce_zero(LinearCombination::zero() + input_var);
self.backend.new_k_power(self.q-2);
self.backend.new_k_power(self.q-1);
self.backend.new_k_power(self.q);
Ok(input_var)
}
fn enforce_zero(&mut self, lc: LinearCombination<E>)
{
self.q += 1;
self.backend.new_linear_constraint();
for (var, coeff) in lc.as_ref() {
self.backend.insert_coefficient(*var, *coeff);
}
// now we need to "rotate" a linear constraint by allocating more dummy variables, so ensuring
// that if for some q (index of LC) there is a coefficient C in front of a variable A(i) (that will result in a term ~ C*Y^{q}*X^{i})
// then there will be some other q' where there is a coefficient C in front of the variable B(i)
// (that will result in a term ~ C*Y^{q'}*X^{i+N}) and another q'' with C in front of C(i)
// (that will result in a term ~ C*Y^{q''}*X^{i+2N}), so S polynomial is indeed a permutation
// allocate at max 1 variable to later work with whole gates directly
self.purge_current_var();
use std::collections::HashMap;
// A -> B, B -> C, C -> A
{
self.q += 1;
self.backend.new_linear_constraint();
let mut allocation_map = HashMap::with_capacity(lc.as_ref().len());
let mut expected_new_index = self.n + 1;
// determine size of the map
for (var, _) in lc.as_ref() {
match var {
Variable::A(index) => {
if allocation_map.get(index).is_none() && *index != 1 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("A{} -> B{}", index, expected_new_index);
}
},
Variable::B(index) => {
if allocation_map.get(index).is_none() && *index != 2 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("B{} -> C{}", index, expected_new_index);
}
},
Variable::C(index) => {
if allocation_map.get(index).is_none() && *index != 3 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("C{} -> A{}", index, expected_new_index);
}
}
}
}
for _ in 0..allocation_map.len() {
self.backend.new_multiplication_gate();
self.n += 1;
}
for (index, new_index) in allocation_map.iter() {
let var_a = Variable::A(*new_index);
let var_b = Variable::B(*new_index);
let var_c = Variable::C(*new_index);
// A -> B, B -> C, C -> A
let b_val = self.backend.get_var(Variable::A(*index));
let c_val = self.backend.get_var(Variable::B(*index));
let a_val = self.backend.get_var(Variable::C(*index));
self.backend.set_var(var_a, || {
let value = a_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
self.backend.set_var(var_b, || {
let value = b_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
self.backend.set_var(var_c, || {
let value = c_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
}
// A -> B, B -> C, C -> A
for (var, coeff) in lc.as_ref() {
let new_var = match var {
Variable::A(index) => {
let var = if *index == 1 {
Variable::B(2)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::B(*new_index)
};
var
},
Variable::B(index) => {
let var = if *index == 2 {
Variable::C(3)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::C(*new_index)
};
var
},
Variable::C(index) => {
let var = if *index == 3 {
Variable::A(1)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::A(*new_index)
};
var
}
};
self.backend.insert_coefficient(new_var, *coeff);
}
}
// A -> C, B -> A, C -> B
{
self.q += 1;
self.backend.new_linear_constraint();
let mut allocation_map = HashMap::with_capacity(lc.as_ref().len());
let mut expected_new_index = self.n + 1;
// determine size of the map
for (var, _) in lc.as_ref() {
match var {
Variable::A(index) => {
if allocation_map.get(index).is_none() && *index != 1 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("A{} -> C{}", index, expected_new_index);
}
},
Variable::B(index) => {
if allocation_map.get(index).is_none() && *index != 2 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("B{} -> A{}", index, expected_new_index);
}
},
Variable::C(index) => {
if allocation_map.get(index).is_none() && *index != 3 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("C{} -> B{}", index, expected_new_index);
}
}
}
}
for _ in 0..allocation_map.len() {
self.backend.new_multiplication_gate();
self.n += 1;
}
// A -> C, B -> A, C -> B
for (index, new_index) in allocation_map.iter() {
let var_a = Variable::A(*new_index);
let var_b = Variable::B(*new_index);
let var_c = Variable::C(*new_index);
let b_val = self.backend.get_var(Variable::C(*index));
let c_val = self.backend.get_var(Variable::A(*index));
let a_val = self.backend.get_var(Variable::B(*index));
self.backend.set_var(var_a, || {
let value = a_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
self.backend.set_var(var_b, || {
let value = b_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
self.backend.set_var(var_c, || {
let value = c_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
}
// A -> C, B -> A, C -> B
for (var, coeff) in lc.as_ref() {
let new_var = match var {
Variable::A(index) => {
let var = if *index == 1 {
Variable::C(3)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::C(*new_index)
};
var
},
Variable::B(index) => {
let var = if *index == 2 {
Variable::A(1)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::A(*new_index)
};
var
},
Variable::C(index) => {
let var = if *index == 3 {
Variable::B(2)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::B(*new_index)
};
var
}
};
self.backend.insert_coefficient(new_var, *coeff);
}
}
}
fn multiply<F>(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
where
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
{
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let a = Variable::A(index);
let b = Variable::B(index);
let c = Variable::C(index);
let mut b_val = None;
let mut c_val = None;
self.backend.set_var(a, || {
let (a, b, c) = values()?;
b_val = Some(b);
c_val = Some(c);
Ok(a)
})?;
self.backend.set_var(b, || {
b_val.ok_or(SynthesisError::AssignmentMissing)
})?;
self.backend.set_var(c, || {
c_val.ok_or(SynthesisError::AssignmentMissing)
})?;
Ok((a, b, c))
}
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
self.backend.get_var(var).ok_or(())
}
}
let mut tmp: Synthesizer<E, B> = Synthesizer {
backend: backend,
current_variable: None,
_marker: PhantomData,
q: 0,
n: 0,
};
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <Synthesizer<E, B> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut tmp)?;
println!("Done synthesizing, N = {}, Q = {}", tmp.n, tmp.q);
Ok(())
}
}
pub fn constraints_info<E: Engine, C: BellmanCircuit<E> + Clone>(
circuit: C,
)
{
let adapted_circuit = AdaptorCircuit(circuit);
create_constraints_info::<_, _, Nonassigning>(&adapted_circuit)
}
pub fn constraints_padding_info<E: Engine, C: BellmanCircuit<E> + Clone>(
circuit: C,
)
{
let adapted_circuit = AdaptorCircuit(circuit);
create_constraints_info::<_, _, Padding>(&adapted_circuit)
}
pub fn create_constraints_info<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
)
{
let mut backend = Debugging::<E> {
constraint_num: 0,
u: vec![],
v: vec![],
w: vec![],
_marker: std::marker::PhantomData
};
S::synthesize(&mut backend, circuit).unwrap();
for (i, ((u, v), w)) in backend.u.iter()
.zip(backend.v.iter())
.zip(backend.w.iter())
.enumerate()
{
println!("Constraint {}: 0 = {}{}{}", i, u, v, w);
}
}
#[test]
fn my_fun_circuit_test() {
use crate::pairing::ff::PrimeField;
use crate::pairing::bls12_381::{Bls12, Fr};
struct MyCircuit;
impl<E: Engine> Circuit<E> for MyCircuit {
fn synthesize<CS: ConstraintSystem<E>>(&self, cs: &mut CS) -> Result<(), SynthesisError> {
let (a, b, _) = cs.multiply(|| {
Ok((
E::Fr::from_str("10").unwrap(),
E::Fr::from_str("20").unwrap(),
E::Fr::from_str("200").unwrap(),
))
})?;
cs.enforce_zero(LinearCombination::from(a) + a - b);
let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?;
cs.enforce_zero(LinearCombination::from(b) - multiplier);
Ok(())
}
}
create_constraints_info::<Bls12, _, Nonassigning>(&MyCircuit);
println!("---------------");
create_constraints_info::<Bls12, _, Padding>(&MyCircuit);
}

@ -9,47 +9,76 @@ use std::marker::PhantomData;
use crate::sonic::srs::SRS;
use crate::sonic::util::*;
use super::wellformed_argument::{WellformednessArgument, WellformednessProof};
use super::grand_product_argument::{GrandProductArgument, GrandProductProof};
use super::grand_product_argument::{GrandProductArgument, GrandProductSignature};
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
#[derive(Clone)]
pub struct SpecializedSRS<E: Engine> {
p_1: E::G1Affine,
p_2: Vec<E::G1Affine>,
p_3: E::G1Affine,
p_4: Vec<E::G1Affine>,
pub p_1: E::G1Affine,
pub p_2: Vec<E::G1Affine>,
pub p_3: E::G1Affine,
pub p_4: Vec<E::G1Affine>,
n: usize
}
#[derive(Clone)]
pub struct PermutationArgument<E: Engine> {
non_permuted_coefficients: Vec<Vec<E::Fr>>,
permuted_coefficients: Vec<Vec<E::Fr>>,
non_permuted_at_y_coefficients: Vec<Vec<E::Fr>>,
permuted_at_y_coefficients: Vec<Vec<E::Fr>>,
inverse_permuted_at_y_coefficients: Vec<Vec<E::Fr>>,
permutations: Vec<Vec<usize>>,
n: usize
}
#[derive(Clone)]
pub struct PermutationProof<E: Engine> {
v_zy: E::Fr,
e_opening: E::G1Affine,
f_opening: E::G1Affine,
pub v_zy: E::Fr,
pub e_opening: E::G1Affine,
pub f_opening: E::G1Affine,
}
#[derive(Clone)]
pub struct Proof<E: Engine> {
j: usize,
s_opening: E::G1Affine,
s_zy: E::Fr
pub struct PermutationArgumentProof<E: Engine> {
pub j: usize,
pub s_opening: E::G1Affine,
pub s_zy: E::Fr
}
#[derive(Clone)]
pub struct SignatureOfCorrectComputation<E: Engine> {
pub s_commitments: Vec<E::G1Affine>,
pub s_prime_commitments: Vec<E::G1Affine>,
pub perm_argument_proof: PermutationArgumentProof<E>,
pub perm_proof: PermutationProof<E>,
pub grand_product_signature: GrandProductSignature<E>
}
// fn permute<F: Field>(coeffs: &[F], permutation: & [usize]) -> Vec<F>{
// assert_eq!(coeffs.len(), permutation.len());
// let mut result: Vec<F> = vec![F::zero(); coeffs.len()];
// for (i, j) in permutation.iter().enumerate() {
// // if *j < 1 {
// // // if permutation information is missing coefficient itself must be zero!
// // assert!(coeffs[i].is_zero());
// // continue;
// // }
// result[*j - 1] = coeffs[i];
// }
// result
// }
fn permute<F: Field>(coeffs: &[F], permutation: & [usize]) -> Vec<F>{
assert_eq!(coeffs.len(), permutation.len());
let mut result: Vec<F> = vec![F::zero(); coeffs.len()];
fn permute_inverse<F: Field>(permuted_coeffs: &[F], permutation: & [usize]) -> Vec<F>{
assert_eq!(permuted_coeffs.len(), permutation.len());
let mut result: Vec<F> = vec![F::zero(); permuted_coeffs.len()];
for (i, j) in permutation.iter().enumerate() {
result[*j - 1] = coeffs[i];
// if *j < 1 {
// // if permutation information is missing coefficient itself must be zero!
// assert!(coeffs[i].is_zero());
// continue;
// }
result[i] = permuted_coeffs[*j - 1];
}
result
}
@ -68,8 +97,10 @@ impl<E: Engine> PermutationArgument<E> {
PermutationArgument {
non_permuted_coefficients: coefficients,
permuted_coefficients: vec![vec![]],
permuted_at_y_coefficients: vec![vec![]],
non_permuted_at_y_coefficients: vec![],
// permuted_coefficients: vec![],
permuted_at_y_coefficients: vec![],
inverse_permuted_at_y_coefficients: vec![],
permutations: permutations,
n: n
}
@ -81,7 +112,7 @@ impl<E: Engine> PermutationArgument<E> {
let n = non_permuted_coefficients[0].len();
// p1 is just a commitment to the powers of x
// p1 is just a commitment to the powers of x. It's indexed from 0 cause there is no g^0
let p_1 = multiexp(srs.g_positive_x_alpha[0..n].iter(), vec![E::Fr::one(); n].iter()).into_affine();
let mut p_2 = vec![];
@ -106,7 +137,6 @@ impl<E: Engine> PermutationArgument<E> {
// p2 is a commitment to the s^{perm}_i * x^i
{
// let permuted_coeffs = permute(&c[..], &p[..]);
let p2 = multiexp(srs.g_positive_x_alpha[0..n].iter(), c.iter()).into_affine();
p_2.push(p2);
}
@ -119,7 +149,9 @@ impl<E: Engine> PermutationArgument<E> {
fe
}).collect();
let p4 = multiexp(srs.g_positive_x_alpha[0..n].iter(), values.iter()).into_affine();
p_4.push(p4);
}
}
@ -135,33 +167,59 @@ impl<E: Engine> PermutationArgument<E> {
// commit to s and s' at y. Mutates the state
pub fn commit(&mut self, y: E::Fr, srs: &SRS<E>) -> Vec<(E::G1Affine, E::G1Affine)> {
assert!(self.inverse_permuted_at_y_coefficients.len() == 0);
let mut result = vec![];
let n = self.non_permuted_coefficients[0].len();
let mut permuted_coefficients = vec![];
let mut permuted_at_y_coefficients = vec![];
let mut non_permuted_at_y_coefficients = vec![];
// let mut permuted_coefficients = vec![];
// let mut permuted_at_y_coefficients = vec![];
let mut inverse_permuted_at_y_coefficients = vec![];
// naive algorithms
// for every permutation poly
// -- go throught all variable_idx
// - take coeff from non-permuted coeffs[permutation[variable_idx]]
// - mul by Y^{permutation[variable_idx]}
// - mul by X^{variable_idx + 1}
for (c, p) in self.non_permuted_coefficients.iter().zip(self.permutations.iter()) {
let mut non_permuted = c.clone();
let permuted = permute(&non_permuted[..], &p[..]);
let mut non_permuted_at_y = c.clone();
mut_distribute_consequitive_powers(&mut non_permuted_at_y[..], y, y);
let s_prime = multiexp(srs.g_positive_x_alpha[0..n].iter(), non_permuted_at_y.iter()).into_affine();
mut_distribute_consequitive_powers(&mut non_permuted[..], y, y);
let s_prime = multiexp(srs.g_positive_x_alpha[0..n].iter(), non_permuted.iter()).into_affine();
// if we pretend that non_permuted_at_y[sigma[i]] = coeffs[sigma[i]] * Y^sigma[i],
// then inverse_permuted_at_y[i] = coeffs[sigma[i]] * Y^sigma[i]
let inverse_permuted_at_y = permute_inverse(&non_permuted_at_y[..], &p[..]);
let mut permuted_at_y = permute(&non_permuted[..], &p[..]);
drop(non_permuted);
// let mut t = vec![E::Fr::zero(); inverse_permuted_at_y.len()];
// for i in 0..t.len() {
// let coeff = c[i];
// let sigma_i = p[i];
// let y_sigma_i = y.pow([sigma_i as u64]);
// t[i] = coeff;
// t[i].mul_assign(&y_sigma_i);
// }
let s = multiexp(srs.g_positive_x_alpha[0..n].iter(), permuted_at_y.iter()).into_affine();
// and commit to S
let s = multiexp(srs.g_positive_x_alpha[0..n].iter(), inverse_permuted_at_y.iter()).into_affine();
// let s = multiexp(srs.g_positive_x_alpha[0..n].iter(), t.iter()).into_affine();
result.push((s, s_prime));
permuted_coefficients.push(permuted);
permuted_at_y_coefficients.push(permuted_at_y);
non_permuted_at_y_coefficients.push(non_permuted_at_y);
// permuted_coefficients.push(permuted);
// permuted_at_y_coefficients.push(t);
// permuted_at_y_coefficients.push(permuted_at_y);
inverse_permuted_at_y_coefficients.push(inverse_permuted_at_y);
}
self.permuted_coefficients = permuted_coefficients;
self.permuted_at_y_coefficients = permuted_at_y_coefficients;
self.non_permuted_at_y_coefficients = non_permuted_at_y_coefficients;
// self.permuted_coefficients = permuted_coefficients;
// self.permuted_at_y_coefficients = permuted_at_y_coefficients;
self.inverse_permuted_at_y_coefficients = inverse_permuted_at_y_coefficients;
result
}
@ -232,9 +290,9 @@ impl<E: Engine> PermutationArgument<E> {
wellformed_challenges: & Vec<E::Fr>,
y: E::Fr,
z: E::Fr,
specialized_srs: &SpecializedSRS<E>,
_specialized_srs: &SpecializedSRS<E>,
srs: &SRS<E>
) -> Proof<E> {
) -> PermutationArgumentProof<E> {
// Sj(P4j)β(P1j)γ is equal to the product of the coefficients of Sj(P3j)β(P1j)γ
// also open s = \sum self.permuted_coefficients(X, y) at z
@ -245,7 +303,7 @@ impl<E: Engine> PermutationArgument<E> {
let mut s_polynomial: Option<Vec<E::Fr>> = None;
for c in self.permuted_at_y_coefficients.iter()
for c in self.inverse_permuted_at_y_coefficients.iter()
{
if s_polynomial.is_some() {
if let Some(poly) = s_polynomial.as_mut() {
@ -255,7 +313,7 @@ impl<E: Engine> PermutationArgument<E> {
s_polynomial = Some(c.clone());
}
}
let mut s_polynomial = s_polynomial.unwrap();
let s_polynomial = s_polynomial.unwrap();
// evaluate at z
let s_zy = evaluate_at_consequitive_powers(& s_polynomial[..], z, z);
@ -283,32 +341,53 @@ impl<E: Engine> PermutationArgument<E> {
let mut grand_products = vec![];
for (i, ((non_permuted, permuted), permutation)) in self.non_permuted_coefficients.into_iter()
.zip(self.permuted_coefficients.into_iter())
.zip(self.permutations.into_iter()).enumerate()
for ((non_permuted, inv_permuted), permutation) in self.non_permuted_at_y_coefficients.into_iter()
.zip(self.inverse_permuted_at_y_coefficients.into_iter())
.zip(self.permutations.into_iter())
{
// \prod si+βσi+γ = \prod s'i + β*i + γ
let mut s_j_combination = non_permuted;
// in S combination at the place i there should be term coeff[sigma(i)] * Y^sigma(i), that we can take
// from non-permuted by inverse_permuting it
// let mut s_combination = permute_inverse(&non_permuted[..], &permutation);
let mut s_combination = inv_permuted;
{
let p_4_values: Vec<E::Fr> = permutation.into_iter().map(|el| {
let mut repr = <<E as ScalarEngine>::Fr as PrimeField>::Repr::default();
repr.as_mut()[0] = el as u64;
let fe = E::Fr::from_repr(repr).unwrap();
let mut repr = <<E as ScalarEngine>::Fr as PrimeField>::Repr::default();
repr.as_mut()[0] = el as u64;
let fe = E::Fr::from_repr(repr).unwrap();
fe
}).collect();
mul_add_polynomials(&mut s_j_combination[..], & p_4_values[..], beta);
mul_add_polynomials(&mut s_j_combination[..], & p_1_values[..], gamma);
fe
}).collect();
mul_add_polynomials(&mut s_combination[..], & p_4_values[..], beta);
mul_add_polynomials(&mut s_combination[..], & p_1_values[..], gamma);
}
let mut s_prime_j_combination = permuted;
// combination of coeff[i]*Y^i + beta * i + gamma
let mut s_prime_combination = non_permuted.clone();
{
mul_add_polynomials(&mut s_prime_j_combination[..], & p_3_values[..], beta);
mul_add_polynomials(&mut s_prime_j_combination[..], & p_1_values[..], gamma);
mul_add_polynomials(&mut s_prime_combination[..], & p_3_values[..], beta);
mul_add_polynomials(&mut s_prime_combination[..], & p_1_values[..], gamma);
}
grand_products.push((s_j_combination, s_prime_j_combination));
// Sanity check
let s_prime_product = s_prime_combination.iter().fold(E::Fr::one(), |mut sum, x|
{
sum.mul_assign(&x);
sum
});
let s_product = s_combination.iter().fold(E::Fr::one(), |mut sum, x|
{
sum.mul_assign(&x);
sum
});
assert_eq!(s_product, s_prime_product, "product of coefficients must be the same");
grand_products.push((s_combination, s_prime_combination));
}
let mut a_commitments = vec![];
@ -349,8 +428,8 @@ impl<E: Engine> PermutationArgument<E> {
let randomness = (0..j).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
let valid = GrandProductArgument::verify_ab_commitment(n,
&randomness,
&a_commitments,
& randomness,
& a_commitments,
& b_commitments,
&grand_product_openings,
y,
@ -373,7 +452,7 @@ impl<E: Engine> PermutationArgument<E> {
assert!(valid, "grand product argument must be valid");
}
Proof {
PermutationArgumentProof {
j: j,
s_opening: s_zy_opening,
s_zy: s_zy
@ -381,7 +460,7 @@ impl<E: Engine> PermutationArgument<E> {
}
pub fn verify_s_prime_commitment(
n: usize,
_n: usize,
randomness: & Vec<E::Fr>,
challenges: & Vec<E::Fr>,
commitments: &Vec<E::G1Affine>,
@ -394,82 +473,6 @@ impl<E: Engine> PermutationArgument<E> {
assert_eq!(randomness.len(), 2);
assert_eq!(challenges.len(), commitments.len());
// let g = srs.g_positive_x[0];
// let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare();
// let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare();
// let mut h_prep = srs.h_positive_x[0];
// h_prep.negate();
// let h_prep = h_prep.prepare();
// let value = proof.v_zy;
// let g_v = g.mul(value.into_repr());
// {
// let mut minus_z_prime = z_prime;
// minus_z_prime.negate();
// let e_z = proof.e_opening.mul(minus_z_prime.into_repr());
// let mut h_alpha_term = e_z;
// h_alpha_term.add_assign(&g_v);
// let h_alpha_x_term = proof.e_opening;
// let s_r = multiexp(
// commitments.iter(),
// challenges.iter()
// ).into_affine();
// let h_term = s_r;
// let valid = E::final_exponentiation(&E::miller_loop(&[
// (&h_alpha_x_term.prepare(), &h_alpha_x_precomp),
// (&h_alpha_term.into_affine().prepare(), &h_alpha_precomp),
// (&h_term.prepare(), &h_prep),
// ])).unwrap() == E::Fqk::one();
// if !valid {
// return false;
// }
// }
// {
// let mut minus_yz = z_prime;
// minus_yz.mul_assign(&y);
// minus_yz.negate();
// let f_yz = proof.f_opening.mul(minus_yz.into_repr());
// let p2_r = multiexp(
// specialized_srs.p_2.iter(),
// challenges.iter()
// ).into_affine();
// let mut h_alpha_term = f_yz;
// h_alpha_term.add_assign(&g_v);
// let h_alpha_x_term = proof.f_opening;
// let h_term = p2_r;
// let valid = E::final_exponentiation(&E::miller_loop(&[
// (&h_alpha_x_term.prepare(), &h_alpha_x_precomp),
// (&h_alpha_term.into_affine().prepare(), &h_alpha_precomp),
// (&h_term.prepare(), &h_prep),
// ])).unwrap() == E::Fqk::one();
// if !valid {
// return false;
// }
// }
// true
// e(E,hαx)e(Ez,hα) = e(􏰗Mj=1Sjrj,h)e(gv,hα)
// e(F,hαx)e(Fyz,hα) = e(􏰗Mj=1P2jrj,h)e(gv,hα)
@ -540,7 +543,7 @@ impl<E: Engine> PermutationArgument<E> {
pub fn verify(
s_commitments: &Vec<E::G1Affine>,
proof: &Proof<E>,
proof: &PermutationArgumentProof<E>,
z: E::Fr,
srs: &SRS<E>
) -> bool {
@ -580,6 +583,195 @@ impl<E: Engine> PermutationArgument<E> {
(&h_term.prepare(), &h_prep),
])).unwrap() == E::Fqk::one()
}
pub fn make_signature(
coefficients: Vec<Vec<E::Fr>>,
permutations: Vec<Vec<usize>>,
y: E::Fr,
z: E::Fr,
srs: &SRS<E>,
) -> SignatureOfCorrectComputation<E> {
let mut argument = PermutationArgument::new(coefficients, permutations);
let commitments = argument.commit(y, &srs);
let mut transcript = Transcript::new(&[]);
let mut s_commitments = vec![];
let mut s_prime_commitments = vec![];
let mut challenges = vec![];
let num_commitments = commitments.len();
for (s, s_prime) in commitments.into_iter() {
transcript.commit_point(&s);
transcript.commit_point(&s_prime);
s_commitments.push(s);
s_prime_commitments.push(s_prime);
}
// get challenges for a full batch
for _ in 0..num_commitments {
let c: E::Fr = transcript.get_challenge_scalar();
challenges.push(c);
}
let z_prime = transcript.get_challenge_scalar();
let s_prime_commitments_opening = argument.open_commitments_to_s_prime(&challenges, y, z_prime, &srs);
let (proof, grand_product_signature) = {
let (proof, grand_product_signature) = argument.make_argument_with_transcript(
&mut transcript,
y,
z,
&srs
);
(proof, grand_product_signature)
};
SignatureOfCorrectComputation {
s_commitments,
s_prime_commitments,
perm_argument_proof: proof,
perm_proof: s_prime_commitments_opening,
grand_product_signature
}
}
// Argument a permutation argument. Current implementation consumes, cause extra arguments are required
pub fn make_argument_with_transcript(self,
transcript: &mut Transcript,
y: E::Fr,
z: E::Fr,
srs: &SRS<E>
) -> (PermutationArgumentProof<E>, GrandProductSignature<E>) {
// create random beta and gamma for every single permutation argument
let mut betas = vec![];
let mut gammas = vec![];
for _ in 0..self.permutations.len() {
let beta: E::Fr = transcript.get_challenge_scalar();
let gamma: E::Fr = transcript.get_challenge_scalar();
betas.push(beta);
gammas.push(gamma);
}
// Sj(P4j)β(P1j)γ is equal to the product of the coefficients of Sj(P3j)β(P1j)γ
// also open s = \sum self.permuted_coefficients(X, y) at z
let n = self.n;
let j = self.non_permuted_coefficients.len();
let mut s_polynomial: Option<Vec<E::Fr>> = None;
for c in self.inverse_permuted_at_y_coefficients.iter()
{
if s_polynomial.is_some() {
if let Some(poly) = s_polynomial.as_mut() {
add_polynomials(&mut poly[..], & c[..]);
}
} else {
s_polynomial = Some(c.clone());
}
}
let s_polynomial = s_polynomial.unwrap();
// evaluate at z
let s_zy = evaluate_at_consequitive_powers(& s_polynomial[..], z, z);
let mut s_zy_neg = s_zy;
s_zy_neg.negate();
let s_zy_opening = polynomial_commitment_opening(
0,
n,
Some(s_zy_neg).iter().chain_ext(s_polynomial.iter()),
z,
&srs
);
// Sj(P4j)^β (P1j)^γ is equal to the product of the coefficients of Sj(P3j)^β (P1j)^γ
let p_1_values = vec![E::Fr::one(); n];
let p_3_values: Vec<E::Fr> = (1..=n).map(|el| {
let mut repr = <<E as ScalarEngine>::Fr as PrimeField>::Repr::default();
repr.as_mut()[0] = el as u64;
let fe = E::Fr::from_repr(repr).unwrap();
fe
}).collect();
let mut grand_products = vec![];
for ((((non_permuted, inv_permuted), permutation), beta), gamma) in
self.non_permuted_at_y_coefficients.into_iter()
.zip(self.inverse_permuted_at_y_coefficients.into_iter())
.zip(self.permutations.into_iter())
.zip(betas.into_iter())
.zip(gammas.into_iter())
{
// in S combination at the place i there should be term coeff[sigma(i)] * Y^sigma(i), that we can take
// from non-permuted by inverse_permuting it
// let mut s_combination = permute_inverse(&non_permuted[..], &permutation);
let mut s_combination = inv_permuted;
{
let p_4_values: Vec<E::Fr> = permutation.into_iter().map(|el| {
let mut repr = <<E as ScalarEngine>::Fr as PrimeField>::Repr::default();
repr.as_mut()[0] = el as u64;
let fe = E::Fr::from_repr(repr).unwrap();
fe
}).collect();
mul_add_polynomials(&mut s_combination[..], & p_4_values[..], beta);
mul_add_polynomials(&mut s_combination[..], & p_1_values[..], gamma);
}
// combination of coeff[i]*Y^i + beta * i + gamma
let mut s_prime_combination = non_permuted.clone();
{
mul_add_polynomials(&mut s_prime_combination[..], & p_3_values[..], beta);
mul_add_polynomials(&mut s_prime_combination[..], & p_1_values[..], gamma);
}
// Sanity check
let s_prime_product = s_prime_combination.iter().fold(E::Fr::one(), |mut sum, x|
{
sum.mul_assign(&x);
sum
});
let s_product = s_combination.iter().fold(E::Fr::one(), |mut sum, x|
{
sum.mul_assign(&x);
sum
});
assert_eq!(s_product, s_prime_product, "product of coefficients must be the same");
assert!(!s_product.is_zero(), "grand products must not be zero");
grand_products.push((s_combination, s_prime_combination));
}
let grand_product_signature = GrandProductArgument::create_signature(
transcript,
grand_products,
y,
z,
&srs
);
let proof = PermutationArgumentProof {
j: j,
s_opening: s_zy_opening,
s_zy: s_zy
};
(proof, grand_product_signature)
}
}
#[test]
@ -590,16 +782,15 @@ fn test_permutation_argument() {
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
// let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
let srs = SRS::<Bls12>::new(128, srs_x, srs_alpha);
let n: usize = 1 << 1;
let n: usize = 1 << 4;
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
// let mut permutation = (0..n).collect::<Vec<_>>();
// rng.shuffle(&mut permutation);
let coeffs = vec![Fr::from_str("3").unwrap(), Fr::from_str("4").unwrap()];
let permutation = vec![2, 1];
let mut coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
coeffs[2] = Fr::zero(); // edge case
let mut permutation = (1..=n).collect::<Vec<_>>();
rng.shuffle(&mut permutation);
let coeffs = vec![coeffs];
let permutations = vec![permutation];
@ -609,12 +800,8 @@ fn test_permutation_argument() {
let mut argument = PermutationArgument::new(coeffs, permutations);
let y : Fr = rng.gen();
let y : Fr = Fr::one();
let y : Fr = Fr::from_str("2").unwrap();
// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let challenges = vec![Fr::one()];
let challenges = (0..1).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let commitments = argument.commit(y, &srs);
let mut s_commitments = vec![];
@ -625,7 +812,6 @@ fn test_permutation_argument() {
}
let z_prime : Fr = rng.gen();
let z_prime : Fr = Fr::one();
let opening = argument.open_commitments_to_s_prime(&challenges, y, z_prime, &srs);
@ -656,7 +842,8 @@ fn test_permutation_argument() {
gamma,
& grand_product_challenges,
& wellformed_challenges,
y, z,
y,
z,
&specialized_srs, &srs);
let valid = PermutationArgument::verify(&s_commitments, &proof, z, &srs);

@ -0,0 +1,743 @@
use crate::pairing::ff::{Field};
use crate::pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use crate::sonic::helped::{Proof, SxyAdvice};
use crate::sonic::helped::batch::Batch;
use crate::sonic::helped::poly::{SxEval, SyEval};
use crate::sonic::helped::Parameters;
use crate::SynthesisError;
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver, ConstraintSystem};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::Preprocess;
use crate::sonic::sonic::M;
use crate::sonic::sonic::PermutationSynthesizer;
use super::s2_proof::*;
use super::permutation_argument::*;
#[derive(Clone)]
pub struct PermutationStructure<E: Engine> {
pub n: usize,
pub q: usize,
pub a: Vec<[Option<(Coeff<E>, usize)>; M]>,
pub b: Vec<[Option<(Coeff<E>, usize)>; M]>,
pub c: Vec<[Option<(Coeff<E>, usize)>; M]>,
}
pub fn create_permutation_structure<E: Engine, C: Circuit<E>>(
circuit: &C,
) -> PermutationStructure<E>
{
let mut backend: Preprocess<E> = Preprocess::new();
let (a, b, c) = {
let mut cs: PermutationSynthesizer<E, &'_ mut Preprocess<E>> = PermutationSynthesizer::new(&mut backend);
let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <PermutationSynthesizer<E, &'_ mut Preprocess<E>> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut cs).expect("should synthesize");
(cs.a, cs.b, cs.c)
};
let n = backend.n;
let q = backend.q;
// println!("Will have {} gates and {} linear constraints", n, q);
PermutationStructure::<E> {
n: n,
q: q,
a: a,
b: b,
c: c
}
}
use rand::{Rng, Rand};
impl<E: Engine> PermutationStructure<E> {
pub fn calculate_s2_commitment_value(&self, srs: &SRS<E>) -> E::G1Affine {
S2Eval::calculate_commitment_element(self.n, srs)
}
pub fn calculate_s2_proof(&self, x: E::Fr, y: E::Fr, srs: &SRS<E>) -> S2Proof<E> {
let s2_eval = S2Eval::new(self.n);
s2_eval.evaluate(x, y, &srs)
}
pub fn create_inverse_permutation_vectors(&self) -> (Vec<Vec<E::Fr>>, Vec<Vec<usize>>) {
// we have to form non-permuted coefficients, as well as permutation structures;
let n = self.n;
let mut non_permuted_coeffs = vec![vec![E::Fr::zero(); 3*n+1]; M];
let mut permutations = vec![vec![0usize; 3*n+1]; M];
let one = E::Fr::one();
let mut minus_one = E::Fr::one();
minus_one.negate();
let mut not_empty = [false; M];
// go other the permutations
for (gate_index, info) in self.a.iter().enumerate() {
let offset = n-1;
for i in 0..M {
// coefficients of A are placed at the offset = 0 from the beginning of the vector
if let Some((coeff, place)) = info[i].as_ref() {
// place it
assert!(*place != 0);
let array_position = offset - gate_index; // special for A
let place_coeff_into = &mut non_permuted_coeffs[i];
let place_permutation_into = &mut permutations[i];
match coeff {
Coeff::Zero => {
},
Coeff::One => {
not_empty[i] = true;
place_coeff_into[array_position] = one;
place_permutation_into[array_position] = *place;
},
Coeff::NegativeOne => {
not_empty[i] = true;
place_coeff_into[array_position] = minus_one;
place_permutation_into[array_position] = *place;
},
Coeff::Full(value) => {
not_empty[i] = true;
place_coeff_into[array_position] = *value;
place_permutation_into[array_position] = *place;
}
}
}
}
}
for (gate_index, info) in self.b.iter().enumerate() {
let offset = n + 1;
for i in 0..M {
if let Some((coeff, place)) = info[i].as_ref() {
// place it
assert!(*place != 0);
let array_position = offset + gate_index;
let place_coeff_into = &mut non_permuted_coeffs[i];
let place_permutation_into = &mut permutations[i];
match coeff {
Coeff::Zero => {
},
Coeff::One => {
not_empty[i] = true;
place_coeff_into[array_position] = one;
place_permutation_into[array_position] = *place;
},
Coeff::NegativeOne => {
not_empty[i] = true;
place_coeff_into[array_position] = minus_one;
place_permutation_into[array_position] = *place;
},
Coeff::Full(value) => {
not_empty[i] = true;
place_coeff_into[array_position] = *value;
place_permutation_into[array_position] = *place;
}
}
}
}
}
for (gate_index, info) in self.c.iter().enumerate() {
let offset = 2*n + 1;
for i in 0..M {
// coefficients of A are placed at the offset = 0 from the beginning of the vector
if let Some((coeff, place)) = info[i].as_ref() {
// place it
assert!(*place != 0);
let array_position = offset + gate_index;
let place_coeff_into = &mut non_permuted_coeffs[i];
let place_permutation_into = &mut permutations[i];
match coeff {
Coeff::Zero => {
},
Coeff::One => {
not_empty[i] = true;
place_coeff_into[array_position] = one;
place_permutation_into[array_position] = *place;
},
Coeff::NegativeOne => {
not_empty[i] = true;
place_coeff_into[array_position] = minus_one;
place_permutation_into[array_position] = *place;
},
Coeff::Full(value) => {
not_empty[i] = true;
place_coeff_into[array_position] = *value;
place_permutation_into[array_position] = *place;
}
}
}
}
}
Self::print_constraints(n, self.q, &non_permuted_coeffs, &permutations);
// need to fill arrays with non-zero indexes just to have full permutation, even while it's just zero coefficient
// TODO: fix
let mut m = M;
for i in (0..M).into_iter().rev() {
// these are no constant terms
assert!(non_permuted_coeffs[i][n].is_zero());
assert!(permutations[i][n] == 0);
}
for i in (0..M).into_iter().rev() {
if !not_empty[i] {
non_permuted_coeffs.pop();
permutations.pop();
m -= 1;
}
}
assert!(m != 0);
// find something faster, although it's still linear
for i in 0..m {
let mut fillers: Vec<usize> = (1..=(3*n+1)).map(|el| el).collect();
for (p, c) in permutations[i].iter_mut().zip(non_permuted_coeffs[i].iter()) {
if *p == 0 {
assert!(c.is_zero());
} else {
fillers[*p - 1] = 0;
}
}
let mut fill_from = 0;
for p in permutations[i].iter_mut() {
if *p == 0 {
loop {
if fillers[fill_from] != 0 {
*p = fillers[fill_from];
fill_from += 1;
break;
} else {
fill_from += 1;
}
}
}
}
}
(non_permuted_coeffs, permutations)
}
pub fn create_permutation_vectors(&self) -> (Vec<Vec<E::Fr>>, Vec<Vec<usize>>) {
// we have to form non-permuted coefficients, as well as permutation structures;
let n = self.n;
let mut non_permuted_coeffs = vec![vec![E::Fr::zero(); 3*n+1]; M];
let mut permutations = vec![vec![0usize; 3*n+1]; M];
let one = E::Fr::one();
let mut minus_one = E::Fr::one();
minus_one.negate();
let mut not_empty = [false; M];
// go other the permutations
for (gate_index, info) in self.a.iter().enumerate() {
let offset = n-1;
for i in 0..M {
// coefficients of A are placed at the offset = 0 from the beginning of the vector
if let Some((coeff, place)) = info[i].as_ref() {
// place it
assert!(*place != 0);
let array_position = offset - gate_index; // special for A
let coeff_position = *place - 1;
let place_coeff_into = &mut non_permuted_coeffs[i];
let place_permutation_into = &mut permutations[i];
match coeff {
Coeff::Zero => {
},
Coeff::One => {
not_empty[i] = true;
place_coeff_into[coeff_position] = one;
place_permutation_into[array_position] = *place;
},
Coeff::NegativeOne => {
not_empty[i] = true;
place_coeff_into[coeff_position] = minus_one;
place_permutation_into[array_position] = *place;
},
Coeff::Full(value) => {
not_empty[i] = true;
place_coeff_into[coeff_position] = *value;
place_permutation_into[array_position] = *place;
}
}
}
}
}
for (gate_index, info) in self.b.iter().enumerate() {
let offset = n + 1;
for i in 0..M {
if let Some((coeff, place)) = info[i].as_ref() {
// place it
assert!(*place != 0);
let array_position = offset + gate_index;
let coeff_position = *place - 1;
let place_coeff_into = &mut non_permuted_coeffs[i];
let place_permutation_into = &mut permutations[i];
match coeff {
Coeff::Zero => {
},
Coeff::One => {
not_empty[i] = true;
place_coeff_into[coeff_position] = one;
place_permutation_into[array_position] = *place;
},
Coeff::NegativeOne => {
not_empty[i] = true;
place_coeff_into[coeff_position] = minus_one;
place_permutation_into[array_position] = *place;
},
Coeff::Full(value) => {
not_empty[i] = true;
place_coeff_into[coeff_position] = *value;
place_permutation_into[array_position] = *place;
}
}
}
}
}
for (gate_index, info) in self.c.iter().enumerate() {
let offset = 2*n + 1;
for i in 0..M {
// coefficients of A are placed at the offset = 0 from the beginning of the vector
if let Some((coeff, place)) = info[i].as_ref() {
// place it
assert!(*place != 0);
let array_position = offset + gate_index;
let coeff_position = *place - 1;
let place_coeff_into = &mut non_permuted_coeffs[i];
let place_permutation_into = &mut permutations[i];
match coeff {
Coeff::Zero => {
},
Coeff::One => {
not_empty[i] = true;
place_coeff_into[coeff_position] = one;
place_permutation_into[array_position] = *place;
},
Coeff::NegativeOne => {
not_empty[i] = true;
place_coeff_into[coeff_position] = minus_one;
place_permutation_into[array_position] = *place;
},
Coeff::Full(value) => {
not_empty[i] = true;
place_coeff_into[coeff_position] = *value;
place_permutation_into[array_position] = *place;
}
}
}
}
}
// Self::print_constraints(n, self.q, &non_permuted_coeffs, &permutations);
// need to fill arrays with non-zero indexes just to have full permutation, even while it's just zero coefficient
// TODO: fix
let mut m = M;
// for i in (0..M).into_iter().rev() {
// // these are no constant terms
// assert!(non_permuted_coeffs[i][n].is_zero());
// assert!(permutations[i][n] == 0);
// }
for i in (0..M).into_iter().rev() {
if !not_empty[i] {
non_permuted_coeffs.pop();
permutations.pop();
m -= 1;
}
}
assert!(m != 0);
// find something faster, although it's still linear
for i in 0..m {
let mut fillers: Vec<usize> = (1..=(3*n+1)).map(|el| el).collect();
for (p, _c) in permutations[i].iter_mut().zip(non_permuted_coeffs[i].iter()) {
if *p == 0 {
continue;
// assert!(c.is_zero());
} else {
fillers[*p - 1] = 0;
}
}
let mut fill_from = 0;
for p in permutations[i].iter_mut() {
if *p == 0 {
loop {
if fillers[fill_from] != 0 {
*p = fillers[fill_from];
fill_from += 1;
break;
} else {
fill_from += 1;
}
}
}
}
}
(non_permuted_coeffs, permutations)
}
pub fn print_constraints(n:usize, q: usize, coeffs: &Vec<Vec<E::Fr>>, permutations: &Vec<Vec<usize>>) {
let m = coeffs.len();
for constraint_idx in 1..=q {
println!("Constraint {} (term for Y^{})", constraint_idx, constraint_idx);
let mut terms = vec![];
for p_idx in 0..m {
if let Some(variable_idx) = permutations[p_idx].iter().position(|&s| s == constraint_idx) {
let coeff = coeffs[p_idx][variable_idx];
terms.push((variable_idx, coeff));
}
}
for (var_idx, coeff) in terms.into_iter() {
if var_idx < n + 1 {
print!("{} * A({})", coeff, n - var_idx);
} else if var_idx < 2*n + 1 {
print!("{} * B({})", coeff, var_idx - n);
} else {
print!("{} * C({})", coeff, var_idx - 2*n);
}
print!("\n");
}
}
}
pub fn create_permutation_special_reference(&self, srs: &SRS<E>) -> SpecializedSRS<E>
{
let (non_permuted_coeffs, permutations) = self.create_permutation_vectors();
let specialized_srs = PermutationArgument::make_specialized_srs(
&non_permuted_coeffs,
&permutations,
&srs
);
specialized_srs
}
pub fn make_signature(&self, y: E::Fr, z: E::Fr, srs: &SRS<E>) -> SignatureOfCorrectComputation<E> {
let (non_permuted_coeffs, permutations) = self.create_permutation_vectors();
let mut s_contrib = E::Fr::zero();
for permutation_index in 0..permutations.len() {
for (variable_index, sigma_i) in permutations[permutation_index].iter().enumerate() {
let y_power = y.pow([*sigma_i as u64]);
let x_power = z.pow([(variable_index+1) as u64]);
let coeff = non_permuted_coeffs[permutation_index][*sigma_i - 1];
let mut result = coeff;
result.mul_assign(&x_power);
result.mul_assign(&y_power);
s_contrib.add_assign(&result);
}
}
let z_n_plus_1_inv = z.pow([(self.n + 1) as u64]).inverse().unwrap();
let y_n = y.pow([self.n as u64]);
println!("Naive S contribution = {}", s_contrib);
s_contrib.mul_assign(&z_n_plus_1_inv);
s_contrib.mul_assign(&y_n);
println!("Naive S contribution scaled = {}", s_contrib);
// let specialized_srs = PermutationArgument::make_specialized_srs(
// &non_permuted_coeffs,
// &permutations,
// &srs
// );
let signature = PermutationArgument::make_signature(
non_permuted_coeffs,
permutations,
y,
z,
&srs,
);
signature
}
pub fn create_permutation_arguments<R: Rng>(&self, y: E::Fr, z: E::Fr, rng: &mut R, srs: &SRS<E>)
-> (Vec<(E::G1Affine, E::G1Affine)>, Vec<E::Fr>, PermutationProof<E>, PermutationArgumentProof<E>, E::Fr, usize, E::Fr)
{
// we have to form non-permuted coefficients, as well as permutation structures;
let n = self.n;
let (non_permuted_coeffs, permutations) = self.create_permutation_vectors();
let m = non_permuted_coeffs.len();
println!("Will need {} permutation polynomials", m);
let specialized_srs = PermutationArgument::make_specialized_srs(
&non_permuted_coeffs,
&permutations,
&srs
);
// evaluate S naively
let mut s_contrib = E::Fr::zero();
for permutation_index in 0..m {
for (variable_index, sigma_i) in permutations[permutation_index].iter().enumerate() {
let y_power = y.pow([*sigma_i as u64]);
let x_power = z.pow([(variable_index+1) as u64]);
let coeff = non_permuted_coeffs[permutation_index][*sigma_i - 1];
let mut result = coeff;
result.mul_assign(&x_power);
result.mul_assign(&y_power);
s_contrib.add_assign(&result);
}
}
println!("Naive S contribution = {}", s_contrib);
let mut argument = PermutationArgument::new(non_permuted_coeffs, permutations);
let challenges = (0..m).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
let commitments = argument.commit(y, &srs);
let mut s_commitments = vec![];
let mut s_prime_commitments = vec![];
for (s, s_prime) in commitments.clone().into_iter() {
s_commitments.push(s);
// println!("S' = {}", s_prime);
s_prime_commitments.push(s_prime);
}
let z_prime : E::Fr = rng.gen();
let opening = argument.open_commitments_to_s_prime(&challenges, y, z_prime, &srs);
let randomness = (0..2).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
let valid = PermutationArgument::verify_s_prime_commitment(n,
&randomness,
&challenges,
&s_prime_commitments,
&opening,
y,
z_prime,
&specialized_srs,
&srs);
assert!(valid, "s' commitment must be valid");
let beta : E::Fr = rng.gen();
let gamma : E::Fr = rng.gen();
let grand_product_challenges = (0..m).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
let wellformed_challenges = (0..(2*m)).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>();
let proof = argument.make_argument(
beta,
gamma,
& grand_product_challenges,
& wellformed_challenges,
y,
z,
&specialized_srs, &srs);
let valid = PermutationArgument::verify(&s_commitments, &proof, z, &srs);
assert!(valid, "permutation argument must be valid");
(commitments, challenges, opening, proof, z_prime, m, s_contrib)
}
}
#[test]
fn test_simple_succinct_sonic() {
use crate::pairing::ff::{Field, PrimeField};
use crate::pairing::{Engine, CurveAffine, CurveProjective};
use crate::pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use crate::sonic::srs::SRS;
use crate::sonic::cs::{Circuit, ConstraintSystem, LinearCombination};
struct MyCircuit;
impl<E: Engine> Circuit<E> for MyCircuit {
fn synthesize<CS: ConstraintSystem<E>>(&self, cs: &mut CS) -> Result<(), SynthesisError> {
let (a, b, c) = cs.multiply(|| {
Ok((
E::Fr::from_str("10").unwrap(),
E::Fr::from_str("20").unwrap(),
E::Fr::from_str("200").unwrap(),
))
})?;
cs.enforce_zero(LinearCombination::zero() + (Coeff::Full(E::Fr::from_str("2").unwrap()), a) - b);
cs.enforce_zero(LinearCombination::zero() + (Coeff::Full(E::Fr::from_str("20").unwrap()), a) - c);
cs.enforce_zero(LinearCombination::zero() + (Coeff::Full(E::Fr::from_str("10").unwrap()), b) - c);
// let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?;
// cs.enforce_zero(LinearCombination::from(b) - multiplier);
// let (a1, b1, _) = cs.multiply(|| {
// Ok((
// E::Fr::from_str("5").unwrap(),
// E::Fr::from_str("5").unwrap(),
// E::Fr::from_str("25").unwrap(),
// ))
// })?;
// cs.enforce_zero(LinearCombination::zero() + (Coeff::Full(E::Fr::from_str("2").unwrap()), b1) - a);
// cs.enforce_zero(LinearCombination::zero() + (Coeff::Full(E::Fr::from_str("4").unwrap()), a1) - b);
// cs.enforce_zero(LinearCombination::zero() + (Coeff::Full(E::Fr::from_str("40").unwrap()), b1) - c);
Ok(())
}
}
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
println!("making srs");
let start = Instant::now();
// let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
let srs = SRS::<Bls12>::new(100, srs_x, srs_alpha);
println!("done in {:?}", start.elapsed());
{
use rand::{XorShiftRng, SeedableRng, Rand, Rng};
let _rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
use crate::sonic::sonic::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters_for_succinct_sonic};
use crate::sonic::helped::helper::{create_aggregate_on_srs};
use crate::sonic::sonic::Permutation3;
use crate::sonic::unhelped::permutation_structure::*;
// let z: Fr = rng.gen();
// let y: Fr = rng.gen();
let z: Fr = Fr::from_str("2").unwrap();
let y: Fr = Fr::one();
let perm_structure = create_permutation_structure::<Bls12, _>(&MyCircuit);
let (non_permuted_coeffs, permutations) = perm_structure.create_permutation_vectors();
println!("Non-permuted = {:?}", non_permuted_coeffs[0]);
println!("Permutation = {:?}", permutations[0]);
println!("N = {}, Q = {}", perm_structure.n, perm_structure.q);
let n = perm_structure.n;
let szy = {
let mut tmp = SxEval::<Bls12>::new(y, n);
Permutation3::synthesize(&mut tmp, &MyCircuit).unwrap(); // TODO
tmp.finalize(z)
};
let naive_s1 = {
let mut res = Fr::zero();
for j in 0..permutations.len() {
for i in 0..non_permuted_coeffs[j].len() {
let sigma_i = permutations[j][i];
let coeff_i = non_permuted_coeffs[j][i];
// let coeff_sigma_i = non_permuted_coeffs[j][sigma_i - 1];
let y_power = y.pow([sigma_i as u64]);
let x_power = z.pow([(i+1) as u64]);
// let mut result = coeff_sigma_i;
let mut result = coeff_i;
result.mul_assign(&y_power);
result.mul_assign(&x_power);
res.add_assign(&result);
}
}
res
};
println!("Naive s1 = {}", naive_s1);
// perm_structure.create_permutation_arguments(y, z, rng, &srs);
let signature = perm_structure.make_signature(y, z, &srs);
let s2 = S2Eval::new(perm_structure.n);
let s2 = s2.evaluate(z, y, &srs);
let mut s2_value = s2.c_value;
s2_value.add_assign(&s2.d_value);
let mut expected_s2_value = Fr::zero();
let y_inv = y.inverse().unwrap();
let mut p1 = y;
p1.add_assign(&y_inv);
p1.mul_assign(&z);
expected_s2_value.add_assign(&p1);
let mut t0 = y;
t0.square();
let mut t1 = y_inv;
t1.square();
let mut p2 = t0;
p2.add_assign(&t1);
p2.mul_assign(&z);
p2.mul_assign(&z);
expected_s2_value.add_assign(&p2);
let z_n = z.pow([n as u64]);
let z_n_plus_1_inv = z.pow([(n + 1) as u64]).inverse().unwrap();
let y_n = y.pow([n as u64]);
assert!(expected_s2_value == s2_value);
s2_value.mul_assign(&z_n);
let mut s1 = signature.perm_argument_proof.s_zy;
println!("S1 = {}", s1);
s1.mul_assign(&z_n_plus_1_inv);
s1.mul_assign(&y_n);
s1.sub_assign(&s2_value);
let mut naive_s1 = naive_s1;
naive_s1.mul_assign(&z_n_plus_1_inv);
naive_s1.mul_assign(&y_n);
naive_s1.sub_assign(&s2_value);
println!("S1(?) = {}", naive_s1);
assert_eq!(s1, szy);
}
}

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

@ -14,10 +14,10 @@ pub struct S2Eval<E: Engine> {
#[derive(Clone)]
pub struct S2Proof<E: Engine> {
o: E::G1Affine,
c_value: E::Fr,
d_value: E::Fr,
c_opening: E::G1Affine,
d_opening: E::G1Affine
pub c_value: E::Fr,
pub d_value: E::Fr,
pub c_opening: E::G1Affine,
pub d_opening: E::G1Affine
}
impl<E: Engine> S2Eval<E> {
@ -48,7 +48,7 @@ impl<E: Engine> S2Eval<E> {
let (c, c_opening) = {
let mut point = y;
point.mul_assign(&x);
let val = evaluate_at_consequitive_powers(&poly[1..], E::Fr::one(), point);
let val = evaluate_at_consequitive_powers(&poly[1..], point, point);
poly[0] = val;
poly[0].negate();
let opening = polynomial_commitment_opening(0, self.n, poly.iter(), point, &srs);
@ -59,7 +59,7 @@ impl<E: Engine> S2Eval<E> {
let (d, d_opening) = {
let mut point = y.inverse().unwrap();
point.mul_assign(&x);
let val = evaluate_at_consequitive_powers(&poly[1..], E::Fr::one(), point);
let val = evaluate_at_consequitive_powers(&poly[1..], point, point);
poly[0] = val;
poly[0].negate();
let opening = polynomial_commitment_opening(0, self.n, poly.iter(), point, &srs);
@ -87,17 +87,19 @@ impl<E: Engine> S2Eval<E> {
h_prep.negate();
let h_prep = h_prep.prepare();
let mut c_minus_xy = proof.c_value;
let mut xy = x;
xy.mul_assign(&y);
let mut minus_xy = x;
minus_xy.mul_assign(&y);
minus_xy.negate();
c_minus_xy.sub_assign(&xy);
let mut h_alpha_term = proof.c_opening.mul(minus_xy.into_repr());
let g_in_c = E::G1Affine::one().mul(proof.c_value);
h_alpha_term.add_assign(&g_in_c);
let c_in_c_minus_xy = proof.c_opening.mul(c_minus_xy.into_repr()).into_affine();
let h_alpha_term = h_alpha_term.into_affine();
let valid = E::final_exponentiation(&E::miller_loop(&[
(&proof.c_opening.prepare(), &alpha_x_precomp),
(&c_in_c_minus_xy.prepare(), &alpha_precomp),
(&h_alpha_term.prepare(), &alpha_precomp),
(&proof.o.prepare(), &h_prep),
])).unwrap() == E::Fqk::one();
@ -107,17 +109,19 @@ impl<E: Engine> S2Eval<E> {
// e(D,hαx)e(Dy1z,hα) = e(O,h)e(gd,hα)
let mut d_minus_x_y_inv = proof.d_value;
let mut x_y_inv = x;
x_y_inv.mul_assign(&y.inverse().unwrap());
let mut minus_x_y_inv = x;
minus_x_y_inv.mul_assign(&y.inverse().unwrap());
minus_x_y_inv.negate();
d_minus_x_y_inv.sub_assign(&x_y_inv);
let mut h_alpha_term = proof.d_opening.mul(minus_x_y_inv.into_repr());
let g_in_d = E::G1Affine::one().mul(proof.d_value);
h_alpha_term.add_assign(&g_in_d);
let d_in_d_minus_x_y_inv = proof.d_opening.mul(d_minus_x_y_inv.into_repr()).into_affine();
let h_alpha_term = h_alpha_term.into_affine();
let valid = E::final_exponentiation(&E::miller_loop(&[
(&proof.d_opening.prepare(), &alpha_x_precomp),
(&d_in_d_minus_x_y_inv.prepare(), &alpha_precomp),
(&h_alpha_term.prepare(), &alpha_precomp),
(&proof.o.prepare(), &h_prep),
])).unwrap() == E::Fqk::one();
@ -127,4 +131,37 @@ impl<E: Engine> S2Eval<E> {
true
}
}
#[test]
fn test_s2_proof() {
use crate::pairing::ff::{Field, PrimeField};
use crate::pairing::{Engine, CurveAffine, CurveProjective};
use crate::pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use crate::sonic::srs::SRS;
use crate::sonic::cs::{Circuit, ConstraintSystem, LinearCombination};
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
println!("making srs");
let start = Instant::now();
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
println!("done in {:?}", start.elapsed());
{
use rand::{XorShiftRng, SeedableRng, Rand, Rng};
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let x: Fr = rng.gen();
let y: Fr = rng.gen();
let proof = S2Eval::new(1024);
let proof = proof.evaluate(x, y, &srs);
let valid = S2Eval::verify(x, y, &proof, &srs);
assert!(valid);
}
}

@ -0,0 +1,761 @@
use crate::pairing::ff::{Field};
use crate::pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use rand::{Rand, Rng};
use crate::sonic::helped::{Proof, SxyAdvice};
use crate::sonic::helped::batch::Batch;
use crate::sonic::helped::poly::{SxEval, SyEval};
use crate::sonic::helped::helper::Aggregate;
use crate::sonic::helped::parameters::{Parameters};
use crate::SynthesisError;
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
use crate::sonic::util::*;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
use crate::sonic::sonic::Preprocess;
use super::s2_proof::{S2Proof, S2Eval};
use super::aggregate::SuccinctAggregate;
use super::permutation_structure::create_permutation_structure;
use super::permutation_argument::{
PermutationArgumentProof,
PermutationProof,
PermutationArgument,
SpecializedSRS
};
pub struct SuccinctMultiVerifier<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> {
circuit: C,
s1_special_reference: SpecializedSRS<E>,
s2_special_reference: E::G1Affine,
pub(crate) batch: Batch<E>,
k_map: Vec<usize>,
n: usize,
q: usize,
randomness_source: R,
_marker: PhantomData<(E, S)>
}
impl<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng> SuccinctMultiVerifier<E, C, S, R> {
// This constructor consumes randomness source cause it's later used internally
pub fn new(circuit: C, srs: &SRS<E>, rng: R) -> Result<Self, SynthesisError> {
let (n, q, k_map) = {
let mut preprocess = Preprocess::new();
S::synthesize(&mut preprocess, &circuit)?;
(preprocess.n, preprocess.q, preprocess.k_map)
};
// also calculate special reference for s1
let permutation_structure = create_permutation_structure(&circuit);
let s2_special_reference = permutation_structure.calculate_s2_commitment_value(&srs);
let s1_special_reference = permutation_structure.create_permutation_special_reference(&srs);
Ok(SuccinctMultiVerifier {
circuit,
s1_special_reference,
s2_special_reference,
batch: Batch::new(srs, n),
k_map: k_map,
n: n,
q: q,
randomness_source: rng,
_marker: PhantomData
})
}
pub fn add_aggregate(
&mut self,
proofs: &[(Proof<E>, SxyAdvice<E>)],
aggregate: &SuccinctAggregate<E>,
srs: &SRS<E>
)
{
let mut transcript = Transcript::new(&[]);
let mut y_values: Vec<E::Fr> = Vec::with_capacity(proofs.len());
for &(ref proof, ref sxyadvice) in proofs {
{
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&proof.r);
y_values.push(transcript.get_challenge_scalar());
}
transcript.commit_point(&sxyadvice.s);
}
let z: E::Fr = transcript.get_challenge_scalar();
transcript.commit_point(&aggregate.c);
let w: E::Fr = transcript.get_challenge_scalar();
let szw = {
// prover will supply s1 and s2, need to calculate
// s(z, w) = X^-(N+1) * Y^N * s1 - X^N * s2
let x_n = z.pow(&[self.n as u64]);
let mut x_n_plus_1 = x_n;
x_n_plus_1.mul_assign(&z);
let x_n_plus_1_inv = x_n_plus_1.inverse().unwrap();
let y_n = w.pow(&[self.n as u64]);
// simultaneously add components to the batch verifier
// this is s2 contribution itself
let s2_proof = &aggregate.s2_proof;
let mut s2_part = s2_proof.c_value;
s2_part.add_assign(&s2_proof.d_value);
s2_part.mul_assign(&x_n);
// add terms for S2 for verification
{
let random: E::Fr = self.randomness_source.gen();
// e(C,hαx)e(Cyz,hα) = e(O,h)e(gc,hα) that is
// e(C,hαx)e(C^yz,hα)*e(O,-h)e(g^c,hα) = 1
let mut xy = z;
xy.mul_assign(&w);
self.batch.add_opening(s2_proof.c_opening, random, xy);
self.batch.add_opening_value(random, s2_proof.c_value);
self.batch.add_commitment(self.s2_special_reference, random);
}
{
let random: E::Fr = self.randomness_source.gen();
// e(D,hαx)e(Dy1z,hα) = e(O,h)e(gd,hα) that is
// e(D,hαx)e(D^y-1z,hα)*e(O,-h)e(g^d,hα) = 1
let mut y_inv_by_x = z;
y_inv_by_x.mul_assign(&w.inverse().unwrap());
self.batch.add_opening(s2_proof.d_opening, random, y_inv_by_x);
self.batch.add_opening_value(random, s2_proof.d_value);
self.batch.add_commitment(self.s2_special_reference, random);
}
// now work with s1 part
let mut s1_part = aggregate.signature.perm_argument_proof.s_zy;
s1_part.mul_assign(&x_n_plus_1_inv);
s1_part.mul_assign(&y_n);
let mut szw = s1_part;
szw.sub_assign(&s2_part);
// verify commitments for s' and s
{
let mut transcript = Transcript::new(&[]);
// let s_commitments = &aggregate.signature.s_commitments;
// let s_prime_commitments = &aggregate.signature.s_prime_commitments;
let mut challenges = vec![];
for (s, s_prime) in aggregate.signature.s_commitments.iter()
.zip(aggregate.signature.s_prime_commitments.iter()) {
transcript.commit_point(s);
transcript.commit_point(s_prime);
}
for _ in 0..aggregate.signature.s_commitments.len() {
let challenge = transcript.get_challenge_scalar();
challenges.push(challenge);
}
let z_prime: E::Fr = transcript.get_challenge_scalar();
// we expect M permutation proofs, add them all into verification
// using batching with random challenges and extra randomness for pairing equation
{
// e(E,hαx)e(Ez,hα) = e(􏰇Mj=1Sjrj,h)e(gv,hα)
let perm_proof = &aggregate.signature.perm_proof;
let s_r = multiexp(
aggregate.signature.s_prime_commitments.iter(),
challenges.iter()
).into_affine();
let p2_r = multiexp(
self.s1_special_reference.p_2.iter(),
challenges.iter()
).into_affine();
let value = perm_proof.v_zy;
let random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(perm_proof.e_opening, random, z_prime);
self.batch.add_opening_value(random, value);
self.batch.add_commitment(s_r, random);
// e(F,hαx)e(Fyz,hα) = e(􏰇Mj=1P2jrj,h)e(gv,hα)
let mut y_z_prime = z_prime;
y_z_prime.mul_assign(&w);
let random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(perm_proof.f_opening, random, y_z_prime);
self.batch.add_opening_value(random, value);
self.batch.add_commitment(p2_r, random);
}
// now we can actually take an opening of S commitments and
{
// e(I,hαx)e(Iz,hα) = e(􏰇Mj=1 Sj,h)e(gs,hα)
let value = aggregate.signature.perm_argument_proof.s_zy;
let mut s_commitment = E::G1::zero();
for s in aggregate.signature.s_commitments.iter() {
s_commitment.add_assign_mixed(s);
}
let random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(aggregate.signature.perm_argument_proof.s_opening, random, z);
self.batch.add_opening_value(random, value);
self.batch.add_commitment(s_commitment.into_affine(), random);
}
// TODO: Add grand product argument!
// for each of the grand product arguments create a corresponding commitment
// from already known elements
let mut betas = vec![];
let mut gammas = vec![];
let mut a_commitments = vec![];
let mut b_commitments = vec![];
for _ in 0..aggregate.signature.s_commitments.len() {
let beta: E::Fr = transcript.get_challenge_scalar();
let gamma: E::Fr = transcript.get_challenge_scalar();
betas.push(beta);
gammas.push(gamma);
}
let mut wellformedness_argument_commitments = vec![];
use crate::pairing::CurveAffine;
use crate::pairing::ff::PrimeField;
for (j, (((s, s_prime), beta), gamma)) in aggregate.signature.s_commitments.iter()
.zip(aggregate.signature.s_prime_commitments.iter())
.zip(betas.iter())
.zip(gammas.iter())
.enumerate()
{
// Sj(P4j)β(P1j)γ
let mut a = s.into_projective();
a.add_assign(&self.s1_special_reference.p_4[j].mul(beta.into_repr()));
a.add_assign(&self.s1_special_reference.p_1.mul(gamma.into_repr()));
let a = a.into_affine();
// Sj(P3j)β(P1j)γ
let mut b = s_prime.into_projective();
b.add_assign(&self.s1_special_reference.p_3.mul(beta.into_repr()));
b.add_assign(&self.s1_special_reference.p_1.mul(gamma.into_repr()));
let b = b.into_affine();
a_commitments.push(a);
b_commitments.push(b);
wellformedness_argument_commitments.push(a);
wellformedness_argument_commitments.push(b);
}
// commitments to invidvidual grand products are assembled, now check first part of a grand
// product argument
// Now perform an actual check
{
let randomness: Vec<E::Fr> = (0..aggregate.signature.s_commitments.len()).map(|_| self.randomness_source.gen()).collect();
// e(Dj,hαx)e(Dyz,hα) = e(Aj,h)e(Bj,hxn+1)e(gaj ,hα)
let g = srs.g_positive_x[0];
let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare();
let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare();
let mut h_x_n_plus_one_precomp = srs.h_positive_x[self.n+1];
h_x_n_plus_one_precomp.negate();
let h_x_n_plus_one_precomp = h_x_n_plus_one_precomp.prepare();
let mut h_prep = srs.h_positive_x[0];
h_prep.negate();
let h_prep = h_prep.prepare();
let a = multiexp(
a_commitments.iter(),
randomness.iter(),
).into_affine();
let a = a.prepare();
let b = multiexp(
b_commitments.iter(),
randomness.iter(),
).into_affine();
let b = b.prepare();
let mut yz_neg = w;
yz_neg.mul_assign(&z);
yz_neg.negate();
let mut ops = vec![];
let mut value = E::Fr::zero();
for (el, r) in aggregate.signature.grand_product_signature.grand_product_openings.iter().zip(randomness.iter()) {
let (v, o) = el;
ops.push(o.clone());
let mut val = *v;
val.mul_assign(&r);
value.add_assign(&val);
}
let value = g.mul(value.into_repr()).into_affine().prepare();
let openings = multiexp(
ops.iter(),
randomness.iter(),
).into_affine();
let openings_zy = openings.mul(yz_neg.into_repr()).into_affine().prepare();
let openings = openings.prepare();
// e(Dj,hαx)e(Dyz,hα) = e(Aj,h)e(Bj,hxn+1)e(gaj ,hα)
let valid = E::final_exponentiation(&E::miller_loop(&[
(&openings, &h_alpha_x_precomp),
(&openings_zy, &h_alpha_precomp),
(&a, &h_prep),
(&b, &h_x_n_plus_one_precomp),
(&value, &h_alpha_precomp)
])).unwrap() == E::Fqk::one();
// TODO
assert!(valid, "grand product arguments must be valid for individual commitments");
}
// Now the second part of the grand product argument
{
let mut grand_product_challenges = vec![];
for _ in 0..aggregate.signature.grand_product_signature.c_commitments.len() {
let c: E::Fr = transcript.get_challenge_scalar();
grand_product_challenges.push(c);
}
// first re-calculate cj and t(z,y)
let mut yz = w;
yz.mul_assign(&z);
let z_inv = z.inverse().unwrap();
let mut t_zy = E::Fr::zero();
let mut commitments_points = vec![];
let mut rc_vec = vec![];
let mut ry_vec = vec![];
// in grand product arguments n is not a number of gates, but 3n+1 - number of variables + 1
let three_n_plus_1 = 3*self.n + 1;
for ((r, commitment), (a, _)) in grand_product_challenges.iter()
.zip(aggregate.signature.grand_product_signature.c_commitments.iter())
.zip(aggregate.signature.grand_product_signature.grand_product_openings.iter())
{
let (c, v) = commitment;
commitments_points.push(*c);
// cj = ((aj + vj(yz)n+1)y + zn+2 + zn+1y z2n+2y)z1
let mut c_zy = yz.pow([(three_n_plus_1 + 1) as u64]);
c_zy.mul_assign(v);
c_zy.add_assign(a);
c_zy.mul_assign(&w);
let mut z_n_plus_1 = z.pow([(three_n_plus_1 + 1) as u64]);
let mut z_n_plus_2 = z_n_plus_1;
z_n_plus_2.mul_assign(&z);
let mut z_2n_plus_2 = z_n_plus_1;
z_2n_plus_2.square();
z_2n_plus_2.mul_assign(&w);
z_n_plus_1.mul_assign(&w);
c_zy.add_assign(&z_n_plus_1);
c_zy.add_assign(&z_n_plus_2);
c_zy.sub_assign(&z_2n_plus_2);
c_zy.mul_assign(&z_inv);
let mut rc = c_zy;
rc.mul_assign(&r);
rc_vec.push(rc);
let mut ry = w;
ry.mul_assign(&r);
ry_vec.push(ry);
let mut val = rc;
val.sub_assign(&r);
t_zy.add_assign(&val);
}
t_zy.add_assign(&aggregate.signature.grand_product_signature.proof.e_zinv);
t_zy.sub_assign(&aggregate.signature.grand_product_signature.proof.f_y);
// t(z, y) is now calculated
let c_rc = multiexp(
commitments_points.iter(),
rc_vec.iter(),
).into_affine();
let c_ry = multiexp(
commitments_points.iter(),
ry_vec.iter(),
).into_affine();
// e(E,h^alphax)e(E^-z^-1,h^alpha) = e(\sumCj^(rj*cj),h)e(g^-e,h^alpha)
{
let random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(aggregate.signature.grand_product_signature.proof.e_opening, random, z_inv);
self.batch.add_opening_value(random, aggregate.signature.grand_product_signature.proof.e_zinv);
self.batch.add_commitment(c_rc, random);
}
// e(F,h^alphax)e(F^-y,h) = e(\sumCj^(rj&y),h)e(g^-f,h^alpha)
{
let random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(aggregate.signature.grand_product_signature.proof.f_opening, random, w);
self.batch.add_opening_value(random, aggregate.signature.grand_product_signature.proof.f_y);
self.batch.add_commitment(c_ry, random);
}
// e(T,hαx)e(Tz,hα) = e(T,h)e(gt(z,y),hα)
{
let random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(aggregate.signature.grand_product_signature.proof.t_opening, random, z);
self.batch.add_opening_value(random, t_zy);
self.batch.add_commitment(aggregate.signature.grand_product_signature.t_commitment, random);
}
}
// finally check the wellformedness arguments
{
let mut wellformedness_challenges = vec![];
for _ in 0..wellformedness_argument_commitments.len() {
let c: E::Fr = transcript.get_challenge_scalar();
wellformedness_challenges.push(c);
}
let d = srs.d;
let n = 3*self.n + 1; // same as for grand products
let alpha_x_d_precomp = srs.h_positive_x_alpha[d].prepare();
// TODO: not strictly required
assert!(n < d);
let d_minus_n = d - n;
let alpha_x_n_minus_d_precomp = srs.h_negative_x_alpha[d_minus_n].prepare();
let mut h_prep = srs.h_positive_x[0];
h_prep.negate();
let h_prep = h_prep.prepare();
let a = multiexp(
wellformedness_argument_commitments.iter(),
wellformedness_challenges.iter(),
).into_affine();
let r1: E::Fr = self.randomness_source.gen();
let r2: E::Fr = self.randomness_source.gen();
let mut r = r1;
r.add_assign(&r2);
let l_r1 = aggregate.signature.grand_product_signature.wellformedness_signature.proof.l.mul(r1.into_repr()).into_affine();
let r_r2 = aggregate.signature.grand_product_signature.wellformedness_signature.proof.r.mul(r2.into_repr()).into_affine();
let a_r = a.mul(r.into_repr()).into_affine();
let valid = E::final_exponentiation(&E::miller_loop(&[
(&a_r.prepare(), &h_prep),
(&l_r1.prepare(), &alpha_x_d_precomp),
(&r_r2.prepare(), &alpha_x_n_minus_d_precomp)
])).unwrap() == E::Fqk::one();
assert!(valid, "wellformedness argument must be valid");
}
}
szw
};
{
let random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(aggregate.opening, random, w);
self.batch.add_commitment(aggregate.c, random);
self.batch.add_opening_value(szw, random);
}
for ((opening, value), &y) in aggregate.c_openings.iter().zip(y_values.iter()) {
let random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(*opening, random, y);
self.batch.add_commitment(aggregate.c, random);
self.batch.add_opening_value(*value, random);
}
let random: E::Fr = self.randomness_source.gen();
let mut expected_value = E::Fr::zero();
for ((_, advice), c_opening) in proofs.iter().zip(aggregate.c_openings.iter()) {
let mut r: E::Fr = transcript.get_challenge_scalar();
// expected value of the later opening
{
let mut tmp = c_opening.1;
tmp.mul_assign(&r);
expected_value.add_assign(&tmp);
}
r.mul_assign(&random);
self.batch.add_commitment(advice.s, r);
}
self.batch.add_opening_value(expected_value, random);
self.batch.add_opening(aggregate.s_opening, random, z);
}
/// Caller must ensure to add aggregate after adding a proof
pub fn add_proof_with_advice(
&mut self,
proof: &Proof<E>,
inputs: &[E::Fr],
advice: &SxyAdvice<E>,
)
{
let mut z = None;
self.add_proof(proof, inputs, |_z, _y| {
z = Some(_z);
Some(advice.szy)
});
let z = z.unwrap();
// We need to open up SxyAdvice.s at z using SxyAdvice.opening
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&advice.opening);
transcript.commit_point(&advice.s);
transcript.commit_scalar(&advice.szy);
let random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(advice.opening, random, z);
self.batch.add_commitment(advice.s, random);
self.batch.add_opening_value(advice.szy, random);
}
pub fn add_proof<F>(
&mut self,
proof: &Proof<E>,
inputs: &[E::Fr],
sxy: F
)
where F: FnOnce(E::Fr, E::Fr) -> Option<E::Fr>
{
let mut transcript = Transcript::new(&[]);
transcript.commit_point(&proof.r);
let y: E::Fr = transcript.get_challenge_scalar();
transcript.commit_point(&proof.t);
let z: E::Fr = transcript.get_challenge_scalar();
transcript.commit_scalar(&proof.rz);
transcript.commit_scalar(&proof.rzy);
let r1: E::Fr = transcript.get_challenge_scalar();
transcript.commit_point(&proof.z_opening);
transcript.commit_point(&proof.zy_opening);
// First, the easy one. Let's open up proof.r at zy, using proof.zy_opening
// as the evidence and proof.rzy as the opening.
{
let random: E::Fr = self.randomness_source.gen();
let mut zy = z;
zy.mul_assign(&y);
self.batch.add_opening(proof.zy_opening, random, zy);
self.batch.add_commitment_max_n(proof.r, random);
self.batch.add_opening_value(proof.rzy, random);
}
// Now we need to compute t(z, y) with what we have. Let's compute k(y).
let mut ky = E::Fr::zero();
for (exp, input) in self.k_map.iter().zip(Some(E::Fr::one()).iter().chain(inputs.iter())) {
let mut term = y.pow(&[(*exp + self.n) as u64]);
term.mul_assign(input);
ky.add_assign(&term);
}
// Compute s(z, y)
let szy = sxy(z, y).unwrap_or_else(|| {
let mut tmp = SxEval::new(y, self.n);
S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO
tmp.finalize(z)
// let mut tmp = SyEval::new(z, self.n, self.q);
// S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO
// tmp.finalize(y)
});
// Finally, compute t(z, y)
// t(z, y) = (r(z, y) + s(z,y))*r(z, 1) - k(y)
let mut tzy = proof.rzy;
tzy.add_assign(&szy);
tzy.mul_assign(&proof.rz);
tzy.sub_assign(&ky);
// We open these both at the same time by keeping their commitments
// linearly independent (using r1).
{
let mut random: E::Fr = self.randomness_source.gen();
self.batch.add_opening(proof.z_opening, random, z);
self.batch.add_opening_value(tzy, random);
self.batch.add_commitment(proof.t, random);
random.mul_assign(&r1);
self.batch.add_opening_value(proof.rz, random);
self.batch.add_commitment_max_n(proof.r, random);
}
}
pub fn get_k_map(&self) -> Vec<usize> {
return self.k_map.clone();
}
pub fn get_n(&self) -> usize {
return self.n;
}
pub fn get_q(&self) -> usize {
return self.q;
}
pub fn check_all(self) -> bool {
self.batch.check_all()
}
}
// /// Check multiple proofs without aggregation. Verifier's work is
// /// not succint due to `S(X, Y)` evaluation
// pub fn verify_proofs<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng>(
// proofs: &[Proof<E>],
// inputs: &[Vec<E::Fr>],
// circuit: C,
// rng: R,
// params: &Parameters<E>,
// ) -> Result<bool, SynthesisError> {
// verify_proofs_on_srs::<E, C, S, R>(proofs, inputs, circuit, rng, &params.srs)
// }
// /// Check multiple proofs without aggregation. Verifier's work is
// /// not succint due to `S(X, Y)` evaluation
// pub fn verify_proofs_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng>(
// proofs: &[Proof<E>],
// inputs: &[Vec<E::Fr>],
// circuit: C,
// rng: R,
// srs: &SRS<E>,
// ) -> Result<bool, SynthesisError> {
// let mut verifier = MultiVerifier::<E, C, S, R>::new(circuit, srs, rng)?;
// let expected_inputs_size = verifier.get_k_map().len() - 1;
// for (proof, inputs) in proofs.iter().zip(inputs.iter()) {
// if inputs.len() != expected_inputs_size {
// return Err(SynthesisError::Unsatisfiable);
// }
// verifier.add_proof(proof, &inputs, |_, _| None);
// }
// Ok(verifier.check_all())
// }
// /// Check multiple proofs with aggregation. Verifier's work is
// /// not succint due to `S(X, Y)` evaluation
// pub fn verify_aggregate<E: Engine, C: Circuit<E>, S: SynthesisDriver,R: Rng>(
// proofs: &[(Proof<E>, SxyAdvice<E>)],
// aggregate: &Aggregate<E>,
// inputs: &[Vec<E::Fr>],
// circuit: C,
// rng: R,
// params: &Parameters<E>,
// ) -> Result<bool, SynthesisError> {
// verify_aggregate_on_srs::<E, C, S, R>(proofs, aggregate, inputs, circuit, rng, &params.srs)
// }
// /// Check multiple proofs with aggregation. Verifier's work is
// /// not succint due to `S(X, Y)` evaluation
// pub fn verify_aggregate_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver, R: Rng>(
// proofs: &[(Proof<E>, SxyAdvice<E>)],
// aggregate: &Aggregate<E>,
// inputs: &[Vec<E::Fr>],
// circuit: C,
// rng: R,
// srs: &SRS<E>,
// ) -> Result<bool, SynthesisError> {
// let mut verifier = MultiVerifier::<E, C, S, R>::new(circuit, srs, rng)?;
// let expected_inputs_size = verifier.get_k_map().len() - 1;
// for ((proof, advice), inputs) in proofs.iter().zip(inputs.iter()) {
// if inputs.len() != expected_inputs_size {
// return Err(SynthesisError::Unsatisfiable);
// }
// verifier.add_proof_with_advice(proof, &inputs, &advice);
// }
// verifier.add_aggregate(proofs, aggregate);
// Ok(verifier.check_all())
// }

@ -7,6 +7,7 @@ use std::marker::PhantomData;
use crate::sonic::srs::SRS;
use crate::sonic::util::*;
use crate::sonic::transcript::{Transcript, TranscriptProtocol};
#[derive(Clone)]
pub struct WellformednessArgument<E: Engine> {
@ -15,11 +16,31 @@ pub struct WellformednessArgument<E: Engine> {
#[derive(Clone)]
pub struct WellformednessProof<E: Engine> {
l: E::G1Affine,
r: E::G1Affine
pub l: E::G1Affine,
pub r: E::G1Affine
}
#[derive(Clone)]
pub struct WellformednessSignature<E: Engine> {
pub proof: WellformednessProof<E>
}
impl<E: Engine> WellformednessArgument<E> {
pub fn create_signature(
all_polys: Vec<Vec<E::Fr>>,
wellformed_challenges: Vec<E::Fr>,
srs: &SRS<E>
) -> WellformednessSignature<E> {
let wellformed_argument = WellformednessArgument::new(all_polys);
let proof = wellformed_argument.make_argument(wellformed_challenges, &srs);
WellformednessSignature {
proof
}
}
pub fn new(polynomials: Vec<Vec<E::Fr>>) -> Self {
assert!(polynomials.len() > 0);
@ -71,18 +92,21 @@ impl<E: Engine> WellformednessArgument<E> {
let d = srs.d;
// TODO: it's not necessary to have n < d, fix later
assert!(n < d);
// here the multiplier is x^-d, so largest negative power is -(d - 1), smallest negative power is -(d - n)
// here the multiplier is x^-d, so largest negative power is -(d - 1), smallest negative power is - (d - n)
// H^{x^k} are labeled from 0 power, so we need to use proper indexes
let l = multiexp(
srs.g_negative_x[(d - n)..d].iter().rev(),
srs.g_negative_x[(d - n)..=(d - 1)].iter().rev(),
p0.iter()
).into_affine();
// here the multiplier is x^d-n, so largest positive power is d, smallest positive power is d - n + 1
let r = multiexp(
srs.g_positive_x[(d - n + 1)..].iter().rev(),
srs.g_positive_x[(d - n + 1)..=d].iter(),
p0.iter()
).into_affine();
@ -96,7 +120,10 @@ impl<E: Engine> WellformednessArgument<E> {
let d = srs.d;
let alpha_x_d_precomp = srs.h_positive_x_alpha[d].prepare();
let alpha_x_n_minus_d_precomp = srs.h_negative_x_alpha[d - n].prepare();
// TODO: not strictly required
assert!(n < d);
let d_minus_n = d - n;
let alpha_x_n_minus_d_precomp = srs.h_negative_x_alpha[d_minus_n].prepare();
let mut h_prep = srs.h_positive_x[0];
h_prep.negate();
let h_prep = h_prep.prepare();
@ -138,11 +165,12 @@ fn test_argument() {
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
// let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
let srs = SRS::<Bls12>::new(128, srs_x, srs_alpha);
let n: usize = 1 << 16;
let n: usize = 1 << 5;
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let coeffs = (1..=n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let argument = WellformednessArgument::new(vec![coeffs]);
let challenges = (0..1).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
@ -168,12 +196,12 @@ fn test_argument_soundness() {
let n: usize = 1 << 8;
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let coeffs = (1..=n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let argument = WellformednessArgument::new(vec![coeffs]);
let commitments = argument.commit(&srs);
let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let coeffs = (1..=n).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let argument = WellformednessArgument::new(vec![coeffs]);
let challenges = (0..1).map(|_| Fr::rand(rng)).collect::<Vec<_>>();

@ -116,18 +116,25 @@ pub fn polynomial_commitment_opening<
I: IntoIterator<Item = &'a E::Fr>
>(
largest_negative_power: usize,
largest_positive_power: usize,
_largest_positive_power: usize,
polynomial_coefficients: I,
point: E::Fr,
srs: &'a SRS<E>,
) -> E::G1Affine
where I::IntoIter: DoubleEndedIterator + ExactSizeIterator,
{
// let poly = parallel_kate_divison::<E, _>(polynomial_coefficients, point);
// use std::time::Instant;
// let start = Instant::now();
let poly = kate_divison(
polynomial_coefficients,
point,
);
// println!("Kate division of size {} taken {:?}", poly.len(), start.elapsed());
let negative_poly = poly[0..largest_negative_power].iter().rev();
let positive_poly = poly[largest_negative_power..].iter();
multiexp(
@ -214,7 +221,7 @@ pub fn mut_evaluate_at_consequitive_powers<'a, F: Field> (
let mut acc = F::zero();
for mut p in coeffs {
for p in coeffs {
p.mul_assign(&current_power);
acc.add_assign(&p);
@ -261,7 +268,7 @@ pub fn mut_distribute_consequitive_powers<'a, F: Field> (
let mut current_power = base.pow(&[(i*chunk) as u64]);
current_power.mul_assign(&first_power);
for mut p in coeffs_chunk {
for p in coeffs_chunk {
p.mul_assign(&current_power);
current_power.mul_assign(&base);
@ -271,6 +278,49 @@ pub fn mut_distribute_consequitive_powers<'a, F: Field> (
});
}
// pub fn multiexp<
// 'a,
// G: CurveAffine,
// IB: IntoIterator<Item = &'a G>,
// IS: IntoIterator<Item = &'a G::Scalar>,
// >(
// g: IB,
// s: IS,
// ) -> G::Projective
// where
// IB::IntoIter: ExactSizeIterator + Clone,
// IS::IntoIter: ExactSizeIterator,
// {
// use crate::multicore::Worker;
// use crate::multiexp::dense_multiexp;
// use std::time::Instant;
// let start = Instant::now();
// let s: Vec<<G::Scalar as PrimeField>::Repr> = s.into_iter().map(|e| e.into_repr()).collect::<Vec<_>>();
// let g: Vec<G> = g.into_iter().map(|e| *e).collect::<Vec<_>>();
// println!("Multiexp collecting taken {:?}", start.elapsed());
// assert_eq!(s.len(), g.len(), "scalars and exponents must have the same length");
// let start = Instant::now();
// let pool = Worker::new();
// println!("Multiexp pool creation taken {:?}", start.elapsed());
// let start = Instant::now();
// let result = dense_multiexp(
// &pool,
// &g,
// &s
// ).unwrap();
// println!("Multiexp taken {:?}", start.elapsed());
// result
// }
pub fn multiexp<
'a,
G: CurveAffine,
@ -285,7 +335,10 @@ where
IS::IntoIter: ExactSizeIterator,
{
use crate::multicore::Worker;
use crate::multiexp::dense_multiexp;
use crate::multiexp::multiexp;
use crate::source::FullDensity;
use futures::Future;
use std::sync::Arc;
let s: Vec<<G::Scalar as PrimeField>::Repr> = s.into_iter().map(|e| e.into_repr()).collect::<Vec<_>>();
let g: Vec<G> = g.into_iter().map(|e| *e).collect::<Vec<_>>();
@ -294,15 +347,24 @@ where
let pool = Worker::new();
let result = dense_multiexp(
// use std::time::Instant;
// let start = Instant::now();
let result = multiexp(
&pool,
&g,
&s
).unwrap();
(Arc::new(g), 0),
FullDensity,
Arc::new(s)
).wait().unwrap();
// println!("Multiexp taken {:?}", start.elapsed());
result
}
pub fn multiexp_serial<
'a,
G: CurveAffine,
@ -400,6 +462,75 @@ where
q
}
/// Divides polynomial `a` in `x` by `x - b` with
/// no remainder using fft.
pub fn parallel_kate_divison<'a, E: Engine, I: IntoIterator<Item = &'a E::Fr>>(a: I, b: E::Fr) -> Vec<E::Fr>
where
I::IntoIter: DoubleEndedIterator + ExactSizeIterator,
{
// this implementation is only for division by `x - b` form polynomail,
// so we can manuall calculate the reciproical poly of the form `x^2/(x-b)`
// and the reminder
// x^2 /(x - b) = x + b*x/(x - b) = (x + b) + b^2/(x - b)
let reciproical = vec![b, E::Fr::one()]; // x + b
// and remainder b^2
let mut b_squared = b;
b_squared.square();
let mut b_neg = b;
b_neg.negate();
let divisor = vec![b_neg, E::Fr::one()];
let poly: Vec<E::Fr> = a.into_iter().map(|el| el.clone()).collect();
let (q, _) = kate_divison_inner::<E>(poly, divisor, reciproical, b_squared);
// assert_eq!(r.len(), 0);
q
}
fn kate_divison_inner<E: Engine>(
poly: Vec<E::Fr>,
divisor: Vec<E::Fr>,
reciproical: Vec<E::Fr>,
remainder: E::Fr
) -> (Vec<E::Fr>, Vec<E::Fr>) {
if poly.len() == 1 {
return (vec![], poly);
}
// TODO: Change generic multiplications by multiplications by degree 1 polynomial
let poly_degree = poly.len() - 1;
let mut q = multiply_polynomials::<E>(poly.clone(), reciproical.clone());
q.drain(0..2);
// recursion step
if poly_degree > 2 {
let mut rec_step = poly.clone();
mul_polynomial_by_scalar(&mut rec_step[..], remainder);
// truncate low order terms
rec_step.drain(0..2);
let (q2, _) = kate_divison_inner::<E>(rec_step, divisor.clone(), reciproical, remainder);
// length of q2 is smaller
add_polynomials(&mut q[..q2.len()], &q2[..]);
}
// although r must be zero, calculate it for now
if q.len() == 0 {
return (q, poly);
}
// r = u - v*q
let mut poly = poly;
let tmp = multiply_polynomials::<E>(divisor, q.clone());
sub_polynomials(&mut poly[..], &tmp[..]);
return (q, poly);
}
/// Convenience function to check polynomail commitment
pub fn check_polynomial_commitment<E: Engine>(
commitment: &E::G1Affine,
@ -524,6 +655,90 @@ pub fn multiply_polynomials<E: Engine>(a: Vec<E::Fr>, b: Vec<E::Fr>) -> Vec<E::F
mul_result
}
// alternative implementation that does not require an `Evaluation domain` struct
pub fn multiply_polynomials_fft<E: Engine>(a: Vec<E::Fr>, b: Vec<E::Fr>) -> Vec<E::Fr> {
use crate::multicore::Worker;
use crate::domain::{best_fft, Scalar};
use crate::group::Group;
let result_len = a.len() + b.len() - 1;
// m is a size of domain where Z polynomial does NOT vanish
// in normal domain Z is in a form of (X-1)(X-2)...(X-N)
let mut m = 1;
let mut exp = 0;
let mut omega = E::Fr::root_of_unity();
let max_degree = (1 << E::Fr::S) - 1;
if result_len > max_degree {
panic!("multiplication result degree is too large");
}
while m < result_len {
m *= 2;
exp += 1;
// The pairing-friendly curve may not be able to support
// large enough (radix2) evaluation domains.
if exp > E::Fr::S {
panic!("multiplication result degree is too large");
}
}
// If full domain is not needed - limit it,
// e.g. if (2^N)th power is not required, just double omega and get 2^(N-1)th
// Compute omega, the 2^exp primitive root of unity
for _ in exp..E::Fr::S {
omega.square();
}
let omegainv = omega.inverse().unwrap();
let minv = E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap();
let worker = Worker::new();
let mut scalars_a: Vec<Scalar<E>> = a.into_iter().map(|e| Scalar::<E>(e)).collect();
let mut scalars_b: Vec<Scalar<E>> = b.into_iter().map(|e| Scalar::<E>(e)).collect();
scalars_a.resize(m, Scalar::<E>(E::Fr::zero()));
scalars_b.resize(m, Scalar::<E>(E::Fr::zero()));
best_fft(&mut scalars_a[..], &worker, &omega, exp);
best_fft(&mut scalars_b[..], &worker, &omega, exp);
// do the convolution
worker.scope(scalars_a.len(), |scope, chunk| {
for (a, b) in scalars_a.chunks_mut(chunk).zip(scalars_b.chunks(chunk)) {
scope.spawn(move |_| {
for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_mul_assign(&b.0);
}
});
}
});
// no longer need it
drop(scalars_b);
best_fft(&mut scalars_a[..], &worker, &omegainv, exp);
worker.scope(scalars_a.len(), |scope, chunk| {
for v in scalars_a.chunks_mut(chunk) {
scope.spawn(move |_| {
for v in v {
v.group_mul_assign(&minv);
}
});
}
});
let mut mul_result: Vec<E::Fr> = scalars_a.into_iter().map(|e| e.0).collect();
mul_result.truncate(result_len);
mul_result
}
pub fn multiply_polynomials_serial<E: Engine>(mut a: Vec<E::Fr>, mut b: Vec<E::Fr>) -> Vec<E::Fr> {
let result_len = a.len() + b.len() - 1;
@ -574,6 +789,7 @@ pub fn multiply_polynomials_serial<E: Engine>(mut a: Vec<E::Fr>, mut b: Vec<E::F
a
}
// add polynomails in coefficient form
pub fn add_polynomials<F: Field>(a: &mut [F], b: &[F]) {
use crate::multicore::Worker;
use crate::domain::{EvaluationDomain, Scalar};
@ -594,6 +810,28 @@ pub fn add_polynomials<F: Field>(a: &mut [F], b: &[F]) {
});
}
// subtract polynomails in coefficient form
pub fn sub_polynomials<F: Field>(a: &mut [F], b: &[F]) {
use crate::multicore::Worker;
use crate::domain::{EvaluationDomain, Scalar};
let worker = Worker::new();
assert_eq!(a.len(), b.len());
worker.scope(a.len(), |scope, chunk| {
for (a, b) in a.chunks_mut(chunk).zip(b.chunks(chunk))
{
scope.spawn(move |_| {
for (a, b) in a.iter_mut().zip(b.iter()) {
a.sub_assign(b);
}
});
}
});
}
// multiply coefficients of the polynomial by the scalar
pub fn mul_polynomial_by_scalar<F: Field>(a: &mut [F], b: F) {
use crate::multicore::Worker;
use crate::domain::{EvaluationDomain, Scalar};
@ -612,6 +850,8 @@ pub fn mul_polynomial_by_scalar<F: Field>(a: &mut [F], b: F) {
});
}
// elementwise add coeffs of one polynomial with coeffs of other, that are
// first multiplied by a scalar
pub fn mul_add_polynomials<F: Field>(a: &mut [F], b: &[F], c: F) {
use crate::multicore::Worker;
use crate::domain::{EvaluationDomain, Scalar};
@ -693,7 +933,6 @@ impl<T> OptionExt<T> for Option<T> {
}
#[test]
fn test_mul() {
use rand::{self, Rand};
use crate::pairing::bls12_381::Bls12;
@ -763,7 +1002,7 @@ fn test_mut_eval_at_powers() {
{
let mut tmp = x.pow(&[n as u64]);
for mut coeff in a.iter_mut() {
for coeff in a.iter_mut() {
coeff.mul_assign(&tmp);
acc.add_assign(&coeff);
tmp.mul_assign(&x);
@ -794,7 +1033,7 @@ fn test_mut_distribute_powers() {
{
let mut tmp = x.pow(&[n as u64]);
for mut coeff in a.iter_mut() {
for coeff in a.iter_mut() {
coeff.mul_assign(&tmp);
tmp.mul_assign(&x);
}
@ -804,4 +1043,119 @@ fn test_mut_distribute_powers() {
mut_distribute_consequitive_powers(&mut b[..], first_power, x);
assert!(a == b);
}
#[test]
fn test_trivial_parallel_kate_division() {
use crate::pairing::ff::PrimeField;
use crate::pairing::bls12_381::{Bls12, Fr};
let mut minus_one = Fr::one();
minus_one.negate();
let z = Fr::one();
// this is x^2 - 1
let poly = vec![
minus_one,
Fr::from_str("0").unwrap(),
Fr::from_str("1").unwrap(),
];
let quotient_poly = kate_divison(&poly, z);
let parallel_q_poly = parallel_kate_divison::<Bls12, _>(&poly, z);
assert_eq!(quotient_poly, parallel_q_poly);
}
#[test]
fn test_less_trivial_parallel_kate_division() {
use crate::pairing::ff::PrimeField;
use crate::pairing::bls12_381::{Bls12, Fr};
let z = Fr::one();
let mut poly = vec![
Fr::from_str("328947234").unwrap(),
Fr::from_str("3545623451111").unwrap(),
Fr::from_str("5").unwrap(),
Fr::from_str("55555").unwrap(),
Fr::from_str("1235685").unwrap(),
];
fn eval(poly: &[Fr], point: Fr) -> Fr {
let mut acc = Fr::zero();
let mut tmp = Fr::one();
for p in &poly[..] {
let mut t = *p;
t.mul_assign(&tmp);
acc.add_assign(&t);
tmp.mul_assign(&point);
}
acc
}
let p_at_z = eval(&poly, z);
// poly = poly(X) - poly(z)
poly[0].sub_assign(&p_at_z);
let quotient_poly = kate_divison(&poly, z);
let parallel_q_poly = parallel_kate_divison::<Bls12, _>(&poly, z);
assert_eq!(quotient_poly, parallel_q_poly);
}
#[test]
fn test_parallel_kate_division() {
use crate::pairing::ff::PrimeField;
use crate::pairing::bls12_381::{Bls12, Fr};
let mut poly = vec![
Fr::from_str("328947234").unwrap(),
Fr::from_str("3545623451111").unwrap(),
Fr::from_str("0").unwrap(),
Fr::from_str("55555").unwrap(),
Fr::from_str("1235685").unwrap(),
];
fn eval(poly: &[Fr], point: Fr) -> Fr {
let point_inv = point.inverse().unwrap();
let mut acc = Fr::zero();
let mut tmp = Fr::one();
for p in &poly[2..] {
let mut t = *p;
t.mul_assign(&tmp);
acc.add_assign(&t);
tmp.mul_assign(&point);
}
let mut tmp = point_inv;
for p in poly[0..2].iter().rev() {
let mut t = *p;
t.mul_assign(&tmp);
acc.add_assign(&t);
tmp.mul_assign(&point_inv);
}
acc
}
let z = Fr::from_str("2000").unwrap();
let p_at_z = eval(&poly, z);
// poly = poly(X) - poly(z)
poly[2].sub_assign(&p_at_z);
let quotient_poly = kate_divison(&poly, z);
let parallel_q_poly = parallel_kate_divison::<Bls12, _>(&poly, z);
assert_eq!(quotient_poly, parallel_q_poly);
}

@ -3,6 +3,7 @@ use crate::pairing::{
CurveProjective,
CurveAffine,
GroupDecodingError,
RawEncodable,
EncodedPoint
};
@ -90,6 +91,23 @@ impl Field for Fr {
}
}
impl RawEncodable for Fr {
fn into_raw_uncompressed_le(&self) -> Self::Uncompressed {
Self::Uncompressed::empty()
}
fn from_raw_uncompressed_le_unchecked(
_encoded: &Self::Uncompressed,
_infinity: bool
) -> Result<Self, GroupDecodingError> {
Ok(<Self as Field>::zero())
}
fn from_raw_uncompressed_le(encoded: &Self::Uncompressed, _infinity: bool) -> Result<Self, GroupDecodingError> {
Self::from_raw_uncompressed_le_unchecked(&encoded, _infinity)
}
}
impl SqrtField for Fr {
fn legendre(&self) -> LegendreSymbol {
// s = self^((r - 1) // 2)
@ -247,6 +265,14 @@ impl PrimeField for Fr {
}
}
fn from_raw_repr(repr: FrRepr) -> Result<Self, PrimeFieldDecodingError> {
if repr.0[0] >= (MODULUS_R.0 as u64) {
Err(PrimeFieldDecodingError::NotInField(format!("{}", repr)))
} else {
Ok(Fr(Wrapping(repr.0[0] as u32)))
}
}
fn into_repr(&self) -> FrRepr {
FrRepr::from(*self)
}
@ -255,6 +281,10 @@ impl PrimeField for Fr {
Fr(MODULUS_R).into()
}
fn into_raw_repr(&self) -> FrRepr {
FrRepr::from(*self)
}
fn multiplicative_generator() -> Fr {
Fr(Wrapping(5))
}

@ -40,6 +40,8 @@ use bellman_ce::groth16::{
const MIMC_ROUNDS: usize = 322;
// const MIMC_ROUNDS: usize = 1000000;
/// This is an implementation of MiMC, specifically a
/// variant named `LongsightF322p3` for BLS12-381.
/// See http://eprint.iacr.org/2016/492 for more
@ -171,7 +173,7 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
}
#[test]
fn test_mimc() {
fn test_mimc_bls12() {
// This may not be cryptographically safe, use
// `OsRng` (for example) in production software.
let rng = &mut thread_rng();
@ -198,7 +200,7 @@ fn test_mimc() {
println!("Creating proofs...");
// Let's benchmark stuff!
const SAMPLES: u32 = 50;
const SAMPLES: u32 = 1;
let mut total_proving = Duration::new(0, 0);
let mut total_verifying = Duration::new(0, 0);