New implementation of library API.
This commit is contained in:
parent
7211d98c1e
commit
cb53708767
@ -13,7 +13,7 @@ rand = "0.3"
|
|||||||
bit-vec = "0.4.4"
|
bit-vec = "0.4.4"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
futures-cpupool = "0.1"
|
futures-cpupool = "0.1"
|
||||||
num_cpus = "1.6"
|
num_cpus = "1"
|
||||||
crossbeam = "0.3"
|
crossbeam = "0.3"
|
||||||
pairing = "0.13"
|
pairing = "0.13"
|
||||||
|
|
||||||
|
239
examples/mimc.rs
Normal file
239
examples/mimc.rs
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
extern crate bellman;
|
||||||
|
extern crate pairing;
|
||||||
|
extern crate rand;
|
||||||
|
|
||||||
|
// For randomness (during paramgen and proof generation)
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
|
||||||
|
// For benchmarking
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
// Bring in some tools for using pairing-friendly curves
|
||||||
|
use pairing::{
|
||||||
|
Engine,
|
||||||
|
Field
|
||||||
|
};
|
||||||
|
|
||||||
|
// We're going to use the BLS12-381 pairing-friendly elliptic curve.
|
||||||
|
use pairing::bls12_381::{
|
||||||
|
Bls12
|
||||||
|
};
|
||||||
|
|
||||||
|
// We'll use these interfaces to construct our circuit.
|
||||||
|
use bellman::{
|
||||||
|
Circuit,
|
||||||
|
ConstraintSystem,
|
||||||
|
SynthesisError
|
||||||
|
};
|
||||||
|
|
||||||
|
// We're going to use the Groth16 proving system.
|
||||||
|
use bellman::groth16::{
|
||||||
|
generate_random_parameters,
|
||||||
|
prepare_verifying_key,
|
||||||
|
create_random_proof,
|
||||||
|
verify_proof,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MIMC_ROUNDS: usize = 322;
|
||||||
|
|
||||||
|
/// This is an implementation of MiMC, specifically a
|
||||||
|
/// variant named `LongsightF322p3` for BLS12-381.
|
||||||
|
/// See http://eprint.iacr.org/2016/492 for more
|
||||||
|
/// information about this construction.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) {
|
||||||
|
/// for i from 0 up to 321 {
|
||||||
|
/// xL, xR := xR + (xL + Ci)^3, xL
|
||||||
|
/// }
|
||||||
|
/// return xL
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
fn mimc<E: Engine>(
|
||||||
|
mut xl: E::Fr,
|
||||||
|
mut xr: E::Fr,
|
||||||
|
constants: &[E::Fr]
|
||||||
|
) -> E::Fr
|
||||||
|
{
|
||||||
|
assert_eq!(constants.len(), MIMC_ROUNDS);
|
||||||
|
|
||||||
|
for i in 0..MIMC_ROUNDS {
|
||||||
|
let mut tmp1 = xl;
|
||||||
|
tmp1.add_assign(&constants[i]);
|
||||||
|
let mut tmp2 = tmp1;
|
||||||
|
tmp2.square();
|
||||||
|
tmp2.mul_assign(&tmp1);
|
||||||
|
tmp2.add_assign(&xr);
|
||||||
|
xr = xl;
|
||||||
|
xl = tmp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
xl
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is our demo circuit for proving knowledge of the
|
||||||
|
/// preimage of a MiMC hash invocation.
|
||||||
|
struct MiMCDemo<'a, E: Engine> {
|
||||||
|
xl: Option<E::Fr>,
|
||||||
|
xr: Option<E::Fr>,
|
||||||
|
constants: &'a [E::Fr]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our demo circuit implements this `Circuit` trait which
|
||||||
|
/// is used during paramgen and proving in order to
|
||||||
|
/// synthesize the constraint system.
|
||||||
|
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
|
||||||
|
fn synthesize<CS: ConstraintSystem<E>>(
|
||||||
|
self,
|
||||||
|
cs: &mut CS
|
||||||
|
) -> Result<(), SynthesisError>
|
||||||
|
{
|
||||||
|
assert_eq!(self.constants.len(), MIMC_ROUNDS);
|
||||||
|
|
||||||
|
// Allocate the first component of the preimage.
|
||||||
|
let mut xl_value = self.xl;
|
||||||
|
let mut xl = cs.alloc(|| "preimage xl", || {
|
||||||
|
xl_value.ok_or(SynthesisError::AssignmentMissing)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Allocate the second component of the preimage.
|
||||||
|
let mut xr_value = self.xr;
|
||||||
|
let mut xr = cs.alloc(|| "preimage xr", || {
|
||||||
|
xr_value.ok_or(SynthesisError::AssignmentMissing)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for i in 0..MIMC_ROUNDS {
|
||||||
|
// xL, xR := xR + (xL + Ci)^3, xL
|
||||||
|
let cs = &mut cs.namespace(|| format!("round {}", i));
|
||||||
|
|
||||||
|
// tmp = (xL + Ci)^2
|
||||||
|
let mut tmp_value = xl_value.map(|mut e| {
|
||||||
|
e.add_assign(&self.constants[i]);
|
||||||
|
e.square();
|
||||||
|
e
|
||||||
|
});
|
||||||
|
let mut tmp = cs.alloc(|| "tmp", || {
|
||||||
|
tmp_value.ok_or(SynthesisError::AssignmentMissing)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "tmp = (xL + Ci)^2",
|
||||||
|
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||||
|
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||||
|
|lc| lc + tmp
|
||||||
|
);
|
||||||
|
|
||||||
|
// new_xL = xR + (xL + Ci)^3
|
||||||
|
// new_xL = xR + tmp * (xL + Ci)
|
||||||
|
// new_xL - xR = tmp * (xL + Ci)
|
||||||
|
let mut new_xl_value = xl_value.map(|mut e| {
|
||||||
|
e.add_assign(&self.constants[i]);
|
||||||
|
e.mul_assign(&tmp_value.unwrap());
|
||||||
|
e.add_assign(&xr_value.unwrap());
|
||||||
|
e
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut new_xl = if i == (MIMC_ROUNDS-1) {
|
||||||
|
// This is the last round, xL is our image and so
|
||||||
|
// we allocate a public input.
|
||||||
|
cs.alloc_input(|| "image", || {
|
||||||
|
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
cs.alloc(|| "new_xl", || {
|
||||||
|
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "new_xL = xR + (xL + Ci)^3",
|
||||||
|
|lc| lc + tmp,
|
||||||
|
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||||
|
|lc| lc + new_xl - xr
|
||||||
|
);
|
||||||
|
|
||||||
|
// xR = xL
|
||||||
|
xr = xl;
|
||||||
|
xr_value = xl_value;
|
||||||
|
|
||||||
|
// xL = new_xL
|
||||||
|
xl = new_xl;
|
||||||
|
xl_value = new_xl_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// This may not be cryptographically safe, use
|
||||||
|
// `OsRng` (for example) in production software.
|
||||||
|
let rng = &mut thread_rng();
|
||||||
|
|
||||||
|
// Generate the MiMC round constants
|
||||||
|
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
println!("Creating parameters...");
|
||||||
|
|
||||||
|
// Create parameters for our circuit
|
||||||
|
let params = {
|
||||||
|
let c = MiMCDemo::<Bls12> {
|
||||||
|
xl: None,
|
||||||
|
xr: None,
|
||||||
|
constants: &constants
|
||||||
|
};
|
||||||
|
|
||||||
|
generate_random_parameters(c, rng).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare the verification key (for proof verification)
|
||||||
|
let pvk = prepare_verifying_key(¶ms.vk);
|
||||||
|
|
||||||
|
println!("Creating proofs...");
|
||||||
|
|
||||||
|
// Let's benchmark stuff!
|
||||||
|
const SAMPLES: u32 = 50;
|
||||||
|
let mut total_proving = Duration::new(0, 0);
|
||||||
|
let mut total_verifying = Duration::new(0, 0);
|
||||||
|
|
||||||
|
for _ in 0..SAMPLES {
|
||||||
|
// Generate a random preimage and compute the image
|
||||||
|
let xl = rng.gen();
|
||||||
|
let xr = rng.gen();
|
||||||
|
let image = mimc::<Bls12>(xl, xr, &constants);
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let proof = {
|
||||||
|
// Create an instance of our circuit (with the
|
||||||
|
// witness)
|
||||||
|
let c = MiMCDemo {
|
||||||
|
xl: Some(xl),
|
||||||
|
xr: Some(xr),
|
||||||
|
constants: &constants
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a groth16 proof with our parameters.
|
||||||
|
create_random_proof(c, ¶ms, rng).unwrap()
|
||||||
|
};
|
||||||
|
total_proving += start.elapsed();
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
// Check the proof
|
||||||
|
assert!(verify_proof(
|
||||||
|
&pvk,
|
||||||
|
&proof,
|
||||||
|
&[image]
|
||||||
|
).unwrap());
|
||||||
|
total_verifying += start.elapsed();
|
||||||
|
}
|
||||||
|
let proving_avg = total_proving / SAMPLES;
|
||||||
|
let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64
|
||||||
|
+ (proving_avg.as_secs() as f64);
|
||||||
|
|
||||||
|
let verifying_avg = total_verifying / SAMPLES;
|
||||||
|
let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64
|
||||||
|
+ (verifying_avg.as_secs() as f64);
|
||||||
|
|
||||||
|
println!("Average proving time: {:?} seconds", proving_avg);
|
||||||
|
println!("Average verifying time: {:?} seconds", verifying_avg);
|
||||||
|
}
|
@ -1,424 +0,0 @@
|
|||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,233 +0,0 @@
|
|||||||
use pairing::*;
|
|
||||||
use domain::{Scalar, EvaluationDomain};
|
|
||||||
use ::{
|
|
||||||
ConstraintSystem,
|
|
||||||
PublicConstraintSystem,
|
|
||||||
Circuit,
|
|
||||||
Input,
|
|
||||||
Index,
|
|
||||||
Error,
|
|
||||||
Variable,
|
|
||||||
LinearCombination,
|
|
||||||
Namespace
|
|
||||||
};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
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<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
_: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>
|
|
||||||
{
|
|
||||||
self.input_assignment.push(f()?);
|
|
||||||
self.b_input_density.add_element();
|
|
||||||
|
|
||||||
Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
|
||||||
type Root = Self;
|
|
||||||
|
|
||||||
fn alloc<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
_: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>
|
|
||||||
{
|
|
||||||
self.aux_assignment.push(f()?);
|
|
||||||
self.a_aux_density.add_element();
|
|
||||||
self.b_aux_density.add_element();
|
|
||||||
|
|
||||||
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enforce<NR: Into<String>, N: FnOnce() -> NR>(
|
|
||||||
&mut self,
|
|
||||||
_: N,
|
|
||||||
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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin a namespace for the constraint system
|
|
||||||
fn namespace<'a, NR, N>(
|
|
||||||
&'a mut self,
|
|
||||||
_: N
|
|
||||||
) -> Namespace<'a, E, Self::Root>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
Namespace(self, PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,185 +0,0 @@
|
|||||||
use pairing::*;
|
|
||||||
use ::{
|
|
||||||
Input,
|
|
||||||
Error,
|
|
||||||
LinearCombination,
|
|
||||||
Index,
|
|
||||||
Variable,
|
|
||||||
ConstraintSystem,
|
|
||||||
PublicConstraintSystem,
|
|
||||||
Namespace
|
|
||||||
};
|
|
||||||
use super::{Proof, VerifyingKey, PreparedVerifyingKey};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
/// 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<'cs, E: Engine> ConstraintSystem<E> for VerifierInput<'cs, E> {
|
|
||||||
type Root = Self;
|
|
||||||
|
|
||||||
fn alloc<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
_: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, 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<NR: Into<String>, N: FnOnce() -> NR>(
|
|
||||||
&mut self,
|
|
||||||
_: N,
|
|
||||||
_: LinearCombination<E>,
|
|
||||||
_: LinearCombination<E>,
|
|
||||||
_: LinearCombination<E>
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Do nothing; we don't care about the constraint system
|
|
||||||
// in this context.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin a namespace for the constraint system
|
|
||||||
fn namespace<'a, NR, N>(
|
|
||||||
&'a mut self,
|
|
||||||
_: N
|
|
||||||
) -> Namespace<'a, E, Self::Root>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
Namespace(self, PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is intended to be a wrapper around VerifierInput that is kept
|
|
||||||
/// private and used for input allocation.
|
|
||||||
struct InputAllocator<T>(T);
|
|
||||||
|
|
||||||
impl<'cs, 'b, E: Engine> ConstraintSystem<E> for InputAllocator<&'cs mut VerifierInput<'b, E>> {
|
|
||||||
type Root = Self;
|
|
||||||
|
|
||||||
fn alloc<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>
|
|
||||||
{
|
|
||||||
self.0.alloc(name_fn, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enforce<NR: Into<String>, N: FnOnce() -> NR>(
|
|
||||||
&mut self,
|
|
||||||
_: N,
|
|
||||||
_: LinearCombination<E>,
|
|
||||||
_: LinearCombination<E>,
|
|
||||||
_: LinearCombination<E>
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Do nothing; we don't care about the constraint system
|
|
||||||
// in this context.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin a namespace for the constraint system
|
|
||||||
fn namespace<'a, NR, N>(
|
|
||||||
&'a mut self,
|
|
||||||
_: N
|
|
||||||
) -> Namespace<'a, E, Self::Root>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
Namespace(self, PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, E: Engine> PublicConstraintSystem<E> for InputAllocator<&'a mut VerifierInput<'b, E>> {
|
|
||||||
fn alloc_input<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
_: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>
|
|
||||||
{
|
|
||||||
if self.0.ic.len() == 0 {
|
|
||||||
self.0.insufficient_inputs = true;
|
|
||||||
} else {
|
|
||||||
self.0.acc.add_assign(&self.0.ic[0].mul(f()?));
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
534
oldsrc/lib.rs
534
oldsrc/lib.rs
@ -1,534 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
#[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 multiexp::DensityTracker>,
|
|
||||||
mut aux_density: Option<&mut multiexp::DensityTracker>,
|
|
||||||
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 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<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ConstraintSystem<E: Engine>: Sized {
|
|
||||||
type Root: ConstraintSystem<E>;
|
|
||||||
|
|
||||||
/// 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<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>;
|
|
||||||
|
|
||||||
/// Enforce that `A` * `B` = `C`.
|
|
||||||
fn enforce<NR: Into<String>, N: FnOnce() -> NR>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
a: LinearCombination<E>,
|
|
||||||
b: LinearCombination<E>,
|
|
||||||
c: LinearCombination<E>
|
|
||||||
);
|
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, _: N)
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
// Default is to do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
|
||||||
{
|
|
||||||
// Default is to do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin a namespace for the constraint system
|
|
||||||
fn namespace<'a, NR, N>(
|
|
||||||
&'a mut self,
|
|
||||||
name_fn: N
|
|
||||||
) -> Namespace<'a, E, Self::Root>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs mut CS {
|
|
||||||
type Root = CS::Root;
|
|
||||||
|
|
||||||
/// Allocate a private variable in the constraint system. The provided function is used to
|
|
||||||
/// determine the assignment of the variable.
|
|
||||||
fn alloc<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>
|
|
||||||
{
|
|
||||||
(*self).alloc(name_fn, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enforce that `A` * `B` = `C`.
|
|
||||||
fn enforce<NR: Into<String>, N: FnOnce() -> NR>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
a: LinearCombination<E>,
|
|
||||||
b: LinearCombination<E>,
|
|
||||||
c: LinearCombination<E>
|
|
||||||
)
|
|
||||||
{
|
|
||||||
(*self).enforce(name_fn, a, b, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
(*self).push_namespace(name_fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
|
||||||
{
|
|
||||||
(*self).pop_namespace()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin a namespace for the constraint system
|
|
||||||
fn namespace<'a, NR, N>(
|
|
||||||
&'a mut self,
|
|
||||||
name_fn: N
|
|
||||||
) -> Namespace<'a, E, Self::Root>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
(*self).namespace(name_fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
pub struct Namespace<'a, E: Engine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>);
|
|
||||||
|
|
||||||
impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
|
|
||||||
type Root = CS;
|
|
||||||
|
|
||||||
fn alloc<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>
|
|
||||||
{
|
|
||||||
self.0.alloc(name_fn, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enforce<NR: Into<String>, N: FnOnce() -> NR>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
a: LinearCombination<E>,
|
|
||||||
b: LinearCombination<E>,
|
|
||||||
c: LinearCombination<E>
|
|
||||||
)
|
|
||||||
{
|
|
||||||
self.0.enforce(name_fn, a, b, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
self.0.push_namespace(name_fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
|
||||||
{
|
|
||||||
self.0.pop_namespace();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin a namespace for the constraint system
|
|
||||||
fn namespace<'a, NR, N>(
|
|
||||||
&'a mut self,
|
|
||||||
name_fn: N
|
|
||||||
) -> Namespace<'a, E, Self::Root>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
self.0.push_namespace(name_fn);
|
|
||||||
|
|
||||||
Namespace(self.0, PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, E: Engine, CS: ConstraintSystem<E>> Drop for Namespace<'a, E, CS> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.0.pop_namespace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum NamedObject {
|
|
||||||
Constraint(usize),
|
|
||||||
Input(usize),
|
|
||||||
Aux(usize),
|
|
||||||
Namespace
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constraint system for testing purposes.
|
|
||||||
pub struct TestConstraintSystem<E: Engine> {
|
|
||||||
named_objects: HashMap<String, NamedObject>,
|
|
||||||
current_namespace: Vec<String>,
|
|
||||||
constraints: Vec<(LinearCombination<E>, LinearCombination<E>, LinearCombination<E>, String)>,
|
|
||||||
inputs: Vec<E::Fr>,
|
|
||||||
aux: Vec<E::Fr>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> TestConstraintSystem<E> {
|
|
||||||
pub fn new() -> TestConstraintSystem<E> {
|
|
||||||
TestConstraintSystem {
|
|
||||||
named_objects: HashMap::new(),
|
|
||||||
current_namespace: vec![],
|
|
||||||
constraints: vec![],
|
|
||||||
inputs: vec![E::Fr::one()],
|
|
||||||
aux: vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn which_is_unsatisfied(&self) -> Option<&str> {
|
|
||||||
for &(ref a, ref b, ref c, ref path) in &self.constraints {
|
|
||||||
let mut a = a.eval(None, None, &self.inputs, &self.aux);
|
|
||||||
let b = b.eval(None, None, &self.inputs, &self.aux);
|
|
||||||
let c = c.eval(None, None, &self.inputs, &self.aux);
|
|
||||||
|
|
||||||
a.mul_assign(&b);
|
|
||||||
|
|
||||||
if a != c {
|
|
||||||
return Some(&*path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_satisfied(&self) -> bool
|
|
||||||
{
|
|
||||||
self.which_is_unsatisfied().is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn num_constraints(&self) -> usize
|
|
||||||
{
|
|
||||||
self.constraints.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assign(&mut self, path: &str, to: E::Fr)
|
|
||||||
{
|
|
||||||
match self.named_objects.get(path) {
|
|
||||||
Some(&NamedObject::Input(index)) => self.inputs[index] = to,
|
|
||||||
Some(&NamedObject::Aux(index)) => self.aux[index] = to,
|
|
||||||
Some(e) => panic!("tried to assign `{:?}` a value at path: {}", e, path),
|
|
||||||
_ => panic!("no variable exists at path: {}", path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&mut self, path: &str) -> E::Fr
|
|
||||||
{
|
|
||||||
match self.named_objects.get(path) {
|
|
||||||
Some(&NamedObject::Input(index)) => self.inputs[index],
|
|
||||||
Some(&NamedObject::Aux(index)) => self.aux[index],
|
|
||||||
Some(e) => panic!("tried to get value of `{:?}` at path: {}", e, path),
|
|
||||||
_ => panic!("no variable exists at path: {}", path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_named_obj(&mut self, path: String, to: NamedObject) {
|
|
||||||
if self.named_objects.contains_key(&path) {
|
|
||||||
panic!("tried to create object at existing path: {}", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.named_objects.insert(path, to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_path(ns: &[String], this: String) -> String {
|
|
||||||
if this.chars().any(|a| a == '/') {
|
|
||||||
panic!("'/' is not allowed in names");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut name = String::new();
|
|
||||||
|
|
||||||
let mut needs_separation = false;
|
|
||||||
for ns in ns.iter().chain(Some(&this).into_iter())
|
|
||||||
{
|
|
||||||
if needs_separation {
|
|
||||||
name += "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
name += ns;
|
|
||||||
needs_separation = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
name
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> PublicConstraintSystem<E> for TestConstraintSystem<E> {
|
|
||||||
fn alloc_input<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>
|
|
||||||
{
|
|
||||||
let this_path = compute_path(&self.current_namespace, name_fn().into());
|
|
||||||
let this_obj = NamedObject::Input(self.inputs.len());
|
|
||||||
self.set_named_obj(this_path, this_obj);
|
|
||||||
|
|
||||||
let var = Variable(Index::Input(self.inputs.len()));
|
|
||||||
|
|
||||||
self.inputs.push(f()?);
|
|
||||||
|
|
||||||
Ok(var)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
|
||||||
type Root = Self;
|
|
||||||
|
|
||||||
fn alloc<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>
|
|
||||||
{
|
|
||||||
let this_path = compute_path(&self.current_namespace, name_fn().into());
|
|
||||||
let this_obj = NamedObject::Aux(self.aux.len());
|
|
||||||
self.set_named_obj(this_path, this_obj);
|
|
||||||
|
|
||||||
let var = Variable(Index::Aux(self.aux.len()));
|
|
||||||
|
|
||||||
self.aux.push(f()?);
|
|
||||||
|
|
||||||
Ok(var)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enforce<NR: Into<String>, N: FnOnce() -> NR>(
|
|
||||||
&mut self,
|
|
||||||
name_fn: N,
|
|
||||||
a: LinearCombination<E>,
|
|
||||||
b: LinearCombination<E>,
|
|
||||||
c: LinearCombination<E>
|
|
||||||
)
|
|
||||||
{
|
|
||||||
let this_path = compute_path(&self.current_namespace, name_fn().into());
|
|
||||||
let this_obj = NamedObject::Constraint(self.constraints.len());
|
|
||||||
self.set_named_obj(this_path.clone(), this_obj);
|
|
||||||
|
|
||||||
self.constraints.push((a, b, c, this_path));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
let name = name_fn().into();
|
|
||||||
let this_path = compute_path(&self.current_namespace, name.clone());
|
|
||||||
|
|
||||||
self.set_named_obj(this_path, NamedObject::Namespace);
|
|
||||||
|
|
||||||
self.current_namespace.push(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
|
||||||
{
|
|
||||||
self.current_namespace.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin a namespace for the constraint system
|
|
||||||
fn namespace<'a, NR, N>(
|
|
||||||
&'a mut self,
|
|
||||||
name_fn: N
|
|
||||||
) -> Namespace<'a, E, Self::Root>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
self.push_namespace(name_fn);
|
|
||||||
|
|
||||||
Namespace(self, PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,14 @@
|
|||||||
use pairing::*;
|
use pairing::{
|
||||||
use super::{
|
Engine,
|
||||||
Error
|
Field,
|
||||||
|
PrimeField
|
||||||
};
|
};
|
||||||
use crossbeam;
|
|
||||||
use num_cpus;
|
use super::{
|
||||||
use multicore;
|
SynthesisError
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::multicore::Worker;
|
||||||
|
|
||||||
const LARGEST_POLYNOMIAL_DEGREE: usize = 1 << 28;
|
const LARGEST_POLYNOMIAL_DEGREE: usize = 1 << 28;
|
||||||
|
|
||||||
@ -22,36 +26,43 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
&self.coeffs
|
&self.coeffs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_coeffs(self) -> Vec<G> {
|
|
||||||
self.coeffs
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_mut(&mut self) -> &mut [G] {
|
pub fn as_mut(&mut self) -> &mut [G] {
|
||||||
&mut self.coeffs
|
&mut self.coeffs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, Error>
|
pub fn into_coeffs(self) -> Vec<G> {
|
||||||
{
|
self.coeffs
|
||||||
if coeffs.len() > LARGEST_POLYNOMIAL_DEGREE {
|
|
||||||
return Err(Error::PolynomialDegreeTooLarge)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError>
|
||||||
|
{
|
||||||
|
// For platform compatibility, we expect not to
|
||||||
|
// deal with these kinds of large polynomials.
|
||||||
|
if coeffs.len() > LARGEST_POLYNOMIAL_DEGREE {
|
||||||
|
return Err(SynthesisError::PolynomialDegreeTooLarge)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the size of our evaluation domain
|
||||||
let mut m = 1;
|
let mut m = 1;
|
||||||
let mut exp = 0;
|
let mut exp = 0;
|
||||||
while m < coeffs.len() {
|
while m < coeffs.len() {
|
||||||
m *= 2;
|
m *= 2;
|
||||||
exp += 1;
|
exp += 1;
|
||||||
|
|
||||||
|
// The pairing-friendly curve may not be able to support
|
||||||
|
// large enough (radix2) evaluation domains.
|
||||||
if exp >= E::Fr::S {
|
if exp >= E::Fr::S {
|
||||||
return Err(Error::PolynomialDegreeTooLarge)
|
return Err(SynthesisError::PolynomialDegreeTooLarge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compute omega, the 2^exp primitive root of unity
|
||||||
let mut omega = E::Fr::root_of_unity();
|
let mut omega = E::Fr::root_of_unity();
|
||||||
for _ in exp..E::Fr::S {
|
for _ in exp..E::Fr::S {
|
||||||
omega.square();
|
omega.square();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extend the coeffs vector with zeroes if necessary
|
||||||
coeffs.resize(m, G::group_zero());
|
coeffs.resize(m, G::group_zero());
|
||||||
|
|
||||||
Ok(EvaluationDomain {
|
Ok(EvaluationDomain {
|
||||||
@ -64,16 +75,16 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fft(&mut self)
|
pub fn fft(&mut self, worker: &Worker)
|
||||||
{
|
{
|
||||||
best_fft(&mut self.coeffs, &self.omega, self.exp);
|
best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ifft(&mut self)
|
pub fn ifft(&mut self, worker: &Worker)
|
||||||
{
|
{
|
||||||
best_fft(&mut self.coeffs, &self.omegainv, self.exp);
|
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
|
||||||
|
|
||||||
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
let minv = self.minv;
|
let minv = self.minv;
|
||||||
|
|
||||||
for v in self.coeffs.chunks_mut(chunk) {
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
@ -86,9 +97,9 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mul_coset(&mut self, g: E::Fr)
|
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr)
|
||||||
{
|
{
|
||||||
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
||||||
scope.spawn(move || {
|
scope.spawn(move || {
|
||||||
let mut u = g.pow(&[(i * chunk) as u64]);
|
let mut u = g.pow(&[(i * chunk) as u64]);
|
||||||
@ -101,18 +112,18 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn coset_fft(&mut self)
|
pub fn coset_fft(&mut self, worker: &Worker)
|
||||||
{
|
{
|
||||||
self.mul_coset(E::Fr::multiplicative_generator());
|
self.distribute_powers(worker, E::Fr::multiplicative_generator());
|
||||||
self.fft();
|
self.fft(worker);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn icoset_fft(&mut self)
|
pub fn icoset_fft(&mut self, worker: &Worker)
|
||||||
{
|
{
|
||||||
let geninv = self.geninv;
|
let geninv = self.geninv;
|
||||||
|
|
||||||
self.ifft();
|
self.ifft(worker);
|
||||||
self.mul_coset(geninv);
|
self.distribute_powers(worker, geninv);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn z(&self, tau: &E::Fr) -> E::Fr {
|
pub fn z(&self, tau: &E::Fr) -> E::Fr {
|
||||||
@ -122,11 +133,11 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn divide_by_z_on_coset(&mut self)
|
pub fn divide_by_z_on_coset(&mut self, worker: &Worker)
|
||||||
{
|
{
|
||||||
let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap();
|
let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap();
|
||||||
|
|
||||||
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for v in self.coeffs.chunks_mut(chunk) {
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
scope.spawn(move || {
|
scope.spawn(move || {
|
||||||
for v in v {
|
for v in v {
|
||||||
@ -137,10 +148,10 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mul_assign(&mut self, other: &EvaluationDomain<E, Scalar<E>>) {
|
pub fn mul_assign(&mut self, worker: &Worker, other: &EvaluationDomain<E, Scalar<E>>) {
|
||||||
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||||
|
|
||||||
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
|
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
|
||||||
scope.spawn(move || {
|
scope.spawn(move || {
|
||||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
@ -151,10 +162,10 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sub_assign(&mut self, other: &EvaluationDomain<E, G>) {
|
pub fn sub_assign(&mut self, worker: &Worker, other: &EvaluationDomain<E, G>) {
|
||||||
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||||
|
|
||||||
multicore::scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
|
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
|
||||||
scope.spawn(move || {
|
scope.spawn(move || {
|
||||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
@ -204,43 +215,14 @@ impl<E: Engine> Group<E> for Scalar<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_log_cpus() -> u32 {
|
fn best_fft<E: Engine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: 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();
|
let log_cpus = worker.log_num_cpus();
|
||||||
|
|
||||||
if log_n < log_cpus {
|
if log_n <= log_cpus {
|
||||||
serial_fft(a, omega, log_n);
|
serial_fft(a, omega, log_n);
|
||||||
} else {
|
} else {
|
||||||
parallel_fft(a, omega, log_n, log_cpus);
|
parallel_fft(a, worker, omega, log_n, log_cpus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +271,13 @@ fn serial_fft<E: Engine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parallel_fft<E: Engine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32, log_cpus: u32)
|
fn parallel_fft<E: Engine, T: Group<E>>(
|
||||||
|
a: &mut [T],
|
||||||
|
worker: &Worker,
|
||||||
|
omega: &E::Fr,
|
||||||
|
log_n: u32,
|
||||||
|
log_cpus: u32
|
||||||
|
)
|
||||||
{
|
{
|
||||||
assert!(log_n >= log_cpus);
|
assert!(log_n >= log_cpus);
|
||||||
|
|
||||||
@ -298,7 +286,7 @@ fn parallel_fft<E: Engine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32,
|
|||||||
let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus];
|
let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus];
|
||||||
let new_omega = omega.pow(&[num_cpus as u64]);
|
let new_omega = omega.pow(&[num_cpus as u64]);
|
||||||
|
|
||||||
crossbeam::scope(|scope| {
|
worker.scope(0, |scope, _| {
|
||||||
let a = &*a;
|
let a = &*a;
|
||||||
|
|
||||||
for (j, tmp) in tmp.iter_mut().enumerate() {
|
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||||
@ -326,7 +314,7 @@ fn parallel_fft<E: Engine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32,
|
|||||||
});
|
});
|
||||||
|
|
||||||
// TODO: does this hurt or help?
|
// TODO: does this hurt or help?
|
||||||
multicore::scope(a.len(), |scope, chunk| {
|
worker.scope(a.len(), |scope, chunk| {
|
||||||
let tmp = &tmp;
|
let tmp = &tmp;
|
||||||
|
|
||||||
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
||||||
@ -351,6 +339,8 @@ fn polynomial_arith() {
|
|||||||
|
|
||||||
fn test_mul<E: Engine, R: rand::Rng>(rng: &mut R)
|
fn test_mul<E: Engine, R: rand::Rng>(rng: &mut R)
|
||||||
{
|
{
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
for coeffs_a in 0..70 {
|
for coeffs_a in 0..70 {
|
||||||
for coeffs_b 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 a: Vec<_> = (0..coeffs_a).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
|
||||||
@ -372,10 +362,10 @@ fn polynomial_arith() {
|
|||||||
let mut a = EvaluationDomain::from_coeffs(a).unwrap();
|
let mut a = EvaluationDomain::from_coeffs(a).unwrap();
|
||||||
let mut b = EvaluationDomain::from_coeffs(b).unwrap();
|
let mut b = EvaluationDomain::from_coeffs(b).unwrap();
|
||||||
|
|
||||||
a.fft();
|
a.fft(&worker);
|
||||||
b.fft();
|
b.fft(&worker);
|
||||||
a.mul_assign(&b);
|
a.mul_assign(&worker, &b);
|
||||||
a.ifft();
|
a.ifft(&worker);
|
||||||
|
|
||||||
for (naive, fft) in naive.iter().zip(a.coeffs.iter()) {
|
for (naive, fft) in naive.iter().zip(a.coeffs.iter()) {
|
||||||
assert!(naive == fft);
|
assert!(naive == fft);
|
||||||
@ -396,6 +386,8 @@ fn fft_composition() {
|
|||||||
|
|
||||||
fn test_comp<E: Engine, R: rand::Rng>(rng: &mut R)
|
fn test_comp<E: Engine, R: rand::Rng>(rng: &mut R)
|
||||||
{
|
{
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
for coeffs in 0..10 {
|
for coeffs in 0..10 {
|
||||||
let coeffs = 1 << coeffs;
|
let coeffs = 1 << coeffs;
|
||||||
|
|
||||||
@ -405,17 +397,17 @@ fn fft_composition() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
|
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
|
||||||
domain.ifft();
|
domain.ifft(&worker);
|
||||||
domain.fft();
|
domain.fft(&worker);
|
||||||
assert!(v == domain.coeffs);
|
assert!(v == domain.coeffs);
|
||||||
domain.fft();
|
domain.fft(&worker);
|
||||||
domain.ifft();
|
domain.ifft(&worker);
|
||||||
assert!(v == domain.coeffs);
|
assert!(v == domain.coeffs);
|
||||||
domain.icoset_fft();
|
domain.icoset_fft(&worker);
|
||||||
domain.coset_fft();
|
domain.coset_fft(&worker);
|
||||||
assert!(v == domain.coeffs);
|
assert!(v == domain.coeffs);
|
||||||
domain.coset_fft();
|
domain.coset_fft(&worker);
|
||||||
domain.icoset_fft();
|
domain.icoset_fft(&worker);
|
||||||
assert!(v == domain.coeffs);
|
assert!(v == domain.coeffs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -433,6 +425,8 @@ fn parallel_fft_consistency() {
|
|||||||
|
|
||||||
fn test_consistency<E: Engine, R: rand::Rng>(rng: &mut R)
|
fn test_consistency<E: Engine, R: rand::Rng>(rng: &mut R)
|
||||||
{
|
{
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
for log_d in 0..10 {
|
for log_d in 0..10 {
|
||||||
let d = 1 << log_d;
|
let d = 1 << log_d;
|
||||||
@ -441,8 +435,8 @@ fn parallel_fft_consistency() {
|
|||||||
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
|
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
|
||||||
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
|
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
|
||||||
|
|
||||||
for log_cpus in 0..min(log_d, 3) {
|
for log_cpus in log_d..min(log_d+1, 3) {
|
||||||
parallel_fft(&mut v1.coeffs, &v1.omega, log_d, log_cpus);
|
parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus);
|
||||||
serial_fft(&mut v2.coeffs, &v2.omega, log_d);
|
serial_fft(&mut v2.coeffs, &v2.omega, log_d);
|
||||||
|
|
||||||
assert!(v1.coeffs == v2.coeffs);
|
assert!(v1.coeffs == v2.coeffs);
|
@ -1,26 +1,45 @@
|
|||||||
use pairing::*;
|
|
||||||
use ::{
|
|
||||||
Input,
|
|
||||||
Error,
|
|
||||||
LinearCombination,
|
|
||||||
Index,
|
|
||||||
Circuit,
|
|
||||||
Variable,
|
|
||||||
ConstraintSystem,
|
|
||||||
PublicConstraintSystem,
|
|
||||||
Namespace
|
|
||||||
};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use super::{VerifyingKey, Parameters};
|
|
||||||
use domain::{Scalar, EvaluationDomain};
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use multicore;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use pairing::{
|
||||||
|
Engine,
|
||||||
|
PrimeField,
|
||||||
|
Field,
|
||||||
|
Wnaf,
|
||||||
|
CurveProjective,
|
||||||
|
CurveAffine
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
Parameters,
|
||||||
|
VerifyingKey
|
||||||
|
};
|
||||||
|
|
||||||
|
use ::{
|
||||||
|
SynthesisError,
|
||||||
|
Circuit,
|
||||||
|
ConstraintSystem,
|
||||||
|
LinearCombination,
|
||||||
|
Variable,
|
||||||
|
Index
|
||||||
|
};
|
||||||
|
|
||||||
|
use ::domain::{
|
||||||
|
EvaluationDomain,
|
||||||
|
Scalar
|
||||||
|
};
|
||||||
|
|
||||||
|
use ::multicore::{
|
||||||
|
Worker
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Generates a random common reference string for
|
||||||
|
/// a circuit.
|
||||||
pub fn generate_random_parameters<E, C, R>(
|
pub fn generate_random_parameters<E, C, R>(
|
||||||
circuit: C,
|
circuit: C,
|
||||||
rng: &mut R
|
rng: &mut R
|
||||||
) -> Result<Parameters<E>, Error>
|
) -> Result<Parameters<E>, SynthesisError>
|
||||||
where E: Engine, C: Circuit<E>, R: Rng
|
where E: Engine, C: Circuit<E>, R: Rng
|
||||||
{
|
{
|
||||||
let g1 = rng.gen();
|
let g1 = rng.gen();
|
||||||
@ -43,22 +62,9 @@ pub fn generate_random_parameters<E, C, R>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create parameters for a circuit, given some trapdoors.
|
/// This is our assembly structure that we'll use to synthesize the
|
||||||
pub fn generate_parameters<E, C>(
|
/// circuit into a QAP.
|
||||||
circuit: C,
|
struct KeypairAssembly<E: Engine> {
|
||||||
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_inputs: usize,
|
||||||
num_aux: usize,
|
num_aux: usize,
|
||||||
num_constraints: usize,
|
num_constraints: usize,
|
||||||
@ -68,42 +74,20 @@ pub fn generate_parameters<E, C>(
|
|||||||
at_aux: Vec<Vec<(E::Fr, usize)>>,
|
at_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
bt_aux: Vec<Vec<(E::Fr, usize)>>,
|
bt_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
ct_aux: Vec<Vec<(E::Fr, usize)>>
|
ct_aux: Vec<Vec<(E::Fr, usize)>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> PublicConstraintSystem<E> for KeypairAssembly<E> {
|
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
|
||||||
fn alloc_input<NR, N, F>(
|
|
||||||
&mut self,
|
|
||||||
_: N,
|
|
||||||
f: F
|
|
||||||
) -> Result<Variable, Error>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, 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> {
|
|
||||||
type Root = Self;
|
type Root = Self;
|
||||||
|
|
||||||
fn alloc<NR, N, F>(
|
fn alloc<F, A, AR>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: N,
|
_: A,
|
||||||
f: F
|
_: F
|
||||||
) -> Result<Variable, Error>
|
) -> Result<Variable, SynthesisError>
|
||||||
where NR: Into<String>, N: FnOnce() -> NR, F: FnOnce() -> Result<E::Fr, Error>
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
{
|
{
|
||||||
// In this context, we don't have an assignment.
|
// There is no assignment, so we don't even invoke the
|
||||||
let _ = f();
|
// function for obtaining one.
|
||||||
|
|
||||||
let index = self.num_aux;
|
let index = self.num_aux;
|
||||||
self.num_aux += 1;
|
self.num_aux += 1;
|
||||||
@ -115,15 +99,39 @@ pub fn generate_parameters<E, C>(
|
|||||||
Ok(Variable(Index::Aux(index)))
|
Ok(Variable(Index::Aux(index)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce<NR: Into<String>, N: FnOnce() -> NR>(
|
fn alloc_input<F, A, AR>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: N,
|
_: A,
|
||||||
a: LinearCombination<E>,
|
_: F
|
||||||
b: LinearCombination<E>,
|
) -> Result<Variable, SynthesisError>
|
||||||
c: LinearCombination<E>
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
)
|
|
||||||
{
|
{
|
||||||
fn qap_eval<E: Engine>(
|
// There is no assignment, so we don't even invoke the
|
||||||
|
// function for obtaining one.
|
||||||
|
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(
|
||||||
|
&mut self,
|
||||||
|
_: A,
|
||||||
|
a: LA,
|
||||||
|
b: LB,
|
||||||
|
c: LC
|
||||||
|
)
|
||||||
|
where A: FnOnce() -> AR, AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
||||||
|
{
|
||||||
|
fn eval<E: Engine>(
|
||||||
l: LinearCombination<E>,
|
l: LinearCombination<E>,
|
||||||
inputs: &mut [Vec<(E::Fr, usize)>],
|
inputs: &mut [Vec<(E::Fr, usize)>],
|
||||||
aux: &mut [Vec<(E::Fr, usize)>],
|
aux: &mut [Vec<(E::Fr, usize)>],
|
||||||
@ -132,30 +140,48 @@ pub fn generate_parameters<E, C>(
|
|||||||
{
|
{
|
||||||
for (index, coeff) in l.0 {
|
for (index, coeff) in l.0 {
|
||||||
match index {
|
match index {
|
||||||
Index::Input(id) => inputs[id].push((coeff, this_constraint)),
|
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
|
||||||
Index::Aux(id) => aux[id].push((coeff, this_constraint))
|
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qap_eval(a, &mut self.at_inputs, &mut self.at_aux, self.num_constraints);
|
eval(a(LinearCombination::zero()), &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);
|
eval(b(LinearCombination::zero()), &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);
|
eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints);
|
||||||
|
|
||||||
self.num_constraints += 1;
|
self.num_constraints += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Begin a namespace for the constraint system
|
fn push_namespace<NR, N>(&mut self, _: N)
|
||||||
fn namespace<'a, NR, N>(
|
|
||||||
&'a mut self,
|
|
||||||
_: N
|
|
||||||
) -> Namespace<'a, E, Self::Root>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
where NR: Into<String>, N: FnOnce() -> NR
|
||||||
{
|
{
|
||||||
Namespace(self, PhantomData)
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pop_namespace(&mut self)
|
||||||
|
{
|
||||||
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create parameters for a circuit, given some toxic waste.
|
||||||
|
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>, SynthesisError>
|
||||||
|
where E: Engine, C: Circuit<E>
|
||||||
|
{
|
||||||
let mut assembly = KeypairAssembly {
|
let mut assembly = KeypairAssembly {
|
||||||
num_inputs: 0,
|
num_inputs: 0,
|
||||||
num_aux: 0,
|
num_aux: 0,
|
||||||
@ -172,27 +198,18 @@ pub fn generate_parameters<E, C>(
|
|||||||
assembly.alloc_input(|| "", || Ok(E::Fr::one()))?;
|
assembly.alloc_input(|| "", || Ok(E::Fr::one()))?;
|
||||||
|
|
||||||
// Synthesize the circuit.
|
// Synthesize the circuit.
|
||||||
circuit.synthesize(&mut assembly)?.synthesize(&mut assembly)?;
|
circuit.synthesize(&mut assembly)?;
|
||||||
|
|
||||||
// Input consistency constraints: x * 0 = 0
|
// Input consistency constraints: x * 0 = 0
|
||||||
for i in 0..assembly.num_inputs {
|
for i in 0..assembly.num_inputs {
|
||||||
assembly.enforce(|| "",
|
assembly.enforce(|| "",
|
||||||
LinearCombination::zero() + Variable(Index::Input(i)),
|
|lc| lc + Variable(Index::Input(i)),
|
||||||
LinearCombination::zero(),
|
|lc| lc,
|
||||||
LinearCombination::zero());
|
|lc| lc,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that all auxillary variables are constrained
|
// Create bases for blind evaluation of polynomials at tau
|
||||||
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 powers_of_tau = vec![Scalar::<E>(E::Fr::zero()); assembly.num_constraints];
|
||||||
let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?;
|
let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?;
|
||||||
|
|
||||||
@ -216,16 +233,17 @@ pub fn generate_parameters<E, C>(
|
|||||||
assembly.num_inputs + assembly.num_aux
|
assembly.num_inputs + assembly.num_aux
|
||||||
});
|
});
|
||||||
|
|
||||||
let gamma_inverse = gamma.inverse().ok_or(Error::UnexpectedIdentity)?;
|
let gamma_inverse = gamma.inverse().ok_or(SynthesisError::UnexpectedIdentity)?;
|
||||||
let delta_inverse = delta.inverse().ok_or(Error::UnexpectedIdentity)?;
|
let delta_inverse = delta.inverse().ok_or(SynthesisError::UnexpectedIdentity)?;
|
||||||
|
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
// Compute the H query
|
|
||||||
let mut h = vec![E::G1::zero(); powers_of_tau.as_ref().len() - 1];
|
let mut h = vec![E::G1::zero(); powers_of_tau.as_ref().len() - 1];
|
||||||
{
|
{
|
||||||
// Compute the powers of tau
|
// Compute powers of tau
|
||||||
{
|
{
|
||||||
let powers_of_tau = powers_of_tau.as_mut();
|
let powers_of_tau = powers_of_tau.as_mut();
|
||||||
multicore::scope(powers_of_tau.len(), |scope, chunk| {
|
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
||||||
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate()
|
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate()
|
||||||
{
|
{
|
||||||
scope.spawn(move || {
|
scope.spawn(move || {
|
||||||
@ -245,7 +263,7 @@ pub fn generate_parameters<E, C>(
|
|||||||
coeff.mul_assign(&delta_inverse);
|
coeff.mul_assign(&delta_inverse);
|
||||||
|
|
||||||
// Compute the H query with multiple threads
|
// Compute the H query with multiple threads
|
||||||
multicore::scope(h.len(), |scope, chunk| {
|
worker.scope(h.len(), |scope, chunk| {
|
||||||
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
|
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
|
||||||
{
|
{
|
||||||
let mut g1_wnaf = g1_wnaf.shared();
|
let mut g1_wnaf = g1_wnaf.shared();
|
||||||
@ -270,7 +288,7 @@ pub fn generate_parameters<E, C>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use inverse FFT to convert powers of tau to Lagrange coefficients
|
// Use inverse FFT to convert powers of tau to Lagrange coefficients
|
||||||
powers_of_tau.ifft();
|
powers_of_tau.ifft(&worker);
|
||||||
let powers_of_tau = powers_of_tau.into_coeffs();
|
let powers_of_tau = powers_of_tau.into_coeffs();
|
||||||
|
|
||||||
let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
|
let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
|
||||||
@ -303,7 +321,10 @@ pub fn generate_parameters<E, C>(
|
|||||||
|
|
||||||
// Trapdoors
|
// Trapdoors
|
||||||
alpha: &E::Fr,
|
alpha: &E::Fr,
|
||||||
beta: &E::Fr
|
beta: &E::Fr,
|
||||||
|
|
||||||
|
// Worker
|
||||||
|
worker: &Worker
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Sanity check
|
// Sanity check
|
||||||
@ -315,7 +336,7 @@ pub fn generate_parameters<E, C>(
|
|||||||
assert_eq!(a.len(), ext.len());
|
assert_eq!(a.len(), ext.len());
|
||||||
|
|
||||||
// Evaluate polynomials in multiple threads
|
// Evaluate polynomials in multiple threads
|
||||||
multicore::scope(a.len(), |scope, chunk| {
|
worker.scope(a.len(), |scope, chunk| {
|
||||||
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk)
|
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk)
|
||||||
.zip(b_g1.chunks_mut(chunk))
|
.zip(b_g1.chunks_mut(chunk))
|
||||||
.zip(b_g2.chunks_mut(chunk))
|
.zip(b_g2.chunks_mut(chunk))
|
||||||
@ -404,7 +425,8 @@ pub fn generate_parameters<E, C>(
|
|||||||
&mut ic,
|
&mut ic,
|
||||||
&gamma_inverse,
|
&gamma_inverse,
|
||||||
&alpha,
|
&alpha,
|
||||||
&beta
|
&beta,
|
||||||
|
&worker
|
||||||
);
|
);
|
||||||
|
|
||||||
// Evaluate for auxillary variables.
|
// Evaluate for auxillary variables.
|
||||||
@ -421,9 +443,18 @@ pub fn generate_parameters<E, C>(
|
|||||||
&mut l,
|
&mut l,
|
||||||
&delta_inverse,
|
&delta_inverse,
|
||||||
&alpha,
|
&alpha,
|
||||||
&beta
|
&beta,
|
||||||
|
&worker
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Don't allow any elements be unconstrained, so that
|
||||||
|
// the L query is always fully dense.
|
||||||
|
for e in l.iter() {
|
||||||
|
if e.is_zero() {
|
||||||
|
return Err(SynthesisError::UnconstrainedVariable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let g1 = g1.into_affine();
|
let g1 = g1.into_affine();
|
||||||
let g2 = g2.into_affine();
|
let g2 = g2.into_affine();
|
||||||
|
|
183
src/groth16/mod.rs
Normal file
183
src/groth16/mod.rs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
use pairing::{
|
||||||
|
Engine,
|
||||||
|
CurveAffine
|
||||||
|
};
|
||||||
|
|
||||||
|
use ::{
|
||||||
|
SynthesisError
|
||||||
|
};
|
||||||
|
|
||||||
|
use multiexp::SourceBuilder;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
mod generator;
|
||||||
|
mod prover;
|
||||||
|
mod verifier;
|
||||||
|
|
||||||
|
pub use self::generator::*;
|
||||||
|
pub use self::prover::*;
|
||||||
|
pub use self::verifier::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Proof<E: Engine> {
|
||||||
|
a: E::G1Affine,
|
||||||
|
b: E::G2Affine,
|
||||||
|
c: E::G1Affine
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
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>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PreparedVerifyingKey<E: Engine> {
|
||||||
|
/// Pairing result of alpha*beta
|
||||||
|
alpha_g1_beta_g2: E::Fqk,
|
||||||
|
/// -gamma in G2
|
||||||
|
neg_gamma_g2: <E::G2Affine as CurveAffine>::Prepared,
|
||||||
|
/// -delta in G2
|
||||||
|
neg_delta_g2: <E::G2Affine as CurveAffine>::Prepared,
|
||||||
|
/// Copy of IC from `VerifiyingKey`.
|
||||||
|
ic: Vec<E::G1Affine>
|
||||||
|
}
|
||||||
|
|
||||||
|
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>, SynthesisError>;
|
||||||
|
fn get_h(
|
||||||
|
&mut self,
|
||||||
|
num_h: usize
|
||||||
|
) -> Result<Self::G1Builder, SynthesisError>;
|
||||||
|
fn get_l(
|
||||||
|
&mut self,
|
||||||
|
num_l: usize
|
||||||
|
) -> Result<Self::G1Builder, SynthesisError>;
|
||||||
|
fn get_a(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
num_aux: usize
|
||||||
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
||||||
|
fn get_b_g1(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
num_aux: usize
|
||||||
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
||||||
|
fn get_b_g2(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
num_aux: usize
|
||||||
|
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
_: usize
|
||||||
|
) -> Result<VerifyingKey<E>, SynthesisError>
|
||||||
|
{
|
||||||
|
Ok(self.vk.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_h(
|
||||||
|
&mut self,
|
||||||
|
_: usize
|
||||||
|
) -> Result<Self::G1Builder, SynthesisError>
|
||||||
|
{
|
||||||
|
Ok((self.h.clone(), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_l(
|
||||||
|
&mut self,
|
||||||
|
_: usize
|
||||||
|
) -> Result<Self::G1Builder, SynthesisError>
|
||||||
|
{
|
||||||
|
Ok((self.l.clone(), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_a(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
_: usize
|
||||||
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
|
||||||
|
{
|
||||||
|
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_b_g1(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
_: usize
|
||||||
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
|
||||||
|
{
|
||||||
|
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_b_g2(
|
||||||
|
&mut self,
|
||||||
|
num_inputs: usize,
|
||||||
|
_: usize
|
||||||
|
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>
|
||||||
|
{
|
||||||
|
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
|
||||||
|
}
|
||||||
|
}
|
328
src/groth16/prover.rs
Normal file
328
src/groth16/prover.rs
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use futures::Future;
|
||||||
|
|
||||||
|
use pairing::{
|
||||||
|
Engine,
|
||||||
|
PrimeField,
|
||||||
|
Field,
|
||||||
|
CurveProjective,
|
||||||
|
CurveAffine
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
ParameterSource,
|
||||||
|
Proof
|
||||||
|
};
|
||||||
|
|
||||||
|
use ::{
|
||||||
|
SynthesisError,
|
||||||
|
Circuit,
|
||||||
|
ConstraintSystem,
|
||||||
|
LinearCombination,
|
||||||
|
Variable,
|
||||||
|
Index
|
||||||
|
};
|
||||||
|
|
||||||
|
use ::domain::{
|
||||||
|
EvaluationDomain,
|
||||||
|
Scalar
|
||||||
|
};
|
||||||
|
|
||||||
|
use ::multiexp::{
|
||||||
|
DensityTracker,
|
||||||
|
FullDensity,
|
||||||
|
multiexp
|
||||||
|
};
|
||||||
|
|
||||||
|
use ::multicore::{
|
||||||
|
Worker
|
||||||
|
};
|
||||||
|
|
||||||
|
fn eval<E: Engine>(
|
||||||
|
lc: &LinearCombination<E>,
|
||||||
|
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 lc.0.iter() {
|
||||||
|
let mut tmp;
|
||||||
|
|
||||||
|
match index {
|
||||||
|
Variable(Index::Input(i)) => {
|
||||||
|
tmp = input_assignment[i];
|
||||||
|
if let Some(ref mut v) = input_density {
|
||||||
|
v.inc(i);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Variable(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
|
||||||
|
}
|
||||||
|
|
||||||
|
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> ConstraintSystem<E> for ProvingAssignment<E> {
|
||||||
|
type Root = Self;
|
||||||
|
|
||||||
|
fn alloc<F, A, AR>(
|
||||||
|
&mut self,
|
||||||
|
_: A,
|
||||||
|
f: F
|
||||||
|
) -> Result<Variable, SynthesisError>
|
||||||
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
|
{
|
||||||
|
self.aux_assignment.push(f()?);
|
||||||
|
self.a_aux_density.add_element();
|
||||||
|
self.b_aux_density.add_element();
|
||||||
|
|
||||||
|
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_input<F, A, AR>(
|
||||||
|
&mut self,
|
||||||
|
_: A,
|
||||||
|
f: F
|
||||||
|
) -> Result<Variable, SynthesisError>
|
||||||
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
|
{
|
||||||
|
self.input_assignment.push(f()?);
|
||||||
|
self.b_input_density.add_element();
|
||||||
|
|
||||||
|
Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(
|
||||||
|
&mut self,
|
||||||
|
_: A,
|
||||||
|
a: LA,
|
||||||
|
b: LB,
|
||||||
|
c: LC
|
||||||
|
)
|
||||||
|
where A: FnOnce() -> AR, AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
||||||
|
{
|
||||||
|
let a = a(LinearCombination::zero());
|
||||||
|
let b = b(LinearCombination::zero());
|
||||||
|
let c = c(LinearCombination::zero());
|
||||||
|
|
||||||
|
self.a.push(Scalar(eval(
|
||||||
|
&a,
|
||||||
|
// Inputs have full density in the A query
|
||||||
|
// because there are constraints of the
|
||||||
|
// form x * 0 = 0 for each input.
|
||||||
|
None,
|
||||||
|
Some(&mut self.a_aux_density),
|
||||||
|
&self.input_assignment,
|
||||||
|
&self.aux_assignment
|
||||||
|
)));
|
||||||
|
self.b.push(Scalar(eval(
|
||||||
|
&b,
|
||||||
|
Some(&mut self.b_input_density),
|
||||||
|
Some(&mut self.b_aux_density),
|
||||||
|
&self.input_assignment,
|
||||||
|
&self.aux_assignment
|
||||||
|
)));
|
||||||
|
self.c.push(Scalar(eval(
|
||||||
|
&c,
|
||||||
|
// There is no C polynomial query,
|
||||||
|
// though there is an (beta)A + (alpha)B + C
|
||||||
|
// query for all aux variables.
|
||||||
|
// However, that query has full density.
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
&self.input_assignment,
|
||||||
|
&self.aux_assignment
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_namespace<NR, N>(&mut self, _: N)
|
||||||
|
where NR: Into<String>, N: FnOnce() -> NR
|
||||||
|
{
|
||||||
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_namespace(&mut self)
|
||||||
|
{
|
||||||
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
|
||||||
|
circuit: C,
|
||||||
|
params: P,
|
||||||
|
rng: &mut R
|
||||||
|
) -> Result<Proof<E>, SynthesisError>
|
||||||
|
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>, SynthesisError>
|
||||||
|
where E: Engine, C: Circuit<E>
|
||||||
|
{
|
||||||
|
let mut prover = ProvingAssignment {
|
||||||
|
a_aux_density: DensityTracker::new(),
|
||||||
|
b_input_density: DensityTracker::new(),
|
||||||
|
b_aux_density: DensityTracker::new(),
|
||||||
|
a: vec![],
|
||||||
|
b: vec![],
|
||||||
|
c: vec![],
|
||||||
|
input_assignment: vec![],
|
||||||
|
aux_assignment: vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
|
||||||
|
|
||||||
|
circuit.synthesize(&mut prover)?;
|
||||||
|
|
||||||
|
for i in 0..prover.input_assignment.len() {
|
||||||
|
prover.enforce(|| "",
|
||||||
|
|lc| lc + Variable(Index::Input(i)),
|
||||||
|
|lc| lc,
|
||||||
|
|lc| lc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let worker = Worker::new();
|
||||||
|
|
||||||
|
let vk = params.get_vk(prover.input_assignment.len())?;
|
||||||
|
|
||||||
|
let 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(&worker);
|
||||||
|
a.coset_fft(&worker);
|
||||||
|
b.ifft(&worker);
|
||||||
|
b.coset_fft(&worker);
|
||||||
|
c.ifft(&worker);
|
||||||
|
c.coset_fft(&worker);
|
||||||
|
|
||||||
|
a.mul_assign(&worker, &b);
|
||||||
|
drop(b);
|
||||||
|
a.sub_assign(&worker, &c);
|
||||||
|
drop(c);
|
||||||
|
a.divide_by_z_on_coset(&worker);
|
||||||
|
a.icoset_fft(&worker);
|
||||||
|
let mut a = a.into_coeffs();
|
||||||
|
let a_len = a.len() - 1;
|
||||||
|
a.truncate(a_len);
|
||||||
|
// TODO: parallelize if it's even helpful
|
||||||
|
let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::<Vec<_>>());
|
||||||
|
|
||||||
|
multiexp(&worker, 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(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone());
|
||||||
|
|
||||||
|
let a_aux_density_total = prover.a_aux_density.get_total_density();
|
||||||
|
|
||||||
|
let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?;
|
||||||
|
|
||||||
|
let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone());
|
||||||
|
let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone());
|
||||||
|
|
||||||
|
let b_input_density = Arc::new(prover.b_input_density);
|
||||||
|
let b_input_density_total = b_input_density.get_total_density();
|
||||||
|
let b_aux_density = Arc::new(prover.b_aux_density);
|
||||||
|
let b_aux_density_total = b_aux_density.get_total_density();
|
||||||
|
|
||||||
|
let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?;
|
||||||
|
|
||||||
|
let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone());
|
||||||
|
let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone());
|
||||||
|
|
||||||
|
let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?;
|
||||||
|
|
||||||
|
let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment);
|
||||||
|
let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment);
|
||||||
|
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
}
|
455
src/groth16/tests/dummy_engine.rs
Normal file
455
src/groth16/tests/dummy_engine.rs
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
use pairing::{
|
||||||
|
Engine,
|
||||||
|
PrimeField,
|
||||||
|
PrimeFieldRepr,
|
||||||
|
Field,
|
||||||
|
SqrtField,
|
||||||
|
LegendreSymbol,
|
||||||
|
CurveProjective,
|
||||||
|
CurveAffine,
|
||||||
|
PrimeFieldDecodingError,
|
||||||
|
GroupDecodingError,
|
||||||
|
EncodedPoint
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::fmt;
|
||||||
|
use rand::{Rand, Rng};
|
||||||
|
use std::num::Wrapping;
|
||||||
|
|
||||||
|
const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Fr(Wrapping<u32>);
|
||||||
|
|
||||||
|
impl fmt::Display for Fr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{}", (self.0).0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rand for Fr {
|
||||||
|
fn rand<R: Rng>(rng: &mut R) -> Self {
|
||||||
|
Fr(Wrapping(rng.gen()) % MODULUS_R)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field for Fr {
|
||||||
|
fn zero() -> Self {
|
||||||
|
Fr(Wrapping(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
Fr(Wrapping(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
(self.0).0 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(&mut self) {
|
||||||
|
self.0 = (self.0 * self.0) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn double(&mut self) {
|
||||||
|
self.0 = (self.0 << 1) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate(&mut self) {
|
||||||
|
if !<Fr as Field>::is_zero(self) {
|
||||||
|
self.0 = MODULUS_R - self.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assign(&mut self, other: &Self) {
|
||||||
|
self.0 = (self.0 + other.0) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub_assign(&mut self, other: &Self) {
|
||||||
|
self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_assign(&mut self, other: &Self) {
|
||||||
|
self.0 = (self.0 * other.0) % MODULUS_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inverse(&self) -> Option<Self> {
|
||||||
|
if <Fr as Field>::is_zero(self) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.pow(&[(MODULUS_R.0 as u64) - 2]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frobenius_map(&mut self, _: usize) {
|
||||||
|
// identity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SqrtField for Fr {
|
||||||
|
fn legendre(&self) -> LegendreSymbol {
|
||||||
|
// s = self^((r - 1) // 2)
|
||||||
|
let s = self.pow([32256]);
|
||||||
|
if s == <Fr as Field>::zero() { LegendreSymbol::Zero }
|
||||||
|
else if s == <Fr as Field>::one() { LegendreSymbol::QuadraticResidue }
|
||||||
|
else { LegendreSymbol::QuadraticNonResidue }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqrt(&self) -> Option<Self> {
|
||||||
|
// Tonelli-Shank's algorithm for q mod 16 = 1
|
||||||
|
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||||
|
match self.legendre() {
|
||||||
|
LegendreSymbol::Zero => Some(*self),
|
||||||
|
LegendreSymbol::QuadraticNonResidue => None,
|
||||||
|
LegendreSymbol::QuadraticResidue => {
|
||||||
|
let mut c = Fr::root_of_unity();
|
||||||
|
// r = self^((t + 1) // 2)
|
||||||
|
let mut r = self.pow([32]);
|
||||||
|
// t = self^t
|
||||||
|
let mut t = self.pow([63]);
|
||||||
|
let mut m = Fr::S;
|
||||||
|
|
||||||
|
while t != <Fr as Field>::one() {
|
||||||
|
let mut i = 1;
|
||||||
|
{
|
||||||
|
let mut t2i = t;
|
||||||
|
t2i.square();
|
||||||
|
loop {
|
||||||
|
if t2i == <Fr as Field>::one() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t2i.square();
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..(m - i - 1) {
|
||||||
|
c.square();
|
||||||
|
}
|
||||||
|
<Fr as Field>::mul_assign(&mut r, &c);
|
||||||
|
c.square();
|
||||||
|
<Fr as Field>::mul_assign(&mut t, &c);
|
||||||
|
m = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct FrRepr([u64; 1]);
|
||||||
|
|
||||||
|
impl Ord for FrRepr {
|
||||||
|
fn cmp(&self, other: &FrRepr) -> Ordering {
|
||||||
|
(self.0)[0].cmp(&(other.0)[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for FrRepr {
|
||||||
|
fn partial_cmp(&self, other: &FrRepr) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rand for FrRepr {
|
||||||
|
fn rand<R: Rng>(rng: &mut R) -> Self {
|
||||||
|
FrRepr([rng.gen()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FrRepr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{}", (self.0)[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for FrRepr {
|
||||||
|
fn from(v: u64) -> FrRepr {
|
||||||
|
FrRepr([v])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Fr> for FrRepr {
|
||||||
|
fn from(v: Fr) -> FrRepr {
|
||||||
|
FrRepr([(v.0).0 as u64])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMut<[u64]> for FrRepr {
|
||||||
|
fn as_mut(&mut self) -> &mut [u64] {
|
||||||
|
&mut self.0[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u64]> for FrRepr {
|
||||||
|
fn as_ref(&self) -> &[u64] {
|
||||||
|
&self.0[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FrRepr {
|
||||||
|
fn default() -> FrRepr {
|
||||||
|
FrRepr::from(0u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeFieldRepr for FrRepr {
|
||||||
|
fn sub_noborrow(&mut self, other: &Self) -> bool {
|
||||||
|
self.0[0] = self.0[0].wrapping_sub(other.0[0]);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn add_nocarry(&mut self, other: &Self) -> bool {
|
||||||
|
self.0[0] = self.0[0].wrapping_add(other.0[0]);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn num_bits(&self) -> u32 {
|
||||||
|
64 - self.0[0].leading_zeros()
|
||||||
|
}
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.0[0] == 0
|
||||||
|
}
|
||||||
|
fn is_odd(&self) -> bool {
|
||||||
|
!self.is_even()
|
||||||
|
}
|
||||||
|
fn is_even(&self) -> bool {
|
||||||
|
self.0[0] % 2 == 0
|
||||||
|
}
|
||||||
|
fn div2(&mut self) {
|
||||||
|
self.divn(1)
|
||||||
|
}
|
||||||
|
fn divn(&mut self, amt: u32) {
|
||||||
|
self.0[0] >>= amt;
|
||||||
|
}
|
||||||
|
fn mul2(&mut self) {
|
||||||
|
self.muln(1)
|
||||||
|
}
|
||||||
|
fn muln(&mut self, amt: u32) {
|
||||||
|
self.0[0] <<= amt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeField for Fr {
|
||||||
|
type Repr = FrRepr;
|
||||||
|
|
||||||
|
const NUM_BITS: u32 = 16;
|
||||||
|
const CAPACITY: u32 = 15;
|
||||||
|
const S: u32 = 10;
|
||||||
|
|
||||||
|
fn from_repr(repr: FrRepr) -> Result<Self, PrimeFieldDecodingError> {
|
||||||
|
if repr.0[0] >= (MODULUS_R.0 as u64) {
|
||||||
|
Err(PrimeFieldDecodingError::NotInField(format!("{}", repr)))
|
||||||
|
} else {
|
||||||
|
Ok(Fr(Wrapping(repr.0[0] as u32)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_repr(&self) -> FrRepr {
|
||||||
|
FrRepr::from(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn char() -> FrRepr {
|
||||||
|
Fr(MODULUS_R).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiplicative_generator() -> Fr {
|
||||||
|
Fr(Wrapping(5))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_of_unity() -> Fr {
|
||||||
|
Fr(Wrapping(57751))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DummyEngine;
|
||||||
|
|
||||||
|
impl Engine for DummyEngine {
|
||||||
|
type Fr = Fr;
|
||||||
|
type G1 = Fr;
|
||||||
|
type G1Affine = Fr;
|
||||||
|
type G2 = Fr;
|
||||||
|
type G2Affine = Fr;
|
||||||
|
type Fq = Fr;
|
||||||
|
type Fqe = Fr;
|
||||||
|
|
||||||
|
// TODO: This should be F_645131 or something. Doesn't matter for now.
|
||||||
|
type Fqk = Fr;
|
||||||
|
|
||||||
|
fn miller_loop<'a, I>(i: I) -> Self::Fqk
|
||||||
|
where I: IntoIterator<Item=&'a (
|
||||||
|
&'a <Self::G1Affine as CurveAffine>::Prepared,
|
||||||
|
&'a <Self::G2Affine as CurveAffine>::Prepared
|
||||||
|
)>
|
||||||
|
{
|
||||||
|
let mut acc = <Fr as Field>::zero();
|
||||||
|
|
||||||
|
for &(a, b) in i {
|
||||||
|
let mut tmp = *a;
|
||||||
|
<Fr as Field>::mul_assign(&mut tmp, b);
|
||||||
|
<Fr as Field>::add_assign(&mut acc, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform final exponentiation of the result of a miller loop.
|
||||||
|
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk>
|
||||||
|
{
|
||||||
|
Some(*this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CurveProjective for Fr {
|
||||||
|
type Affine = Fr;
|
||||||
|
type Base = Fr;
|
||||||
|
type Scalar = Fr;
|
||||||
|
type Engine = DummyEngine;
|
||||||
|
|
||||||
|
fn zero() -> Self {
|
||||||
|
<Fr as Field>::zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
<Fr as Field>::one()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
<Fr as Field>::is_zero(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn batch_normalization(_: &mut [Self]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_normalized(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn double(&mut self) {
|
||||||
|
<Fr as Field>::double(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assign(&mut self, other: &Self) {
|
||||||
|
<Fr as Field>::add_assign(self, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assign_mixed(&mut self, other: &Self) {
|
||||||
|
<Fr as Field>::add_assign(self, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate(&mut self) {
|
||||||
|
<Fr as Field>::negate(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S)
|
||||||
|
{
|
||||||
|
let tmp = Fr::from_repr(other.into()).unwrap();
|
||||||
|
|
||||||
|
<Fr as Field>::mul_assign(self, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_affine(&self) -> Fr {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recommended_wnaf_for_scalar(_: <Self::Scalar as PrimeField>::Repr) -> usize {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recommended_wnaf_for_num_scalars(_: usize) -> usize {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct FakePoint;
|
||||||
|
|
||||||
|
impl AsMut<[u8]> for FakePoint {
|
||||||
|
fn as_mut(&mut self) -> &mut [u8] {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for FakePoint {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncodedPoint for FakePoint {
|
||||||
|
type Affine = Fr;
|
||||||
|
|
||||||
|
fn empty() -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size() -> usize {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_affine(_: Self::Affine) -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CurveAffine for Fr {
|
||||||
|
type Pair = Fr;
|
||||||
|
type PairingResult = Fr;
|
||||||
|
type Compressed = FakePoint;
|
||||||
|
type Uncompressed = FakePoint;
|
||||||
|
type Prepared = Fr;
|
||||||
|
type Projective = Fr;
|
||||||
|
type Base = Fr;
|
||||||
|
type Scalar = Fr;
|
||||||
|
type Engine = DummyEngine;
|
||||||
|
|
||||||
|
fn zero() -> Self {
|
||||||
|
<Fr as Field>::zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
<Fr as Field>::one()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
<Fr as Field>::is_zero(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate(&mut self) {
|
||||||
|
<Fr as Field>::negate(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective
|
||||||
|
{
|
||||||
|
let mut res = *self;
|
||||||
|
let tmp = Fr::from_repr(other.into()).unwrap();
|
||||||
|
|
||||||
|
<Fr as Field>::mul_assign(&mut res, &tmp);
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare(&self) -> Self::Prepared {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
|
||||||
|
self.mul(*other)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_projective(&self) -> Self::Projective {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
400
src/groth16/tests/mod.rs
Normal file
400
src/groth16/tests/mod.rs
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
use pairing::{
|
||||||
|
Engine,
|
||||||
|
Field,
|
||||||
|
PrimeField
|
||||||
|
};
|
||||||
|
|
||||||
|
mod dummy_engine;
|
||||||
|
use self::dummy_engine::*;
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use ::{
|
||||||
|
Circuit,
|
||||||
|
ConstraintSystem,
|
||||||
|
SynthesisError
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
generate_parameters,
|
||||||
|
prepare_verifying_key,
|
||||||
|
create_proof,
|
||||||
|
verify_proof
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XORDemo<E: Engine> {
|
||||||
|
a: Option<bool>,
|
||||||
|
b: Option<bool>,
|
||||||
|
_marker: PhantomData<E>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> Circuit<E> for XORDemo<E> {
|
||||||
|
fn synthesize<CS: ConstraintSystem<E>>(
|
||||||
|
self,
|
||||||
|
cs: &mut CS
|
||||||
|
) -> Result<(), SynthesisError>
|
||||||
|
{
|
||||||
|
let a_var = cs.alloc(|| "a", || {
|
||||||
|
if self.a.is_some() {
|
||||||
|
if self.a.unwrap() {
|
||||||
|
Ok(E::Fr::one())
|
||||||
|
} else {
|
||||||
|
Ok(E::Fr::zero())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SynthesisError::AssignmentMissing)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "a_boolean_constraint",
|
||||||
|
|lc| lc + CS::one() - a_var,
|
||||||
|
|lc| lc + a_var,
|
||||||
|
|lc| lc
|
||||||
|
);
|
||||||
|
|
||||||
|
let b_var = cs.alloc(|| "b", || {
|
||||||
|
if self.b.is_some() {
|
||||||
|
if self.b.unwrap() {
|
||||||
|
Ok(E::Fr::one())
|
||||||
|
} else {
|
||||||
|
Ok(E::Fr::zero())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SynthesisError::AssignmentMissing)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "b_boolean_constraint",
|
||||||
|
|lc| lc + CS::one() - b_var,
|
||||||
|
|lc| lc + b_var,
|
||||||
|
|lc| lc
|
||||||
|
);
|
||||||
|
|
||||||
|
let c_var = cs.alloc_input(|| "c", || {
|
||||||
|
if self.a.is_some() && self.b.is_some() {
|
||||||
|
if self.a.unwrap() ^ self.b.unwrap() {
|
||||||
|
Ok(E::Fr::one())
|
||||||
|
} else {
|
||||||
|
Ok(E::Fr::zero())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SynthesisError::AssignmentMissing)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
cs.enforce(
|
||||||
|
|| "c_xor_constraint",
|
||||||
|
|lc| lc + a_var + a_var,
|
||||||
|
|lc| lc + b_var,
|
||||||
|
|lc| lc + a_var + b_var - c_var
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_xordemo() {
|
||||||
|
let g1 = Fr::one();
|
||||||
|
let g2 = Fr::one();
|
||||||
|
let alpha = Fr::from_str("48577").unwrap();
|
||||||
|
let beta = Fr::from_str("22580").unwrap();
|
||||||
|
let gamma = Fr::from_str("53332").unwrap();
|
||||||
|
let delta = Fr::from_str("5481").unwrap();
|
||||||
|
let tau = Fr::from_str("3673").unwrap();
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
let c = XORDemo::<DummyEngine> {
|
||||||
|
a: None,
|
||||||
|
b: None,
|
||||||
|
_marker: PhantomData
|
||||||
|
};
|
||||||
|
|
||||||
|
generate_parameters(
|
||||||
|
c,
|
||||||
|
g1,
|
||||||
|
g2,
|
||||||
|
alpha,
|
||||||
|
beta,
|
||||||
|
gamma,
|
||||||
|
delta,
|
||||||
|
tau
|
||||||
|
).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// This will synthesize the constraint system:
|
||||||
|
//
|
||||||
|
// public inputs: a_0 = 1, a_1 = c
|
||||||
|
// aux inputs: a_2 = a, a_3 = b
|
||||||
|
// constraints:
|
||||||
|
// (a_0 - a_2) * (a_2) = 0
|
||||||
|
// (a_0 - a_3) * (a_3) = 0
|
||||||
|
// (a_2 + a_2) * (a_3) = (a_2 + a_3 - a_1)
|
||||||
|
// (a_0) * 0 = 0
|
||||||
|
// (a_1) * 0 = 0
|
||||||
|
|
||||||
|
// The evaluation domain is 8. The H query should
|
||||||
|
// have 7 elements (it's a quotient polynomial)
|
||||||
|
assert_eq!(7, params.h.len());
|
||||||
|
|
||||||
|
let mut root_of_unity = Fr::root_of_unity();
|
||||||
|
|
||||||
|
// We expect this to be a 2^10 root of unity
|
||||||
|
assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 10]));
|
||||||
|
|
||||||
|
// Let's turn it into a 2^3 root of unity.
|
||||||
|
root_of_unity = root_of_unity.pow(&[1 << 7]);
|
||||||
|
assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 3]));
|
||||||
|
assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity);
|
||||||
|
|
||||||
|
// Let's compute all the points in our evaluation domain.
|
||||||
|
let mut points = Vec::with_capacity(8);
|
||||||
|
for i in 0..8 {
|
||||||
|
points.push(root_of_unity.pow(&[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's compute t(tau) = (tau - p_0)(tau - p_1)...
|
||||||
|
// = tau^8 - 1
|
||||||
|
let mut t_at_tau = tau.pow(&[8]);
|
||||||
|
t_at_tau.sub_assign(&Fr::one());
|
||||||
|
{
|
||||||
|
let mut tmp = Fr::one();
|
||||||
|
for p in &points {
|
||||||
|
let mut term = tau;
|
||||||
|
term.sub_assign(p);
|
||||||
|
tmp.mul_assign(&term);
|
||||||
|
}
|
||||||
|
assert_eq!(tmp, t_at_tau);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect our H query to be 7 elements of the form...
|
||||||
|
// {tau^i t(tau) / delta}
|
||||||
|
let delta_inverse = delta.inverse().unwrap();
|
||||||
|
let gamma_inverse = gamma.inverse().unwrap();
|
||||||
|
{
|
||||||
|
let mut coeff = delta_inverse;
|
||||||
|
coeff.mul_assign(&t_at_tau);
|
||||||
|
|
||||||
|
let mut cur = Fr::one();
|
||||||
|
for h in params.h.iter() {
|
||||||
|
let mut tmp = cur;
|
||||||
|
tmp.mul_assign(&coeff);
|
||||||
|
|
||||||
|
assert_eq!(*h, tmp);
|
||||||
|
|
||||||
|
cur.mul_assign(&tau);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The density of the IC query is 2 (2 inputs)
|
||||||
|
assert_eq!(2, params.vk.ic.len());
|
||||||
|
|
||||||
|
// The density of the L query is 2 (2 aux variables)
|
||||||
|
assert_eq!(2, params.l.len());
|
||||||
|
|
||||||
|
// The density of the A query is 4 (each variable is in at least one A term)
|
||||||
|
assert_eq!(4, params.a.len());
|
||||||
|
|
||||||
|
// The density of the B query is 2 (two variables are in at least one B term)
|
||||||
|
assert_eq!(2, params.b_g1.len());
|
||||||
|
assert_eq!(2, params.b_g2.len());
|
||||||
|
|
||||||
|
/*
|
||||||
|
Lagrange interpolation polynomials in our evaluation domain:
|
||||||
|
|
||||||
|
,-------------------------------. ,-------------------------------. ,-------------------------------.
|
||||||
|
| A TERM | | B TERM | | C TERM |
|
||||||
|
`-------------------------------. `-------------------------------' `-------------------------------'
|
||||||
|
| a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 |
|
||||||
|
| 1 | 0 | 64512 | 0 | | 0 | 0 | 1 | 0 | | 0 | 0 | 0 | 0 |
|
||||||
|
| 1 | 0 | 0 | 64512 | | 0 | 0 | 0 | 1 | | 0 | 0 | 0 | 0 |
|
||||||
|
| 0 | 0 | 2 | 0 | | 0 | 0 | 0 | 1 | | 0 | 64512 | 1 | 1 |
|
||||||
|
| 1 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
|
||||||
|
| 0 | 1 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
|
||||||
|
`-------'-------'-------'-------' `-------'-------'-------'-------' `-------'-------'-------'-------'
|
||||||
|
|
||||||
|
Example for u_0:
|
||||||
|
|
||||||
|
sage: r = 64513
|
||||||
|
sage: Fr = GF(r)
|
||||||
|
sage: omega = (Fr(5)^63)^(2^7)
|
||||||
|
sage: tau = Fr(3673)
|
||||||
|
sage: R.<x> = PolynomialRing(Fr, 'x')
|
||||||
|
sage: def eval(tau, c0, c1, c2, c3, c4):
|
||||||
|
....: p = R.lagrange_polynomial([(omega^0, c0), (omega^1, c1), (omega^2, c2), (omega^3, c3), (omega^4, c4), (omega^5, 0), (omega^6, 0), (omega^7, 0)])
|
||||||
|
....: return p.substitute(tau)
|
||||||
|
sage: eval(tau, 1, 1, 0, 1, 0)
|
||||||
|
59158
|
||||||
|
*/
|
||||||
|
|
||||||
|
let u_i = [59158, 48317, 21767, 10402].iter().map(|e| {
|
||||||
|
Fr::from_str(&format!("{}", e)).unwrap()
|
||||||
|
}).collect::<Vec<Fr>>();
|
||||||
|
let v_i = [0, 0, 60619, 30791].iter().map(|e| {
|
||||||
|
Fr::from_str(&format!("{}", e)).unwrap()
|
||||||
|
}).collect::<Vec<Fr>>();
|
||||||
|
let w_i = [0, 23320, 41193, 41193].iter().map(|e| {
|
||||||
|
Fr::from_str(&format!("{}", e)).unwrap()
|
||||||
|
}).collect::<Vec<Fr>>();
|
||||||
|
|
||||||
|
for (u, a) in u_i.iter()
|
||||||
|
.zip(¶ms.a[..])
|
||||||
|
{
|
||||||
|
assert_eq!(u, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (v, b) in v_i.iter()
|
||||||
|
.filter(|&&e| e != Fr::zero())
|
||||||
|
.zip(¶ms.b_g1[..])
|
||||||
|
{
|
||||||
|
assert_eq!(v, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (v, b) in v_i.iter()
|
||||||
|
.filter(|&&e| e != Fr::zero())
|
||||||
|
.zip(¶ms.b_g2[..])
|
||||||
|
{
|
||||||
|
assert_eq!(v, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..4 {
|
||||||
|
let mut tmp1 = beta;
|
||||||
|
tmp1.mul_assign(&u_i[i]);
|
||||||
|
|
||||||
|
let mut tmp2 = alpha;
|
||||||
|
tmp2.mul_assign(&v_i[i]);
|
||||||
|
|
||||||
|
tmp1.add_assign(&tmp2);
|
||||||
|
tmp1.add_assign(&w_i[i]);
|
||||||
|
|
||||||
|
if i < 2 {
|
||||||
|
// Check the correctness of the IC query elements
|
||||||
|
tmp1.mul_assign(&gamma_inverse);
|
||||||
|
|
||||||
|
assert_eq!(tmp1, params.vk.ic[i]);
|
||||||
|
} else {
|
||||||
|
// Check the correctness of the L query elements
|
||||||
|
tmp1.mul_assign(&delta_inverse);
|
||||||
|
|
||||||
|
assert_eq!(tmp1, params.l[i - 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check consistency of the other elements
|
||||||
|
assert_eq!(alpha, params.vk.alpha_g1);
|
||||||
|
assert_eq!(beta, params.vk.beta_g1);
|
||||||
|
assert_eq!(beta, params.vk.beta_g2);
|
||||||
|
assert_eq!(gamma, params.vk.gamma_g2);
|
||||||
|
assert_eq!(delta, params.vk.delta_g1);
|
||||||
|
assert_eq!(delta, params.vk.delta_g2);
|
||||||
|
|
||||||
|
let pvk = prepare_verifying_key(¶ms.vk);
|
||||||
|
|
||||||
|
let r = Fr::from_str("27134").unwrap();
|
||||||
|
let s = Fr::from_str("17146").unwrap();
|
||||||
|
|
||||||
|
let proof = {
|
||||||
|
let c = XORDemo {
|
||||||
|
a: Some(true),
|
||||||
|
b: Some(false),
|
||||||
|
_marker: PhantomData
|
||||||
|
};
|
||||||
|
|
||||||
|
create_proof(
|
||||||
|
c,
|
||||||
|
¶ms,
|
||||||
|
r,
|
||||||
|
s
|
||||||
|
).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// A(x) =
|
||||||
|
// a_0 * (44865*x^7 + 56449*x^6 + 44865*x^5 + 8064*x^4 + 3520*x^3 + 56449*x^2 + 3520*x + 40321) +
|
||||||
|
// a_1 * (8064*x^7 + 56449*x^6 + 8064*x^5 + 56449*x^4 + 8064*x^3 + 56449*x^2 + 8064*x + 56449) +
|
||||||
|
// a_2 * (16983*x^7 + 24192*x^6 + 63658*x^5 + 56449*x^4 + 16983*x^3 + 24192*x^2 + 63658*x + 56449) +
|
||||||
|
// a_3 * (5539*x^7 + 27797*x^6 + 6045*x^5 + 56449*x^4 + 58974*x^3 + 36716*x^2 + 58468*x + 8064) +
|
||||||
|
{
|
||||||
|
// proof A = alpha + A(tau) + delta * r
|
||||||
|
let mut expected_a = delta;
|
||||||
|
expected_a.mul_assign(&r);
|
||||||
|
expected_a.add_assign(&alpha);
|
||||||
|
expected_a.add_assign(&u_i[0]); // a_0 = 1
|
||||||
|
expected_a.add_assign(&u_i[1]); // a_1 = 1
|
||||||
|
expected_a.add_assign(&u_i[2]); // a_2 = 1
|
||||||
|
// a_3 = 0
|
||||||
|
assert_eq!(proof.a, expected_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// B(x) =
|
||||||
|
// a_0 * (0) +
|
||||||
|
// a_1 * (0) +
|
||||||
|
// a_2 * (56449*x^7 + 56449*x^6 + 56449*x^5 + 56449*x^4 + 56449*x^3 + 56449*x^2 + 56449*x + 56449) +
|
||||||
|
// a_3 * (31177*x^7 + 44780*x^6 + 21752*x^5 + 42255*x^3 + 35861*x^2 + 33842*x + 48385)
|
||||||
|
{
|
||||||
|
// proof B = beta + B(tau) + delta * s
|
||||||
|
let mut expected_b = delta;
|
||||||
|
expected_b.mul_assign(&s);
|
||||||
|
expected_b.add_assign(&beta);
|
||||||
|
expected_b.add_assign(&v_i[0]); // a_0 = 1
|
||||||
|
expected_b.add_assign(&v_i[1]); // a_1 = 1
|
||||||
|
expected_b.add_assign(&v_i[2]); // a_2 = 1
|
||||||
|
// a_3 = 0
|
||||||
|
assert_eq!(proof.b, expected_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// C(x) =
|
||||||
|
// a_0 * (0) +
|
||||||
|
// a_1 * (27797*x^7 + 56449*x^6 + 36716*x^5 + 8064*x^4 + 27797*x^3 + 56449*x^2 + 36716*x + 8064) +
|
||||||
|
// a_2 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) +
|
||||||
|
// a_3 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449)
|
||||||
|
//
|
||||||
|
// If A * B = C at each point in the domain, then the following polynomial...
|
||||||
|
// P(x) = A(x) * B(x) - C(x)
|
||||||
|
// = 49752*x^14 + 13914*x^13 + 29243*x^12 + 27227*x^11 + 62362*x^10 + 35703*x^9 + 4032*x^8 + 14761*x^6 + 50599*x^5 + 35270*x^4 + 37286*x^3 + 2151*x^2 + 28810*x + 60481
|
||||||
|
//
|
||||||
|
// ... should be divisible by t(x), producing the quotient polynomial:
|
||||||
|
// h(x) = P(x) / t(x)
|
||||||
|
// = 49752*x^6 + 13914*x^5 + 29243*x^4 + 27227*x^3 + 62362*x^2 + 35703*x + 4032
|
||||||
|
{
|
||||||
|
let mut expected_c = Fr::zero();
|
||||||
|
|
||||||
|
// A * s
|
||||||
|
let mut tmp = proof.a;
|
||||||
|
tmp.mul_assign(&s);
|
||||||
|
expected_c.add_assign(&tmp);
|
||||||
|
|
||||||
|
// B * r
|
||||||
|
let mut tmp = proof.b;
|
||||||
|
tmp.mul_assign(&r);
|
||||||
|
expected_c.add_assign(&tmp);
|
||||||
|
|
||||||
|
// delta * r * s
|
||||||
|
let mut tmp = delta;
|
||||||
|
tmp.mul_assign(&r);
|
||||||
|
tmp.mul_assign(&s);
|
||||||
|
expected_c.sub_assign(&tmp);
|
||||||
|
|
||||||
|
// L query answer
|
||||||
|
// a_2 = 1, a_3 = 0
|
||||||
|
expected_c.add_assign(¶ms.l[0]);
|
||||||
|
|
||||||
|
// H query answer
|
||||||
|
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() {
|
||||||
|
let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
|
||||||
|
|
||||||
|
let mut tmp = params.h[i];
|
||||||
|
tmp.mul_assign(&coeff);
|
||||||
|
expected_c.add_assign(&tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(expected_c, proof.c);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(verify_proof(
|
||||||
|
&pvk,
|
||||||
|
&proof,
|
||||||
|
&[Fr::one()]
|
||||||
|
).unwrap());
|
||||||
|
}
|
66
src/groth16/verifier.rs
Normal file
66
src/groth16/verifier.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use pairing::{
|
||||||
|
Engine,
|
||||||
|
CurveProjective,
|
||||||
|
CurveAffine,
|
||||||
|
PrimeField
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
Proof,
|
||||||
|
VerifyingKey,
|
||||||
|
PreparedVerifyingKey
|
||||||
|
};
|
||||||
|
|
||||||
|
use ::{
|
||||||
|
SynthesisError
|
||||||
|
};
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_proof<'a, E: Engine>(
|
||||||
|
pvk: &'a PreparedVerifyingKey<E>,
|
||||||
|
proof: &Proof<E>,
|
||||||
|
public_inputs: &[E::Fr]
|
||||||
|
) -> Result<bool, SynthesisError>
|
||||||
|
{
|
||||||
|
if (public_inputs.len() + 1) != pvk.ic.len() {
|
||||||
|
return Err(SynthesisError::MalformedVerifyingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut acc = pvk.ic[0].into_projective();
|
||||||
|
|
||||||
|
for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) {
|
||||||
|
acc.add_assign(&b.mul(i.into_repr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()),
|
||||||
|
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
|
||||||
|
(&proof.c.prepare(), &pvk.neg_delta_g2)
|
||||||
|
].into_iter())
|
||||||
|
).unwrap() == pvk.alpha_g1_beta_g2)
|
||||||
|
}
|
692
src/lib.rs
692
src/lib.rs
@ -1,67 +1,119 @@
|
|||||||
extern crate pairing;
|
extern crate pairing;
|
||||||
|
extern crate rand;
|
||||||
|
extern crate num_cpus;
|
||||||
|
extern crate futures;
|
||||||
|
extern crate futures_cpupool;
|
||||||
|
extern crate bit_vec;
|
||||||
|
extern crate crossbeam;
|
||||||
|
|
||||||
|
pub mod multicore;
|
||||||
|
pub mod multiexp;
|
||||||
|
pub mod domain;
|
||||||
|
pub mod groth16;
|
||||||
|
|
||||||
use pairing::{Engine, Field};
|
use pairing::{Engine, Field};
|
||||||
|
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
/// Computations are expressed in terms of arithmetic circuits, in particular
|
||||||
|
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a
|
||||||
|
/// circuit that can be synthesized. The `synthesize` method is called during
|
||||||
|
/// CRS generation and during proving.
|
||||||
|
pub trait Circuit<E: Engine> {
|
||||||
|
/// Synthesize the circuit into a rank-1 quadratic constraint system
|
||||||
|
fn synthesize<CS: ConstraintSystem<E>>(
|
||||||
|
self,
|
||||||
|
cs: &mut CS
|
||||||
|
) -> Result<(), SynthesisError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a variable in our constraint system.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Variable(Index);
|
||||||
|
|
||||||
|
impl Variable {
|
||||||
|
/// This constructs a variable with an arbitrary index.
|
||||||
|
/// Circuit implementations are not recommended to use this.
|
||||||
|
pub fn new_unchecked(idx: Index) -> Variable {
|
||||||
|
Variable(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This returns the index underlying the variable.
|
||||||
|
/// Circuit implementations are not recommended to use this.
|
||||||
|
pub fn get_unchecked(&self) -> Index {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the index of either an input variable or
|
||||||
|
/// auxillary variable.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
pub enum Index {
|
||||||
|
Input(usize),
|
||||||
|
Aux(usize)
|
||||||
|
}
|
||||||
|
|
||||||
/// This represents a linear combination of some variables, with coefficients
|
/// This represents a linear combination of some variables, with coefficients
|
||||||
/// in the scalar field of a pairing-friendly elliptic curve group.
|
/// in the scalar field of a pairing-friendly elliptic curve group.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LinearCombination<T: Copy, E: Engine>(Vec<(T, E::Fr)>);
|
pub struct LinearCombination<E: Engine>(Vec<(Variable, E::Fr)>);
|
||||||
|
|
||||||
impl<T: Copy, E: Engine> AsRef<[(T, E::Fr)]> for LinearCombination<T, E> {
|
impl<E: Engine> AsRef<[(Variable, E::Fr)]> for LinearCombination<E> {
|
||||||
fn as_ref(&self) -> &[(T, E::Fr)] {
|
fn as_ref(&self) -> &[(Variable, E::Fr)] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy, E: Engine> LinearCombination<T, E> {
|
impl<E: Engine> LinearCombination<E> {
|
||||||
pub fn zero() -> LinearCombination<T, E> {
|
pub fn zero() -> LinearCombination<E> {
|
||||||
LinearCombination(vec![])
|
LinearCombination(vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy, E: Engine> Add<(E::Fr, T)> for LinearCombination<T, E> {
|
impl<E: Engine> Add<(E::Fr, Variable)> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<T, E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
fn add(mut self, (coeff, var): (E::Fr, T)) -> LinearCombination<T, E> {
|
fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||||
self.0.push((var, coeff));
|
self.0.push((var, coeff));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy, E: Engine> Sub<(E::Fr, T)> for LinearCombination<T, E> {
|
impl<E: Engine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<T, E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
fn sub(self, (mut coeff, var): (E::Fr, T)) -> LinearCombination<T, E> {
|
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||||
coeff.negate();
|
coeff.negate();
|
||||||
|
|
||||||
self + (coeff, var)
|
self + (coeff, var)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy, E: Engine> Add<T> for LinearCombination<T, E> {
|
impl<E: Engine> Add<Variable> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<T, E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
fn add(self, other: T) -> LinearCombination<T, E> {
|
fn add(self, other: Variable) -> LinearCombination<E> {
|
||||||
self + (E::Fr::one(), other)
|
self + (E::Fr::one(), other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy, E: Engine> Sub<T> for LinearCombination<T, E> {
|
impl<E: Engine> Sub<Variable> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<T, E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
fn sub(self, other: T) -> LinearCombination<T, E> {
|
fn sub(self, other: Variable) -> LinearCombination<E> {
|
||||||
self - (E::Fr::one(), other)
|
self - (E::Fr::one(), other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Copy, E: Engine> Add<&'a LinearCombination<T, E>> for LinearCombination<T, E> {
|
impl<'a, E: Engine> Add<&'a LinearCombination<E>> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<T, E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
fn add(mut self, other: &'a LinearCombination<T, E>) -> LinearCombination<T, E> {
|
fn add(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
|
||||||
for s in &other.0 {
|
for s in &other.0 {
|
||||||
self = self + (s.1, s.0);
|
self = self + (s.1, s.0);
|
||||||
}
|
}
|
||||||
@ -70,10 +122,10 @@ impl<'a, T: Copy, E: Engine> Add<&'a LinearCombination<T, E>> for LinearCombinat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Copy, E: Engine> Sub<&'a LinearCombination<T, E>> for LinearCombination<T, E> {
|
impl<'a, E: Engine> Sub<&'a LinearCombination<E>> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<T, E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
fn sub(mut self, other: &'a LinearCombination<T, E>) -> LinearCombination<T, E> {
|
fn sub(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
|
||||||
for s in &other.0 {
|
for s in &other.0 {
|
||||||
self = self - (s.1, s.0);
|
self = self - (s.1, s.0);
|
||||||
}
|
}
|
||||||
@ -82,10 +134,10 @@ impl<'a, T: Copy, E: Engine> Sub<&'a LinearCombination<T, E>> for LinearCombinat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Copy, E: Engine> Add<(E::Fr, &'a LinearCombination<T, E>)> for LinearCombination<T, E> {
|
impl<'a, E: Engine> Add<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<T, E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination<T, E>)) -> LinearCombination<T, E> {
|
fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
|
||||||
for s in &other.0 {
|
for s in &other.0 {
|
||||||
let mut tmp = s.1;
|
let mut tmp = s.1;
|
||||||
tmp.mul_assign(&coeff);
|
tmp.mul_assign(&coeff);
|
||||||
@ -96,10 +148,10 @@ impl<'a, T: Copy, E: Engine> Add<(E::Fr, &'a LinearCombination<T, E>)> for Linea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Copy, E: Engine> Sub<(E::Fr, &'a LinearCombination<T, E>)> for LinearCombination<T, E> {
|
impl<'a, E: Engine> Sub<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<T, E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination<T, E>)) -> LinearCombination<T, E> {
|
fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
|
||||||
for s in &other.0 {
|
for s in &other.0 {
|
||||||
let mut tmp = s.1;
|
let mut tmp = s.1;
|
||||||
tmp.mul_assign(&coeff);
|
tmp.mul_assign(&coeff);
|
||||||
@ -110,76 +162,71 @@ impl<'a, T: Copy, E: Engine> Sub<(E::Fr, &'a LinearCombination<T, E>)> for Linea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_lc() {
|
|
||||||
use pairing::bls12_381::{Bls12, Fr};
|
|
||||||
use pairing::PrimeField;
|
|
||||||
|
|
||||||
let a = LinearCombination::<usize, Bls12>::zero() + 0usize + 1usize + 2usize - 3usize;
|
|
||||||
|
|
||||||
let mut negone = Fr::one();
|
|
||||||
negone.negate();
|
|
||||||
|
|
||||||
assert_eq!(a.0, vec![(0usize, Fr::one()), (1usize, Fr::one()), (2usize, Fr::one()), (3usize, negone)]);
|
|
||||||
|
|
||||||
let x = LinearCombination::<usize, Bls12>::zero() + (Fr::one(), 0usize) - (Fr::one(), 1usize);
|
|
||||||
let y = LinearCombination::<usize, Bls12>::zero() + (Fr::one(), 2usize) - (Fr::one(), 3usize);
|
|
||||||
let z = x.clone() + &y - &y;
|
|
||||||
|
|
||||||
assert_eq!(z.0, vec![
|
|
||||||
(0usize, Fr::one()),
|
|
||||||
(1usize, negone),
|
|
||||||
(2usize, Fr::one()),
|
|
||||||
(3usize, negone),
|
|
||||||
(2usize, negone),
|
|
||||||
(3usize, Fr::one())
|
|
||||||
]);
|
|
||||||
|
|
||||||
let coeff = Fr::from_str("3").unwrap();
|
|
||||||
let mut neg_coeff = coeff;
|
|
||||||
neg_coeff.negate();
|
|
||||||
let z = x + (coeff, &y) - (coeff, &y);
|
|
||||||
|
|
||||||
assert_eq!(z.0, vec![
|
|
||||||
(0usize, Fr::one()),
|
|
||||||
(1usize, negone),
|
|
||||||
(2usize, Fr::from_str("3").unwrap()),
|
|
||||||
(3usize, neg_coeff),
|
|
||||||
(2usize, neg_coeff),
|
|
||||||
(3usize, Fr::from_str("3").unwrap())
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is an error that could occur during circuit synthesis contexts,
|
/// This is an error that could occur during circuit synthesis contexts,
|
||||||
/// such as CRS generation, proving or verification.
|
/// such as CRS generation, proving or verification.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SynthesisError {
|
pub enum SynthesisError {
|
||||||
AssignmentMissing
|
/// During synthesis, we lacked knowledge of a variable assignment.
|
||||||
|
AssignmentMissing,
|
||||||
|
/// During synthesis, we divided by zero.
|
||||||
|
DivisionByZero,
|
||||||
|
/// During synthesis, we constructed an unsatisfiable constraint system.
|
||||||
|
Unsatisfiable,
|
||||||
|
/// During synthesis, our polynomials ended up being too high of degree
|
||||||
|
PolynomialDegreeTooLarge,
|
||||||
|
/// During proof generation, we encountered an identity in the CRS
|
||||||
|
UnexpectedIdentity,
|
||||||
|
/// During proof generation, we encountered an I/O error with the CRS
|
||||||
|
IoError(io::Error),
|
||||||
|
/// During verification, our verifying key was malformed.
|
||||||
|
MalformedVerifyingKey,
|
||||||
|
/// During CRS generation, we observed an unconstrained auxillary variable
|
||||||
|
UnconstrainedVariable
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for SynthesisError {
|
||||||
|
fn from(e: io::Error) -> SynthesisError {
|
||||||
|
SynthesisError::IoError(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for SynthesisError {
|
impl Error for SynthesisError {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed"
|
SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed",
|
||||||
|
SynthesisError::DivisionByZero => "division by zero",
|
||||||
|
SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
|
||||||
|
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
|
||||||
|
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
|
||||||
|
SynthesisError::IoError(_) => "encountered an I/O error",
|
||||||
|
SynthesisError::MalformedVerifyingKey => "malformed verifying key",
|
||||||
|
SynthesisError::UnconstrainedVariable => "auxillary variable was unconstrained"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SynthesisError {
|
impl fmt::Display for SynthesisError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
if let &SynthesisError::IoError(ref e) = self {
|
||||||
|
write!(f, "I/O error: ")?;
|
||||||
|
e.fmt(f)
|
||||||
|
} else {
|
||||||
write!(f, "{}", self.description())
|
write!(f, "{}", self.description())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a constraint system which can have new variables
|
||||||
|
/// allocated and constrains between them formed.
|
||||||
pub trait ConstraintSystem<E: Engine>: Sized {
|
pub trait ConstraintSystem<E: Engine>: Sized {
|
||||||
type Variable: Sized + Copy + Clone;
|
|
||||||
|
|
||||||
/// Represents the type of the "root" of this constraint system
|
/// Represents the type of the "root" of this constraint system
|
||||||
/// so that nested namespaces can minimize indirection.
|
/// so that nested namespaces can minimize indirection.
|
||||||
type Root: ConstraintSystem<E, Variable=Self::Variable>;
|
type Root: ConstraintSystem<E>;
|
||||||
|
|
||||||
/// Return the "one" input variable
|
/// Return the "one" input variable
|
||||||
fn one(&self) -> Self::Variable;
|
fn one() -> Variable {
|
||||||
|
Variable::new_unchecked(Index::Input(0))
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocate a private variable in the constraint system. The provided function is used to
|
/// Allocate a private variable in the constraint system. The provided function is used to
|
||||||
/// determine the assignment of the variable. The given `annotation` function is invoked
|
/// determine the assignment of the variable. The given `annotation` function is invoked
|
||||||
@ -189,19 +236,31 @@ pub trait ConstraintSystem<E: Engine>: Sized {
|
|||||||
&mut self,
|
&mut self,
|
||||||
annotation: A,
|
annotation: A,
|
||||||
f: F
|
f: F
|
||||||
) -> Result<Self::Variable, SynthesisError>
|
) -> Result<Variable, SynthesisError>
|
||||||
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
|
||||||
|
|
||||||
|
/// Allocate a public variable in the constraint system. The provided function is used to
|
||||||
|
/// determine the assignment of the variable.
|
||||||
|
fn alloc_input<F, A, AR>(
|
||||||
|
&mut self,
|
||||||
|
annotation: A,
|
||||||
|
f: F
|
||||||
|
) -> Result<Variable, SynthesisError>
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
|
||||||
|
|
||||||
/// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
|
/// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
|
||||||
/// in order to derive a unique name for the constraint in the current namespace.
|
/// in order to derive a unique name for the constraint in the current namespace.
|
||||||
fn enforce<A, AR>(
|
fn enforce<A, AR, LA, LB, LC>(
|
||||||
&mut self,
|
&mut self,
|
||||||
annotation: A,
|
annotation: A,
|
||||||
a: LinearCombination<Self::Variable, E>,
|
a: LA,
|
||||||
b: LinearCombination<Self::Variable, E>,
|
b: LB,
|
||||||
c: LinearCombination<Self::Variable, E>
|
c: LC
|
||||||
)
|
)
|
||||||
where A: FnOnce() -> AR, AR: Into<String>;
|
where A: FnOnce() -> AR, AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
|
||||||
|
|
||||||
/// Create a new (sub)namespace and enter into it. Not intended
|
/// Create a new (sub)namespace and enter into it. Not intended
|
||||||
/// for downstream use; use `namespace` instead.
|
/// for downstream use; use `namespace` instead.
|
||||||
@ -229,89 +288,48 @@ pub trait ConstraintSystem<E: Engine>: Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PublicConstraintSystem<E: Engine>: ConstraintSystem<E>
|
|
||||||
{
|
|
||||||
/// Represents the type of the "root" of this constraint system
|
|
||||||
/// so that nested namespaces can minimize indirection.
|
|
||||||
type PublicRoot: PublicConstraintSystem<E, Variable=Self::Variable>;
|
|
||||||
|
|
||||||
/// Allocate a public variable in the constraint system. The provided function is used to
|
|
||||||
/// determine the assignment of the variable.
|
|
||||||
fn alloc_input<F, A, AR>(
|
|
||||||
&mut self,
|
|
||||||
annotation: A,
|
|
||||||
f: F
|
|
||||||
) -> Result<Self::Variable, SynthesisError>
|
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
|
|
||||||
|
|
||||||
/// Gets the "root" constraint system, bypassing the namespacing.
|
|
||||||
/// Not intended for downstream use; use `namespace` instead.
|
|
||||||
fn get_public_root(&mut self) -> &mut Self::PublicRoot;
|
|
||||||
|
|
||||||
/// Begin a namespace for this constraint system.
|
|
||||||
fn namespace_public<'a, NR, N>(
|
|
||||||
&'a mut self,
|
|
||||||
name_fn: N
|
|
||||||
) -> Namespace<'a, E, Self::PublicRoot>
|
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
|
||||||
self.get_root().push_namespace(name_fn);
|
|
||||||
|
|
||||||
Namespace(self.get_public_root(), PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
|
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
|
||||||
/// a namespace context) and, when dropped, pops out of the namespace context.
|
/// a namespace context) and, when dropped, pops out of the namespace context.
|
||||||
pub struct Namespace<'a, E: Engine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>);
|
pub struct Namespace<'a, E: Engine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>);
|
||||||
|
|
||||||
impl<'cs, E: Engine, CS: PublicConstraintSystem<E>> PublicConstraintSystem<E> for Namespace<'cs, E, CS> {
|
|
||||||
type PublicRoot = CS::PublicRoot;
|
|
||||||
|
|
||||||
fn alloc_input<F, A, AR>(
|
|
||||||
&mut self,
|
|
||||||
annotation: A,
|
|
||||||
f: F
|
|
||||||
) -> Result<Self::Variable, SynthesisError>
|
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
|
||||||
self.0.alloc_input(annotation, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_public_root(&mut self) -> &mut Self::PublicRoot
|
|
||||||
{
|
|
||||||
self.0.get_public_root()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
|
impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
|
||||||
type Variable = CS::Variable;
|
|
||||||
type Root = CS::Root;
|
type Root = CS::Root;
|
||||||
|
|
||||||
fn one(&self) -> Self::Variable {
|
fn one() -> Variable {
|
||||||
self.0.one()
|
CS::one()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc<F, A, AR>(
|
fn alloc<F, A, AR>(
|
||||||
&mut self,
|
&mut self,
|
||||||
annotation: A,
|
annotation: A,
|
||||||
f: F
|
f: F
|
||||||
) -> Result<Self::Variable, SynthesisError>
|
) -> Result<Variable, SynthesisError>
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
{
|
{
|
||||||
self.0.alloc(annotation, f)
|
self.0.alloc(annotation, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce<A, AR>(
|
fn alloc_input<F, A, AR>(
|
||||||
&mut self,
|
&mut self,
|
||||||
annotation: A,
|
annotation: A,
|
||||||
a: LinearCombination<Self::Variable, E>,
|
f: F
|
||||||
b: LinearCombination<Self::Variable, E>,
|
) -> Result<Variable, SynthesisError>
|
||||||
c: LinearCombination<Self::Variable, E>
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
|
{
|
||||||
|
self.0.alloc_input(annotation, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(
|
||||||
|
&mut self,
|
||||||
|
annotation: A,
|
||||||
|
a: LA,
|
||||||
|
b: LB,
|
||||||
|
c: LC
|
||||||
)
|
)
|
||||||
where A: FnOnce() -> AR, AR: Into<String>
|
where A: FnOnce() -> AR, AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
||||||
{
|
{
|
||||||
self.0.enforce(annotation, a, b, c)
|
self.0.enforce(annotation, a, b, c)
|
||||||
}
|
}
|
||||||
@ -343,55 +361,46 @@ impl<'a, E: Engine, CS: ConstraintSystem<E>> Drop for Namespace<'a, E, CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience implementation of PublicConstraintSystem<E> for mutable references to
|
|
||||||
/// public constraint systems.
|
|
||||||
impl<'cs, E: Engine, CS: PublicConstraintSystem<E>> PublicConstraintSystem<E> for &'cs mut CS {
|
|
||||||
type PublicRoot = CS::PublicRoot;
|
|
||||||
|
|
||||||
fn alloc_input<F, A, AR>(
|
|
||||||
&mut self,
|
|
||||||
annotation: A,
|
|
||||||
f: F
|
|
||||||
) -> Result<Self::Variable, SynthesisError>
|
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
|
||||||
(**self).alloc_input(annotation, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_public_root(&mut self) -> &mut Self::PublicRoot
|
|
||||||
{
|
|
||||||
(**self).get_public_root()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience implementation of ConstraintSystem<E> for mutable references to
|
/// Convenience implementation of ConstraintSystem<E> for mutable references to
|
||||||
/// constraint systems.
|
/// constraint systems.
|
||||||
impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs mut CS {
|
impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs mut CS {
|
||||||
type Variable = CS::Variable;
|
|
||||||
type Root = CS::Root;
|
type Root = CS::Root;
|
||||||
|
|
||||||
fn one(&self) -> Self::Variable {
|
fn one() -> Variable {
|
||||||
(**self).one()
|
CS::one()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc<F, A, AR>(
|
fn alloc<F, A, AR>(
|
||||||
&mut self,
|
&mut self,
|
||||||
annotation: A,
|
annotation: A,
|
||||||
f: F
|
f: F
|
||||||
) -> Result<Self::Variable, SynthesisError>
|
) -> Result<Variable, SynthesisError>
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
{
|
{
|
||||||
(**self).alloc(annotation, f)
|
(**self).alloc(annotation, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce<A, AR>(
|
fn alloc_input<F, A, AR>(
|
||||||
&mut self,
|
&mut self,
|
||||||
annotation: A,
|
annotation: A,
|
||||||
a: LinearCombination<Self::Variable, E>,
|
f: F
|
||||||
b: LinearCombination<Self::Variable, E>,
|
) -> Result<Variable, SynthesisError>
|
||||||
c: LinearCombination<Self::Variable, E>
|
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
|
{
|
||||||
|
(**self).alloc_input(annotation, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce<A, AR, LA, LB, LC>(
|
||||||
|
&mut self,
|
||||||
|
annotation: A,
|
||||||
|
a: LA,
|
||||||
|
b: LB,
|
||||||
|
c: LC
|
||||||
)
|
)
|
||||||
where A: FnOnce() -> AR, AR: Into<String>
|
where A: FnOnce() -> AR, AR: Into<String>,
|
||||||
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
||||||
{
|
{
|
||||||
(**self).enforce(annotation, a, b, c)
|
(**self).enforce(annotation, a, b, c)
|
||||||
}
|
}
|
||||||
@ -413,180 +422,221 @@ impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs mut C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_cs() {
|
// fn test_cs() {
|
||||||
use pairing::bls12_381::{Bls12, Fr};
|
// use pairing::bls12_381::{Bls12, Fr};
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
// struct MySillyConstraintSystem<E: Engine> {
|
||||||
enum Var {
|
// inputs: Vec<(E::Fr, String)>,
|
||||||
Input(usize),
|
// aux: Vec<(E::Fr, String)>,
|
||||||
Aux(usize)
|
// constraints: Vec<(LinearCombination<E>, LinearCombination<E>, LinearCombination<E>, String)>,
|
||||||
}
|
// current_namespace: Vec<String>
|
||||||
|
// }
|
||||||
|
|
||||||
struct MySillyConstraintSystem<E: Engine> {
|
// fn compute_path(ns: &[String], this: String) -> String {
|
||||||
inputs: Vec<(E::Fr, String)>,
|
// let mut name = String::new();
|
||||||
aux: Vec<(E::Fr, String)>,
|
|
||||||
constraints: Vec<(LinearCombination<Var, E>, LinearCombination<Var, E>, LinearCombination<Var, E>, String)>,
|
|
||||||
current_namespace: Vec<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_path(ns: &[String], this: String) -> String {
|
// let mut needs_separation = false;
|
||||||
let mut name = String::new();
|
// for ns in ns.iter().chain(Some(&this).into_iter())
|
||||||
|
// {
|
||||||
|
// if needs_separation {
|
||||||
|
// name += "/";
|
||||||
|
// }
|
||||||
|
|
||||||
let mut needs_separation = false;
|
// name += ns;
|
||||||
for ns in ns.iter().chain(Some(&this).into_iter())
|
// needs_separation = true;
|
||||||
{
|
// }
|
||||||
if needs_separation {
|
|
||||||
name += "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
name += ns;
|
// name
|
||||||
needs_separation = true;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
name
|
// impl<E: Engine> PublicConstraintSystem<E> for MySillyConstraintSystem<E> {
|
||||||
}
|
// type PublicRoot = Self;
|
||||||
|
|
||||||
impl<E: Engine> PublicConstraintSystem<E> for MySillyConstraintSystem<E> {
|
// fn alloc_input<F, A, AR>(
|
||||||
type PublicRoot = Self;
|
// &mut self,
|
||||||
|
// annotation: A,
|
||||||
|
// f: F
|
||||||
|
// ) -> Result<Variable, SynthesisError>
|
||||||
|
// where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
|
// {
|
||||||
|
// let index = self.inputs.len();
|
||||||
|
// let path = compute_path(&self.current_namespace, annotation().into());
|
||||||
|
// self.inputs.push((f()?, path));
|
||||||
|
|
||||||
fn alloc_input<F, A, AR>(
|
// Ok(Var::Input(index))
|
||||||
&mut self,
|
// }
|
||||||
annotation: A,
|
|
||||||
f: F
|
|
||||||
) -> Result<Self::Variable, SynthesisError>
|
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
|
||||||
let index = self.inputs.len();
|
|
||||||
let path = compute_path(&self.current_namespace, annotation().into());
|
|
||||||
self.inputs.push((f()?, path));
|
|
||||||
|
|
||||||
Ok(Var::Input(index))
|
// fn get_public_root(&mut self) -> &mut Self::PublicRoot
|
||||||
}
|
// {
|
||||||
|
// self
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
fn get_public_root(&mut self) -> &mut Self::PublicRoot
|
// impl<E: Engine> ConstraintSystem<E> for MySillyConstraintSystem<E> {
|
||||||
{
|
// type Variable = Var;
|
||||||
self
|
// type Root = Self;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> ConstraintSystem<E> for MySillyConstraintSystem<E> {
|
// fn one(&self) -> Variable {
|
||||||
type Variable = Var;
|
// Var::Input(0)
|
||||||
type Root = Self;
|
// }
|
||||||
|
|
||||||
fn one(&self) -> Self::Variable {
|
// fn alloc<F, A, AR>(
|
||||||
Var::Input(0)
|
// &mut self,
|
||||||
}
|
// annotation: A,
|
||||||
|
// f: F
|
||||||
|
// ) -> Result<Variable, SynthesisError>
|
||||||
|
// where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
||||||
|
// {
|
||||||
|
// let index = self.aux.len();
|
||||||
|
// let path = compute_path(&self.current_namespace, annotation().into());
|
||||||
|
// self.aux.push((f()?, path));
|
||||||
|
|
||||||
fn alloc<F, A, AR>(
|
// Ok(Var::Aux(index))
|
||||||
&mut self,
|
// }
|
||||||
annotation: A,
|
|
||||||
f: F
|
|
||||||
) -> Result<Self::Variable, SynthesisError>
|
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
|
||||||
let index = self.aux.len();
|
|
||||||
let path = compute_path(&self.current_namespace, annotation().into());
|
|
||||||
self.aux.push((f()?, path));
|
|
||||||
|
|
||||||
Ok(Var::Aux(index))
|
// fn enforce<A, AR, LA, LB, LC>(
|
||||||
}
|
// &mut self,
|
||||||
|
// annotation: A,
|
||||||
|
// a: LA,
|
||||||
|
// b: LB,
|
||||||
|
// c: LC
|
||||||
|
// )
|
||||||
|
// where A: FnOnce() -> AR, AR: Into<String>,
|
||||||
|
// LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
// LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
|
// LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
||||||
|
// {
|
||||||
|
// let path = compute_path(&self.current_namespace, annotation().into());
|
||||||
|
|
||||||
fn enforce<A, AR>(
|
// let a = a(LinearCombination::zero());
|
||||||
&mut self,
|
// let b = b(LinearCombination::zero());
|
||||||
annotation: A,
|
// let c = c(LinearCombination::zero());
|
||||||
a: LinearCombination<Self::Variable, E>,
|
|
||||||
b: LinearCombination<Self::Variable, E>,
|
|
||||||
c: LinearCombination<Self::Variable, E>
|
|
||||||
)
|
|
||||||
where A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
|
||||||
let path = compute_path(&self.current_namespace, annotation().into());
|
|
||||||
|
|
||||||
self.constraints.push((a, b, c, path));
|
// self.constraints.push((a, b, c, path));
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
// fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
// where NR: Into<String>, N: FnOnce() -> NR
|
||||||
{
|
// {
|
||||||
self.current_namespace.push(name_fn().into());
|
// self.current_namespace.push(name_fn().into());
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
// fn pop_namespace(&mut self)
|
||||||
{
|
// {
|
||||||
self.current_namespace.pop();
|
// self.current_namespace.pop();
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn get_root(&mut self) -> &mut Self::Root
|
// fn get_root(&mut self) -> &mut Self::Root
|
||||||
{
|
// {
|
||||||
self
|
// self
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn do_stuff_with_pcs<E: Engine, CS: PublicConstraintSystem<E>>(mut cs: CS, one_more: bool)
|
// fn do_stuff_with_pcs<E: Engine, CS: PublicConstraintSystem<E>>(mut cs: CS, one_more: bool)
|
||||||
{
|
// {
|
||||||
cs.alloc_input(|| "something", || Ok(E::Fr::zero())).unwrap();
|
// cs.alloc_input(|| "something", || Ok(E::Fr::zero())).unwrap();
|
||||||
|
|
||||||
if one_more {
|
// if one_more {
|
||||||
do_stuff_with_pcs(cs.namespace_public(|| "cool namespace"), false);
|
// do_stuff_with_pcs(cs.namespace_public(|| "cool namespace"), false);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
let mut cs = MySillyConstraintSystem::<Bls12> {
|
// let mut cs = MySillyConstraintSystem::<Bls12> {
|
||||||
inputs: vec![(Fr::one(), "ONE".into())],
|
// inputs: vec![(Fr::one(), "ONE".into())],
|
||||||
aux: vec![],
|
// aux: vec![],
|
||||||
constraints: vec![],
|
// constraints: vec![],
|
||||||
current_namespace: vec![]
|
// current_namespace: vec![]
|
||||||
};
|
// };
|
||||||
cs.alloc(|| "something", || Ok(Fr::zero())).unwrap();
|
// cs.alloc(|| "something", || Ok(Fr::zero())).unwrap();
|
||||||
assert_eq!(cs.inputs, vec![(Fr::one(), "ONE".into())]);
|
// assert_eq!(cs.inputs, vec![(Fr::one(), "ONE".into())]);
|
||||||
assert_eq!(cs.aux, vec![(Fr::zero(), "something".into())]);
|
// assert_eq!(cs.aux, vec![(Fr::zero(), "something".into())]);
|
||||||
{
|
// {
|
||||||
let mut cs = cs.namespace(|| "woohoo");
|
// let mut cs = cs.namespace(|| "woohoo");
|
||||||
|
|
||||||
cs.alloc(|| "whatever", || Ok(Fr::one())).unwrap();
|
// cs.alloc(|| "whatever", || Ok(Fr::one())).unwrap();
|
||||||
cs.alloc(|| "you", || Ok(Fr::zero())).unwrap();
|
// cs.alloc(|| "you", || Ok(Fr::zero())).unwrap();
|
||||||
cs.alloc(|| "say", || Ok(Fr::one())).unwrap();
|
// cs.alloc(|| "say", || Ok(Fr::one())).unwrap();
|
||||||
|
|
||||||
{
|
// {
|
||||||
let mut cs = cs.namespace(|| "hehe");
|
// let mut cs = cs.namespace(|| "hehe");
|
||||||
|
|
||||||
let v1 = cs.alloc(|| "hehe, indeed", || Ok(Fr::one())).unwrap();
|
// let v1 = cs.alloc(|| "hehe, indeed", || Ok(Fr::one())).unwrap();
|
||||||
let v2 = cs.alloc_input(|| "works lol", || Ok(Fr::zero())).unwrap();
|
// let v2 = cs.alloc_input(|| "works lol", || Ok(Fr::zero())).unwrap();
|
||||||
|
|
||||||
let one = cs.one();
|
// let one = cs.one();
|
||||||
|
|
||||||
cs.enforce(
|
// cs.enforce(
|
||||||
|| "great constraint",
|
// || "great constraint",
|
||||||
LinearCombination::zero() + v1,
|
// |lc| lc + v1,
|
||||||
LinearCombination::zero() + one,
|
// |lc| lc + one,
|
||||||
LinearCombination::zero() + v2
|
// |lc| lc + v2
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
assert_eq!(cs.aux, vec![
|
// assert_eq!(cs.aux, vec![
|
||||||
(Fr::zero(), "something".into()),
|
// (Fr::zero(), "something".into()),
|
||||||
(Fr::one(), "woohoo/whatever".into()),
|
// (Fr::one(), "woohoo/whatever".into()),
|
||||||
(Fr::zero(), "woohoo/you".into()),
|
// (Fr::zero(), "woohoo/you".into()),
|
||||||
(Fr::one(), "woohoo/say".into()),
|
// (Fr::one(), "woohoo/say".into()),
|
||||||
(Fr::one(), "woohoo/hehe/hehe, indeed".into()),
|
// (Fr::one(), "woohoo/hehe/hehe, indeed".into()),
|
||||||
]);
|
// ]);
|
||||||
assert_eq!(cs.inputs, vec![
|
// assert_eq!(cs.inputs, vec![
|
||||||
(Fr::one(), "ONE".into()),
|
// (Fr::one(), "ONE".into()),
|
||||||
(Fr::zero(), "woohoo/hehe/works lol".into()),
|
// (Fr::zero(), "woohoo/hehe/works lol".into()),
|
||||||
]);
|
// ]);
|
||||||
assert!(cs.constraints.len() == 1);
|
// assert!(cs.constraints.len() == 1);
|
||||||
assert!((cs.constraints[0].0).0 == vec![(Var::Aux(4), Fr::one())]);
|
// assert!((cs.constraints[0].0).0 == vec![(Var::Aux(4), Fr::one())]);
|
||||||
assert!((cs.constraints[0].1).0 == vec![(Var::Input(0), Fr::one())]);
|
// assert!((cs.constraints[0].1).0 == vec![(Var::Input(0), Fr::one())]);
|
||||||
assert!((cs.constraints[0].2).0 == vec![(Var::Input(1), Fr::one())]);
|
// assert!((cs.constraints[0].2).0 == vec![(Var::Input(1), Fr::one())]);
|
||||||
assert!(cs.constraints[0].3 == "woohoo/hehe/great constraint");
|
// assert!(cs.constraints[0].3 == "woohoo/hehe/great constraint");
|
||||||
|
|
||||||
do_stuff_with_pcs(cs.namespace(|| "namey"), true);
|
// do_stuff_with_pcs(cs.namespace(|| "namey"), true);
|
||||||
|
|
||||||
assert_eq!(cs.inputs, vec![
|
// assert_eq!(cs.inputs, vec![
|
||||||
(Fr::one(), "ONE".into()),
|
// (Fr::one(), "ONE".into()),
|
||||||
(Fr::zero(), "woohoo/hehe/works lol".into()),
|
// (Fr::zero(), "woohoo/hehe/works lol".into()),
|
||||||
(Fr::zero(), "namey/something".into()),
|
// (Fr::zero(), "namey/something".into()),
|
||||||
(Fr::zero(), "namey/cool namespace/something".into()),
|
// (Fr::zero(), "namey/cool namespace/something".into()),
|
||||||
]);
|
// ]);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn test_lc() {
|
||||||
|
// use pairing::bls12_381::{Bls12, Fr};
|
||||||
|
// use pairing::PrimeField;
|
||||||
|
|
||||||
|
// let a = LinearCombination::<Bls12>::zero() + 0usize + 1usize + 2usize - 3usize;
|
||||||
|
|
||||||
|
// let mut negone = Fr::one();
|
||||||
|
// negone.negate();
|
||||||
|
|
||||||
|
// assert_eq!(a.0, vec![(0usize, Fr::one()), (1usize, Fr::one()), (2usize, Fr::one()), (3usize, negone)]);
|
||||||
|
|
||||||
|
// let x = LinearCombination::<Bls12>::zero() + (Fr::one(), 0usize) - (Fr::one(), 1usize);
|
||||||
|
// let y = LinearCombination::<Bls12>::zero() + (Fr::one(), 2usize) - (Fr::one(), 3usize);
|
||||||
|
// let z = x.clone() + &y - &y;
|
||||||
|
|
||||||
|
// assert_eq!(z.0, vec![
|
||||||
|
// (0usize, Fr::one()),
|
||||||
|
// (1usize, negone),
|
||||||
|
// (2usize, Fr::one()),
|
||||||
|
// (3usize, negone),
|
||||||
|
// (2usize, negone),
|
||||||
|
// (3usize, Fr::one())
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// let coeff = Fr::from_str("3").unwrap();
|
||||||
|
// let mut neg_coeff = coeff;
|
||||||
|
// neg_coeff.negate();
|
||||||
|
// let z = x + (coeff, &y) - (coeff, &y);
|
||||||
|
|
||||||
|
// assert_eq!(z.0, vec![
|
||||||
|
// (0usize, Fr::one()),
|
||||||
|
// (1usize, negone),
|
||||||
|
// (2usize, Fr::from_str("3").unwrap()),
|
||||||
|
// (3usize, neg_coeff),
|
||||||
|
// (2usize, neg_coeff),
|
||||||
|
// (3usize, Fr::from_str("3").unwrap())
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
106
src/multicore.rs
Normal file
106
src/multicore.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
//! This is an interface for dealing with the kinds of
|
||||||
|
//! parallel computations involved in bellman. It's
|
||||||
|
//! currently just a thin wrapper around CpuPool and
|
||||||
|
//! crossbeam but may be extended in the future to
|
||||||
|
//! allow for various parallelism strategies.
|
||||||
|
|
||||||
|
use num_cpus;
|
||||||
|
use futures::{Future, IntoFuture, Poll};
|
||||||
|
use futures_cpupool::{CpuPool, CpuFuture};
|
||||||
|
use crossbeam::{self, Scope};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Worker {
|
||||||
|
cpus: usize,
|
||||||
|
pool: CpuPool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Worker {
|
||||||
|
// We don't expose this outside the library so that
|
||||||
|
// all `Worker` instances have the same number of
|
||||||
|
// CPUs configured.
|
||||||
|
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
|
||||||
|
Worker {
|
||||||
|
cpus: cpus,
|
||||||
|
pool: CpuPool::new(cpus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Worker {
|
||||||
|
Self::new_with_cpus(num_cpus::get())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_num_cpus(&self) -> u32 {
|
||||||
|
log2_floor(self.cpus)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute<F, R>(
|
||||||
|
&self, f: F
|
||||||
|
) -> WorkerFuture<R::Item, R::Error>
|
||||||
|
where F: FnOnce() -> R + Send + 'static,
|
||||||
|
R: IntoFuture + 'static,
|
||||||
|
R::Future: Send + 'static,
|
||||||
|
R::Item: Send + 'static,
|
||||||
|
R::Error: Send + 'static
|
||||||
|
{
|
||||||
|
WorkerFuture {
|
||||||
|
future: self.pool.spawn_fn(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope<'a, F, R>(
|
||||||
|
&self,
|
||||||
|
elements: usize,
|
||||||
|
f: F
|
||||||
|
) -> R
|
||||||
|
where F: FnOnce(&Scope<'a>, usize) -> R
|
||||||
|
{
|
||||||
|
let chunk_size = if elements < self.cpus {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
elements / self.cpus
|
||||||
|
};
|
||||||
|
|
||||||
|
crossbeam::scope(|scope| {
|
||||||
|
f(scope, chunk_size)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WorkerFuture<T, E> {
|
||||||
|
future: CpuFuture<T, E>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
|
||||||
|
type Item = T;
|
||||||
|
type Error = E;
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error>
|
||||||
|
{
|
||||||
|
self.future.poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
@ -1,12 +1,19 @@
|
|||||||
use pairing::*;
|
use pairing::{
|
||||||
|
CurveAffine,
|
||||||
|
CurveProjective,
|
||||||
|
Engine,
|
||||||
|
PrimeField,
|
||||||
|
Field,
|
||||||
|
PrimeFieldRepr
|
||||||
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::io;
|
use std::io;
|
||||||
use bit_vec::{self, BitVec};
|
use bit_vec::{self, BitVec};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use futures::{Future};
|
use futures::{Future};
|
||||||
use futures_cpupool::CpuPool;
|
use super::multicore::Worker;
|
||||||
|
|
||||||
use super::Error;
|
use super::SynthesisError;
|
||||||
|
|
||||||
/// An object that builds a source of bases.
|
/// An object that builds a source of bases.
|
||||||
pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
|
pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
|
||||||
@ -18,10 +25,10 @@ pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
|
|||||||
/// A source of bases, like an iterator.
|
/// A source of bases, like an iterator.
|
||||||
pub trait Source<G: CurveAffine> {
|
pub trait Source<G: CurveAffine> {
|
||||||
/// Parses the element from the source. Fails if the point is at infinity.
|
/// 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>;
|
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError>;
|
||||||
|
|
||||||
/// Skips `amt` elements from the source, avoiding deserialization.
|
/// Skips `amt` elements from the source, avoiding deserialization.
|
||||||
fn skip(&mut self, amt: usize) -> Result<(), Error>;
|
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
||||||
@ -33,13 +40,13 @@ impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
||||||
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), Error> {
|
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError> {
|
||||||
if self.0.len() <= self.1 {
|
if self.0.len() <= self.1 {
|
||||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.0[self.1].is_zero() {
|
if self.0[self.1].is_zero() {
|
||||||
return Err(Error::UnexpectedIdentity)
|
return Err(SynthesisError::UnexpectedIdentity)
|
||||||
}
|
}
|
||||||
|
|
||||||
to.add_assign_mixed(&self.0[self.1]);
|
to.add_assign_mixed(&self.0[self.1]);
|
||||||
@ -49,7 +56,7 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip(&mut self, amt: usize) -> Result<(), Error> {
|
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
|
||||||
if self.0.len() <= self.1 {
|
if self.0.len() <= self.1 {
|
||||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
||||||
}
|
}
|
||||||
@ -131,14 +138,14 @@ impl DensityTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn multiexp_inner<Q, D, G, S>(
|
fn multiexp_inner<Q, D, G, S>(
|
||||||
pool: &CpuPool,
|
pool: &Worker,
|
||||||
bases: S,
|
bases: S,
|
||||||
density_map: D,
|
density_map: D,
|
||||||
exponents: Arc<Vec<<<G::Engine as Engine>::Fr as PrimeField>::Repr>>,
|
exponents: Arc<Vec<<<G::Engine as Engine>::Fr as PrimeField>::Repr>>,
|
||||||
mut skip: u32,
|
mut skip: u32,
|
||||||
c: u32,
|
c: u32,
|
||||||
handle_trivial: bool
|
handle_trivial: bool
|
||||||
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=Error>>
|
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
|
||||||
where for<'a> &'a Q: QueryDensity,
|
where for<'a> &'a Q: QueryDensity,
|
||||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
G: CurveAffine,
|
G: CurveAffine,
|
||||||
@ -150,7 +157,7 @@ fn multiexp_inner<Q, D, G, S>(
|
|||||||
let exponents = exponents.clone();
|
let exponents = exponents.clone();
|
||||||
let density_map = density_map.clone();
|
let density_map = density_map.clone();
|
||||||
|
|
||||||
pool.spawn_fn(move || {
|
pool.compute(move || {
|
||||||
// Accumulate the result
|
// Accumulate the result
|
||||||
let mut acc = G::Projective::zero();
|
let mut acc = G::Projective::zero();
|
||||||
|
|
||||||
@ -228,11 +235,11 @@ fn multiexp_inner<Q, D, G, S>(
|
|||||||
/// Perform multi-exponentiation. The caller is responsible for ensuring the
|
/// Perform multi-exponentiation. The caller is responsible for ensuring the
|
||||||
/// query size is the same as the number of exponents.
|
/// query size is the same as the number of exponents.
|
||||||
pub fn multiexp<Q, D, G, S>(
|
pub fn multiexp<Q, D, G, S>(
|
||||||
pool: &CpuPool,
|
pool: &Worker,
|
||||||
bases: S,
|
bases: S,
|
||||||
density_map: D,
|
density_map: D,
|
||||||
exponents: Arc<Vec<<<G::Engine as Engine>::Fr as PrimeField>::Repr>>
|
exponents: Arc<Vec<<<G::Engine as Engine>::Fr as PrimeField>::Repr>>
|
||||||
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=Error>>
|
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
|
||||||
where for<'a> &'a Q: QueryDensity,
|
where for<'a> &'a Q: QueryDensity,
|
||||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
G: CurveAffine,
|
G: CurveAffine,
|
||||||
@ -275,7 +282,7 @@ fn test_with_bls12() {
|
|||||||
use rand::{self, Rand};
|
use rand::{self, Rand};
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
|
|
||||||
const SAMPLES: usize = 1 << 17;
|
const SAMPLES: usize = 1 << 14;
|
||||||
|
|
||||||
let rng = &mut rand::thread_rng();
|
let rng = &mut rand::thread_rng();
|
||||||
let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>());
|
let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>());
|
||||||
@ -283,7 +290,7 @@ fn test_with_bls12() {
|
|||||||
|
|
||||||
let naive = naive_multiexp(g.clone(), v.clone());
|
let naive = naive_multiexp(g.clone(), v.clone());
|
||||||
|
|
||||||
let pool = CpuPool::new_num_cpus();
|
let pool = Worker::new();
|
||||||
|
|
||||||
let fast = multiexp(
|
let fast = multiexp(
|
||||||
&pool,
|
&pool,
|
Loading…
Reference in New Issue
Block a user