Browse Source

Add convenience impls for common types (#137)

* Work

* Tweak

* Fmt

* Work

* Format + typo fixes

* `no-std` fix
avoid_assigned_value
Pratyush Mishra 1 year ago
committed by GitHub
parent
commit
4020fbc226
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
13 changed files with 341 additions and 121 deletions
  1. +52
    -10
      src/alloc.rs
  2. +3
    -3
      src/boolean/cmp.rs
  3. +1
    -1
      src/boolean/mod.rs
  4. +35
    -3
      src/cmp.rs
  5. +54
    -0
      src/convert.rs
  6. +100
    -9
      src/eq.rs
  7. +1
    -1
      src/fields/emulated_fp/allocated_field_var.rs
  8. +2
    -2
      src/fields/fp/mod.rs
  9. +3
    -56
      src/lib.rs
  10. +1
    -1
      src/poly/domain/mod.rs
  11. +86
    -0
      src/r1cs_var.rs
  12. +3
    -13
      src/select.rs
  13. +0
    -22
      src/uint/convert.rs

+ 52
- 10
src/alloc.rs

@ -37,11 +37,7 @@ impl AllocationMode {
/// Specifies how variables of type `Self` should be allocated in a /// Specifies how variables of type `Self` should be allocated in a
/// `ConstraintSystem`. /// `ConstraintSystem`.
pub trait AllocVar<V, F: Field>
where
Self: Sized,
V: ?Sized,
{
pub trait AllocVar<V: ?Sized, F: Field>: Sized {
/// Allocates a new variable of type `Self` in the `ConstraintSystem` `cs`. /// Allocates a new variable of type `Self` in the `ConstraintSystem` `cs`.
/// The mode of allocation is decided by `mode`. /// The mode of allocation is decided by `mode`.
fn new_variable<T: Borrow<V>>( fn new_variable<T: Borrow<V>>(
@ -92,10 +88,56 @@ impl> AllocVar<[I], F> for Vec {
) -> Result<Self, SynthesisError> { ) -> Result<Self, SynthesisError> {
let ns = cs.into(); let ns = cs.into();
let cs = ns.cs(); let cs = ns.cs();
let mut vec = Vec::new();
for value in f()?.borrow().iter() {
vec.push(A::new_variable(cs.clone(), || Ok(value), mode)?);
}
Ok(vec)
f().and_then(|v| {
v.borrow()
.iter()
.map(|e| A::new_variable(cs.clone(), || Ok(e), mode))
.collect()
})
}
}
/// Dummy impl for `()`.
impl<F: Field> AllocVar<(), F> for () {
fn new_variable<T: Borrow<()>>(
_cs: impl Into<Namespace<F>>,
_f: impl FnOnce() -> Result<T, SynthesisError>,
_mode: AllocationMode,
) -> Result<Self, SynthesisError> {
Ok(())
}
}
/// This blanket implementation just allocates variables in `Self`
/// element by element.
impl<I, F: Field, A: AllocVar<I, F>, const N: usize> AllocVar<[I; N], F> for [A; N] {
fn new_variable<T: Borrow<[I; N]>>(
cs: impl Into<Namespace<F>>,
f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode,
) -> Result<Self, SynthesisError> {
let ns = cs.into();
let cs = ns.cs();
f().map(|v| {
let v = v.borrow();
core::array::from_fn(|i| A::new_variable(cs.clone(), || Ok(&v[i]), mode).unwrap())
})
}
}
/// This blanket implementation just allocates variables in `Self`
/// element by element.
impl<I, F: Field, A: AllocVar<I, F>, const N: usize> AllocVar<[I], F> for [A; N] {
fn new_variable<T: Borrow<[I]>>(
cs: impl Into<Namespace<F>>,
f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode,
) -> Result<Self, SynthesisError> {
let ns = cs.into();
let cs = ns.cs();
f().map(|v| {
let v = v.borrow();
core::array::from_fn(|i| A::new_variable(cs.clone(), || Ok(&v[i]), mode).unwrap())
})
} }
} }

+ 3
- 3
src/boolean/cmp.rs

@ -48,7 +48,7 @@ impl Boolean {
let mut bits_iter = bits.iter().rev(); // Iterate in big-endian let mut bits_iter = bits.iter().rev(); // Iterate in big-endian
// Runs of ones in r // Runs of ones in r
let mut last_run = Boolean::constant(true);
let mut last_run = Boolean::TRUE;
let mut current_run = vec![]; let mut current_run = vec![];
let mut element_num_bits = 0; let mut element_num_bits = 0;
@ -57,12 +57,12 @@ impl Boolean {
} }
if bits.len() > element_num_bits { if bits.len() > element_num_bits {
let mut or_result = Boolean::constant(false);
let mut or_result = Boolean::FALSE;
for should_be_zero in &bits[element_num_bits..] { for should_be_zero in &bits[element_num_bits..] {
or_result |= should_be_zero; or_result |= should_be_zero;
let _ = bits_iter.next().unwrap(); let _ = bits_iter.next().unwrap();
} }
or_result.enforce_equal(&Boolean::constant(false))?;
or_result.enforce_equal(&Boolean::FALSE)?;
} }
for (b, a) in BitIteratorBE::without_leading_zeros(b).zip(bits_iter.by_ref()) { for (b, a) in BitIteratorBE::without_leading_zeros(b).zip(bits_iter.by_ref()) {

+ 1
- 1
src/boolean/mod.rs

@ -100,7 +100,7 @@ impl Boolean {
/// let true_var = Boolean::<Fr>::TRUE; /// let true_var = Boolean::<Fr>::TRUE;
/// let false_var = Boolean::<Fr>::FALSE; /// let false_var = Boolean::<Fr>::FALSE;
/// ///
/// true_var.enforce_equal(&Boolean::constant(true))?;
/// true_var.enforce_equal(&Boolean::TRUE)?;
/// false_var.enforce_equal(&Boolean::constant(false))?; /// false_var.enforce_equal(&Boolean::constant(false))?;
/// # Ok(()) /// # Ok(())
/// # } /// # }

+ 35
- 3
src/cmp.rs

@ -1,10 +1,10 @@
use ark_ff::Field;
use ark_ff::{Field, PrimeField};
use ark_relations::r1cs::SynthesisError; use ark_relations::r1cs::SynthesisError;
use crate::{boolean::Boolean, R1CSVar};
use crate::{boolean::Boolean, eq::EqGadget, R1CSVar};
/// Specifies how to generate constraints for comparing two variables. /// Specifies how to generate constraints for comparing two variables.
pub trait CmpGadget<F: Field>: R1CSVar<F> {
pub trait CmpGadget<F: Field>: R1CSVar<F> + EqGadget<F> {
fn is_gt(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> { fn is_gt(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
other.is_lt(self) other.is_lt(self)
} }
@ -19,3 +19,35 @@ pub trait CmpGadget: R1CSVar {
other.is_ge(self) other.is_ge(self)
} }
} }
/// Mimics the behavior of `std::cmp::PartialOrd` for `()`.
impl<F: Field> CmpGadget<F> for () {
fn is_gt(&self, _other: &Self) -> Result<Boolean<F>, SynthesisError> {
Ok(Boolean::FALSE)
}
fn is_ge(&self, _other: &Self) -> Result<Boolean<F>, SynthesisError> {
Ok(Boolean::TRUE)
}
fn is_lt(&self, _other: &Self) -> Result<Boolean<F>, SynthesisError> {
Ok(Boolean::FALSE)
}
fn is_le(&self, _other: &Self) -> Result<Boolean<F>, SynthesisError> {
Ok(Boolean::TRUE)
}
}
/// Mimics the lexicographic comparison behavior of `std::cmp::PartialOrd` for `[T]`.
impl<T: CmpGadget<F>, F: PrimeField> CmpGadget<F> for [T] {
fn is_ge(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
let mut result = Boolean::TRUE;
let mut all_equal_so_far = Boolean::TRUE;
for (a, b) in self.iter().zip(other) {
all_equal_so_far &= a.is_eq(b)?;
result &= a.is_gt(b)? | &all_equal_so_far;
}
Ok(result)
}
}

+ 54
- 0
src/convert.rs

@ -90,6 +90,60 @@ impl<'a, F: Field, T: 'a + ToBytesGadget> ToBytesGadget for &'a T {
} }
} }
impl<T: ToBytesGadget<F>, F: Field> ToBytesGadget<F> for [T] {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let mut bytes = Vec::new();
for elem in self {
let elem = elem.to_bytes_le()?;
bytes.extend_from_slice(&elem);
// Make sure that there's enough capacity to avoid reallocations.
bytes.reserve(elem.len() * (self.len() - 1));
}
Ok(bytes)
}
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let mut bytes = Vec::new();
for elem in self {
let elem = elem.to_non_unique_bytes_le()?;
bytes.extend_from_slice(&elem);
// Make sure that there's enough capacity to avoid reallocations.
bytes.reserve(elem.len() * (self.len() - 1));
}
Ok(bytes)
}
}
impl<T: ToBytesGadget<F>, F: Field> ToBytesGadget<F> for Vec<T> {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
self.as_slice().to_bytes_le()
}
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
self.as_slice().to_non_unique_bytes_le()
}
}
impl<T: ToBytesGadget<F>, F: Field, const N: usize> ToBytesGadget<F> for [T; N] {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
self.as_slice().to_bytes_le()
}
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
self.as_slice().to_non_unique_bytes_le()
}
}
impl<F: Field> ToBytesGadget<F> for () {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
Ok(Vec::new())
}
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
Ok(Vec::new())
}
}
/// Specifies how to convert a variable of type `Self` to variables of /// Specifies how to convert a variable of type `Self` to variables of
/// type `FpVar<ConstraintF>` /// type `FpVar<ConstraintF>`
pub trait ToConstraintFieldGadget<ConstraintF: ark_ff::PrimeField> { pub trait ToConstraintFieldGadget<ConstraintF: ark_ff::PrimeField> {

+ 100
- 9
src/eq.rs

@ -33,7 +33,7 @@ pub trait EqGadget {
should_enforce: &Boolean<F>, should_enforce: &Boolean<F>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
self.is_eq(&other)? self.is_eq(&other)?
.conditional_enforce_equal(&Boolean::constant(true), should_enforce)
.conditional_enforce_equal(&Boolean::TRUE, should_enforce)
} }
/// Enforce that `self` and `other` are equal. /// Enforce that `self` and `other` are equal.
@ -46,7 +46,7 @@ pub trait EqGadget {
/// are encouraged to carefully analyze the efficiency and safety of these. /// are encouraged to carefully analyze the efficiency and safety of these.
#[tracing::instrument(target = "r1cs", skip(self, other))] #[tracing::instrument(target = "r1cs", skip(self, other))]
fn enforce_equal(&self, other: &Self) -> Result<(), SynthesisError> { fn enforce_equal(&self, other: &Self) -> Result<(), SynthesisError> {
self.conditional_enforce_equal(other, &Boolean::constant(true))
self.conditional_enforce_equal(other, &Boolean::TRUE)
} }
/// If `should_enforce == true`, enforce that `self` and `other` are *not* /// If `should_enforce == true`, enforce that `self` and `other` are *not*
@ -65,7 +65,7 @@ pub trait EqGadget {
should_enforce: &Boolean<F>, should_enforce: &Boolean<F>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
self.is_neq(&other)? self.is_neq(&other)?
.conditional_enforce_equal(&Boolean::constant(true), should_enforce)
.conditional_enforce_equal(&Boolean::TRUE, should_enforce)
} }
/// Enforce that `self` and `other` are *not* equal. /// Enforce that `self` and `other` are *not* equal.
@ -78,7 +78,7 @@ pub trait EqGadget {
/// are encouraged to carefully analyze the efficiency and safety of these. /// are encouraged to carefully analyze the efficiency and safety of these.
#[tracing::instrument(target = "r1cs", skip(self, other))] #[tracing::instrument(target = "r1cs", skip(self, other))]
fn enforce_not_equal(&self, other: &Self) -> Result<(), SynthesisError> { fn enforce_not_equal(&self, other: &Self) -> Result<(), SynthesisError> {
self.conditional_enforce_not_equal(other, &Boolean::constant(true))
self.conditional_enforce_not_equal(other, &Boolean::TRUE)
} }
} }
@ -86,12 +86,15 @@ impl + R1CSVar, F: PrimeField> EqGadget for [T] {
#[tracing::instrument(target = "r1cs", skip(self, other))] #[tracing::instrument(target = "r1cs", skip(self, other))]
fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> { fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
assert_eq!(self.len(), other.len()); 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)?);
if self.is_empty() & other.is_empty() {
Ok(Boolean::TRUE)
} else {
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)
} }
Boolean::kary_and(&results)
} }
#[tracing::instrument(target = "r1cs", skip(self, other))] #[tracing::instrument(target = "r1cs", skip(self, other))]
@ -128,3 +131,91 @@ impl + R1CSVar, F: PrimeField> EqGadget for [T] {
} }
} }
} }
/// This blanket implementation just forwards to the impl on [`[T]`].
impl<T: EqGadget<F> + R1CSVar<F>, F: PrimeField> EqGadget<F> for Vec<T> {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
self.as_slice().is_eq(other.as_slice())
}
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn conditional_enforce_equal(
&self,
other: &Self,
condition: &Boolean<F>,
) -> Result<(), SynthesisError> {
self.as_slice()
.conditional_enforce_equal(other.as_slice(), condition)
}
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn conditional_enforce_not_equal(
&self,
other: &Self,
should_enforce: &Boolean<F>,
) -> Result<(), SynthesisError> {
self.as_slice()
.conditional_enforce_not_equal(other.as_slice(), should_enforce)
}
}
/// Dummy impl for `()`.
impl<F: Field> EqGadget<F> for () {
/// Output a `Boolean` value representing whether `self.value() ==
/// other.value()`.
#[inline]
fn is_eq(&self, _other: &Self) -> Result<Boolean<F>, SynthesisError> {
Ok(Boolean::TRUE)
}
/// If `should_enforce == true`, enforce that `self` and `other` are equal;
/// else, enforce a vacuously true statement.
///
/// This is a no-op as `self.is_eq(other)?` is always `true`.
#[tracing::instrument(target = "r1cs", skip(self, _other))]
fn conditional_enforce_equal(
&self,
_other: &Self,
_should_enforce: &Boolean<F>,
) -> Result<(), SynthesisError> {
Ok(())
}
/// Enforce that `self` and `other` are equal.
///
/// This does not generate any constraints as `self.is_eq(other)?` is always
/// `true`.
#[tracing::instrument(target = "r1cs", skip(self, _other))]
fn enforce_equal(&self, _other: &Self) -> Result<(), SynthesisError> {
Ok(())
}
}
/// This blanket implementation just forwards to the impl on [`[T]`].
impl<T: EqGadget<F> + R1CSVar<F>, F: PrimeField, const N: usize> EqGadget<F> for [T; N] {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
self.as_slice().is_eq(other.as_slice())
}
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn conditional_enforce_equal(
&self,
other: &Self,
condition: &Boolean<F>,
) -> Result<(), SynthesisError> {
self.as_slice()
.conditional_enforce_equal(other.as_slice(), condition)
}
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn conditional_enforce_not_equal(
&self,
other: &Self,
should_enforce: &Boolean<F>,
) -> Result<(), SynthesisError> {
self.as_slice()
.conditional_enforce_not_equal(other.as_slice(), should_enforce)
}
}

