From b7f2f9e40935afe87cb38d9484363adb2b34a84a Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 21 Nov 2017 00:04:49 -0700 Subject: [PATCH 1/7] Part one of Bellman being recoded. --- src/lib.rs | 441 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 src/lib.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..5535f25b4 --- /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"); +} From af91dcda33e722d67006a70583ab089d0fafdf80 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 21 Nov 2017 13:44:56 -0700 Subject: [PATCH 2/7] Infrastructure for public input namespacing. --- src/lib.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5535f25b4..2581043b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,7 +142,12 @@ pub trait ConstraintSystem: Sized { } } -pub trait PublicConstraintSystem: ConstraintSystem { +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( @@ -151,6 +156,22 @@ pub trait PublicConstraintSystem: ConstraintSystem { 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 public_namespace<'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; @@ -160,6 +181,8 @@ use std::marker::PhantomData; 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, @@ -169,6 +192,11 @@ impl<'cs, E: Engine, CS: PublicConstraintSystem> PublicConstraintSystem fo { 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> { @@ -201,15 +229,19 @@ impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for Namespace< self.0.enforce(annotation, a, b, c) } - fn push_namespace(&mut self, name_fn: N) + // 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 { - self.0.push_namespace(name_fn) + panic!("only the root's push_namespace should be called"); } fn pop_namespace(&mut self) { - self.0.pop_namespace() + panic!("only the root's pop_namespace should be called"); } fn get_root(&mut self) -> &mut Self::Root @@ -227,6 +259,8 @@ impl<'a, E: Engine, CS: ConstraintSystem> Drop for Namespace<'a, E, CS> { /// 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, @@ -236,6 +270,11 @@ impl<'cs, E: Engine, CS: PublicConstraintSystem> PublicConstraintSystem fo { (**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 @@ -322,6 +361,8 @@ fn test_cs() { } impl PublicConstraintSystem for MySillyConstraintSystem { + type PublicRoot = Self; + fn alloc_input( &mut self, annotation: A, @@ -335,6 +376,11 @@ fn test_cs() { Ok(Var::Input(index)) } + + fn get_public_root(&mut self) -> &mut Self::PublicRoot + { + self + } } impl ConstraintSystem for MySillyConstraintSystem { @@ -390,6 +436,15 @@ fn test_cs() { } } + 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.public_namespace(|| "cool namespace"), false); + } + } + let mut cs = MySillyConstraintSystem:: { inputs: vec![(Fr::one(), "ONE".into())], aux: vec![], @@ -438,4 +493,13 @@ fn test_cs() { 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()), + ]); } From e54c4bc1b35a69e5a89ed402c7da5e1d814c1967 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 21 Nov 2017 14:00:00 -0700 Subject: [PATCH 3/7] Rename to `namespace_public`. --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2581043b7..da7387e3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -162,7 +162,7 @@ pub trait PublicConstraintSystem: ConstraintSystem fn get_public_root(&mut self) -> &mut Self::PublicRoot; /// Begin a namespace for this constraint system. - fn public_namespace<'a, NR, N>( + fn namespace_public<'a, NR, N>( &'a mut self, name_fn: N ) -> Namespace<'a, E, Self::PublicRoot> @@ -441,7 +441,7 @@ fn test_cs() { cs.alloc_input(|| "something", || Ok(E::Fr::zero())).unwrap(); if one_more { - do_stuff_with_pcs(cs.public_namespace(|| "cool namespace"), false); + do_stuff_with_pcs(cs.namespace_public(|| "cool namespace"), false); } } From 1d394e00f6121fc81c24f0b368596708f91702b6 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 22 Nov 2017 21:11:41 -0700 Subject: [PATCH 4/7] Allow linear combination terms to be inspected for downstream evaluation. --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index da7387e3e..c12fc725c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,12 @@ use std::error::Error; /// 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![]) From 92029393f6057018898135ddabe58d44fa60c536 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 22 Nov 2017 22:55:32 -0700 Subject: [PATCH 5/7] Guarantee that variables are Copy+Clone. --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c12fc725c..e33b3db47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,7 +91,7 @@ impl fmt::Display for SynthesisError { } pub trait ConstraintSystem: Sized { - type Variable; + type Variable: Sized + Copy + Clone; /// Represents the type of the "root" of this constraint system /// so that nested namespaces can minimize indirection. @@ -336,7 +336,7 @@ impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for &'cs mut C fn test_cs() { use pairing::bls12_381::{Bls12, Fr}; - #[derive(PartialEq)] + #[derive(PartialEq, Copy, Clone)] enum Var { Input(usize), Aux(usize) From 1fc640e88f369e870cbbec352c9c65c820dcd133 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Fri, 24 Nov 2017 10:14:11 -0700 Subject: [PATCH 6/7] Guarantee that we inherit the correct variable type. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index e33b3db47..af591222c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,7 @@ pub trait ConstraintSystem: Sized { /// Represents the type of the "root" of this constraint system /// so that nested namespaces can minimize indirection. - type Root: ConstraintSystem; + type Root: ConstraintSystem; /// Return the "one" input variable fn one(&self) -> Self::Variable; From 5a88b4f8187b5d6407401824fcd7fe727d3585b8 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 2 Dec 2017 23:30:23 -0700 Subject: [PATCH 7/7] Guarantee inheritance of variable type for public constraint system contexts. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index af591222c..866fb9b67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -152,7 +152,7 @@ pub trait PublicConstraintSystem: ConstraintSystem { /// Represents the type of the "root" of this constraint system /// so that nested namespaces can minimize indirection. - type PublicRoot: PublicConstraintSystem; + type PublicRoot: PublicConstraintSystem; /// Allocate a public variable in the constraint system. The provided function is used to /// determine the assignment of the variable.