mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-02-12 01:55:48 +00:00
Implementation of Jens Groth's generic group zk-SNARK proving system.
This commit is contained in:
parent
9a3743c7c4
commit
c9fbf490dc
192
src/groth/domain.rs
Normal file
192
src/groth/domain.rs
Normal file
@ -0,0 +1,192 @@
|
||||
use curves::{Engine, Field, SnarkField, PrimeField};
|
||||
|
||||
pub struct EvaluationDomain<E: Engine> {
|
||||
pub m: u64,
|
||||
exp: u64,
|
||||
omega: E::Fr,
|
||||
omegainv: E::Fr,
|
||||
geninv: E::Fr,
|
||||
minv: E::Fr
|
||||
}
|
||||
|
||||
impl<E: Engine> EvaluationDomain<E> {
|
||||
pub fn new(e: &E, needed: u64) -> Self {
|
||||
if needed > 268435456 {
|
||||
panic!("circuit depths larger than 2^28 are not supported");
|
||||
}
|
||||
|
||||
let mut m = 1;
|
||||
let mut exp = 0;
|
||||
while m < needed {
|
||||
m *= 2;
|
||||
exp += 1;
|
||||
|
||||
assert!(exp < E::Fr::s(e));
|
||||
}
|
||||
|
||||
let mut omega = E::Fr::root_of_unity(e);
|
||||
for _ in exp..E::Fr::s(e) {
|
||||
omega.square(e);
|
||||
}
|
||||
|
||||
EvaluationDomain {
|
||||
m: m,
|
||||
exp: exp,
|
||||
omega: omega,
|
||||
omegainv: omega.inverse(e).unwrap(),
|
||||
geninv: E::Fr::multiplicative_generator(e).inverse(e).unwrap(),
|
||||
minv: E::Fr::from_u64(e, m).inverse(e).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn z(&self, e: &E, tau: &E::Fr) -> E::Fr {
|
||||
let mut tmp = tau.pow(e, &[self.m]);
|
||||
tmp.sub_assign(e, &E::Fr::one(e));
|
||||
|
||||
tmp
|
||||
}
|
||||
|
||||
pub fn ifft(&self, e: &E, v: &mut [E::Fr])
|
||||
{
|
||||
assert!(v.len() == self.m as usize);
|
||||
self._fft(e, v, &self.omegainv);
|
||||
for v in v {
|
||||
v.mul_assign(e, &self.minv);
|
||||
}
|
||||
}
|
||||
|
||||
fn mul_coset(&self, e: &E, v: &mut [E::Fr], g: &E::Fr)
|
||||
{
|
||||
let mut u = *g;
|
||||
for v in v.iter_mut().skip(1) {
|
||||
v.mul_assign(e, &u);
|
||||
u.mul_assign(e, g);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn coset_fft(&self, e: &E, v: &mut [E::Fr])
|
||||
{
|
||||
self.mul_coset(e, v, &E::Fr::multiplicative_generator(e));
|
||||
self.fft(e, v);
|
||||
}
|
||||
|
||||
pub fn icoset_fft(&self, e: &E, v: &mut [E::Fr])
|
||||
{
|
||||
self.ifft(e, v);
|
||||
self.mul_coset(e, v, &self.geninv);
|
||||
}
|
||||
|
||||
pub fn divide_by_z_on_coset(&self, e: &E, v: &mut [E::Fr])
|
||||
{
|
||||
let i = self.z(e, &E::Fr::multiplicative_generator(e)).inverse(e).unwrap();
|
||||
for v in v {
|
||||
v.mul_assign(e, &i);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fft(&self, e: &E, a: &mut [E::Fr])
|
||||
{
|
||||
self._fft(e, a, &self.omega);
|
||||
}
|
||||
|
||||
fn _fft(&self, e: &E, a: &mut [E::Fr], omega: &E::Fr)
|
||||
{
|
||||
fn bitreverse(mut n: usize, l: u64) -> usize {
|
||||
let mut r = 0;
|
||||
for _ in 0..l {
|
||||
r = (r << 1) | (n & 1);
|
||||
n >>= 1;
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
for k in 0..(self.m as usize) {
|
||||
let rk = bitreverse(k, self.exp);
|
||||
if k < rk {
|
||||
let tmp1 = a[rk];
|
||||
let tmp2 = a[k];
|
||||
a[rk] = tmp2;
|
||||
a[k] = tmp1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut m = 1;
|
||||
for _ in 0..self.exp {
|
||||
let w_m = omega.pow(e, &[(self.m / (2*m)) as u64]);
|
||||
|
||||
let mut k = 0;
|
||||
while k < self.m {
|
||||
let mut w = E::Fr::one(e);
|
||||
for j in 0..m {
|
||||
let mut t = w;
|
||||
t.mul_assign(e, &a[(k+j+m) as usize]);
|
||||
let mut tmp = a[(k+j) as usize];
|
||||
tmp.sub_assign(e, &t);
|
||||
a[(k+j+m) as usize] = tmp;
|
||||
a[(k+j) as usize].add_assign(e, &t);
|
||||
w.mul_assign(e, &w_m);
|
||||
}
|
||||
|
||||
k += 2*m;
|
||||
}
|
||||
|
||||
m *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test multiplying various (low degree) polynomials together and
|
||||
// comparing with naive evaluations.
|
||||
#[test]
|
||||
fn polynomial_arith() {
|
||||
use curves::*;
|
||||
use curves::bls381::Bls381;
|
||||
use rand;
|
||||
|
||||
fn test_mul<E: Engine, R: rand::Rng>(e: &E, rng: &mut R)
|
||||
{
|
||||
for coeffs_a in 1..70 {
|
||||
for coeffs_b in 1..70 {
|
||||
let final_degree = coeffs_a + coeffs_b - 1;
|
||||
|
||||
let domain = EvaluationDomain::new(e, final_degree as u64);
|
||||
let mut a: Vec<_> = (0..coeffs_a).map(|_| E::Fr::random(e, rng)).collect();
|
||||
let mut b: Vec<_> = (0..coeffs_b).map(|_| E::Fr::random(e, rng)).collect();
|
||||
|
||||
// naive evaluation
|
||||
let mut naive = vec![E::Fr::zero(); domain.m as usize];
|
||||
for (i1, a) in a.iter().enumerate() {
|
||||
for (i2, b) in b.iter().enumerate() {
|
||||
let mut prod = *a;
|
||||
prod.mul_assign(e, b);
|
||||
naive[i1 + i2].add_assign(e, &prod);
|
||||
}
|
||||
}
|
||||
|
||||
a.resize(domain.m as usize, E::Fr::zero());
|
||||
b.resize(domain.m as usize, E::Fr::zero());
|
||||
let mut c = vec![];
|
||||
c.resize(domain.m as usize, E::Fr::zero());
|
||||
|
||||
domain.fft(e, &mut a);
|
||||
domain.fft(e, &mut b);
|
||||
|
||||
for ((a, b), c) in a.iter().zip(b.iter()).zip(c.iter_mut()) {
|
||||
*c = *a;
|
||||
c.mul_assign(e, b);
|
||||
}
|
||||
|
||||
domain.ifft(e, &mut c);
|
||||
|
||||
for (naive, fft) in naive.iter().zip(c.iter()) {
|
||||
assert_eq!(naive, fft);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let e = &Bls381::new();
|
||||
let rng = &mut rand::thread_rng();
|
||||
|
||||
test_mul(e, rng);
|
||||
}
|
515
src/groth/mod.rs
Normal file
515
src/groth/mod.rs
Normal file
@ -0,0 +1,515 @@
|
||||
use curves::*;
|
||||
use super::*;
|
||||
|
||||
mod domain;
|
||||
|
||||
pub struct ProvingKey<E: Engine> {
|
||||
a_inputs: Vec<<E::G1 as Group<E>>::Affine>,
|
||||
b1_inputs: Vec<<E::G1 as Group<E>>::Affine>,
|
||||
b2_inputs: Vec<<E::G2 as Group<E>>::Affine>,
|
||||
a_aux: Vec<<E::G1 as Group<E>>::Affine>,
|
||||
b1_aux: Vec<<E::G1 as Group<E>>::Affine>,
|
||||
b2_aux: Vec<<E::G2 as Group<E>>::Affine>,
|
||||
h: Vec<<E::G1 as Group<E>>::Affine>,
|
||||
l: Vec<<E::G1 as Group<E>>::Affine>,
|
||||
alpha_g1: <E::G1 as Group<E>>::Affine,
|
||||
beta_g1: <E::G1 as Group<E>>::Affine,
|
||||
beta_g2: <E::G2 as Group<E>>::Affine,
|
||||
delta_g1: <E::G1 as Group<E>>::Affine,
|
||||
delta_g2: <E::G2 as Group<E>>::Affine
|
||||
}
|
||||
|
||||
pub struct VerifyingKey<E: Engine> {
|
||||
alpha_g1: <E::G1 as Group<E>>::Affine,
|
||||
beta_g2: <E::G2 as Group<E>>::Affine,
|
||||
gamma_g2: <E::G2 as Group<E>>::Affine,
|
||||
delta_g2: <E::G2 as Group<E>>::Affine,
|
||||
ic: Vec<<E::G1 as Group<E>>::Affine>
|
||||
}
|
||||
|
||||
pub struct PreparedVerifyingKey<E: Engine> {
|
||||
alpha_g1_beta_g2: E::Fqk,
|
||||
neg_gamma_g2: <E::G2 as Group<E>>::Prepared,
|
||||
neg_delta_g2: <E::G2 as Group<E>>::Prepared,
|
||||
ic: Vec<<E::G1 as Group<E>>::Affine>
|
||||
}
|
||||
|
||||
pub struct Proof<E: Engine> {
|
||||
a: E::G1,
|
||||
b: E::G2,
|
||||
c: E::G1
|
||||
}
|
||||
|
||||
pub fn keypair<E: Engine, C: Circuit<E>>(
|
||||
e: &E,
|
||||
circuit: C,
|
||||
tau: &E::Fr,
|
||||
alpha: &E::Fr,
|
||||
beta: &E::Fr,
|
||||
gamma: &E::Fr,
|
||||
delta: &E::Fr
|
||||
) -> (ProvingKey<E>, VerifyingKey<E>)
|
||||
{
|
||||
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(&mut self, _: E::Fr) -> Variable {
|
||||
let index = self.num_inputs;
|
||||
self.num_inputs += 1;
|
||||
|
||||
self.at_inputs.push(vec![]);
|
||||
self.bt_inputs.push(vec![]);
|
||||
self.ct_inputs.push(vec![]);
|
||||
|
||||
Variable(Index::Input(index))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
|
||||
fn alloc(&mut self, _: E::Fr) -> Variable {
|
||||
let index = self.num_aux;
|
||||
self.num_aux += 1;
|
||||
|
||||
self.at_aux.push(vec![]);
|
||||
self.bt_aux.push(vec![]);
|
||||
self.ct_aux.push(vec![]);
|
||||
|
||||
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![]
|
||||
};
|
||||
|
||||
assembly.alloc_input(E::Fr::one(e));
|
||||
|
||||
circuit.synthesize(e, &mut assembly).synthesize(e, &mut assembly);
|
||||
|
||||
// Input consistency constraints: x * 0 = 0
|
||||
for i in 0..assembly.num_inputs {
|
||||
assembly.enforce(LinearCombination::zero(e).add(E::Fr::one(e), Variable(Index::Input(i))),
|
||||
LinearCombination::zero(e),
|
||||
LinearCombination::zero(e));
|
||||
}
|
||||
|
||||
let domain = domain::EvaluationDomain::new(e, assembly.num_constraints as u64);
|
||||
|
||||
let mut u = Vec::with_capacity(domain.m as usize);
|
||||
{
|
||||
let mut acc = E::Fr::one(e);
|
||||
for _ in 0..domain.m {
|
||||
u.push(acc);
|
||||
acc.mul_assign(e, tau);
|
||||
}
|
||||
}
|
||||
|
||||
let gamma_inverse = gamma.inverse(e).unwrap();
|
||||
let delta_inverse = delta.inverse(e).unwrap();
|
||||
|
||||
let g1_table;
|
||||
let h;
|
||||
{
|
||||
let mut powers_of_tau = u.clone();
|
||||
powers_of_tau.truncate((domain.m - 1) as usize);
|
||||
|
||||
let mut coeff = delta_inverse;
|
||||
coeff.mul_assign(e, &domain.z(e, tau));
|
||||
for h in &mut powers_of_tau {
|
||||
h.mul_assign(e, &coeff);
|
||||
}
|
||||
|
||||
g1_table = E::G1::one(e).optimal_window_batch(e,
|
||||
(domain.m - 1) as usize + (assembly.num_inputs + assembly.num_aux) * 3
|
||||
);
|
||||
|
||||
h = e.batch_baseexp(&g1_table, powers_of_tau);
|
||||
}
|
||||
|
||||
domain.ifft(e, &mut u);
|
||||
|
||||
fn eval<E: Engine>(
|
||||
e: &E,
|
||||
u: &[E::Fr],
|
||||
alpha: &E::Fr,
|
||||
beta: &E::Fr,
|
||||
inv: &E::Fr,
|
||||
at_in: Vec<Vec<(E::Fr, usize)>>,
|
||||
bt_in: Vec<Vec<(E::Fr, usize)>>,
|
||||
ct_in: Vec<Vec<(E::Fr, usize)>>
|
||||
) -> (Vec<E::Fr>, Vec<E::Fr>, Vec<E::Fr>)
|
||||
{
|
||||
assert_eq!(at_in.len(), bt_in.len());
|
||||
assert_eq!(bt_in.len(), ct_in.len());
|
||||
|
||||
fn eval_at_tau<E: Engine>(
|
||||
e: &E,
|
||||
val: Vec<(E::Fr, usize)>,
|
||||
u: &[E::Fr]
|
||||
) -> E::Fr
|
||||
{
|
||||
let mut acc = E::Fr::zero();
|
||||
|
||||
for (coeff, index) in val {
|
||||
let mut n = u[index];
|
||||
n.mul_assign(e, &coeff);
|
||||
acc.add_assign(e, &n);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
let mut a_out = Vec::with_capacity(at_in.len());
|
||||
let mut b_out = Vec::with_capacity(at_in.len());
|
||||
let mut l_out = Vec::with_capacity(at_in.len());
|
||||
|
||||
for ((a, b), c) in at_in.into_iter().zip(bt_in.into_iter()).zip(ct_in.into_iter()) {
|
||||
let a = eval_at_tau(e, a, u);
|
||||
let b = eval_at_tau(e, b, u);
|
||||
|
||||
let mut t0 = a;
|
||||
t0.mul_assign(e, beta);
|
||||
|
||||
let mut t1 = b;
|
||||
t1.mul_assign(e, alpha);
|
||||
|
||||
t0.add_assign(e, &t1);
|
||||
t0.add_assign(e, &eval_at_tau(e, c, u));
|
||||
t0.mul_assign(e, inv);
|
||||
|
||||
a_out.push(a);
|
||||
b_out.push(b);
|
||||
l_out.push(t0);
|
||||
}
|
||||
|
||||
(a_out, b_out, l_out)
|
||||
}
|
||||
|
||||
let (a_inputs, b_inputs, ic_coeffs) = eval(e, &u, alpha, beta, &gamma_inverse, assembly.at_inputs, assembly.bt_inputs, assembly.ct_inputs);
|
||||
let a_inputs = e.batch_baseexp(&g1_table, a_inputs);
|
||||
let b1_inputs = e.batch_baseexp(&g1_table, &b_inputs);
|
||||
let ic_coeffs = e.batch_baseexp(&g1_table, ic_coeffs);
|
||||
let (a_aux, b_aux, l_coeffs) = eval(e, &u, alpha, beta, &delta_inverse, assembly.at_aux, assembly.bt_aux, assembly.ct_aux);
|
||||
let a_aux = e.batch_baseexp(&g1_table, a_aux);
|
||||
let b1_aux = e.batch_baseexp(&g1_table, &b_aux);
|
||||
let l_coeffs = e.batch_baseexp(&g1_table, l_coeffs);
|
||||
|
||||
drop(g1_table);
|
||||
|
||||
let g2_table = E::G2::one(e).optimal_window_batch(e,
|
||||
(assembly.num_inputs + assembly.num_aux)
|
||||
);
|
||||
|
||||
let b2_inputs = e.batch_baseexp(&g2_table, b_inputs);
|
||||
let b2_aux = e.batch_baseexp(&g2_table, b_aux);
|
||||
|
||||
let mut alpha_g1 = E::G1::one(e);
|
||||
alpha_g1.mul_assign(e, alpha);
|
||||
let mut beta_g1 = E::G1::one(e);
|
||||
beta_g1.mul_assign(e, beta);
|
||||
let mut beta_g2 = E::G2::one(e);
|
||||
beta_g2.mul_assign(e, beta);
|
||||
let mut gamma_g2 = E::G2::one(e);
|
||||
gamma_g2.mul_assign(e, gamma);
|
||||
let mut delta_g1 = E::G1::one(e);
|
||||
delta_g1.mul_assign(e, delta);
|
||||
let mut delta_g2 = E::G2::one(e);
|
||||
delta_g2.mul_assign(e, delta);
|
||||
|
||||
(
|
||||
ProvingKey {
|
||||
a_inputs: a_inputs,
|
||||
b1_inputs: b1_inputs,
|
||||
b2_inputs: b2_inputs,
|
||||
a_aux: a_aux,
|
||||
b1_aux: b1_aux,
|
||||
b2_aux: b2_aux,
|
||||
h: h,
|
||||
l: l_coeffs,
|
||||
delta_g1: delta_g1.to_affine(e),
|
||||
delta_g2: delta_g2.to_affine(e),
|
||||
alpha_g1: alpha_g1.to_affine(e),
|
||||
beta_g1: beta_g1.to_affine(e),
|
||||
beta_g2: beta_g2.to_affine(e)
|
||||
},
|
||||
VerifyingKey {
|
||||
alpha_g1: alpha_g1.to_affine(e),
|
||||
beta_g2: beta_g2.to_affine(e),
|
||||
gamma_g2: gamma_g2.to_affine(e),
|
||||
delta_g2: delta_g2.to_affine(e),
|
||||
ic: ic_coeffs
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn prepare_verifying_key<E: Engine>(
|
||||
e: &E,
|
||||
vk: &VerifyingKey<E>
|
||||
) -> PreparedVerifyingKey<E>
|
||||
{
|
||||
let mut gamma = vk.gamma_g2;
|
||||
gamma.negate(e);
|
||||
let mut delta = vk.delta_g2;
|
||||
delta.negate(e);
|
||||
|
||||
PreparedVerifyingKey {
|
||||
alpha_g1_beta_g2: e.pairing(&vk.alpha_g1, &vk.beta_g2),
|
||||
neg_gamma_g2: gamma.prepare(e),
|
||||
neg_delta_g2: delta.prepare(e),
|
||||
ic: vk.ic.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify<E: Engine, C: Witness<E>, F: FnOnce(&mut ConstraintSystem<E>) -> C>(
|
||||
e: &E,
|
||||
circuit: F,
|
||||
proof: &Proof<E>,
|
||||
pvk: &PreparedVerifyingKey<E>
|
||||
) -> bool
|
||||
{
|
||||
struct VerifierInput<'a, E: Engine + 'a> {
|
||||
e: &'a E,
|
||||
acc: E::G1,
|
||||
ic: &'a [<E::G1 as Group<E>>::Affine],
|
||||
insufficient_inputs: bool,
|
||||
num_inputs: usize,
|
||||
num_aux: usize
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> PublicConstraintSystem<E> for VerifierInput<'a, E> {
|
||||
fn alloc_input(&mut self, value: E::Fr) -> Variable {
|
||||
if self.ic.len() == 0 {
|
||||
self.insufficient_inputs = true;
|
||||
} else {
|
||||
self.acc.add_assign(self.e, &self.ic[0].mul(self.e, &value));
|
||||
self.ic = &self.ic[1..];
|
||||
}
|
||||
|
||||
let index = self.num_inputs;
|
||||
self.num_inputs += 1;
|
||||
|
||||
Variable(Index::Input(index))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> ConstraintSystem<E> for VerifierInput<'a, E> {
|
||||
fn alloc(&mut self, _: E::Fr) -> Variable {
|
||||
let index = self.num_aux;
|
||||
self.num_aux += 1;
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
let mut witness = VerifierInput {
|
||||
e: e,
|
||||
acc: pvk.ic[0].to_jacobian(e),
|
||||
ic: &pvk.ic[1..],
|
||||
insufficient_inputs: false,
|
||||
num_inputs: 1,
|
||||
num_aux: 0
|
||||
};
|
||||
|
||||
circuit(&mut witness).synthesize(e, &mut witness);
|
||||
|
||||
if witness.ic.len() != 0 || witness.insufficient_inputs {
|
||||
return false;
|
||||
}
|
||||
|
||||
e.final_exponentiation(
|
||||
&e.miller_loop([
|
||||
(&proof.a.prepare(e), &proof.b.prepare(e)),
|
||||
(&witness.acc.prepare(e), &pvk.neg_gamma_g2),
|
||||
(&proof.c.prepare(e), &pvk.neg_delta_g2)
|
||||
].into_iter())
|
||||
) == pvk.alpha_g1_beta_g2
|
||||
}
|
||||
|
||||
pub fn prove<E: Engine, C: Circuit<E>>(
|
||||
e: &E,
|
||||
circuit: C,
|
||||
r: &E::Fr,
|
||||
s: &E::Fr,
|
||||
pk: &ProvingKey<E>
|
||||
) -> Result<Proof<E>, ()>
|
||||
{
|
||||
struct ProvingAssignment<'a, E: Engine + 'a> {
|
||||
e: &'a E,
|
||||
// Evaluations of A, B, C polynomials
|
||||
a: Vec<E::Fr>,
|
||||
b: Vec<E::Fr>,
|
||||
c: Vec<E::Fr>,
|
||||
// Assignments of variables
|
||||
input_assignment: Vec<E::Fr>,
|
||||
aux_assignment: Vec<E::Fr>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> PublicConstraintSystem<E> for ProvingAssignment<'a, E> {
|
||||
fn alloc_input(&mut self, value: E::Fr) -> Variable {
|
||||
self.input_assignment.push(value);
|
||||
|
||||
Variable(Index::Input(self.input_assignment.len() - 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> ConstraintSystem<E> for ProvingAssignment<'a, E> {
|
||||
fn alloc(&mut self, value: E::Fr) -> Variable {
|
||||
self.aux_assignment.push(value);
|
||||
|
||||
Variable(Index::Aux(self.aux_assignment.len() - 1))
|
||||
}
|
||||
|
||||
fn enforce(
|
||||
&mut self,
|
||||
a: LinearCombination<E>,
|
||||
b: LinearCombination<E>,
|
||||
c: LinearCombination<E>
|
||||
)
|
||||
{
|
||||
self.a.push(a.evaluate(self.e, &self.input_assignment, &self.aux_assignment));
|
||||
self.b.push(b.evaluate(self.e, &self.input_assignment, &self.aux_assignment));
|
||||
self.c.push(c.evaluate(self.e, &self.input_assignment, &self.aux_assignment));
|
||||
}
|
||||
}
|
||||
|
||||
let mut prover = ProvingAssignment {
|
||||
e: e,
|
||||
a: vec![],
|
||||
b: vec![],
|
||||
c: vec![],
|
||||
input_assignment: vec![],
|
||||
aux_assignment: vec![]
|
||||
};
|
||||
|
||||
prover.alloc_input(E::Fr::one(e));
|
||||
|
||||
circuit.synthesize(e, &mut prover).synthesize(e, &mut prover);
|
||||
|
||||
// Input consistency constraints: x * 0 = 0
|
||||
for i in 0..prover.input_assignment.len() {
|
||||
prover.enforce(LinearCombination::zero(e).add(E::Fr::one(e), Variable(Index::Input(i))),
|
||||
LinearCombination::zero(e),
|
||||
LinearCombination::zero(e));
|
||||
}
|
||||
|
||||
// Perform FFTs
|
||||
let h = {
|
||||
let domain = domain::EvaluationDomain::new(e, prover.a.len() as u64);
|
||||
prover.a.resize(domain.m as usize, E::Fr::zero());
|
||||
prover.b.resize(domain.m as usize, E::Fr::zero());
|
||||
prover.c.resize(domain.m as usize, E::Fr::zero());
|
||||
domain.ifft(e, &mut prover.a);
|
||||
domain.coset_fft(e, &mut prover.a);
|
||||
domain.ifft(e, &mut prover.b);
|
||||
domain.coset_fft(e, &mut prover.b);
|
||||
domain.ifft(e, &mut prover.c);
|
||||
domain.coset_fft(e, &mut prover.c);
|
||||
|
||||
let mut h = prover.a;
|
||||
for (h, b) in h.iter_mut().zip(prover.b.into_iter()) {
|
||||
h.mul_assign(e, &b);
|
||||
}
|
||||
for (h, c) in h.iter_mut().zip(prover.c.into_iter()) {
|
||||
h.sub_assign(e, &c);
|
||||
}
|
||||
domain.divide_by_z_on_coset(e, &mut h);
|
||||
domain.icoset_fft(e, &mut h);
|
||||
|
||||
e.multiexp(&pk.h, &h[0..(domain.m-1) as usize])?
|
||||
};
|
||||
|
||||
// Construct proof
|
||||
let mut g_a = pk.delta_g1.mul(e, r);
|
||||
g_a.add_assign(e, &pk.alpha_g1.to_jacobian(e));
|
||||
let mut g_b = pk.delta_g2.mul(e, s);
|
||||
g_b.add_assign(e, &pk.beta_g2.to_jacobian(e));
|
||||
let mut g_c;
|
||||
{
|
||||
let mut rs = *r;
|
||||
rs.mul_assign(e, s);
|
||||
g_c = pk.delta_g1.mul(e, &rs);
|
||||
g_c.add_assign(e, &pk.alpha_g1.mul(e, s));
|
||||
g_c.add_assign(e, &pk.beta_g1.mul(e, r));
|
||||
}
|
||||
let mut a_answer: E::G1 = e.multiexp(&pk.a_inputs, &prover.input_assignment)?;
|
||||
a_answer.add_assign(e, &e.multiexp(&pk.a_aux, &prover.aux_assignment)?);
|
||||
g_a.add_assign(e, &a_answer);
|
||||
a_answer.mul_assign(e, s);
|
||||
g_c.add_assign(e, &a_answer);
|
||||
let mut b1_answer: E::G1 = e.multiexp(&pk.b1_inputs, &prover.input_assignment)?;
|
||||
b1_answer.add_assign(e, &e.multiexp(&pk.b1_aux, &prover.aux_assignment)?);
|
||||
let mut b2_answer: E::G2 = e.multiexp(&pk.b2_inputs, &prover.input_assignment)?;
|
||||
b2_answer.add_assign(e, &e.multiexp(&pk.b2_aux, &prover.aux_assignment)?);
|
||||
g_b.add_assign(e, &b2_answer);
|
||||
b1_answer.mul_assign(e, r);
|
||||
g_c.add_assign(e, &b1_answer);
|
||||
g_c.add_assign(e, &h);
|
||||
g_c.add_assign(e, &e.multiexp(&pk.l, &prover.aux_assignment)?);
|
||||
|
||||
Ok(Proof {
|
||||
a: g_a,
|
||||
b: g_b,
|
||||
c: g_c
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
207
src/groth/tests/mod.rs
Normal file
207
src/groth/tests/mod.rs
Normal file
@ -0,0 +1,207 @@
|
||||
use super::*;
|
||||
use rand::{Rng, thread_rng};
|
||||
|
||||
struct RootCircuit<E: Engine> {
|
||||
root: E::Fr
|
||||
}
|
||||
|
||||
impl<E: Engine> Circuit<E> for RootCircuit<E> {
|
||||
type WitnessMap = RootWitness<E>;
|
||||
|
||||
fn synthesize<CS: ConstraintSystem<E>>(self,
|
||||
e: &E,
|
||||
cs: &mut CS)
|
||||
-> Self::WitnessMap
|
||||
{
|
||||
let root_var = cs.alloc(self.root);
|
||||
|
||||
let mut cur = root_var;
|
||||
let mut cur_val = self.root;
|
||||
|
||||
for _ in 0..99 {
|
||||
cur_val.mul_assign(e, &self.root);
|
||||
let new = cs.alloc(cur_val);
|
||||
|
||||
cs.enforce(
|
||||
LinearCombination::zero(e) + (E::Fr::from_str(e, "3").unwrap(), cur),
|
||||
LinearCombination::zero(e) + (E::Fr::from_str(e, "4").unwrap(), root_var),
|
||||
LinearCombination::zero(e) + (E::Fr::from_str(e, "12").unwrap(), new),
|
||||
);
|
||||
|
||||
cur = new;
|
||||
}
|
||||
|
||||
RootWitness {
|
||||
num: cur_val,
|
||||
num_var: cur
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RootWitness<E: Engine> {
|
||||
num: E::Fr,
|
||||
num_var: Variable
|
||||
}
|
||||
|
||||
impl<E: Engine> Witness<E> for RootWitness<E> {
|
||||
fn synthesize<CS: PublicConstraintSystem<E>>(
|
||||
self,
|
||||
e: &E,
|
||||
cs: &mut CS
|
||||
)
|
||||
{
|
||||
let result_input = cs.alloc_input(self.num);
|
||||
cs.enforce(
|
||||
LinearCombination::zero(e) + result_input,
|
||||
LinearCombination::one(e),
|
||||
LinearCombination::zero(e) + self.num_var
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_snark_system<E: Engine, R: Rng>(
|
||||
e: &E,
|
||||
rng: &mut R
|
||||
)
|
||||
{
|
||||
let tau = E::Fr::random(e, rng);
|
||||
let alpha = E::Fr::random(e, rng);
|
||||
let beta = E::Fr::random(e, rng);
|
||||
let gamma = E::Fr::random(e, rng);
|
||||
let delta = E::Fr::random(e, rng);
|
||||
|
||||
// create keypair
|
||||
let (pk, vk) = {
|
||||
let c = RootCircuit {
|
||||
root: E::Fr::zero()
|
||||
};
|
||||
|
||||
keypair(e, c, &tau, &alpha, &beta, &gamma, &delta)
|
||||
};
|
||||
|
||||
// construct proof
|
||||
let proof = {
|
||||
let r = E::Fr::random(e, rng);
|
||||
let s = E::Fr::random(e, rng);
|
||||
|
||||
let c = RootCircuit {
|
||||
root: E::Fr::from_str(e, "2").unwrap()
|
||||
};
|
||||
|
||||
prove(e, c, &r, &s, &pk).unwrap()
|
||||
};
|
||||
|
||||
// prepare verifying key
|
||||
let pvk = prepare_verifying_key(e, &vk);
|
||||
|
||||
// verify proof
|
||||
assert!(verify(e, |cs| {
|
||||
RootWitness {
|
||||
num: E::Fr::from_str(e, "1267650600228229401496703205376").unwrap(),
|
||||
num_var: cs.alloc(E::Fr::one(e))
|
||||
}
|
||||
}, &proof, &pvk));
|
||||
|
||||
// verify invalid proof
|
||||
assert!(!verify(e, |cs| {
|
||||
RootWitness {
|
||||
num: E::Fr::from_str(e, "1267650600228229401496703205375").unwrap(),
|
||||
num_var: cs.alloc(E::Fr::one(e))
|
||||
}
|
||||
}, &proof, &pvk));
|
||||
|
||||
// simulate a groth proof with trapdoors
|
||||
// ----------------
|
||||
// 99: a1 * a0 = l*
|
||||
// 100: a0 * 0 = 0
|
||||
// 101: a1 * 0 = 0
|
||||
// ---
|
||||
// u_0(tau) = tau^100
|
||||
// u_1(tau) = tau^99 + tau^101
|
||||
// v_0(tau) = tau^99
|
||||
// v_1(tau) = 0
|
||||
// w_0(tau) = 0
|
||||
// w_1(tau) = 0
|
||||
// ---
|
||||
|
||||
let mut lagrange_coeffs: Vec<E::Fr> = (0..128).map(|i| tau.pow(e, &[i])).collect();
|
||||
|
||||
let d = domain::EvaluationDomain::new(e, 128);
|
||||
d.ifft(e, &mut lagrange_coeffs);
|
||||
|
||||
let a = E::Fr::random(e, rng);
|
||||
let b = E::Fr::random(e, rng);
|
||||
|
||||
let mut c = a;
|
||||
c.mul_assign(e, &b);
|
||||
|
||||
let mut alphabeta = alpha;
|
||||
alphabeta.mul_assign(e, &beta);
|
||||
c.sub_assign(e, &alphabeta);
|
||||
|
||||
let mut ic = E::Fr::zero();
|
||||
{
|
||||
let mut ic_i_beta = lagrange_coeffs[100];
|
||||
ic_i_beta.mul_assign(e, &beta);
|
||||
|
||||
let mut ic_i_alpha = lagrange_coeffs[99];
|
||||
ic_i_alpha.mul_assign(e, &alpha);
|
||||
|
||||
ic_i_beta.add_assign(e, &ic_i_alpha);
|
||||
|
||||
ic.add_assign(e, &ic_i_beta);
|
||||
}
|
||||
{
|
||||
let mut ic_i_beta = lagrange_coeffs[99];
|
||||
ic_i_beta.add_assign(e, &lagrange_coeffs[101]);
|
||||
ic_i_beta.mul_assign(e, &beta);
|
||||
|
||||
ic_i_beta.mul_assign(e, &E::Fr::from_str(e, "100").unwrap());
|
||||
|
||||
ic.add_assign(e, &ic_i_beta);
|
||||
}
|
||||
|
||||
c.sub_assign(e, &ic);
|
||||
c.mul_assign(e, &delta.inverse(e).unwrap());
|
||||
|
||||
let mut a_g = E::G1::one(e);
|
||||
a_g.mul_assign(e, &a);
|
||||
|
||||
let mut b_g = E::G2::one(e);
|
||||
b_g.mul_assign(e, &b);
|
||||
|
||||
let mut c_g = E::G1::one(e);
|
||||
c_g.mul_assign(e, &c);
|
||||
|
||||
let fake_proof = Proof {
|
||||
a: a_g,
|
||||
b: b_g,
|
||||
c: c_g
|
||||
};
|
||||
|
||||
// verify fake proof
|
||||
assert!(verify(e, |cs| {
|
||||
RootWitness {
|
||||
num: E::Fr::from_str(e, "100").unwrap(),
|
||||
num_var: cs.alloc(E::Fr::one(e))
|
||||
}
|
||||
}, &fake_proof, &pvk));
|
||||
|
||||
// verify fake proof with wrong input
|
||||
assert!(!verify(e, |cs| {
|
||||
RootWitness {
|
||||
num: E::Fr::from_str(e, "101").unwrap(),
|
||||
num_var: cs.alloc(E::Fr::one(e))
|
||||
}
|
||||
}, &fake_proof, &pvk));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn groth_with_bls381() {
|
||||
use curves::bls381::Bls381;
|
||||
|
||||
let e = &Bls381::new();
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
test_snark_system(e, rng);
|
||||
}
|
145
src/lib.rs
145
src/lib.rs
@ -1,8 +1,151 @@
|
||||
#![feature(i128_type)]
|
||||
|
||||
extern crate rand;
|
||||
extern crate rayon;
|
||||
extern crate num_cpus;
|
||||
extern crate crossbeam;
|
||||
extern crate byteorder;
|
||||
extern crate serde;
|
||||
|
||||
pub mod curves;
|
||||
pub mod groth;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ops;
|
||||
|
||||
use curves::{Engine, Field};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Variable(Index);
|
||||
|
||||
impl Variable {
|
||||
pub fn one() -> Self {
|
||||
Variable(Index::Input(0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum Index {
|
||||
Input(usize),
|
||||
Aux(usize)
|
||||
}
|
||||
|
||||
pub struct LinearCombination<'a, E: Engine + 'a>(HashMap<Index, E::Fr>, &'a E);
|
||||
|
||||
impl<'a, E: Engine + 'a> ops::Add<Variable> for LinearCombination<'a, E> {
|
||||
type Output = LinearCombination<'a, E>;
|
||||
|
||||
fn add(self, other: Variable) -> LinearCombination<'a, E> {
|
||||
let one = E::Fr::one(self.1);
|
||||
|
||||
self.add(one, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: Engine + 'a> ops::Add<(E::Fr, Variable)> for LinearCombination<'a, E> {
|
||||
type Output = LinearCombination<'a, E>;
|
||||
|
||||
fn add(self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<'a, E> {
|
||||
self.add(coeff, var)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: Engine + 'a> ops::Sub<Variable> for LinearCombination<'a, E> {
|
||||
type Output = LinearCombination<'a, E>;
|
||||
|
||||
fn sub(self, other: Variable) -> LinearCombination<'a, E> {
|
||||
let one = E::Fr::one(self.1);
|
||||
|
||||
self.sub(one, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: Engine + 'a> ops::Sub<(E::Fr, Variable)> for LinearCombination<'a, E> {
|
||||
type Output = LinearCombination<'a, E>;
|
||||
|
||||
fn sub(self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<'a, E> {
|
||||
self.sub(coeff, var)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> LinearCombination<'a, E> {
|
||||
pub fn zero(e: &'a E) -> LinearCombination<'a, E> {
|
||||
LinearCombination(HashMap::new(), e)
|
||||
}
|
||||
|
||||
pub fn one(e: &'a E) -> LinearCombination<'a, E> {
|
||||
LinearCombination::zero(e).add(E::Fr::one(e), Variable::one())
|
||||
}
|
||||
|
||||
pub fn add(mut self, coeff: E::Fr, var: Variable) -> Self
|
||||
{
|
||||
self.0.entry(var.0)
|
||||
.or_insert(E::Fr::zero())
|
||||
.add_assign(self.1, &coeff);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sub(self, mut coeff: E::Fr, var: Variable) -> Self
|
||||
{
|
||||
coeff.negate(self.1);
|
||||
|
||||
self.add(coeff, var)
|
||||
}
|
||||
|
||||
fn evaluate(
|
||||
&self,
|
||||
e: &E,
|
||||
input_assignment: &[E::Fr],
|
||||
aux_assignment: &[E::Fr]
|
||||
) -> E::Fr
|
||||
{
|
||||
let mut acc = E::Fr::zero();
|
||||
for (index, coeff) in self.0.iter() {
|
||||
let mut n = *coeff;
|
||||
match index {
|
||||
&Index::Input(id) => {
|
||||
n.mul_assign(e, &input_assignment[id]);
|
||||
},
|
||||
&Index::Aux(id) => {
|
||||
n.mul_assign(e, &aux_assignment[id]);
|
||||
}
|
||||
}
|
||||
acc.add_assign(e, &n);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Circuit<E: Engine> {
|
||||
type WitnessMap: Witness<E>;
|
||||
|
||||
/// Synthesize the circuit into a rank-1 quadratic constraint system
|
||||
#[must_use]
|
||||
fn synthesize<CS: ConstraintSystem<E>>(self, engine: &E, cs: &mut CS) -> Self::WitnessMap;
|
||||
}
|
||||
|
||||
pub trait Witness<E: Engine> {
|
||||
/// Synthesize the circuit, except with additional access to public input
|
||||
/// variables
|
||||
fn synthesize<CS: PublicConstraintSystem<E>>(self, engine: &E, cs: &mut CS);
|
||||
}
|
||||
|
||||
pub trait PublicConstraintSystem<E: Engine>: ConstraintSystem<E> {
|
||||
/// Allocate a public input that the verifier knows.
|
||||
fn alloc_input(&mut self, value: E::Fr) -> Variable;
|
||||
}
|
||||
|
||||
pub trait ConstraintSystem<E: Engine> {
|
||||
/// Allocate a private variable in the constraint system, setting it to
|
||||
/// the provided value.
|
||||
fn alloc(&mut self, value: E::Fr) -> Variable;
|
||||
|
||||
/// Enforce that `A` * `B` = `C`.
|
||||
fn enforce(
|
||||
&mut self,
|
||||
a: LinearCombination<E>,
|
||||
b: LinearCombination<E>,
|
||||
c: LinearCombination<E>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user