+ 1
- 1
src/fields/emulated_fp/allocated_field_var.rs

@ -686,7 +686,7 @@ impl ToBytesGadget
let num_bits = TargetF::BigInt::NUM_LIMBS * 64; let num_bits = TargetF::BigInt::NUM_LIMBS * 64;
assert!(bits.len() <= num_bits); assert!(bits.len() <= num_bits);
bits.resize_with(num_bits, || Boolean::constant(false));
bits.resize_with(num_bits, || Boolean::FALSE);
let bytes = bits.chunks(8).map(UInt8::from_bits_le).collect(); let bytes = bits.chunks(8).map(UInt8::from_bits_le).collect();
Ok(bytes) Ok(bytes)

+ 2
- 2
src/fields/fp/mod.rs

@ -555,7 +555,7 @@ impl ToBytesGadget for AllocatedFp {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> { fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let num_bits = F::BigInt::NUM_LIMBS * 64; let num_bits = F::BigInt::NUM_LIMBS * 64;
let mut bits = self.to_bits_le()?; let mut bits = self.to_bits_le()?;
let remainder = core::iter::repeat(Boolean::constant(false)).take(num_bits - bits.len());
let remainder = core::iter::repeat(Boolean::FALSE).take(num_bits - bits.len());
bits.extend(remainder); bits.extend(remainder);
let bytes = bits let bytes = bits
.chunks(8) .chunks(8)
@ -568,7 +568,7 @@ impl ToBytesGadget for AllocatedFp {
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> { fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let num_bits = F::BigInt::NUM_LIMBS * 64; let num_bits = F::BigInt::NUM_LIMBS * 64;
let mut bits = self.to_non_unique_bits_le()?; let mut bits = self.to_non_unique_bits_le()?;
let remainder = core::iter::repeat(Boolean::constant(false)).take(num_bits - bits.len());
let remainder = core::iter::repeat(Boolean::FALSE).take(num_bits - bits.len());
bits.extend(remainder); bits.extend(remainder);
let bytes = bits let bytes = bits
.chunks(8) .chunks(8)

+ 3
- 56
src/lib.rs

@ -29,7 +29,9 @@ pub mod macros;
pub(crate) use ark_std::vec::Vec; pub(crate) use ark_std::vec::Vec;
use ark_ff::Field;
#[doc(hidden)]
pub mod r1cs_var;
pub use r1cs_var::*;
/// This module contains `Boolean`, an R1CS equivalent of the `bool` type. /// This module contains `Boolean`, an R1CS equivalent of the `bool` type.
pub mod boolean; pub mod boolean;
@ -105,61 +107,6 @@ pub mod prelude {
}; };
} }
/// This trait describes some core functionality that is common to high-level
/// variables, such as `Boolean`s, `FieldVar`s, `GroupVar`s, etc.
pub trait R1CSVar<F: Field> {
/// The type of the "native" value that `Self` represents in the constraint
/// system.
type Value: core::fmt::Debug + Eq + Clone;
/// Returns the underlying `ConstraintSystemRef`.
///
/// If `self` is a constant value, then this *must* return
/// `ark_relations::r1cs::ConstraintSystemRef::None`.
fn cs(&self) -> ark_relations::r1cs::ConstraintSystemRef<F>;
/// Returns `true` if `self` is a circuit-generation-time constant.
fn is_constant(&self) -> bool {
self.cs().is_none()
}
/// Returns the value that is assigned to `self` in the underlying
/// `ConstraintSystem`.
fn value(&self) -> Result<Self::Value, ark_relations::r1cs::SynthesisError>;
}
impl<F: Field, T: R1CSVar<F>> R1CSVar<F> for [T] {
type Value = Vec<T::Value>;
fn cs(&self) -> ark_relations::r1cs::ConstraintSystemRef<F> {
let mut result = ark_relations::r1cs::ConstraintSystemRef::None;
for var in self {
result = var.cs().or(result);
}
result
}
fn value(&self) -> Result<Self::Value, ark_relations::r1cs::SynthesisError> {
let mut result = Vec::new();
for var in self {
result.push(var.value()?);
}
Ok(result)
}
}
impl<'a, F: Field, T: 'a + R1CSVar<F>> R1CSVar<F> for &'a T {
type Value = T::Value;
fn cs(&self) -> ark_relations::r1cs::ConstraintSystemRef<F> {
(*self).cs()
}
fn value(&self) -> Result<Self::Value, ark_relations::r1cs::SynthesisError> {
(*self).value()
}
}
/// A utility trait to convert `Self` to `Result<T, SynthesisErrorA`.> /// A utility trait to convert `Self` to `Result<T, SynthesisErrorA`.>
pub trait Assignment<T> { pub trait Assignment<T> {
/// Converts `self` to `Result`. /// Converts `self` to `Result`.

+ 1
- 1
src/poly/domain/mod.rs

@ -44,7 +44,7 @@ impl Radix2DomainVar {
impl<F: PrimeField> EqGadget<F> for Radix2DomainVar<F> { impl<F: PrimeField> EqGadget<F> for Radix2DomainVar<F> {
fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> { fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
if self.gen != other.gen || self.dim != other.dim { if self.gen != other.gen || self.dim != other.dim {
Ok(Boolean::constant(false))
Ok(Boolean::FALSE)
} else { } else {
self.offset.is_eq(&other.offset) self.offset.is_eq(&other.offset)
} }

+ 86
- 0
src/r1cs_var.rs

@ -0,0 +1,86 @@
use ark_ff::Field;
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
use ark_std::vec::Vec;
/// This trait describes some core functionality that is common to high-level
/// variables, such as `Boolean`s, `FieldVar`s, `GroupVar`s, etc.
pub trait R1CSVar<F: Field> {
/// The type of the "native" value that `Self` represents in the constraint
/// system.
type Value: core::fmt::Debug + Eq + Clone;
/// Returns the underlying `ConstraintSystemRef`.
///
/// If `self` is a constant value, then this *must* return
/// `ark_relations::r1cs::ConstraintSystemRef::None`.
fn cs(&self) -> ConstraintSystemRef<F>;
/// Returns `true` if `self` is a circuit-generation-time constant.
fn is_constant(&self) -> bool {
self.cs().is_none()
}
/// Returns the value that is assigned to `self` in the underlying
/// `ConstraintSystem`.
fn value(&self) -> Result<Self::Value, SynthesisError>;
}
impl<F: Field, T: R1CSVar<F>> R1CSVar<F> for [T] {
type Value = Vec<T::Value>;
fn cs(&self) -> ConstraintSystemRef<F> {
let mut result = ConstraintSystemRef::None;
for var in self {
result = var.cs().or(result);
}
result
}
fn value(&self) -> Result<Self::Value, SynthesisError> {
let mut result = Vec::new();
for var in self {
result.push(var.value()?);
}
Ok(result)
}
}
impl<'a, F: Field, T: 'a + R1CSVar<F>> R1CSVar<F> for &'a T {
type Value = T::Value;
fn cs(&self) -> ConstraintSystemRef<F> {
(*self).cs()
}
fn value(&self) -> Result<Self::Value, SynthesisError> {
(*self).value()
}
}
impl<F: Field, T: R1CSVar<F>, const N: usize> R1CSVar<F> for [T; N] {
type Value = [T::Value; N];
fn cs(&self) -> ConstraintSystemRef<F> {
let mut result = ConstraintSystemRef::None;
for var in self {
result = var.cs().or(result);
}
result
}
fn value(&self) -> Result<Self::Value, SynthesisError> {
Ok(core::array::from_fn(|i| self[i].value().unwrap()))
}
}
impl<F: Field> R1CSVar<F> for () {
type Value = ();
fn cs(&self) -> ConstraintSystemRef<F> {
ConstraintSystemRef::None
}
fn value(&self) -> Result<Self::Value, SynthesisError> {
Ok(())
}
}

+ 3
- 13
src/select.rs

@ -3,11 +3,7 @@ use ark_ff::Field;
use ark_relations::r1cs::SynthesisError; use ark_relations::r1cs::SynthesisError;
use ark_std::vec::Vec; use ark_std::vec::Vec;
/// Generates constraints for selecting between one of two values. /// Generates constraints for selecting between one of two values.
pub trait CondSelectGadget<ConstraintF: Field>
where
Self: Sized,
Self: Clone,
{
pub trait CondSelectGadget<ConstraintF: Field>: Sized + Clone {
/// If `cond == &Boolean::TRUE`, then this returns `true_value`; else, /// If `cond == &Boolean::TRUE`, then this returns `true_value`; else,
/// returns `false_value`. /// returns `false_value`.
/// ///
@ -68,10 +64,7 @@ where
} }
/// Performs a lookup in a 4-element table using two bits. /// Performs a lookup in a 4-element table using two bits.
pub trait TwoBitLookupGadget<ConstraintF: Field>
where
Self: Sized,
{
pub trait TwoBitLookupGadget<ConstraintF: Field>: Sized {
/// The type of values being looked up. /// The type of values being looked up.
type TableConstant; type TableConstant;
@ -92,10 +85,7 @@ where
/// Uses three bits to perform a lookup into a table, where the last bit /// Uses three bits to perform a lookup into a table, where the last bit
/// conditionally negates the looked-up value. /// conditionally negates the looked-up value.
pub trait ThreeBitCondNegLookupGadget<ConstraintF: Field>
where
Self: Sized,
{
pub trait ThreeBitCondNegLookupGadget<ConstraintF: Field>: Sized {
/// The type of values being looked up. /// The type of values being looked up.
type TableConstant; type TableConstant;

+ 0
- 22
src/uint/convert.rs

@ -171,28 +171,6 @@ impl ToBytesGadget
} }
} }
impl<const N: usize, T: PrimUInt, F: Field> ToBytesGadget<F> for [UInt<N, T, F>] {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let mut bytes = Vec::with_capacity(self.len() * (N / 8));
for elem in self {
bytes.extend_from_slice(&elem.to_bytes_le()?);
}
Ok(bytes)
}
}
impl<const N: usize, T: PrimUInt, F: Field> ToBytesGadget<F> for Vec<UInt<N, T, F>> {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
self.as_slice().to_bytes_le()
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> ToBytesGadget<F> for &'a [UInt<N, T, F>] {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
(*self).to_bytes_le()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

Loading…
Cancel
Save