extern crate pairing; extern crate rand; extern crate bit_vec; extern crate futures; extern crate futures_cpupool; extern crate num_cpus; extern crate crossbeam; use pairing::{Engine, Field}; use std::ops::{Add, Sub}; use std::io; pub mod multicore; pub mod domain; pub mod groth16; pub mod multiexp; // TODO: remove this from public API? pub use self::multiexp::{DensityTracker, FullDensity, multiexp}; #[derive(Debug)] pub enum Error { PolynomialDegreeTooLarge, MalformedVerifyingKey, AssignmentMissing, UnexpectedIdentity, UnconstrainedVariable(Variable), IoError(io::Error) } impl From 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(Vec<(Index, E::Fr)>); impl Clone for LinearCombination { fn clone(&self) -> LinearCombination { LinearCombination(self.0.clone()) } } impl LinearCombination { pub fn zero() -> LinearCombination { LinearCombination(vec![]) } pub fn eval( &self, mut input_density: Option<&mut DensityTracker>, mut aux_density: Option<&mut DensityTracker>, input_assignment: &[E::Fr], aux_assignment: &[E::Fr] ) -> E::Fr { let mut acc = E::Fr::zero(); for &(index, coeff) in self.0.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 Add for LinearCombination { type Output = LinearCombination; fn add(self, other: Variable) -> LinearCombination { self + (E::Fr::one(), other) } } impl Sub for LinearCombination { type Output = LinearCombination; fn sub(self, other: Variable) -> LinearCombination { self - (E::Fr::one(), other) } } impl Add<(E::Fr, Variable)> for LinearCombination { type Output = LinearCombination; fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { 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 Sub<(E::Fr, Variable)> for LinearCombination { type Output = LinearCombination; fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination { coeff.negate(); self + (coeff, var) } } impl<'a, E: Engine> Add<&'a LinearCombination> for LinearCombination { type Output = LinearCombination; fn add(mut self, other: &'a LinearCombination) -> LinearCombination { for &(k, v) in other.0.iter() { self = self + (v, Variable(k)); } self } } impl<'a, E: Engine> Sub<&'a LinearCombination> for LinearCombination { type Output = LinearCombination; fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { for &(k, v) in other.0.iter() { self = self - (v, Variable(k)); } self } } pub trait Circuit { type InputMap: Input; /// Synthesize the circuit into a rank-1 quadratic constraint system #[must_use] fn synthesize>(self, cs: &mut CS) -> Result; } pub trait Input { /// Synthesize the circuit, except with additional access to public input /// variables fn synthesize>(self, cs: &mut CS) -> Result<(), Error>; } pub trait PublicConstraintSystem: ConstraintSystem { /// Allocate a public input that the verifier knows. The provided function is used to /// determine the assignment of the variable. fn alloc_input( &mut self, name_fn: N, f: F ) -> Result where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result; } pub trait ConstraintSystem { /// 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( &mut self, name_fn: N, f: F ) -> Result where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result; /// Enforce that `A` * `B` = `C`. fn enforce, N: FnOnce() -> NR>( &mut self, name_fn: N, a: LinearCombination, b: LinearCombination, c: LinearCombination ); /// Begin a namespace for the constraint system fn namespace( &mut self, _: N, space_fn: F ) -> Result where NR: Into, N: FnOnce() -> NR, F: FnOnce(&mut Self) -> Result { space_fn(self) } } use std::collections::HashMap; #[derive(Debug)] enum NamedObject { Constraint(usize), Input(usize), Aux(usize), Namespace } /// Constraint system for testing purposes. pub struct TestConstraintSystem { named_objects: HashMap, current_namespace: Vec, constraints: Vec<(LinearCombination, LinearCombination, LinearCombination)>, inputs: Vec, aux: Vec } impl TestConstraintSystem { pub fn new() -> TestConstraintSystem { TestConstraintSystem { named_objects: HashMap::new(), current_namespace: vec![], constraints: vec![], inputs: vec![E::Fr::one()], aux: vec![] } } pub fn is_satisfied(&self) -> bool { for &(ref a, ref b, ref c) 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 false } } true } 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 PublicConstraintSystem for TestConstraintSystem { fn alloc_input( &mut self, name_fn: N, f: F ) -> Result where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result { 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 ConstraintSystem for TestConstraintSystem { fn alloc( &mut self, name_fn: N, f: F ) -> Result where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result { 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, N: FnOnce() -> NR>( &mut self, name_fn: N, a: LinearCombination, b: LinearCombination, c: LinearCombination ) { 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, this_obj); self.constraints.push((a, b, c)); } fn namespace( &mut self, name_fn: N, space_fn: F ) -> Result where NR: Into, N: FnOnce() -> NR, F: FnOnce(&mut Self) -> Result { 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); let r = space_fn(self)?; self.current_namespace.pop(); Ok(r) } }