mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-09 07:21:29 +01:00
Add convenience impls for common types (#137)
* Work * Tweak * Fmt * Work * Format + typo fixes * `no-std` fix
This commit is contained in:
62
src/alloc.rs
62
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>
|
pub trait AllocVar<V: ?Sized, F: Field>: Sized {
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
V: ?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<I, F: Field, A: AllocVar<I, F>> AllocVar<[I], F> for Vec<A> {
|
|||||||
) -> 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();
|
f().and_then(|v| {
|
||||||
for value in f()?.borrow().iter() {
|
v.borrow()
|
||||||
vec.push(A::new_variable(cs.clone(), || Ok(value), mode)?);
|
.iter()
|
||||||
}
|
.map(|e| A::new_variable(cs.clone(), || Ok(e), mode))
|
||||||
Ok(vec)
|
.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())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ impl<F: PrimeField> Boolean<F> {
|
|||||||
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<F: PrimeField> Boolean<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ impl<F: Field> Boolean<F> {
|
|||||||
/// 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(())
|
||||||
/// # }
|
/// # }
|
||||||
|
|||||||
38
src/cmp.rs
38
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<F: Field>: R1CSVar<F> {
|
|||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,6 +90,60 @@ impl<'a, F: Field, T: 'a + ToBytesGadget<F>> ToBytesGadget<F> 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> {
|
||||||
|
|||||||
109
src/eq.rs
109
src/eq.rs
@@ -33,7 +33,7 @@ pub trait EqGadget<F: Field> {
|
|||||||
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<F: Field> {
|
|||||||
/// 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<F: Field> {
|
|||||||
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<F: Field> {
|
|||||||
/// 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<T: EqGadget<F> + R1CSVar<F>, F: PrimeField> EqGadget<F> 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());
|
if self.is_empty() & other.is_empty() {
|
||||||
let mut results = Vec::with_capacity(self.len());
|
Ok(Boolean::TRUE)
|
||||||
for (a, b) in self.iter().zip(other) {
|
} else {
|
||||||
results.push(a.is_eq(b)?);
|
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<T: EqGadget<F> + R1CSVar<F>, F: PrimeField> EqGadget<F> 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -686,7 +686,7 @@ impl<TargetF: PrimeField, BaseF: PrimeField> ToBytesGadget<BaseF>
|
|||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -555,7 +555,7 @@ impl<F: PrimeField> ToBytesGadget<F> for AllocatedFp<F> {
|
|||||||
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<F: PrimeField> ToBytesGadget<F> for AllocatedFp<F> {
|
|||||||
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)
|
||||||
|
|||||||
59
src/lib.rs
59
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`.
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ impl<F: PrimeField> Radix2DomainVar<F> {
|
|||||||
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
src/r1cs_var.rs
Normal file
86
src/r1cs_var.rs
Normal file
@@ -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,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>
|
pub trait CondSelectGadget<ConstraintF: Field>: Sized + Clone {
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
Self: 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>
|
pub trait TwoBitLookupGadget<ConstraintF: Field>: Sized {
|
||||||
where
|
|
||||||
Self: 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>
|
pub trait ThreeBitCondNegLookupGadget<ConstraintF: Field>: Sized {
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
/// The type of values being looked up.
|
/// The type of values being looked up.
|
||||||
type TableConstant;
|
type TableConstant;
|
||||||
|
|
||||||
|
|||||||
@@ -171,28 +171,6 @@ impl<const N: usize, T: PrimUInt, ConstraintF: Field> ToBytesGadget<ConstraintF>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user