mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-11-01 20:07:02 +00:00
Reimplementation of groth16 using pairing library.
This commit is contained in:
20
Cargo.toml
20
Cargo.toml
@@ -9,11 +9,17 @@ repository = "https://github.com/ebfull/bellman"
|
|||||||
version = "0.0.3"
|
version = "0.0.3"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
#rand = "0.3.*"
|
rand = "0.3"
|
||||||
#byteorder = "1.*"
|
bit-vec = "0.4.4"
|
||||||
#serde = "1.0"
|
futures = "0.1"
|
||||||
#crossbeam = "0.2"
|
futures-cpupool = "0.1"
|
||||||
#num_cpus = "1.0"
|
num_cpus = "1.6"
|
||||||
|
crossbeam = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dependencies.pairing]
|
||||||
#bincode = "0.8.0"
|
version = "0.11"
|
||||||
|
features = ["unstable-wnaf"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["u128-support"]
|
||||||
|
u128-support = ["pairing/u128-support"]
|
||||||
|
|||||||
457
src/domain.rs
Normal file
457
src/domain.rs
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
use pairing::*;
|
||||||
|
use super::{
|
||||||
|
Error
|
||||||
|
};
|
||||||
|
use crossbeam;
|
||||||
|
use num_cpus;
|
||||||
|
use multicore;
|
||||||
|
|
||||||
|
const LARGEST_POLYNOMIAL_DEGREE: usize = 1 << 28;
|
||||||
|
|
||||||
|
pub struct EvaluationDomain<E: Engine, G: Group<E>> {
|
||||||
|
coeffs: Vec<G>,
|
||||||
|
exp: u32,
|
||||||
|
omega: E::Fr,
|
||||||
|
omegainv: E::Fr,
|
||||||
|
geninv: E::Fr,
|
||||||
|
minv: E::Fr
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
|
||||||
|
pub fn as_ref(&self) -> &[G] {
|
||||||
|
&self.coeffs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_coeffs(self) -> Vec<G> {
|
||||||
|
self.coeffs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mut(&mut self) -> &mut [G] {
|
||||||
|
&mut self.coeffs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, Error>
|
||||||
|
{
|
||||||
|
if coeffs.len() > LARGEST_POLYNOMIAL_DEGREE {
|
||||||
|
return Err(Error::PolynomialDegreeTooLarge)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut m = 1;
|
||||||
|
let mut exp = 0;
|
||||||
|
while m < coeffs.len() {
|
||||||
|
m *= 2;
|
||||||
|
exp += 1;
|
||||||
|
|
||||||
|
if exp >= E::Fr::s() {
|
||||||
|
return Err(Error::PolynomialDegreeTooLarge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut omega = E::Fr::root_of_unity();
|
||||||
|
for _ in exp..E::Fr::s() {
|
||||||
|
omega.square();
|
||||||
|
}
|
||||||
|
|
||||||
|
coeffs.resize(m, G::group_zero());
|
||||||
|
|
||||||
|
Ok(EvaluationDomain {
|
||||||
|
coeffs: coeffs,
|
||||||
|
exp: exp,
|
||||||
|
omega: omega,
|
||||||
|
omegainv: omega.inverse().unwrap(),
|
||||||
|
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
|
||||||
|
minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fft(&mut self)
|
||||||
|
{
|
||||||
|
best_fft(&mut self.coeffs, &self.omega, self.exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ifft(&mut self)
|
||||||
|
{
|
||||||
|
best_fft(&mut self.coeffs, &self.omegainv, self.exp);
|
||||||
|
|
||||||
|
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
let minv = self.minv;
|
||||||
|
|
||||||
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
|
scope.spawn(move || {
|
||||||
|
for v in v {
|
||||||
|
v.group_mul_assign(&minv);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_coset(&mut self, g: E::Fr)
|
||||||
|
{
|
||||||
|
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
||||||
|
scope.spawn(move || {
|
||||||
|
let mut u = g.pow(&[(i * chunk) as u64]);
|
||||||
|
for v in v.iter_mut() {
|
||||||
|
v.group_mul_assign(&u);
|
||||||
|
u.mul_assign(&g);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn coset_fft(&mut self)
|
||||||
|
{
|
||||||
|
self.mul_coset(E::Fr::multiplicative_generator());
|
||||||
|
self.fft();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn icoset_fft(&mut self)
|
||||||
|
{
|
||||||
|
let geninv = self.geninv;
|
||||||
|
|
||||||
|
self.ifft();
|
||||||
|
self.mul_coset(geninv);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z(&self, tau: &E::Fr) -> E::Fr {
|
||||||
|
let mut tmp = tau.pow(&[self.coeffs.len() as u64]);
|
||||||
|
tmp.sub_assign(&E::Fr::one());
|
||||||
|
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn divide_by_z_on_coset(&mut self)
|
||||||
|
{
|
||||||
|
let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap();
|
||||||
|
|
||||||
|
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
|
scope.spawn(move || {
|
||||||
|
for v in v {
|
||||||
|
v.group_mul_assign(&i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul_assign(&mut self, other: &EvaluationDomain<E, Scalar<E>>) {
|
||||||
|
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||||
|
|
||||||
|
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
|
||||||
|
scope.spawn(move || {
|
||||||
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
|
a.group_mul_assign(&b.0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub_assign(&mut self, other: &EvaluationDomain<E, G>) {
|
||||||
|
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||||
|
|
||||||
|
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
||||||
|
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
|
||||||
|
scope.spawn(move || {
|
||||||
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
|
a.group_sub_assign(&b);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Group<E: Engine>: Sized + Copy + Clone + Send + Sync {
|
||||||
|
fn group_zero() -> Self;
|
||||||
|
fn group_mul_assign(&mut self, by: &E::Fr);
|
||||||
|
fn group_add_assign(&mut self, other: &Self);
|
||||||
|
fn group_sub_assign(&mut self, other: &Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Scalar<E: Engine>(pub E::Fr);
|
||||||
|
|
||||||
|
impl<E: Engine> PartialEq for Scalar<E> {
|
||||||
|
fn eq(&self, other: &Scalar<E>) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Copy for Scalar<E> { }
|
||||||
|
|
||||||
|
impl<E: Engine> Clone for Scalar<E> {
|
||||||
|
fn clone(&self) -> Scalar<E> {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Group<E> for Scalar<E> {
|
||||||
|
fn group_zero() -> Self {
|
||||||
|
Scalar(E::Fr::zero())
|
||||||
|
}
|
||||||
|
fn group_mul_assign(&mut self, by: &E::Fr) {
|
||||||
|
self.0.mul_assign(by);
|
||||||
|
}
|
||||||
|
fn group_add_assign(&mut self, other: &Self) {
|
||||||
|
self.0.add_assign(&other.0);
|
||||||
|
}
|
||||||
|
fn group_sub_assign(&mut self, other: &Self) {
|
||||||
|
self.0.sub_assign(&other.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_log_cpus() -> u32 {
|
||||||
|
let num = num_cpus::get();
|
||||||
|
log2_floor(num)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log2_floor(num: usize) -> u32 {
|
||||||
|
assert!(num > 0);
|
||||||
|
|
||||||
|
let mut pow = 0;
|
||||||
|
|
||||||
|
while (1 << (pow+1)) <= num {
|
||||||
|
pow += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pow
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_log2_floor() {
|
||||||
|
assert_eq!(log2_floor(1), 0);
|
||||||
|
assert_eq!(log2_floor(2), 1);
|
||||||
|
assert_eq!(log2_floor(3), 1);
|
||||||
|
assert_eq!(log2_floor(4), 2);
|
||||||
|
assert_eq!(log2_floor(5), 2);
|
||||||
|
assert_eq!(log2_floor(6), 2);
|
||||||
|
assert_eq!(log2_floor(7), 2);
|
||||||
|
assert_eq!(log2_floor(8), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_fft<E: Engine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32)
|
||||||
|
{
|
||||||
|
let log_cpus = get_log_cpus();
|
||||||
|
|
||||||
|
if log_n < log_cpus {
|
||||||
|
serial_fft(a, omega, log_n);
|
||||||
|
} else {
|
||||||
|
parallel_fft(a, omega, log_n, log_cpus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serial_fft<E: Engine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32)
|
||||||
|
{
|
||||||
|
fn bitreverse(mut n: u32, l: u32) -> u32 {
|
||||||
|
let mut r = 0;
|
||||||
|
for _ in 0..l {
|
||||||
|
r = (r << 1) | (n & 1);
|
||||||
|
n >>= 1;
|
||||||
|
}
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = a.len() as u32;
|
||||||
|
assert_eq!(n, 1 << log_n);
|
||||||
|
|
||||||
|
for k in 0..n {
|
||||||
|
let rk = bitreverse(k, log_n);
|
||||||
|
if k < rk {
|
||||||
|
a.swap(rk as usize, k as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut m = 1;
|
||||||
|
for _ in 0..log_n {
|
||||||
|
let w_m = omega.pow(&[(n / (2*m)) as u64]);
|
||||||
|
|
||||||
|
let mut k = 0;
|
||||||
|
while k < n {
|
||||||
|
let mut w = E::Fr::one();
|
||||||
|
for j in 0..m {
|
||||||
|
let mut t = a[(k+j+m) as usize];
|
||||||
|
t.group_mul_assign(&w);
|
||||||
|
let mut tmp = a[(k+j) as usize];
|
||||||
|
tmp.group_sub_assign(&t);
|
||||||
|
a[(k+j+m) as usize] = tmp;
|
||||||
|
a[(k+j) as usize].group_add_assign(&t);
|
||||||
|
w.mul_assign(&w_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
k += 2*m;
|
||||||
|
}
|
||||||
|
|
||||||
|
m *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parallel_fft<E: Engine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32, log_cpus: u32)
|
||||||
|
{
|
||||||
|
assert!(log_n >= log_cpus);
|
||||||
|
|
||||||
|
let num_cpus = 1 << log_cpus;
|
||||||
|
let log_new_n = log_n - log_cpus;
|
||||||
|
let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus];
|
||||||
|
let new_omega = omega.pow(&[num_cpus as u64]);
|
||||||
|
|
||||||
|
crossbeam::scope(|scope| {
|
||||||
|
let a = &*a;
|
||||||
|
|
||||||
|
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||||
|
scope.spawn(move || {
|
||||||
|
// Shuffle into a sub-FFT
|
||||||
|
let omega_j = omega.pow(&[j as u64]);
|
||||||
|
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
|
||||||
|
|
||||||
|
let mut elt = E::Fr::one();
|
||||||
|
for i in 0..(1 << log_new_n) {
|
||||||
|
for s in 0..num_cpus {
|
||||||
|
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
||||||
|
let mut t = a[idx];
|
||||||
|
t.group_mul_assign(&elt);
|
||||||
|
tmp[i].group_add_assign(&t);
|
||||||
|
elt.mul_assign(&omega_step);
|
||||||
|
}
|
||||||
|
elt.mul_assign(&omega_j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform sub-FFT
|
||||||
|
serial_fft(tmp, &new_omega, log_new_n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: does this hurt or help?
|
||||||
|
multicore::scope(a.len(), |scope, chunk| {
|
||||||
|
let tmp = &tmp;
|
||||||
|
|
||||||
|
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
||||||
|
scope.spawn(move || {
|
||||||
|
let mut idx = idx * chunk;
|
||||||
|
let mask = (1 << log_cpus) - 1;
|
||||||
|
for a in a {
|
||||||
|
*a = tmp[idx & mask][idx >> log_cpus];
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test multiplying various (low degree) polynomials together and
|
||||||
|
// comparing with naive evaluations.
|
||||||
|
#[test]
|
||||||
|
fn polynomial_arith() {
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand::{self, Rand};
|
||||||
|
|
||||||
|
fn test_mul<E: Engine, R: rand::Rng>(rng: &mut R)
|
||||||
|
{
|
||||||
|
for coeffs_a in 0..70 {
|
||||||
|
for coeffs_b in 0..70 {
|
||||||
|
let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
|
||||||
|
let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
|
||||||
|
|
||||||
|
// naive evaluation
|
||||||
|
let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b];
|
||||||
|
for (i1, a) in a.iter().enumerate() {
|
||||||
|
for (i2, b) in b.iter().enumerate() {
|
||||||
|
let mut prod = *a;
|
||||||
|
prod.group_mul_assign(&b.0);
|
||||||
|
naive[i1 + i2].group_add_assign(&prod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero()));
|
||||||
|
b.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero()));
|
||||||
|
|
||||||
|
let mut a = EvaluationDomain::from_coeffs(a).unwrap();
|
||||||
|
let mut b = EvaluationDomain::from_coeffs(b).unwrap();
|
||||||
|
|
||||||
|
a.fft();
|
||||||
|
b.fft();
|
||||||
|
a.mul_assign(&b);
|
||||||
|
a.ifft();
|
||||||
|
|
||||||
|
for (naive, fft) in naive.iter().zip(a.coeffs.iter()) {
|
||||||
|
assert!(naive == fft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rng = &mut rand::thread_rng();
|
||||||
|
|
||||||
|
test_mul::<Bls12, _>(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fft_composition() {
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand;
|
||||||
|
|
||||||
|
fn test_comp<E: Engine, R: rand::Rng>(rng: &mut R)
|
||||||
|
{
|
||||||
|
for coeffs in 0..10 {
|
||||||
|
let coeffs = 1 << coeffs;
|
||||||
|
|
||||||
|
let mut v = vec![];
|
||||||
|
for _ in 0..coeffs {
|
||||||
|
v.push(Scalar::<E>(rng.gen()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
|
||||||
|
domain.ifft();
|
||||||
|
domain.fft();
|
||||||
|
assert!(v == domain.coeffs);
|
||||||
|
domain.fft();
|
||||||
|
domain.ifft();
|
||||||
|
assert!(v == domain.coeffs);
|
||||||
|
domain.icoset_fft();
|
||||||
|
domain.coset_fft();
|
||||||
|
assert!(v == domain.coeffs);
|
||||||
|
domain.coset_fft();
|
||||||
|
domain.icoset_fft();
|
||||||
|
assert!(v == domain.coeffs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rng = &mut rand::thread_rng();
|
||||||
|
|
||||||
|
test_comp::<Bls12, _>(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parallel_fft_consistency() {
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand::{self, Rand};
|
||||||
|
use std::cmp::min;
|
||||||
|
|
||||||
|
fn test_consistency<E: Engine, R: rand::Rng>(rng: &mut R)
|
||||||
|
{
|
||||||
|
for _ in 0..5 {
|
||||||
|
for log_d in 0..10 {
|
||||||
|
let d = 1 << log_d;
|
||||||
|
|
||||||
|
let v1 = (0..d).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect::<Vec<_>>();
|
||||||
|
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
|
||||||
|
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
|
||||||
|
|
||||||
|
for log_cpus in 0..min(log_d, 3) {
|
||||||
|
parallel_fft(&mut v1.coeffs, &v1.omega, log_d, log_cpus);
|
||||||
|
serial_fft(&mut v2.coeffs, &v2.omega, log_d);
|
||||||
|
|
||||||
|
assert!(v1.coeffs == v2.coeffs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rng = &mut rand::thread_rng();
|
||||||
|
|
||||||
|
test_consistency::<Bls12, _>(rng);
|
||||||
|
}
|
||||||
448
src/groth16/generator.rs
Normal file
448
src/groth16/generator.rs
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
use pairing::*;
|
||||||
|
use pairing::wnaf::*;
|
||||||
|
use ::{
|
||||||
|
Input,
|
||||||
|
Error,
|
||||||
|
LinearCombination,
|
||||||
|
Index,
|
||||||
|
Circuit,
|
||||||
|
Variable,
|
||||||
|
ConstraintSystem,
|
||||||
|
PublicConstraintSystem
|
||||||
|
};
|
||||||
|
use super::{VerifyingKey, Parameters};
|
||||||
|
use domain::{Scalar, EvaluationDomain};
|
||||||
|
use rand::Rng;
|
||||||
|
use multicore;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub fn generate_random_parameters<E, C, R>(
|
||||||
|
circuit: C,
|
||||||
|
rng: &mut R
|
||||||
|
) -> Result<Parameters<E>, Error>
|
||||||
|
where E: Engine, C: Circuit<E>, R: Rng
|
||||||
|
{
|
||||||
|
let g1 = rng.gen();
|
||||||
|
let g2 = rng.gen();
|
||||||
|
let alpha = rng.gen();
|
||||||
|
let beta = rng.gen();
|
||||||
|
let gamma = rng.gen();
|
||||||
|
let delta = rng.gen();
|
||||||
|
let tau = rng.gen();
|
||||||
|
|
||||||
|
generate_parameters::<E, C>(
|
||||||
|
circuit,
|
||||||
|
g1,
|
||||||
|
g2,
|
||||||
|
alpha,
|
||||||
|
beta,
|
||||||
|
gamma,
|
||||||
|
delta,
|
||||||
|
tau
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create parameters for a circuit, given some trapdoors.
|
||||||
|
pub fn generate_parameters<E, C>(
|
||||||
|
circuit: C,
|
||||||
|
g1: E::G1,
|
||||||
|
g2: E::G2,
|
||||||
|
alpha: E::Fr,
|
||||||
|
beta: E::Fr,
|
||||||
|
gamma: E::Fr,
|
||||||
|
delta: E::Fr,
|
||||||
|
tau: E::Fr
|
||||||
|
) -> Result<Parameters<E>, Error>
|
||||||
|
where E: Engine, C: Circuit<E>
|
||||||
|
{
|
||||||
|
// This is our assembly structure that we'll use to synthesize the
|
||||||
|
// circuit into a QAP.
|
||||||
|
struct KeypairAssembly<E: Engine> {
|
||||||
|
num_inputs: usize,
|
||||||
|
num_aux: usize,
|
||||||
|
num_constraints: usize,
|
||||||
|
at_inputs: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
bt_inputs: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
ct_inputs: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
at_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
bt_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
|
ct_aux: Vec<Vec<(E::Fr, usize)>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> PublicConstraintSystem<E> for KeypairAssembly<E> {
|
||||||
|
fn alloc_input<F: FnOnce() -> Result<E::Fr, Error>>(&mut self, f: F) -> Result<Variable, Error> {
|
||||||
|
// In this context, we don't have an assignment.
|
||||||
|
let _ = f();
|
||||||
|
|
||||||
|
let index = self.num_inputs;
|
||||||
|
self.num_inputs += 1;
|
||||||
|
|
||||||
|
self.at_inputs.push(vec![]);
|
||||||
|
self.bt_inputs.push(vec![]);
|
||||||
|
self.ct_inputs.push(vec![]);
|
||||||
|
|
||||||
|
Ok(Variable(Index::Input(index)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
|
||||||
|
fn alloc<F: FnOnce() -> Result<E::Fr, Error>>(&mut self, f: F) -> Result<Variable, Error> {
|
||||||
|
// In this context, we don't have an assignment.
|
||||||
|
let _ = f();
|
||||||
|
|
||||||
|
let index = self.num_aux;
|
||||||
|
self.num_aux += 1;
|
||||||
|
|
||||||
|
self.at_aux.push(vec![]);
|
||||||
|
self.bt_aux.push(vec![]);
|
||||||
|
self.ct_aux.push(vec![]);
|
||||||
|
|
||||||
|
Ok(Variable(Index::Aux(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce(
|
||||||
|
&mut self,
|
||||||
|
a: LinearCombination<E>,
|
||||||
|
b: LinearCombination<E>,
|
||||||
|
c: LinearCombination<E>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
fn qap_eval<E: Engine>(
|
||||||
|
l: LinearCombination<E>,
|
||||||
|
inputs: &mut [Vec<(E::Fr, usize)>],
|
||||||
|
aux: &mut [Vec<(E::Fr, usize)>],
|
||||||
|
this_constraint: usize
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (index, coeff) in l.0 {
|
||||||
|
match index {
|
||||||
|
Index::Input(id) => inputs[id].push((coeff, this_constraint)),
|
||||||
|
Index::Aux(id) => aux[id].push((coeff, this_constraint))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qap_eval(a, &mut self.at_inputs, &mut self.at_aux, self.num_constraints);
|
||||||
|
qap_eval(b, &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints);
|
||||||
|
qap_eval(c, &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints);
|
||||||
|
|
||||||
|
self.num_constraints += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut assembly = KeypairAssembly {
|
||||||
|
num_inputs: 0,
|
||||||
|
num_aux: 0,
|
||||||
|
num_constraints: 0,
|
||||||
|
at_inputs: vec![],
|
||||||
|
bt_inputs: vec![],
|
||||||
|
ct_inputs: vec![],
|
||||||
|
at_aux: vec![],
|
||||||
|
bt_aux: vec![],
|
||||||
|
ct_aux: vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate the "one" input variable
|
||||||
|
assembly.alloc_input(|| Ok(E::Fr::one()))?;
|
||||||
|
|
||||||
|
// Synthesize the circuit.
|
||||||
|
circuit.synthesize(&mut assembly)?.synthesize(&mut assembly)?;
|
||||||
|
|
||||||
|
// Input consistency constraints: x * 0 = 0
|
||||||
|
for i in 0..assembly.num_inputs {
|
||||||
|
assembly.enforce(LinearCombination::zero() + Variable(Index::Input(i)),
|
||||||
|
LinearCombination::zero(),
|
||||||
|
LinearCombination::zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that all auxillary variables are constrained
|
||||||
|
for i in 0..assembly.num_aux {
|
||||||
|
if assembly.at_aux[i].len() == 0 &&
|
||||||
|
assembly.bt_aux[i].len() == 0 &&
|
||||||
|
assembly.ct_aux[i].len() == 0
|
||||||
|
{
|
||||||
|
return Err(Error::UnconstrainedVariable(Variable(Index::Aux(i))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create evaluation domain for the QAP
|
||||||
|
let powers_of_tau = vec![Scalar::<E>(E::Fr::zero()); assembly.num_constraints];
|
||||||
|
let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?;
|
||||||
|
|
||||||
|
// Compute G1 window table
|
||||||
|
let mut g1_table = vec![];
|
||||||
|
let g1_table_size = E::G1::recommended_wnaf_for_num_scalars(
|
||||||
|
// H query
|
||||||
|
(powers_of_tau.as_ref().len() - 1)
|
||||||
|
// IC/L queries
|
||||||
|
+ assembly.num_inputs + assembly.num_aux
|
||||||
|
// A query
|
||||||
|
+ assembly.num_inputs + assembly.num_aux
|
||||||
|
// B query
|
||||||
|
+ assembly.num_inputs + assembly.num_aux
|
||||||
|
);
|
||||||
|
wnaf_table(&mut g1_table, g1, g1_table_size);
|
||||||
|
|
||||||
|
// Compute G2 window table
|
||||||
|
let mut g2_table = vec![];
|
||||||
|
let g2_table_size = E::G2::recommended_wnaf_for_num_scalars(
|
||||||
|
// B query
|
||||||
|
assembly.num_inputs + assembly.num_aux
|
||||||
|
);
|
||||||
|
wnaf_table(&mut g2_table, g2, g2_table_size);
|
||||||
|
|
||||||
|
let gamma_inverse = gamma.inverse().ok_or(Error::UnexpectedIdentity)?;
|
||||||
|
let delta_inverse = delta.inverse().ok_or(Error::UnexpectedIdentity)?;
|
||||||
|
|
||||||
|
// Compute the H query
|
||||||
|
let mut h = vec![E::G1::zero(); powers_of_tau.as_ref().len() - 1];
|
||||||
|
{
|
||||||
|
// Compute the powers of tau
|
||||||
|
{
|
||||||
|
let powers_of_tau = powers_of_tau.as_mut();
|
||||||
|
multicore::scope(powers_of_tau.len(), |scope, chunk| {
|
||||||
|
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate()
|
||||||
|
{
|
||||||
|
scope.spawn(move || {
|
||||||
|
let mut current_tau_power = tau.pow(&[(i*chunk) as u64]);
|
||||||
|
|
||||||
|
for p in powers_of_tau {
|
||||||
|
p.0 = current_tau_power;
|
||||||
|
current_tau_power.mul_assign(&tau);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// coeff = t(x) / delta
|
||||||
|
let mut coeff = powers_of_tau.z(&tau);
|
||||||
|
coeff.mul_assign(&delta_inverse);
|
||||||
|
|
||||||
|
// Compute the H query with multiple threads
|
||||||
|
multicore::scope(h.len(), |scope, chunk| {
|
||||||
|
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
|
||||||
|
{
|
||||||
|
let g1_table = &g1_table;
|
||||||
|
|
||||||
|
scope.spawn(move || {
|
||||||
|
// Create wNAF form storage location for this thread
|
||||||
|
let mut wnaf = vec![];
|
||||||
|
|
||||||
|
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
||||||
|
for (h, p) in h.iter_mut().zip(p.iter())
|
||||||
|
{
|
||||||
|
// Compute final exponent
|
||||||
|
let mut exp = p.0;
|
||||||
|
exp.mul_assign(&coeff);
|
||||||
|
|
||||||
|
// Compute wNAF form of exponent
|
||||||
|
wnaf_form(&mut wnaf, exp.into_repr(), g1_table_size);
|
||||||
|
|
||||||
|
// Exponentiate
|
||||||
|
*h = wnaf_exp(g1_table, &wnaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch normalize
|
||||||
|
E::G1::batch_normalization(h);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use inverse FFT to convert powers of tau to Lagrange coefficients
|
||||||
|
powers_of_tau.ifft();
|
||||||
|
let powers_of_tau = powers_of_tau.into_coeffs();
|
||||||
|
|
||||||
|
let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
|
||||||
|
let mut b_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
|
||||||
|
let mut b_g2 = vec![E::G2::zero(); assembly.num_inputs + assembly.num_aux];
|
||||||
|
let mut ic = vec![E::G1::zero(); assembly.num_inputs];
|
||||||
|
let mut l = vec![E::G1::zero(); assembly.num_aux];
|
||||||
|
|
||||||
|
fn eval<E: Engine>(
|
||||||
|
// wNAF window tables
|
||||||
|
g1_table: &[E::G1],
|
||||||
|
g1_table_size: usize,
|
||||||
|
g2_table: &[E::G2],
|
||||||
|
g2_table_size: usize,
|
||||||
|
|
||||||
|
// Lagrange coefficients for tau
|
||||||
|
powers_of_tau: &[Scalar<E>],
|
||||||
|
|
||||||
|
// QAP polynomials
|
||||||
|
at: &[Vec<(E::Fr, usize)>],
|
||||||
|
bt: &[Vec<(E::Fr, usize)>],
|
||||||
|
ct: &[Vec<(E::Fr, usize)>],
|
||||||
|
|
||||||
|
// Resulting evaluated QAP polynomials
|
||||||
|
a: &mut [E::G1],
|
||||||
|
b_g1: &mut [E::G1],
|
||||||
|
b_g2: &mut [E::G2],
|
||||||
|
ext: &mut [E::G1],
|
||||||
|
|
||||||
|
// Inverse coefficient for ext elements
|
||||||
|
inv: &E::Fr,
|
||||||
|
|
||||||
|
// Trapdoors
|
||||||
|
alpha: &E::Fr,
|
||||||
|
beta: &E::Fr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
assert_eq!(a.len(), at.len());
|
||||||
|
assert_eq!(a.len(), bt.len());
|
||||||
|
assert_eq!(a.len(), ct.len());
|
||||||
|
assert_eq!(a.len(), b_g1.len());
|
||||||
|
assert_eq!(a.len(), b_g2.len());
|
||||||
|
assert_eq!(a.len(), ext.len());
|
||||||
|
|
||||||
|
// Evaluate polynomials in multiple threads
|
||||||
|
multicore::scope(a.len(), |scope, chunk| {
|
||||||
|
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk)
|
||||||
|
.zip(b_g1.chunks_mut(chunk))
|
||||||
|
.zip(b_g2.chunks_mut(chunk))
|
||||||
|
.zip(ext.chunks_mut(chunk))
|
||||||
|
.zip(at.chunks(chunk))
|
||||||
|
.zip(bt.chunks(chunk))
|
||||||
|
.zip(ct.chunks(chunk))
|
||||||
|
{
|
||||||
|
scope.spawn(move || {
|
||||||
|
// Create wNAF form storage location for this thread
|
||||||
|
let mut wnaf = vec![];
|
||||||
|
|
||||||
|
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut()
|
||||||
|
.zip(b_g1.iter_mut())
|
||||||
|
.zip(b_g2.iter_mut())
|
||||||
|
.zip(ext.iter_mut())
|
||||||
|
.zip(at.iter())
|
||||||
|
.zip(bt.iter())
|
||||||
|
.zip(ct.iter())
|
||||||
|
{
|
||||||
|
fn eval_at_tau<E: Engine>(
|
||||||
|
powers_of_tau: &[Scalar<E>],
|
||||||
|
p: &[(E::Fr, usize)]
|
||||||
|
) -> E::Fr
|
||||||
|
{
|
||||||
|
let mut acc = E::Fr::zero();
|
||||||
|
|
||||||
|
for &(ref coeff, index) in p {
|
||||||
|
let mut n = powers_of_tau[index].0;
|
||||||
|
n.mul_assign(coeff);
|
||||||
|
acc.add_assign(&n);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate QAP polynomials at tau
|
||||||
|
let mut at = eval_at_tau(powers_of_tau, at);
|
||||||
|
let mut bt = eval_at_tau(powers_of_tau, bt);
|
||||||
|
let ct = eval_at_tau(powers_of_tau, ct);
|
||||||
|
|
||||||
|
// Compute A query (in G1)
|
||||||
|
if !at.is_zero() {
|
||||||
|
wnaf_form(&mut wnaf, at.into_repr(), g1_table_size);
|
||||||
|
*a = wnaf_exp(&g1_table, &wnaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute B query (in G1/G2)
|
||||||
|
if !bt.is_zero() {
|
||||||
|
// Normalize the field element once
|
||||||
|
let bt_repr = bt.into_repr();
|
||||||
|
wnaf_form(&mut wnaf, bt_repr, g1_table_size);
|
||||||
|
*b_g1 = wnaf_exp(&g1_table, &wnaf);
|
||||||
|
|
||||||
|
// G1 window table might use the same window size
|
||||||
|
// as the G2 window table, so we wouldn't need to
|
||||||
|
// recompute the wNAF form of the exponent.
|
||||||
|
if g1_table_size != g2_table_size {
|
||||||
|
wnaf_form(&mut wnaf, bt_repr, g2_table_size);
|
||||||
|
}
|
||||||
|
*b_g2 = wnaf_exp(&g2_table, &wnaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
at.mul_assign(&beta);
|
||||||
|
bt.mul_assign(&alpha);
|
||||||
|
|
||||||
|
let mut e = at;
|
||||||
|
e.add_assign(&bt);
|
||||||
|
e.add_assign(&ct);
|
||||||
|
e.mul_assign(inv);
|
||||||
|
|
||||||
|
wnaf_form(&mut wnaf, e.into_repr(), g1_table_size);
|
||||||
|
*ext = wnaf_exp(&g1_table, &wnaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch normalize
|
||||||
|
E::G1::batch_normalization(a);
|
||||||
|
E::G1::batch_normalization(b_g1);
|
||||||
|
E::G2::batch_normalization(b_g2);
|
||||||
|
E::G1::batch_normalization(ext);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate for inputs.
|
||||||
|
eval(
|
||||||
|
&g1_table,
|
||||||
|
g1_table_size,
|
||||||
|
&g2_table,
|
||||||
|
g2_table_size,
|
||||||
|
&powers_of_tau,
|
||||||
|
&assembly.at_inputs,
|
||||||
|
&assembly.bt_inputs,
|
||||||
|
&assembly.ct_inputs,
|
||||||
|
&mut a[0..assembly.num_inputs],
|
||||||
|
&mut b_g1[0..assembly.num_inputs],
|
||||||
|
&mut b_g2[0..assembly.num_inputs],
|
||||||
|
&mut ic,
|
||||||
|
&gamma_inverse,
|
||||||
|
&alpha,
|
||||||
|
&beta
|
||||||
|
);
|
||||||
|
|
||||||
|
// Evaluate for auxillary variables.
|
||||||
|
eval(
|
||||||
|
&g1_table,
|
||||||
|
g1_table_size,
|
||||||
|
&g2_table,
|
||||||
|
g2_table_size,
|
||||||
|
&powers_of_tau,
|
||||||
|
&assembly.at_aux,
|
||||||
|
&assembly.bt_aux,
|
||||||
|
&assembly.ct_aux,
|
||||||
|
&mut a[assembly.num_inputs..],
|
||||||
|
&mut b_g1[assembly.num_inputs..],
|
||||||
|
&mut b_g2[assembly.num_inputs..],
|
||||||
|
&mut l,
|
||||||
|
&delta_inverse,
|
||||||
|
&alpha,
|
||||||
|
&beta
|
||||||
|
);
|
||||||
|
|
||||||
|
let g1 = g1.into_affine();
|
||||||
|
let g2 = g2.into_affine();
|
||||||
|
|
||||||
|
let vk = VerifyingKey::<E> {
|
||||||
|
alpha_g1: g1.mul(alpha).into_affine(),
|
||||||
|
beta_g1: g1.mul(beta).into_affine(),
|
||||||
|
beta_g2: g2.mul(beta).into_affine(),
|
||||||
|
gamma_g2: g2.mul(gamma).into_affine(),
|
||||||
|
delta_g1: g1.mul(delta).into_affine(),
|
||||||
|
delta_g2: g2.mul(delta).into_affine(),
|
||||||
|
ic: ic.into_iter().map(|e| e.into_affine()).collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Parameters {
|
||||||
|
vk: vk,
|
||||||
|
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
|
||||||
|
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
|
||||||
|
|
||||||
|
// Filter points at infinity away from A/B queries
|
||||||
|
a: Arc::new(a.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
|
||||||
|
b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
|
||||||
|
b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect())
|
||||||
|
})
|
||||||
|
}
|
||||||
424
src/groth16/mod.rs
Normal file
424
src/groth16/mod.rs
Normal file
@@ -0,0 +1,424 @@
|
|||||||
|
use pairing::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
mod generator;
|
||||||
|
pub use self::generator::*;
|
||||||
|
mod prover;
|
||||||
|
pub use self::prover::*;
|
||||||
|
mod verifier;
|
||||||
|
pub use self::verifier::*;
|
||||||
|
|
||||||
|
use ::Error;
|
||||||
|
use std::io::{self, Write, Read};
|
||||||
|
use multiexp::{Source, SourceBuilder};
|
||||||
|
|
||||||
|
pub struct Proof<E: Engine> {
|
||||||
|
a: E::G1Affine,
|
||||||
|
b: E::G2Affine,
|
||||||
|
c: E::G1Affine
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PreparedVerifyingKey<E: Engine> {
|
||||||
|
alpha_g1_beta_g2: E::Fqk,
|
||||||
|
neg_gamma_g2: <E::G2Affine as CurveAffine>::Prepared,
|
||||||
|
neg_delta_g2: <E::G2Affine as CurveAffine>::Prepared,
|
||||||
|
ic: Vec<E::G1Affine>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VerifyingKey<E: Engine> {
|
||||||
|
// alpha in g1 for verifying and for creating A/C elements of
|
||||||
|
// proof. Never the point at infinity.
|
||||||
|
alpha_g1: E::G1Affine,
|
||||||
|
|
||||||
|
// beta in g1 and g2 for verifying and for creating B/C elements
|
||||||
|
// of proof. Never the point at infinity.
|
||||||
|
beta_g1: E::G1Affine,
|
||||||
|
beta_g2: E::G2Affine,
|
||||||
|
|
||||||
|
// gamma in g2 for verifying. Never the point at infinity.
|
||||||
|
gamma_g2: E::G2Affine,
|
||||||
|
|
||||||
|
// delta in g1/g2 for verifying and proving, essentially the magic
|
||||||
|
// trapdoor that forces the prover to evaluate the C element of the
|
||||||
|
// proof with only components from the CRS. Never the point at
|
||||||
|
// infinity.
|
||||||
|
delta_g1: E::G1Affine,
|
||||||
|
delta_g2: E::G2Affine,
|
||||||
|
|
||||||
|
// Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / gamma
|
||||||
|
// for all public inputs. Because all public inputs have a "soundness
|
||||||
|
// of input consistency" constraint, this is the same size as the
|
||||||
|
// number of inputs, and never contains points at infinity.
|
||||||
|
ic: Vec<E::G1Affine>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Clone for VerifyingKey<E> {
|
||||||
|
fn clone(&self) -> VerifyingKey<E> {
|
||||||
|
VerifyingKey {
|
||||||
|
alpha_g1: self.alpha_g1.clone(),
|
||||||
|
beta_g1: self.beta_g1.clone(),
|
||||||
|
beta_g2: self.beta_g2.clone(),
|
||||||
|
gamma_g2: self.gamma_g2.clone(),
|
||||||
|
delta_g1: self.delta_g1.clone(),
|
||||||
|
delta_g2: self.delta_g2.clone(),
|
||||||
|
ic: self.ic.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> PartialEq for VerifyingKey<E> {
|
||||||
|
fn eq(&self, other: &VerifyingKey<E>) -> bool {
|
||||||
|
self.alpha_g1 == other.alpha_g1 &&
|
||||||
|
self.beta_g1 == other.beta_g1 &&
|
||||||
|
self.beta_g2 == other.beta_g2 &&
|
||||||
|
self.gamma_g2 == other.gamma_g2 &&
|
||||||
|
self.delta_g1 == other.delta_g1 &&
|
||||||
|
self.delta_g2 == other.delta_g2 &&
|
||||||
|
self.ic == other.ic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_nonzero<R: Read, G: CurveAffine>(reader: &mut R) -> Result<G, Error> {
|
||||||
|
let mut repr = G::Uncompressed::empty();
|
||||||
|
reader.read_exact(repr.as_mut())?;
|
||||||
|
|
||||||
|
let affine = repr.into_affine_unchecked(); // TODO
|
||||||
|
|
||||||
|
match affine {
|
||||||
|
Ok(affine) => {
|
||||||
|
if affine.is_zero() {
|
||||||
|
Err(Error::UnexpectedIdentity)
|
||||||
|
} else {
|
||||||
|
Ok(affine)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> VerifyingKey<E> {
|
||||||
|
fn size(num_ic: usize) -> usize {
|
||||||
|
let mut acc = 0;
|
||||||
|
acc += <E::G1Affine as CurveAffine>::Uncompressed::size(); // alpha_g1
|
||||||
|
acc += <E::G1Affine as CurveAffine>::Uncompressed::size(); // beta_g1
|
||||||
|
acc += <E::G1Affine as CurveAffine>::Uncompressed::size(); // delta_g1
|
||||||
|
acc += <E::G1Affine as CurveAffine>::Uncompressed::size() * num_ic; // IC
|
||||||
|
acc += <E::G2Affine as CurveAffine>::Uncompressed::size(); // beta_g2
|
||||||
|
acc += <E::G2Affine as CurveAffine>::Uncompressed::size(); // gamma_g2
|
||||||
|
acc += <E::G2Affine as CurveAffine>::Uncompressed::size(); // delta_g2
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
|
||||||
|
writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.beta_g1.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.beta_g2.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.gamma_g2.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.delta_g1.into_uncompressed().as_ref())?;
|
||||||
|
writer.write_all(self.delta_g2.into_uncompressed().as_ref())?;
|
||||||
|
for ic in &self.ic {
|
||||||
|
writer.write_all(ic.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<R: Read>(reader: &mut R, num_ic: usize) -> Result<VerifyingKey<E>, Error> {
|
||||||
|
let alpha_g1 = read_nonzero(reader)?;
|
||||||
|
let beta_g1 = read_nonzero(reader)?;
|
||||||
|
let beta_g2 = read_nonzero(reader)?;
|
||||||
|
let gamma_g2 = read_nonzero(reader)?;
|
||||||
|
let delta_g1 = read_nonzero(reader)?;
|
||||||
|
let delta_g2 = read_nonzero(reader)?;
|
||||||
|
|
||||||
|
let mut ic = vec![];
|
||||||
|
for _ in 0..num_ic {
|
||||||
|
ic.push(read_nonzero(reader)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(VerifyingKey {
|
||||||
|
alpha_g1: alpha_g1,
|
||||||
|
beta_g1: beta_g1,
|
||||||
|
beta_g2: beta_g2,
|
||||||
|
gamma_g2: gamma_g2,
|
||||||
|
delta_g1: delta_g1,
|
||||||
|
delta_g2: delta_g2,
|
||||||
|
ic: ic
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parameters<E: Engine> {
|
||||||
|
pub vk: VerifyingKey<E>,
|
||||||
|
|
||||||
|
// Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and
|
||||||
|
// m-2 inclusive. Never contains points at infinity.
|
||||||
|
h: Arc<Vec<E::G1Affine>>,
|
||||||
|
|
||||||
|
// Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta
|
||||||
|
// for all auxillary inputs. Variables can never be unconstrained, so this
|
||||||
|
// never contains points at infinity.
|
||||||
|
l: Arc<Vec<E::G1Affine>>,
|
||||||
|
|
||||||
|
// QAP "A" polynomials evaluated at tau in the Lagrange basis. Never contains
|
||||||
|
// points at infinity: polynomials that evaluate to zero are omitted from
|
||||||
|
// the CRS and the prover can deterministically skip their evaluation.
|
||||||
|
a: Arc<Vec<E::G1Affine>>,
|
||||||
|
|
||||||
|
// QAP "B" polynomials evaluated at tau in the Lagrange basis. Needed in
|
||||||
|
// G1 and G2 for C/B queries, respectively. Never contains points at
|
||||||
|
// infinity for the same reason as the "A" polynomials.
|
||||||
|
b_g1: Arc<Vec<E::G1Affine>>,
|
||||||
|
b_g2: Arc<Vec<E::G2Affine>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Parameters<E> {
|
||||||
|
pub fn write<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
|
||||||
|
self.vk.write(writer)?;
|
||||||
|
|
||||||
|
for e in &*self.h {
|
||||||
|
writer.write_all(e.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for e in &*self.l {
|
||||||
|
writer.write_all(e.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for e in &*self.a {
|
||||||
|
writer.write_all(e.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for e in &*self.b_g1 {
|
||||||
|
writer.write_all(e.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for e in &*self.b_g2 {
|
||||||
|
writer.write_all(e.into_uncompressed().as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ParameterSource<E: Engine> {
|
||||||
|
type G1Builder: SourceBuilder<E::G1Affine>;
|
||||||
|
type G2Builder: SourceBuilder<E::G2Affine>;
|
||||||
|
|
||||||
|
fn get_vk(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, Error>;
|
||||||
|
fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, Error>;
|
||||||
|
fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, Error>;
|
||||||
|
fn get_a(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error>;
|
||||||
|
fn get_b_g1(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error>;
|
||||||
|
fn get_b_g2(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G2Builder, Self::G2Builder), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
|
||||||
|
type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
|
||||||
|
type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
|
||||||
|
|
||||||
|
fn get_vk(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, Error> {
|
||||||
|
assert_eq!(self.vk.ic.len(), num_ic);
|
||||||
|
|
||||||
|
Ok(self.vk.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, Error> {
|
||||||
|
assert_eq!(self.h.len(), num_h);
|
||||||
|
|
||||||
|
Ok((self.h.clone(), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, Error> {
|
||||||
|
assert_eq!(self.l.len(), num_l);
|
||||||
|
|
||||||
|
Ok((self.l.clone(), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_a(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error> {
|
||||||
|
assert_eq!(self.a.len(), num_inputs + num_aux);
|
||||||
|
|
||||||
|
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_b_g1(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error> {
|
||||||
|
assert_eq!(self.b_g1.len(), num_inputs + num_aux);
|
||||||
|
|
||||||
|
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_b_g2(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G2Builder, Self::G2Builder), Error> {
|
||||||
|
assert_eq!(self.b_g2.len(), num_inputs + num_aux);
|
||||||
|
|
||||||
|
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Seek, SeekFrom};
|
||||||
|
|
||||||
|
pub struct ProverStream {
|
||||||
|
path: String,
|
||||||
|
cursor: u64,
|
||||||
|
fh: Option<File>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for ProverStream {
|
||||||
|
fn clone(&self) -> ProverStream {
|
||||||
|
ProverStream {
|
||||||
|
path: self.path.clone(),
|
||||||
|
cursor: self.cursor,
|
||||||
|
fh: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProverStream {
|
||||||
|
pub fn new(path: &str) -> Result<ProverStream, io::Error> {
|
||||||
|
Ok(ProverStream {
|
||||||
|
path: path.to_string(),
|
||||||
|
cursor: 0,
|
||||||
|
fh: None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_if_needed(&mut self) -> Result<(), Error> {
|
||||||
|
if self.fh.is_none() {
|
||||||
|
let mut fh = File::open(&self.path)?;
|
||||||
|
fh.seek(SeekFrom::Start(self.cursor))?;
|
||||||
|
|
||||||
|
self.fh = Some(fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveAffine> Source<G> for ProverStream {
|
||||||
|
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), Error> {
|
||||||
|
self.open_if_needed()?;
|
||||||
|
|
||||||
|
let r: G = read_nonzero(self.fh.as_mut().unwrap())?;
|
||||||
|
|
||||||
|
self.cursor += G::Uncompressed::size() as u64;
|
||||||
|
|
||||||
|
to.add_assign_mixed(&r);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn skip(&mut self, amt: usize) -> Result<(), Error> {
|
||||||
|
self.open_if_needed()?;
|
||||||
|
|
||||||
|
let size_to_skip = amt * G::Uncompressed::size();
|
||||||
|
|
||||||
|
self.cursor += size_to_skip as u64;
|
||||||
|
|
||||||
|
self.fh.as_mut().unwrap().seek(SeekFrom::Current(size_to_skip as i64))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveAffine> SourceBuilder<G> for ProverStream {
|
||||||
|
type Source = Self;
|
||||||
|
|
||||||
|
fn new(self) -> Self::Source {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> ParameterSource<E> for ProverStream {
|
||||||
|
type G1Builder = ProverStream;
|
||||||
|
type G2Builder = ProverStream;
|
||||||
|
|
||||||
|
fn get_vk(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, Error> {
|
||||||
|
self.open_if_needed()?;
|
||||||
|
|
||||||
|
let vk = VerifyingKey::read(self.fh.as_mut().unwrap(), num_ic)?;
|
||||||
|
|
||||||
|
self.cursor += VerifyingKey::<E>::size(num_ic) as u64;
|
||||||
|
|
||||||
|
Ok(vk)
|
||||||
|
}
|
||||||
|
fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, Error> {
|
||||||
|
self.open_if_needed()?;
|
||||||
|
|
||||||
|
let res = self.clone();
|
||||||
|
|
||||||
|
let amount_to_seek = num_h * <E::G1Affine as CurveAffine>::Uncompressed::size();
|
||||||
|
|
||||||
|
self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?;
|
||||||
|
self.cursor += amount_to_seek as u64;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, Error> {
|
||||||
|
self.open_if_needed()?;
|
||||||
|
|
||||||
|
let res = self.clone();
|
||||||
|
|
||||||
|
let amount_to_seek = num_l * <E::G1Affine as CurveAffine>::Uncompressed::size();
|
||||||
|
|
||||||
|
self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?;
|
||||||
|
self.cursor += amount_to_seek as u64;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
fn get_a(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error> {
|
||||||
|
self.open_if_needed()?;
|
||||||
|
|
||||||
|
let res1 = self.clone();
|
||||||
|
|
||||||
|
let amount_to_seek = num_inputs * <E::G1Affine as CurveAffine>::Uncompressed::size();
|
||||||
|
|
||||||
|
self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?;
|
||||||
|
self.cursor += amount_to_seek as u64;
|
||||||
|
|
||||||
|
let res2 = self.clone();
|
||||||
|
|
||||||
|
let amount_to_seek = num_aux * <E::G1Affine as CurveAffine>::Uncompressed::size();
|
||||||
|
|
||||||
|
self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?;
|
||||||
|
self.cursor += amount_to_seek as u64;
|
||||||
|
|
||||||
|
Ok((res1, res2))
|
||||||
|
}
|
||||||
|
fn get_b_g1(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error> {
|
||||||
|
self.open_if_needed()?;
|
||||||
|
|
||||||
|
let res1 = self.clone();
|
||||||
|
|
||||||
|
let amount_to_seek = num_inputs * <E::G1Affine as CurveAffine>::Uncompressed::size();
|
||||||
|
|
||||||
|
self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?;
|
||||||
|
self.cursor += amount_to_seek as u64;
|
||||||
|
|
||||||
|
let res2 = self.clone();
|
||||||
|
|
||||||
|
let amount_to_seek = num_aux * <E::G1Affine as CurveAffine>::Uncompressed::size();
|
||||||
|
|
||||||
|
self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?;
|
||||||
|
self.cursor += amount_to_seek as u64;
|
||||||
|
|
||||||
|
Ok((res1, res2))
|
||||||
|
}
|
||||||
|
fn get_b_g2(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G2Builder, Self::G2Builder), Error> {
|
||||||
|
self.open_if_needed()?;
|
||||||
|
|
||||||
|
let res1 = self.clone();
|
||||||
|
|
||||||
|
let amount_to_seek = num_inputs * <E::G2Affine as CurveAffine>::Uncompressed::size();
|
||||||
|
|
||||||
|
self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?;
|
||||||
|
self.cursor += amount_to_seek as u64;
|
||||||
|
|
||||||
|
let res2 = self.clone();
|
||||||
|
|
||||||
|
let amount_to_seek = num_aux * <E::G2Affine as CurveAffine>::Uncompressed::size();
|
||||||
|
|
||||||
|
self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?;
|
||||||
|
self.cursor += amount_to_seek as u64;
|
||||||
|
|
||||||
|
Ok((res1, res2))
|
||||||
|
}
|
||||||
|
}
|
||||||
205
src/groth16/prover.rs
Normal file
205
src/groth16/prover.rs
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
use pairing::*;
|
||||||
|
use domain::{Scalar, EvaluationDomain};
|
||||||
|
use ::{
|
||||||
|
ConstraintSystem,
|
||||||
|
PublicConstraintSystem,
|
||||||
|
Circuit,
|
||||||
|
Input,
|
||||||
|
Index,
|
||||||
|
Error,
|
||||||
|
Variable,
|
||||||
|
LinearCombination
|
||||||
|
};
|
||||||
|
use multiexp::*;
|
||||||
|
use super::{ParameterSource, Proof};
|
||||||
|
use rand::Rng;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use futures::Future;
|
||||||
|
use futures_cpupool::CpuPool;
|
||||||
|
|
||||||
|
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
|
||||||
|
circuit: C,
|
||||||
|
params: P,
|
||||||
|
rng: &mut R
|
||||||
|
) -> Result<Proof<E>, Error>
|
||||||
|
where E: Engine, C: Circuit<E>, R: Rng
|
||||||
|
{
|
||||||
|
let r = rng.gen();
|
||||||
|
let s = rng.gen();
|
||||||
|
|
||||||
|
create_proof::<E, C, P>(circuit, params, r, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_proof<E, C, P: ParameterSource<E>>(
|
||||||
|
circuit: C,
|
||||||
|
mut params: P,
|
||||||
|
r: E::Fr,
|
||||||
|
s: E::Fr
|
||||||
|
) -> Result<Proof<E>, Error>
|
||||||
|
where E: Engine, C: Circuit<E>
|
||||||
|
{
|
||||||
|
struct ProvingAssignment<E: Engine> {
|
||||||
|
// Density of queries
|
||||||
|
a_aux_density: DensityTracker,
|
||||||
|
b_input_density: DensityTracker,
|
||||||
|
b_aux_density: DensityTracker,
|
||||||
|
|
||||||
|
// Evaluations of A, B, C polynomials
|
||||||
|
a: Vec<Scalar<E>>,
|
||||||
|
b: Vec<Scalar<E>>,
|
||||||
|
c: Vec<Scalar<E>>,
|
||||||
|
|
||||||
|
// Assignments of variables
|
||||||
|
input_assignment: Vec<E::Fr>,
|
||||||
|
aux_assignment: Vec<E::Fr>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> PublicConstraintSystem<E> for ProvingAssignment<E> {
|
||||||
|
fn alloc_input<F: FnOnce() -> Result<E::Fr, Error>>(&mut self, value: F) -> Result<Variable, Error> {
|
||||||
|
self.input_assignment.push(value()?);
|
||||||
|
self.b_input_density.add_element();
|
||||||
|
|
||||||
|
Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
||||||
|
fn alloc<F: FnOnce() -> Result<E::Fr, Error>>(&mut self, value: F) -> Result<Variable, Error> {
|
||||||
|
self.aux_assignment.push(value()?);
|
||||||
|
self.a_aux_density.add_element();
|
||||||
|
self.b_aux_density.add_element();
|
||||||
|
|
||||||
|
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce(
|
||||||
|
&mut self,
|
||||||
|
a: LinearCombination<E>,
|
||||||
|
b: LinearCombination<E>,
|
||||||
|
c: LinearCombination<E>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
self.a.push(Scalar(a.eval(None, Some(&mut self.a_aux_density), &self.input_assignment, &self.aux_assignment)));
|
||||||
|
self.b.push(Scalar(b.eval(Some(&mut self.b_input_density), Some(&mut self.b_aux_density), &self.input_assignment, &self.aux_assignment)));
|
||||||
|
self.c.push(Scalar(c.eval(None, None, &self.input_assignment, &self.aux_assignment)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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![]
|
||||||
|
};
|
||||||
|
|
||||||
|
prover.alloc_input(|| Ok(E::Fr::one()))?;
|
||||||
|
|
||||||
|
circuit.synthesize(&mut prover)?.synthesize(&mut prover)?;
|
||||||
|
|
||||||
|
// Input consistency constraints: x * 0 = 0
|
||||||
|
for i in 0..prover.input_assignment.len() {
|
||||||
|
prover.enforce(LinearCombination::zero() + Variable(Index::Input(i)),
|
||||||
|
LinearCombination::zero(),
|
||||||
|
LinearCombination::zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
let cpupool = CpuPool::new_num_cpus();
|
||||||
|
|
||||||
|
let vk = params.get_vk(prover.input_assignment.len())?;
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
a.ifft();
|
||||||
|
a.coset_fft();
|
||||||
|
b.ifft();
|
||||||
|
b.coset_fft();
|
||||||
|
c.ifft();
|
||||||
|
c.coset_fft();
|
||||||
|
|
||||||
|
a.mul_assign(&b);
|
||||||
|
drop(b);
|
||||||
|
a.sub_assign(&c);
|
||||||
|
drop(c);
|
||||||
|
a.divide_by_z_on_coset();
|
||||||
|
a.icoset_fft();
|
||||||
|
let mut a = a.into_coeffs();
|
||||||
|
let a_len = a.len() - 1;
|
||||||
|
a.truncate(a_len);
|
||||||
|
// TODO: parallelize if it's even helpful
|
||||||
|
let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::<Vec<_>>());
|
||||||
|
|
||||||
|
multiexp(&cpupool, params.get_h(a.len())?, FullDensity, a)
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: parallelize if it's even helpful
|
||||||
|
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 l = multiexp(&cpupool, 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(&cpupool, a_inputs_source, FullDensity, input_assignment.clone());
|
||||||
|
let a_aux = multiexp(&cpupool, 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(&cpupool, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone());
|
||||||
|
let b_g1_aux = multiexp(&cpupool, 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(&cpupool, b_g2_inputs_source, b_input_density, input_assignment.clone());
|
||||||
|
let b_g2_aux = multiexp(&cpupool, b_g2_aux_source, b_aux_density, aux_assignment);
|
||||||
|
|
||||||
|
drop(input_assignment);
|
||||||
|
|
||||||
|
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()?);
|
||||||
|
|
||||||
|
Ok(Proof {
|
||||||
|
a: g_a.into_affine(),
|
||||||
|
b: g_b.into_affine(),
|
||||||
|
c: g_c.into_affine()
|
||||||
|
})
|
||||||
|
}
|
||||||
139
src/groth16/verifier.rs
Normal file
139
src/groth16/verifier.rs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
use pairing::*;
|
||||||
|
use ::{
|
||||||
|
Input,
|
||||||
|
Error,
|
||||||
|
LinearCombination,
|
||||||
|
Index,
|
||||||
|
Variable,
|
||||||
|
ConstraintSystem,
|
||||||
|
PublicConstraintSystem
|
||||||
|
};
|
||||||
|
use super::{Proof, VerifyingKey, PreparedVerifyingKey};
|
||||||
|
|
||||||
|
/// This is the constraint system synthesizer that is made available to
|
||||||
|
/// callers of the verification function when they wish to perform
|
||||||
|
/// allocations. In that context, allocation of inputs is not allowed.
|
||||||
|
pub struct VerifierInput<'a, E: Engine> {
|
||||||
|
acc: E::G1,
|
||||||
|
ic: &'a [E::G1Affine],
|
||||||
|
insufficient_inputs: bool,
|
||||||
|
num_inputs: usize,
|
||||||
|
num_aux: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: Engine> ConstraintSystem<E> for VerifierInput<'a, E> {
|
||||||
|
fn alloc<F: FnOnce() -> Result<E::Fr, Error>>(&mut self, f: F) -> Result<Variable, Error> {
|
||||||
|
// Run the function for calculating the allocation but ignore the output,
|
||||||
|
// since we don't care about the assignment of auxillary variables during
|
||||||
|
// verification.
|
||||||
|
let _ = f();
|
||||||
|
|
||||||
|
let index = self.num_aux;
|
||||||
|
self.num_aux += 1;
|
||||||
|
|
||||||
|
Ok(Variable(Index::Aux(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce(
|
||||||
|
&mut self,
|
||||||
|
_: LinearCombination<E>,
|
||||||
|
_: LinearCombination<E>,
|
||||||
|
_: LinearCombination<E>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Do nothing; we don't care about the constraint system
|
||||||
|
// in this context.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is intended to be a wrapper around VerifierInput that is kept
|
||||||
|
/// private and used for input allocation.
|
||||||
|
struct InputAllocator<T>(T);
|
||||||
|
|
||||||
|
impl<'a, 'b, E: Engine> ConstraintSystem<E> for InputAllocator<&'a mut VerifierInput<'b, E>> {
|
||||||
|
fn alloc<F: FnOnce() -> Result<E::Fr, Error>>(&mut self, value: F) -> Result<Variable, Error> {
|
||||||
|
self.0.alloc(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce(
|
||||||
|
&mut self,
|
||||||
|
_: LinearCombination<E>,
|
||||||
|
_: LinearCombination<E>,
|
||||||
|
_: LinearCombination<E>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Do nothing; we don't care about the constraint system
|
||||||
|
// in this context.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, E: Engine> PublicConstraintSystem<E> for InputAllocator<&'a mut VerifierInput<'b, E>> {
|
||||||
|
fn alloc_input<F: FnOnce() -> Result<E::Fr, Error>>(&mut self, value: F) -> Result<Variable, Error> {
|
||||||
|
if self.0.ic.len() == 0 {
|
||||||
|
self.0.insufficient_inputs = true;
|
||||||
|
} else {
|
||||||
|
self.0.acc.add_assign(&self.0.ic[0].mul(value()?));
|
||||||
|
self.0.ic = &self.0.ic[1..];
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = self.0.num_inputs;
|
||||||
|
self.0.num_inputs += 1;
|
||||||
|
|
||||||
|
Ok(Variable(Index::Input(index)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_proof<'a, E, C, F>(
|
||||||
|
pvk: &'a PreparedVerifyingKey<E>,
|
||||||
|
proof: &Proof<E>,
|
||||||
|
circuit: F
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where E: Engine, C: Input<E>, F: FnOnce(&mut VerifierInput<'a, E>) -> Result<C, Error>
|
||||||
|
{
|
||||||
|
let mut witness = VerifierInput::<E> {
|
||||||
|
acc: pvk.ic[0].into_projective(),
|
||||||
|
ic: &pvk.ic[1..],
|
||||||
|
insufficient_inputs: false,
|
||||||
|
num_inputs: 1,
|
||||||
|
num_aux: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
circuit(&mut witness)?.synthesize(&mut InputAllocator(&mut witness))?;
|
||||||
|
|
||||||
|
if witness.ic.len() != 0 || witness.insufficient_inputs {
|
||||||
|
return Err(Error::MalformedVerifyingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The original verification equation is:
|
||||||
|
// A * B = alpha * beta + inputs * gamma + C * delta
|
||||||
|
// ... however, we rearrange it so that it is:
|
||||||
|
// A * B - inputs * gamma - C * delta = alpha * beta
|
||||||
|
// or equivalently:
|
||||||
|
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
|
||||||
|
// which allows us to do a single final exponentiation.
|
||||||
|
|
||||||
|
Ok(E::final_exponentiation(
|
||||||
|
&E::miller_loop([
|
||||||
|
(&proof.a.prepare(), &proof.b.prepare()),
|
||||||
|
(&witness.acc.into_affine().prepare(), &pvk.neg_gamma_g2),
|
||||||
|
(&proof.c.prepare(), &pvk.neg_delta_g2)
|
||||||
|
].into_iter())
|
||||||
|
).unwrap() == pvk.alpha_g1_beta_g2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare_verifying_key<E: Engine>(
|
||||||
|
vk: &VerifyingKey<E>
|
||||||
|
) -> PreparedVerifyingKey<E>
|
||||||
|
{
|
||||||
|
let mut gamma = vk.gamma_g2;
|
||||||
|
gamma.negate();
|
||||||
|
let mut delta = vk.delta_g2;
|
||||||
|
delta.negate();
|
||||||
|
|
||||||
|
PreparedVerifyingKey {
|
||||||
|
alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2),
|
||||||
|
neg_gamma_g2: gamma.prepare(),
|
||||||
|
neg_delta_g2: delta.prepare(),
|
||||||
|
ic: vk.ic.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
208
src/lib.rs
Normal file
208
src/lib.rs
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
extern crate pairing;
|
||||||
|
extern crate rand;
|
||||||
|
extern crate bit_vec;
|
||||||
|
extern crate futures;
|
||||||
|
extern crate futures_cpupool;
|
||||||
|
extern crate num_cpus;
|
||||||
|
extern crate crossbeam;
|
||||||
|
|
||||||
|
use pairing::{Engine, Field};
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
pub mod multicore;
|
||||||
|
pub mod domain;
|
||||||
|
pub mod groth16;
|
||||||
|
|
||||||
|
pub mod multiexp;
|
||||||
|
// TODO: remove this from public API?
|
||||||
|
pub use self::multiexp::{DensityTracker, FullDensity, multiexp};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
PolynomialDegreeTooLarge,
|
||||||
|
MalformedVerifyingKey,
|
||||||
|
AssignmentMissing,
|
||||||
|
UnexpectedIdentity,
|
||||||
|
UnconstrainedVariable(Variable),
|
||||||
|
IoError(io::Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(e: io::Error) -> Error {
|
||||||
|
Error::IoError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Variable(Index);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
|
enum Index {
|
||||||
|
Input(usize),
|
||||||
|
Aux(usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LinearCombination<E: Engine>(Vec<(Index, E::Fr)>);
|
||||||
|
|
||||||
|
impl<E: Engine> Clone for LinearCombination<E> {
|
||||||
|
fn clone(&self) -> LinearCombination<E> {
|
||||||
|
LinearCombination(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> LinearCombination<E> {
|
||||||
|
pub fn zero() -> LinearCombination<E> {
|
||||||
|
LinearCombination(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval(
|
||||||
|
self,
|
||||||
|
mut input_density: Option<&mut DensityTracker>,
|
||||||
|
mut aux_density: Option<&mut DensityTracker>,
|
||||||
|
input_assignment: &[E::Fr],
|
||||||
|
aux_assignment: &[E::Fr]
|
||||||
|
) -> E::Fr
|
||||||
|
{
|
||||||
|
let mut acc = E::Fr::zero();
|
||||||
|
|
||||||
|
for (index, coeff) in self.0.into_iter() {
|
||||||
|
let mut tmp;
|
||||||
|
|
||||||
|
match index {
|
||||||
|
Index::Input(i) => {
|
||||||
|
tmp = input_assignment[i];
|
||||||
|
if let Some(ref mut v) = input_density {
|
||||||
|
v.inc(i);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Index::Aux(i) => {
|
||||||
|
tmp = aux_assignment[i];
|
||||||
|
if let Some(ref mut v) = aux_density {
|
||||||
|
v.inc(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if coeff == E::Fr::one() {
|
||||||
|
acc.add_assign(&tmp);
|
||||||
|
} else {
|
||||||
|
tmp.mul_assign(&coeff);
|
||||||
|
acc.add_assign(&tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Add<Variable> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn add(self, other: Variable) -> LinearCombination<E> {
|
||||||
|
self + (E::Fr::one(), other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Sub<Variable> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn sub(self, other: Variable) -> LinearCombination<E> {
|
||||||
|
self - (E::Fr::one(), other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Add<(E::Fr, Variable)> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||||
|
let mut must_insert = true;
|
||||||
|
|
||||||
|
for &mut (ref index, ref mut fr) in &mut self.0 {
|
||||||
|
if *index == var.0 {
|
||||||
|
fr.add_assign(&coeff);
|
||||||
|
must_insert = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if must_insert {
|
||||||
|
self.0.push((var.0, coeff));
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||||
|
coeff.negate();
|
||||||
|
|
||||||
|
self + (coeff, var)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: Engine> Add<&'a LinearCombination<E>> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn add(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
|
||||||
|
for &(k, v) in other.0.iter() {
|
||||||
|
self = self + (v, Variable(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: Engine> Sub<&'a LinearCombination<E>> for LinearCombination<E> {
|
||||||
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
fn sub(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
|
||||||
|
for &(k, v) in other.0.iter() {
|
||||||
|
self = self - (v, Variable(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Circuit<E: Engine> {
|
||||||
|
type InputMap: Input<E>;
|
||||||
|
|
||||||
|
/// Synthesize the circuit into a rank-1 quadratic constraint system
|
||||||
|
#[must_use]
|
||||||
|
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<Self::InputMap, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Input<E: Engine> {
|
||||||
|
/// Synthesize the circuit, except with additional access to public input
|
||||||
|
/// variables
|
||||||
|
fn synthesize<CS: PublicConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PublicConstraintSystem<E: Engine>: ConstraintSystem<E> {
|
||||||
|
/// Allocate a public input that the verifier knows. The provided function is used to
|
||||||
|
/// determine the assignment of the variable.
|
||||||
|
fn alloc_input<F: FnOnce() -> Result<E::Fr, Error>>(&mut self, f: F) -> Result<Variable, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ConstraintSystem<E: Engine> {
|
||||||
|
/// Return the "one" input variable
|
||||||
|
fn one() -> Variable {
|
||||||
|
Variable(Index::Input(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a private variable in the constraint system. The provided function is used to
|
||||||
|
/// determine the assignment of the variable.
|
||||||
|
fn alloc<F: FnOnce() -> Result<E::Fr, Error>>(&mut self, f: F) -> Result<Variable, Error>;
|
||||||
|
|
||||||
|
/// Enforce that `A` * `B` = `C`.
|
||||||
|
fn enforce(
|
||||||
|
&mut self,
|
||||||
|
a: LinearCombination<E>,
|
||||||
|
b: LinearCombination<E>,
|
||||||
|
c: LinearCombination<E>
|
||||||
|
);
|
||||||
|
}
|
||||||
53
src/multicore.rs
Normal file
53
src/multicore.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use crossbeam::{self, Scope, ScopedJoinHandle};
|
||||||
|
use num_cpus;
|
||||||
|
|
||||||
|
pub enum MaybeJoinHandle<T> {
|
||||||
|
MultiThreaded(ScopedJoinHandle<T>),
|
||||||
|
SingleThreaded(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MaybeJoinHandle<T> {
|
||||||
|
pub fn join(self) -> T {
|
||||||
|
match self {
|
||||||
|
MaybeJoinHandle::MultiThreaded(scope) => scope.join(),
|
||||||
|
MaybeJoinHandle::SingleThreaded(t) => t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum MaybeScope<'a, 'b: 'a> {
|
||||||
|
MultiThreaded(&'a Scope<'b>),
|
||||||
|
SingleThreaded
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> MaybeScope<'a, 'b> {
|
||||||
|
pub fn spawn<F, T>(&self, f: F) -> MaybeJoinHandle<T>
|
||||||
|
where F: FnOnce() -> T + Send + 'b, T: Send + 'b
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
&MaybeScope::MultiThreaded(scope) => MaybeJoinHandle::MultiThreaded(scope.spawn(f)),
|
||||||
|
&MaybeScope::SingleThreaded => MaybeJoinHandle::SingleThreaded(f())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope<'a, F, R>(
|
||||||
|
elements: usize,
|
||||||
|
f: F
|
||||||
|
) -> R where F: for<'b> FnOnce(MaybeScope<'b, 'a>, usize) -> R
|
||||||
|
{
|
||||||
|
let num_cpus = num_cpus::get();
|
||||||
|
|
||||||
|
if elements <= num_cpus {
|
||||||
|
if elements == 0 {
|
||||||
|
f(MaybeScope::SingleThreaded, 1)
|
||||||
|
} else {
|
||||||
|
f(MaybeScope::SingleThreaded, elements)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
crossbeam::scope(|scope| {
|
||||||
|
f(MaybeScope::MultiThreaded(scope), elements / num_cpus)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
293
src/multiexp.rs
Normal file
293
src/multiexp.rs
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
use pairing::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::io;
|
||||||
|
use bit_vec::{self, BitVec};
|
||||||
|
use std::iter;
|
||||||
|
use futures::{BoxFuture, Future};
|
||||||
|
use futures_cpupool::CpuPool;
|
||||||
|
|
||||||
|
use super::Error;
|
||||||
|
|
||||||
|
/// An object that builds a source of bases.
|
||||||
|
pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
|
||||||
|
type Source: Source<G>;
|
||||||
|
|
||||||
|
fn new(self) -> Self::Source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A source of bases, like an iterator.
|
||||||
|
pub trait Source<G: CurveAffine> {
|
||||||
|
/// Parses the element from the source. Fails if the point is at infinity.
|
||||||
|
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Skips `amt` elements from the source, avoiding deserialization.
|
||||||
|
fn skip(&mut self, amt: usize) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
||||||
|
type Source = (Arc<Vec<G>>, usize);
|
||||||
|
|
||||||
|
fn new(self) -> (Arc<Vec<G>>, usize) {
|
||||||
|
(self.0.clone(), self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
||||||
|
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), Error> {
|
||||||
|
if self.0.len() <= self.1 {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.0[self.1].is_zero() {
|
||||||
|
return Err(Error::UnexpectedIdentity)
|
||||||
|
}
|
||||||
|
|
||||||
|
to.add_assign_mixed(&self.0[self.1]);
|
||||||
|
|
||||||
|
self.1 += 1;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip(&mut self, amt: usize) -> Result<(), Error> {
|
||||||
|
if self.0.len() <= self.1 {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.1 += amt;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait QueryDensity {
|
||||||
|
/// Returns whether the base exists.
|
||||||
|
type Iter: Iterator<Item=bool>;
|
||||||
|
|
||||||
|
fn iter(self) -> Self::Iter;
|
||||||
|
fn get_query_size(self) -> Option<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FullDensity;
|
||||||
|
|
||||||
|
impl AsRef<FullDensity> for FullDensity {
|
||||||
|
fn as_ref(&self) -> &FullDensity {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> QueryDensity for &'a FullDensity {
|
||||||
|
type Iter = iter::Repeat<bool>;
|
||||||
|
|
||||||
|
fn iter(self) -> Self::Iter {
|
||||||
|
iter::repeat(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_query_size(self) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DensityTracker {
|
||||||
|
bv: BitVec,
|
||||||
|
total_density: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> QueryDensity for &'a DensityTracker {
|
||||||
|
type Iter = bit_vec::Iter<'a>;
|
||||||
|
|
||||||
|
fn iter(self) -> Self::Iter {
|
||||||
|
self.bv.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_query_size(self) -> Option<usize> {
|
||||||
|
Some(self.bv.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DensityTracker {
|
||||||
|
pub fn new() -> DensityTracker {
|
||||||
|
DensityTracker {
|
||||||
|
bv: BitVec::new(),
|
||||||
|
total_density: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_element(&mut self) {
|
||||||
|
self.bv.push(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inc(&mut self, idx: usize) {
|
||||||
|
if !self.bv.get(idx).unwrap() {
|
||||||
|
self.bv.set(idx, true);
|
||||||
|
self.total_density += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_total_density(&self) -> usize {
|
||||||
|
self.total_density
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiexp_inner<Q, D, G, S>(
|
||||||
|
pool: &CpuPool,
|
||||||
|
bases: S,
|
||||||
|
density_map: D,
|
||||||
|
exponents: Arc<Vec<<<G::Engine as Engine>::Fr as PrimeField>::Repr>>,
|
||||||
|
mut skip: u32,
|
||||||
|
c: u32,
|
||||||
|
handle_trivial: bool
|
||||||
|
) -> BoxFuture<<G as CurveAffine>::Projective, Error>
|
||||||
|
where for<'a> &'a Q: QueryDensity,
|
||||||
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
|
G: CurveAffine,
|
||||||
|
S: SourceBuilder<G>
|
||||||
|
{
|
||||||
|
// Perform this region of the multiexp
|
||||||
|
let this = {
|
||||||
|
let bases = bases.clone();
|
||||||
|
let exponents = exponents.clone();
|
||||||
|
let density_map = density_map.clone();
|
||||||
|
|
||||||
|
pool.spawn_fn(move || {
|
||||||
|
// Accumulate the result
|
||||||
|
let mut acc = G::Projective::zero();
|
||||||
|
|
||||||
|
// Build a source for the bases
|
||||||
|
let mut bases = bases.new();
|
||||||
|
|
||||||
|
// Create space for the buckets
|
||||||
|
let mut buckets = vec![<G as CurveAffine>::Projective::zero(); (1 << c) - 1];
|
||||||
|
|
||||||
|
let zero = <G::Engine as Engine>::Fr::zero().into_repr();
|
||||||
|
let one = <G::Engine as Engine>::Fr::one().into_repr();
|
||||||
|
|
||||||
|
// Sort the bases into buckets
|
||||||
|
for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) {
|
||||||
|
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 {
|
||||||
|
let mut exp = exp;
|
||||||
|
exp.divn(skip);
|
||||||
|
let exp = exp.as_ref()[0] % (1 << c);
|
||||||
|
|
||||||
|
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 Engine>::Fr::num_bits() {
|
||||||
|
// There isn't another region.
|
||||||
|
this.boxed()
|
||||||
|
} else {
|
||||||
|
// There's another region more significant. Calculate and join it with
|
||||||
|
// this region recursively.
|
||||||
|
this.join(multiexp_inner(pool, bases, density_map, exponents, skip, c, false))
|
||||||
|
.map(move |(this, mut higher)| {
|
||||||
|
for _ in 0..c {
|
||||||
|
higher.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
higher.add_assign(&this);
|
||||||
|
|
||||||
|
higher
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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>(
|
||||||
|
pool: &CpuPool,
|
||||||
|
bases: S,
|
||||||
|
density_map: D,
|
||||||
|
exponents: Arc<Vec<<<G::Engine as Engine>::Fr as PrimeField>::Repr>>,
|
||||||
|
// TODO
|
||||||
|
// c: u32
|
||||||
|
) -> BoxFuture<<G as CurveAffine>::Projective, Error>
|
||||||
|
where for<'a> &'a Q: QueryDensity,
|
||||||
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
|
G: CurveAffine,
|
||||||
|
S: SourceBuilder<G>
|
||||||
|
{
|
||||||
|
if let Some(query_size) = density_map.as_ref().get_query_size() {
|
||||||
|
// If the density map has a known query size, it should not be
|
||||||
|
// inconsistent with the number of exponents.
|
||||||
|
|
||||||
|
assert!(query_size == exponents.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
multiexp_inner(pool, bases, density_map, exponents, 0, 12, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_with_bls12() {
|
||||||
|
fn naive_multiexp<G: CurveAffine>(
|
||||||
|
bases: Arc<Vec<G>>,
|
||||||
|
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>
|
||||||
|
) -> G::Projective
|
||||||
|
{
|
||||||
|
assert_eq!(bases.len(), exponents.len());
|
||||||
|
|
||||||
|
let mut acc = G::Projective::zero();
|
||||||
|
|
||||||
|
for (base, exp) in bases.iter().zip(exponents.iter()) {
|
||||||
|
acc.add_assign(&base.mul(*exp));
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
use rand::{self, Rand};
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
|
||||||
|
const SAMPLES: usize = 1 << 17;
|
||||||
|
|
||||||
|
let rng = &mut rand::thread_rng();
|
||||||
|
let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>());
|
||||||
|
let g = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::G1::rand(rng).into_affine()).collect::<Vec<_>>());
|
||||||
|
|
||||||
|
let naive = naive_multiexp(g.clone(), v.clone());
|
||||||
|
|
||||||
|
let pool = CpuPool::new_num_cpus();
|
||||||
|
|
||||||
|
let fast = multiexp(
|
||||||
|
&pool,
|
||||||
|
(g, 0),
|
||||||
|
FullDensity,
|
||||||
|
v,
|
||||||
|
// TODO
|
||||||
|
//7
|
||||||
|
).wait().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(naive, fast);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user