extern crate pairing; use pairing::{Engine, Field}; use std::ops::{Add, Sub}; use std::fmt; use std::error::Error; /// This represents a linear combination of some variables, with coefficients /// in the scalar field of a pairing-friendly elliptic curve group. pub struct LinearCombination(Vec<(T, E::Fr)>); impl LinearCombination { pub fn zero() -> LinearCombination { LinearCombination(vec![]) } } impl Add<(E::Fr, T)> for LinearCombination { type Output = LinearCombination; fn add(mut self, (coeff, var): (E::Fr, T)) -> LinearCombination { self.0.push((var, coeff)); self } } impl Sub<(E::Fr, T)> for LinearCombination { type Output = LinearCombination; fn sub(self, (mut coeff, var): (E::Fr, T)) -> LinearCombination { coeff.negate(); self + (coeff, var) } } impl Add for LinearCombination { type Output = LinearCombination; fn add(self, other: T) -> LinearCombination { self + (E::Fr::one(), other) } } impl Sub for LinearCombination { type Output = LinearCombination; fn sub(self, other: T) -> LinearCombination { self - (E::Fr::one(), other) } } #[test] fn test_lc() { use pairing::bls12_381::{Bls12, Fr}; let a = LinearCombination::::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)]); } /// This is an error that could occur during circuit synthesis contexts, /// such as CRS generation, proving or verification. #[derive(Debug)] pub enum SynthesisError { AssignmentMissing } impl Error for SynthesisError { fn description(&self) -> &str { match *self { SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed" } } } impl fmt::Display for SynthesisError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{}", self.description()) } } pub trait ConstraintSystem: Sized { type Variable; /// Represents the type of the "root" of this constraint system /// so that nested namespaces can minimize indirection. type Root: ConstraintSystem; /// Return the "one" input variable fn one(&self) -> Self::Variable; /// 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 /// in testing contexts in order to derive a unique name for this variable in the current /// namespace. fn alloc( &mut self, annotation: A, f: F ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; /// 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. fn enforce( &mut self, annotation: A, a: LinearCombination, b: LinearCombination, c: LinearCombination ) where A: FnOnce() -> AR, AR: Into; /// Create a new (sub)namespace and enter into it. Not intended /// for downstream use; use `namespace` instead. fn push_namespace(&mut self, name_fn: N) where NR: Into, N: FnOnce() -> NR; /// Exit out of the existing namespace. Not intended for /// downstream use; use `namespace` instead. fn pop_namespace(&mut self); /// Gets the "root" constraint system, bypassing the namespacing. /// Not intended for downstream use; use `namespace` instead. fn get_root(&mut self) -> &mut Self::Root; /// Begin a namespace for this constraint system. fn namespace<'a, NR, N>( &'a mut self, name_fn: N ) -> Namespace<'a, E, Self::Root> where NR: Into, N: FnOnce() -> NR { self.get_root().push_namespace(name_fn); Namespace(self.get_root(), PhantomData) } } pub trait PublicConstraintSystem: ConstraintSystem { /// Allocate a public variable in the constraint system. The provided function is used to /// determine the assignment of the variable. fn alloc_input( &mut self, annotation: A, f: F ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; } use std::marker::PhantomData; /// This is a "namespaced" constraint system which borrows a constraint system (pushing /// a namespace context) and, when dropped, pops out of the namespace context. pub struct Namespace<'a, E: Engine, CS: ConstraintSystem + 'a>(&'a mut CS, PhantomData); impl<'cs, E: Engine, CS: PublicConstraintSystem> PublicConstraintSystem for Namespace<'cs, E, CS> { fn alloc_input( &mut self, annotation: A, f: F ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { self.0.alloc_input(annotation, f) } } impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for Namespace<'cs, E, CS> { type Variable = CS::Variable; type Root = CS::Root; fn one(&self) -> Self::Variable { self.0.one() } fn alloc( &mut self, annotation: A, f: F ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { self.0.alloc(annotation, f) } fn enforce( &mut self, annotation: A, a: LinearCombination, b: LinearCombination, c: LinearCombination ) where A: FnOnce() -> AR, AR: Into { self.0.enforce(annotation, a, b, c) } fn push_namespace(&mut self, name_fn: N) where NR: Into, N: FnOnce() -> NR { self.0.push_namespace(name_fn) } fn pop_namespace(&mut self) { self.0.pop_namespace() } fn get_root(&mut self) -> &mut Self::Root { self.0.get_root() } } impl<'a, E: Engine, CS: ConstraintSystem> Drop for Namespace<'a, E, CS> { fn drop(&mut self) { self.get_root().pop_namespace() } } /// Convenience implementation of PublicConstraintSystem for mutable references to /// public constraint systems. impl<'cs, E: Engine, CS: PublicConstraintSystem> PublicConstraintSystem for &'cs mut CS { fn alloc_input( &mut self, annotation: A, f: F ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { (**self).alloc_input(annotation, f) } } /// Convenience implementation of ConstraintSystem for mutable references to /// constraint systems. impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for &'cs mut CS { type Variable = CS::Variable; type Root = CS::Root; fn one(&self) -> Self::Variable { (**self).one() } fn alloc( &mut self, annotation: A, f: F ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { (**self).alloc(annotation, f) } fn enforce( &mut self, annotation: A, a: LinearCombination, b: LinearCombination, c: LinearCombination ) where A: FnOnce() -> AR, AR: Into { (**self).enforce(annotation, a, b, c) } fn push_namespace(&mut self, name_fn: N) where NR: Into, N: FnOnce() -> NR { (**self).push_namespace(name_fn) } fn pop_namespace(&mut self) { (**self).pop_namespace() } fn get_root(&mut self) -> &mut Self::Root { (**self).get_root() } } #[test] fn test_cs() { use pairing::bls12_381::{Bls12, Fr}; #[derive(PartialEq)] enum Var { Input(usize), Aux(usize) } struct MySillyConstraintSystem { inputs: Vec<(E::Fr, String)>, aux: Vec<(E::Fr, String)>, constraints: Vec<(LinearCombination, LinearCombination, LinearCombination, String)>, current_namespace: Vec } fn compute_path(ns: &[String], this: String) -> String { 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 MySillyConstraintSystem { fn alloc_input( &mut self, annotation: A, f: F ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { let index = self.inputs.len(); let path = compute_path(&self.current_namespace, annotation().into()); self.inputs.push((f()?, path)); Ok(Var::Input(index)) } } impl ConstraintSystem for MySillyConstraintSystem { type Variable = Var; type Root = Self; fn one(&self) -> Self::Variable { Var::Input(0) } fn alloc( &mut self, annotation: A, f: F ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { 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( &mut self, annotation: A, a: LinearCombination, b: LinearCombination, c: LinearCombination ) where A: FnOnce() -> AR, AR: Into { let path = compute_path(&self.current_namespace, annotation().into()); self.constraints.push((a, b, c, path)); } fn push_namespace(&mut self, name_fn: N) where NR: Into, N: FnOnce() -> NR { self.current_namespace.push(name_fn().into()); } fn pop_namespace(&mut self) { self.current_namespace.pop(); } fn get_root(&mut self) -> &mut Self::Root { self } } let mut cs = MySillyConstraintSystem:: { inputs: vec![(Fr::one(), "ONE".into())], aux: vec![], constraints: vec![], current_namespace: vec![] }; cs.alloc(|| "something", || Ok(Fr::zero())).unwrap(); assert_eq!(cs.inputs, vec![(Fr::one(), "ONE".into())]); assert_eq!(cs.aux, vec![(Fr::zero(), "something".into())]); { let mut cs = cs.namespace(|| "woohoo"); cs.alloc(|| "whatever", || Ok(Fr::one())).unwrap(); cs.alloc(|| "you", || Ok(Fr::zero())).unwrap(); cs.alloc(|| "say", || Ok(Fr::one())).unwrap(); { let mut cs = cs.namespace(|| "hehe"); let v1 = cs.alloc(|| "hehe, indeed", || Ok(Fr::one())).unwrap(); let v2 = cs.alloc_input(|| "works lol", || Ok(Fr::zero())).unwrap(); let one = cs.one(); cs.enforce( || "great constraint", LinearCombination::zero() + v1, LinearCombination::zero() + one, LinearCombination::zero() + v2 ); } } assert_eq!(cs.aux, vec![ (Fr::zero(), "something".into()), (Fr::one(), "woohoo/whatever".into()), (Fr::zero(), "woohoo/you".into()), (Fr::one(), "woohoo/say".into()), (Fr::one(), "woohoo/hehe/hehe, indeed".into()), ]); assert_eq!(cs.inputs, vec![ (Fr::one(), "ONE".into()), (Fr::zero(), "woohoo/hehe/works lol".into()), ]); assert!(cs.constraints.len() == 1); 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].2).0 == vec![(Var::Input(1), Fr::one())]); assert!(cs.constraints[0].3 == "woohoo/hehe/great constraint"); }