diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5535f25 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,441 @@ +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"); +}