From 202ef3204d270761924a6c909bf7a6a32b0fec44 Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Mon, 24 Aug 2020 00:45:03 -0700 Subject: [PATCH] Refactor variable traits in `r1cs-std`. --- r1cs-std/src/alloc.rs | 173 ++++++++++++-------------------------- r1cs-std/src/eq.rs | 183 +++++++++++++++++++---------------------- r1cs-std/src/lib.rs | 80 ++++++++++++++---- r1cs-std/src/macros.rs | 135 ++++++++++++++++++++++++++++++ r1cs-std/src/select.rs | 25 ++---- 5 files changed, 345 insertions(+), 251 deletions(-) create mode 100644 r1cs-std/src/macros.rs diff --git a/r1cs-std/src/alloc.rs b/r1cs-std/src/alloc.rs index 47a43f9..c9ecd0b 100644 --- a/r1cs-std/src/alloc.rs +++ b/r1cs-std/src/alloc.rs @@ -1,141 +1,72 @@ use crate::Vec; use algebra::Field; use core::borrow::Borrow; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use r1cs_core::{Namespace, SynthesisError}; -pub trait AllocGadget -where - Self: Sized, - V: ?Sized, -{ - fn alloc_constant>( - cs: CS, - t: T, - ) -> Result - where - T: Borrow; - - fn alloc>(cs: CS, f: F) -> Result - where - F: FnOnce() -> Result, - T: Borrow; - - fn alloc_checked>( - cs: CS, - f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow, - { - Self::alloc(cs, f) - } - - fn alloc_input>( - cs: CS, - f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow; +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone)] +pub enum AllocationMode { + Constant = 0, + Input = 1, + Witness = 2, +} - fn alloc_input_checked>( - cs: CS, - f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow, - { - Self::alloc_input(cs, f) +impl AllocationMode { + // Outputs the maximum according to the relation `Constant < Input < Witness`. + pub fn max(&self, other: Self) -> Self { + use AllocationMode::*; + match (self, other) { + (Constant, _) => other, + (Input, Constant) => *self, + (Input, _) => other, + (Witness, _) => *self, + } } } -impl> AllocGadget<[I], ConstraintF> - for Vec +pub trait AllocVar +where + Self: Sized, + V: ?Sized, { - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow<[I]>, - { - let mut vec = Vec::new(); - for (i, value) in t.borrow().iter().enumerate() { - vec.push(A::alloc_constant(cs.ns(|| format!("value_{}", i)), value)?); - } - Ok(vec) - } + fn new_variable>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result; - fn alloc>( - mut cs: CS, - f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow<[I]>, - { - let mut vec = Vec::new(); - for (i, value) in f()?.borrow().iter().enumerate() { - vec.push(A::alloc(&mut cs.ns(|| format!("value_{}", i)), || { - Ok(value) - })?); - } - Ok(vec) + fn new_constant( + cs: impl Into>, + t: impl Borrow, + ) -> Result { + Self::new_variable(cs, || Ok(t), AllocationMode::Constant) } - fn alloc_input>( - mut cs: CS, - f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow<[I]>, - { - let mut vec = Vec::new(); - for (i, value) in f()?.borrow().iter().enumerate() { - vec.push(A::alloc_input( - &mut cs.ns(|| format!("value_{}", i)), - || Ok(value), - )?); - } - Ok(vec) + fn new_input>( + cs: impl Into>, + f: impl FnOnce() -> Result, + ) -> Result { + Self::new_variable(cs, f, AllocationMode::Input) } - fn alloc_checked>( - mut cs: CS, - f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow<[I]>, - { - let mut vec = Vec::new(); - for (i, value) in f()?.borrow().iter().enumerate() { - vec.push(A::alloc_checked( - &mut cs.ns(|| format!("value_{}", i)), - || Ok(value), - )?); - } - Ok(vec) + fn new_witness>( + cs: impl Into>, + f: impl FnOnce() -> Result, + ) -> Result { + Self::new_variable(cs, f, AllocationMode::Witness) } +} - fn alloc_input_checked>( - mut cs: CS, - f: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow<[I]>, - { +impl> AllocVar<[I], F> for Vec { + fn new_variable>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); let mut vec = Vec::new(); - for (i, value) in f()?.borrow().iter().enumerate() { - vec.push(A::alloc_input_checked( - &mut cs.ns(|| format!("value_{}", i)), - || Ok(value), - )?); + for value in f()?.borrow().iter() { + vec.push(A::new_variable(cs.clone(), || Ok(value), mode)?); } Ok(vec) } diff --git a/r1cs-std/src/eq.rs b/r1cs-std/src/eq.rs index d978815..65075f9 100644 --- a/r1cs-std/src/eq.rs +++ b/r1cs-std/src/eq.rs @@ -1,142 +1,131 @@ -use crate::prelude::*; +use crate::{prelude::*, Vec}; use algebra::Field; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use r1cs_core::SynthesisError; -/// If `condition == 1`, then enforces that `self` and `other` are equal; -/// otherwise, it doesn't enforce anything. -pub trait ConditionalEqGadget: Eq { - fn conditional_enforce_equal>( - &self, - cs: CS, - other: &Self, - condition: &Boolean, - ) -> Result<(), SynthesisError>; +pub trait EqGadget { + /// Output a `Boolean` value representing whether `self.value() == other.value()`. + fn is_eq(&self, other: &Self) -> Result, SynthesisError>; - fn cost() -> usize; -} -impl, ConstraintF: Field> ConditionalEqGadget - for [T] -{ - fn conditional_enforce_equal>( + /// Output a `Boolean` value representing whether `self.value() != other.value()`. + fn is_neq(&self, other: &Self) -> Result, SynthesisError> { + Ok(self.is_eq(other)?.not()) + } + + /// If `should_enforce == true`, enforce that `self` and `other` are equal; else, + /// enforce a vacuously true statement. + fn conditional_enforce_equal( &self, - mut cs: CS, other: &Self, - condition: &Boolean, + should_enforce: &Boolean, ) -> Result<(), SynthesisError> { - for (i, (a, b)) in self.iter().zip(other.iter()).enumerate() { - let mut cs = cs.ns(|| format!("Iteration {}", i)); - a.conditional_enforce_equal(&mut cs, b, condition)?; - } - Ok(()) + self.is_eq(&other)? + .conditional_enforce_equal(&Boolean::constant(true), should_enforce) } - fn cost() -> usize { - unimplemented!() + /// Enforce that `self` and `other` are equal. + fn enforce_equal(&self, other: &Self) -> Result<(), SynthesisError> { + self.conditional_enforce_equal(other, &Boolean::constant(true)) } -} -pub trait EqGadget: Eq -where - Self: ConditionalEqGadget, -{ - fn enforce_equal>( + /// If `should_enforce == true`, enforce that `self` and `other` are not equal; else, + /// enforce a vacuously true statement. + fn conditional_enforce_not_equal( &self, - cs: CS, other: &Self, + should_enforce: &Boolean, ) -> Result<(), SynthesisError> { - self.conditional_enforce_equal(cs, other, &Boolean::constant(true)) + self.is_neq(&other)? + .conditional_enforce_equal(&Boolean::constant(true), should_enforce) } - fn cost() -> usize { - >::cost() + /// Enforce that `self` and `other` are not equal. + fn enforce_not_equal(&self, other: &Self) -> Result<(), SynthesisError> { + self.conditional_enforce_not_equal(other, &Boolean::constant(true)) } } -impl, ConstraintF: Field> EqGadget for [T] {} +impl + R1CSVar, F: Field> EqGadget for [T] { + fn is_eq(&self, other: &Self) -> Result, SynthesisError> { + assert_eq!(self.len(), other.len()); + assert!(!self.is_empty()); + let mut results = Vec::with_capacity(self.len()); + for (a, b) in self.iter().zip(other) { + results.push(a.is_eq(b)?); + } + Boolean::kary_and(&results) + } -pub trait NEqGadget: Eq { - fn enforce_not_equal>( + fn conditional_enforce_equal( &self, - cs: CS, other: &Self, - ) -> Result<(), SynthesisError>; + condition: &Boolean, + ) -> Result<(), SynthesisError> { + assert_eq!(self.len(), other.len()); + for (a, b) in self.iter().zip(other) { + a.conditional_enforce_equal(b, condition)?; + } + Ok(()) + } - fn cost() -> usize; + fn conditional_enforce_not_equal( + &self, + other: &Self, + should_enforce: &Boolean, + ) -> Result<(), SynthesisError> { + assert_eq!(self.len(), other.len()); + let some_are_different = self.is_neq(other)?; + if let Some(cs) = some_are_different.cs().or(should_enforce.cs()) { + cs.enforce_constraint( + some_are_different.lc(), + should_enforce.lc(), + should_enforce.lc(), + ) + } else { + // `some_are_different` and `should_enforce` are both constants + assert!(some_are_different.value().unwrap()); + Ok(()) + } + } } pub trait OrEqualsGadget where Self: Sized, { - fn enforce_equal_or>( - cs: CS, - cond: &Boolean, - var: &Self, + /// If `should_enforce == true`, enforce that `self` equals + /// (a) `first` (if `cond` is `true`) + /// (b) `second` (if `cond` is `false`) + fn conditional_enforce_equal_or( + &self, + cond: &Boolean, first: &Self, second: &Self, + should_enforce: &Boolean, ) -> Result<(), SynthesisError>; - fn cost() -> usize; -} - -impl> - OrEqualsGadget for T -{ - fn enforce_equal_or>( - cs: CS, - cond: &Boolean, - var: &Self, + fn enforce_equal_or( + &self, + cond: &Boolean, first: &Self, second: &Self, ) -> Result<(), SynthesisError> { - Self::conditional_enforce_equal_or(cs, cond, var, first, second, &Boolean::Constant(true)) - } - - fn cost() -> usize { - >::cost() + self.conditional_enforce_equal_or(cond, first, second, &Boolean::Constant(true)) } } -pub trait ConditionalOrEqualsGadget +impl OrEqualsGadget for T where - Self: Sized, + ConstraintF: Field, + T: Sized + EqGadget + CondSelectGadget, { - fn conditional_enforce_equal_or>( - cs: CS, - cond: &Boolean, - var: &Self, - first: &Self, - second: &Self, - should_enforce: &Boolean, - ) -> Result<(), SynthesisError>; - - fn cost() -> usize; -} - -impl< - ConstraintF: Field, - T: Sized + ConditionalEqGadget + CondSelectGadget, - > ConditionalOrEqualsGadget for T -{ - fn conditional_enforce_equal_or>( - mut cs: CS, - cond: &Boolean, - var: &Self, + fn conditional_enforce_equal_or( + &self, + cond: &Boolean, first: &Self, second: &Self, - should_enforce: &Boolean, + should_enforce: &Boolean, ) -> Result<(), SynthesisError> { - let match_opt = Self::conditionally_select( - &mut cs.ns(|| "conditional_select_in_or"), - cond, - first, - second, - )?; - var.conditional_enforce_equal(&mut cs.ns(|| "equals_in_or"), &match_opt, should_enforce) - } - - fn cost() -> usize { - >::cost() - + >::cost() + let match_opt = cond.select(first, second)?; + self.conditional_enforce_equal(&match_opt, should_enforce) } } diff --git a/r1cs-std/src/lib.rs b/r1cs-std/src/lib.rs index a60b98c..1d4166d 100644 --- a/r1cs-std/src/lib.rs +++ b/r1cs-std/src/lib.rs @@ -20,21 +20,16 @@ extern crate algebra; #[macro_use] extern crate derivative; -/// used by test_constraint_system -#[cfg(not(feature = "std"))] -macro_rules! println { - () => {}; - ($($arg: tt)*) => {}; -} +#[macro_use] +pub mod macros; #[cfg(not(feature = "std"))] -use ralloc::{collections::BTreeMap, string::String, vec::Vec}; +use ralloc::vec::Vec; #[cfg(feature = "std")] -use std::{collections::BTreeMap, string::String, vec::Vec}; +use std::vec::Vec; -pub mod test_constraint_counter; -pub mod test_constraint_system; +use algebra::prelude::Field; pub mod bits; pub use self::bits::*; @@ -48,6 +43,9 @@ mod instantiated; #[cfg(feature = "bls12_377")] pub use instantiated::bls12_377; +#[cfg(feature = "ed_on_bn254")] +pub use instantiated::ed_on_bn254; + #[cfg(feature = "ed_on_bls12_377")] pub use instantiated::ed_on_bls12_377; @@ -60,8 +58,8 @@ pub use instantiated::ed_on_mnt4_753; #[cfg(feature = "ed_on_cp6_782")] pub use instantiated::ed_on_cp6_782; -#[cfg(feature = "ed_on_bn254")] -pub use instantiated::ed_on_bn254; +#[cfg(feature = "ed_on_bw6_761")] +pub use instantiated::ed_on_bw6_761; #[cfg(feature = "ed_on_bls12_381")] pub use instantiated::ed_on_bls12_381; @@ -89,20 +87,70 @@ pub mod prelude { alloc::*, bits::{boolean::Boolean, uint32::UInt32, uint8::UInt8, ToBitsGadget, ToBytesGadget}, eq::*, - fields::{fp::FpGadget, FieldGadget, ToConstraintFieldGadget}, - groups::GroupGadget, + fields::{FieldOpsBounds, FieldVar}, + groups::{CurveVar, GroupOpsBounds}, instantiated::*, - pairing::PairingGadget, + pairing::PairingVar, select::*, + R1CSVar, }; } +pub trait R1CSVar { + type Value: core::fmt::Debug + Eq + Clone; + + /// Returns the underlying `ConstraintSystemRef`. + fn cs(&self) -> Option>; + + /// Returns `true` if `self` is a circuit-generation-time constant. + fn is_constant(&self) -> bool { + self.cs() + .map_or(true, |cs| cs == r1cs_core::ConstraintSystemRef::None) + } + + /// Returns the value that is assigned to `self` in the underlying + /// `ConstraintSystem`. + fn value(&self) -> Result; +} + +impl> R1CSVar for [T] { + type Value = Vec; + + fn cs(&self) -> Option> { + let mut result = None; + for var in self { + result = var.cs().or(result); + } + result + } + + fn value(&self) -> Result { + let mut result = Vec::new(); + for var in self { + result.push(var.value()?); + } + Ok(result) + } +} + +impl<'a, F: Field, T: 'a + R1CSVar> R1CSVar for &'a T { + type Value = T::Value; + + fn cs(&self) -> Option> { + (*self).cs() + } + + fn value(&self) -> Result { + (*self).value() + } +} + pub trait Assignment { fn get(self) -> Result; } impl Assignment for Option { fn get(self) -> Result { - self.ok_or_else(|| r1cs_core::SynthesisError::AssignmentMissing) + self.ok_or(r1cs_core::SynthesisError::AssignmentMissing) } } diff --git a/r1cs-std/src/macros.rs b/r1cs-std/src/macros.rs new file mode 100644 index 0000000..0932d38 --- /dev/null +++ b/r1cs-std/src/macros.rs @@ -0,0 +1,135 @@ +// Implements AddAssign on Self by deferring to an implementation on &Self +#[macro_export] +macro_rules! impl_ops { + ( + $type: ty, + $native: ty, + $trait: ident, + $fn: ident, + $assign_trait: ident, + $assign_fn: ident, + $impl: expr, + $constant_impl: expr, + $($args:tt)* + ) => { + impl_bounded_ops!($type, $native, $trait, $fn, $assign_trait, $assign_fn, $impl, $constant_impl, ($($args)+), ); + }; +} + +macro_rules! impl_bounded_ops { + ( + $type: ty, + $native: ty, + $trait: ident, + $fn: ident, + $assign_trait: ident, + $assign_fn: ident, + $impl: expr, + $constant_impl: expr, + ($($params:tt)+), + $($bounds:tt)* + ) => { + impl<'a, $($params)+> core::ops::$trait<&'a $type> for &'a $type + where + $($bounds)* + { + type Output = $type; + + fn $fn(self, other: Self) -> Self::Output { + $impl(self, other) + } + } + + impl<'a, $($params)+> core::ops::$trait<$type> for &'a $type + where + $($bounds)* + { + type Output = $type; + + fn $fn(self, other: $type) -> Self::Output { + core::ops::$trait::$fn(self, &other) + } + } + + impl<'a, $($params)+> core::ops::$trait<&'a $type> for $type + where + $($bounds)* + { + type Output = $type; + + fn $fn(self, other: &'a $type) -> Self::Output { + core::ops::$trait::$fn(&self, other) + } + } + + impl<$($params)+> core::ops::$trait<$type> for $type + where + + $($bounds)* + { + type Output = $type; + + fn $fn(self, other: $type) -> Self::Output { + core::ops::$trait::$fn(&self, &other) + } + } + + impl<$($params)+> core::ops::$assign_trait<$type> for $type + where + + $($bounds)* + { + fn $assign_fn(&mut self, other: $type) { + let result = core::ops::$trait::$fn(&*self, &other); + *self = result + } + } + + impl<'a, $($params)+> core::ops::$assign_trait<&'a $type> for $type + where + + $($bounds)* + { + fn $assign_fn(&mut self, other: &'a $type) { + let result = core::ops::$trait::$fn(&*self, other); + *self = result + } + } + + impl<'a, $($params)+> core::ops::$trait<$native> for &'a $type + where + + $($bounds)* + { + type Output = $type; + + fn $fn(self, other: $native) -> Self::Output { + $constant_impl(self, other) + } + } + + impl<$($params)+> core::ops::$trait<$native> for $type + where + + $($bounds)* + { + type Output = $type; + + fn $fn(self, other: $native) -> Self::Output { + core::ops::$trait::$fn(&self, other) + } + } + + impl<$($params)+> core::ops::$assign_trait<$native> for $type + where + + $($bounds)* + { + + fn $assign_fn(&mut self, other: $native) { + let result = core::ops::$trait::$fn(&*self, other); + *self = result + } + } + } +} diff --git a/r1cs-std/src/select.rs b/r1cs-std/src/select.rs index 01fc7db..032dcc1 100644 --- a/r1cs-std/src/select.rs +++ b/r1cs-std/src/select.rs @@ -1,20 +1,17 @@ use crate::prelude::*; use algebra::Field; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use r1cs_core::SynthesisError; /// If condition is `true`, return `true_value`; else, select `false_value`. pub trait CondSelectGadget where Self: Sized, { - fn conditionally_select>( - cs: CS, - cond: &Boolean, + fn conditionally_select( + cond: &Boolean, true_value: &Self, false_value: &Self, ) -> Result; - - fn cost() -> usize; } /// Uses two bits to perform a lookup into a table @@ -23,13 +20,10 @@ where Self: Sized, { type TableConstant; - fn two_bit_lookup>( - cs: CS, - bits: &[Boolean], + fn two_bit_lookup( + bits: &[Boolean], constants: &[Self::TableConstant], ) -> Result; - - fn cost() -> usize; } /// Uses three bits to perform a lookup into a table, where the last bit @@ -39,12 +33,9 @@ where Self: Sized, { type TableConstant; - fn three_bit_cond_neg_lookup>( - cs: CS, - bits: &[Boolean], - b0b1: &Boolean, + fn three_bit_cond_neg_lookup( + bits: &[Boolean], + b0b1: &Boolean, constants: &[Self::TableConstant], ) -> Result; - - fn cost() -> usize; }