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 AsRef<[(T, E::Fr)]> for LinearCombination { fn as_ref(&self) -> &[(T, E::Fr)] { &self.0 } } 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) } } impl<'a, T: Copy, E: Engine> Add<&'a LinearCombination> for LinearCombination { type Output = LinearCombination; fn add(mut self, other: &'a LinearCombination) -> LinearCombination { for s in &other.0 { self = self + (s.1, s.0); } self } } impl<'a, T: Copy, E: Engine> Sub<&'a LinearCombination> for LinearCombination { type Output = LinearCombination; fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { for s in &other.0 { self = self - (s.1, s.0); } self } } #[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)]); let x = LinearCombination::::zero() + (Fr::one(), 0usize) - (Fr::one(), 1usize); let y = LinearCombination::::zero() + (Fr::one(), 2usize) - (Fr::one(), 3usize); let z = x + &y - &y; assert_eq!(z.0, vec![ (0usize, Fr::one()), (1usize, negone), (2usize, Fr::one()), (3usize, negone), (2usize, negone), (3usize, Fr::one()) ]); } /// 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: Sized + Copy + Clone; /// 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 { /// Represents the type of the "root" of this constraint system /// so that nested namespaces can minimize indirection. type PublicRoot: PublicConstraintSystem; /// 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; /// 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, 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 /// 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> { type PublicRoot = CS::PublicRoot; 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) } fn get_public_root(&mut self) -> &mut Self::PublicRoot { self.0.get_public_root() } } 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) } // Downstream users who use `namespace` will never interact with these // functions and they will never be invoked because the namespace is // never a root constraint system. fn push_namespace(&mut self, _: N) where NR: Into, N: FnOnce() -> NR { panic!("only the root's push_namespace should be called"); } fn pop_namespace(&mut self) { panic!("only the root's pop_namespace should be called"); } 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 { type PublicRoot = CS::PublicRoot; fn alloc_input( &mut self, annotation: A, f: F ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { (**self).alloc_input(annotation, f) } fn get_public_root(&mut self) -> &mut Self::PublicRoot { (**self).get_public_root() } } /// 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, Copy, Clone)] 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 { type PublicRoot = Self; 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)) } fn get_public_root(&mut self) -> &mut Self::PublicRoot { self } } 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 } } fn do_stuff_with_pcs>(mut cs: CS, one_more: bool) { cs.alloc_input(|| "something", || Ok(E::Fr::zero())).unwrap(); if one_more { do_stuff_with_pcs(cs.namespace_public(|| "cool namespace"), false); } } 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"); do_stuff_with_pcs(cs.namespace(|| "namey"), true); assert_eq!(cs.inputs, vec![ (Fr::one(), "ONE".into()), (Fr::zero(), "woohoo/hehe/works lol".into()), (Fr::zero(), "namey/something".into()), (Fr::zero(), "namey/cool namespace/something".into()), ]); }