mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-02-11 17:55:46 +00:00
Reimplementation of groth16 using pairing
library.
This commit is contained in:
parent
bf03be0b9d
commit
3148662234
20
Cargo.toml
20
Cargo.toml
@ -9,11 +9,17 @@ repository = "https://github.com/ebfull/bellman"
|
||||
version = "0.0.3"
|
||||
|
||||
[dependencies]
|
||||
#rand = "0.3.*"
|
||||
#byteorder = "1.*"
|
||||
#serde = "1.0"
|
||||
#crossbeam = "0.2"
|
||||
#num_cpus = "1.0"
|
||||
rand = "0.3"
|
||||
bit-vec = "0.4.4"
|
||||
futures = "0.1"
|
||||
futures-cpupool = "0.1"
|
||||
num_cpus = "1.6"
|
||||
crossbeam = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
#bincode = "0.8.0"
|
||||
[dependencies.pairing]
|
||||
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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user