arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
Add doctests for `Boolean` and `UInt8`.
Pratyush Mishra pratyushmishra@berkeley.edu
Wed, 16 Sep 2020 20:51:48 -0700
commit

13332bcc1d0594501d825306b5defa4a2aa6f99b

parent

3a18ecee0d44c13ecc9e7fe24427391ce908a5b2

M r1cs-std/src/alloc.rs → r1cs-std/src/alloc.rs

@@ -40,7 +40,7 @@ where

Self: Sized, V: ?Sized, { - /// Allocates a new variable of type `Self` in the `ConstraintSystem`. + /// Allocates a new variable of type `Self` in the `ConstraintSystem` `cs`. /// The mode of allocation is decided by `mode`. fn new_variable<T: Borrow<V>>( cs: impl Into<Namespace<F>>,

@@ -48,7 +48,9 @@ f: impl FnOnce() -> Result<T, SynthesisError>,

mode: AllocationMode, ) -> Result<Self, SynthesisError>; - /// Allocates a new constant of type `Self` in the `ConstraintSystem`. + /// Allocates a new constant of type `Self` in the `ConstraintSystem` `cs`. + /// + /// This should *not* allocate any new variables or constraints in `cs`. #[tracing::instrument(target = "r1cs", skip(cs, t))] fn new_constant( cs: impl Into<Namespace<F>>,

@@ -57,7 +59,7 @@ ) -> Result<Self, SynthesisError> {

Self::new_variable(cs, || Ok(t), AllocationMode::Constant) } - /// Allocates a new public input of type `Self` in the `ConstraintSystem`. + /// Allocates a new public input of type `Self` in the `ConstraintSystem` `cs`. #[tracing::instrument(target = "r1cs", skip(cs, f))] fn new_input<T: Borrow<V>>( cs: impl Into<Namespace<F>>,

@@ -66,7 +68,7 @@ ) -> Result<Self, SynthesisError> {

Self::new_variable(cs, f, AllocationMode::Input) } - /// Allocates a new private witness of type `Self` in the `ConstraintSystem`. + /// Allocates a new private witness of type `Self` in the `ConstraintSystem` `cs`. #[tracing::instrument(target = "r1cs", skip(cs, f))] fn new_witness<T: Borrow<V>>( cs: impl Into<Namespace<F>>,

@@ -76,6 +78,8 @@ Self::new_variable(cs, f, AllocationMode::Witness)

} } +/// This blanket implementation just allocates variables in `Self` +/// element by element. impl<I, F: Field, A: AllocVar<I, F>> AllocVar<[I], F> for Vec<A> { fn new_variable<T: Borrow<[I]>>( cs: impl Into<Namespace<F>>,
M r1cs-std/src/bits/boolean.rs → r1cs-std/src/bits/boolean.rs

@@ -6,6 +6,10 @@ use r1cs_core::{lc, ConstraintSystemRef, LinearCombination, Namespace, SynthesisError, Variable};

/// Represents a variable in the constraint system which is guaranteed /// to be either zero or one. +/// +/// In general, one should prefer using `Boolean` instead of `AllocatedBit`, +/// as `Boolean` offers better support for constant values, and implements +/// more traits. #[derive(Clone, Debug, Eq, PartialEq)] #[must_use] pub struct AllocatedBit<F: Field> {

@@ -156,10 +160,7 @@ }

} impl<F: Field> AllocVar<bool, F> for AllocatedBit<F> { - /// If `self.mode` == `AllocationMode::Constant`, this method simply outputs - /// a `Boolean::Constant`. - /// - /// Otherwise, it produces a new variable of the appropriate type + /// Produces a new variable of the appropriate kind /// (instance or witness), with a booleanity check. /// /// N.B.: we could omit the booleanity check when allocating `self`

@@ -215,16 +216,16 @@ }

} } -/// This is a boolean value which may be either a constant or -/// an interpretation of an `AllocatedBit`. +/// Represents a boolean value in the constraint system which is guaranteed +/// to be either zero or one. #[derive(Clone, Debug, Eq, PartialEq)] #[must_use] pub enum Boolean<F: Field> { - /// Existential view of the boolean variable + /// Existential view of the boolean variable. Is(AllocatedBit<F>), - /// Negated view of the boolean variable + /// Negated view of the boolean variable. Not(AllocatedBit<F>), - /// Constant (not an allocated variable) + /// Constant (not an allocated variable). Constant(bool), }

@@ -248,14 +249,17 @@ }

} impl<F: Field> Boolean<F> { - /// Returns the constrant `true`. + /// The constant `true`. pub const TRUE: Self = Boolean::Constant(true); - /// Returns the constrant `false`. + /// The constant `false`. pub const FALSE: Self = Boolean::Constant(false); - /// Constructs a `LinearCombination` from `Self`'s variables. + /// Constructs a `LinearCombination` from `Self`'s variables according + /// to the following map. /// + /// * `Boolean::Constant(true) => lc!() + Variable::One` + /// * `Boolean::Constant(false) => lc!()` /// * `Boolean::Is(v) => lc!() + v.variable()` /// * `Boolean::Not(v) => lc!() + Variable::One - v.variable()` pub fn lc(&self) -> LinearCombination<F> {

@@ -268,24 +272,84 @@ }

} /// Constructs a `Boolean` vector from a slice of constant `u8`. + /// The `u8`s are decomposed in little-endian manner. /// - /// This *does not* create any new variables. + /// This *does not* create any new variables or constraints. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// let t = Boolean::<Fr>::TRUE; + /// let f = Boolean::<Fr>::FALSE; + /// + /// let bits = vec![f, t]; + /// let generated_bits = Boolean::constant_vec_from_bytes(&[2]); + /// bits[..2].enforce_equal(&generated_bits[..2])?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn constant_vec_from_bytes(values: &[u8]) -> Vec<Self> { - let mut input_bits = vec![]; - for input_byte in values { - for bit_i in (0..8).rev() { - input_bits.push(Self::Constant(((input_byte >> bit_i) & 1u8) == 1u8)); + let mut bits = vec![]; + for byte in values { + for i in 0..8 { + bits.push(Self::Constant(((byte >> i) & 1u8) == 1u8)); } } - input_bits + bits } /// Constructs a constant `Boolean` with value `b`. + /// + /// This *does not* create any new variables or constraints. + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_std::prelude::*; + /// + /// let true_var = Boolean::<Fr>::TRUE; + /// let false_var = Boolean::<Fr>::FALSE; + /// + /// true_var.enforce_equal(&Boolean::constant(true))?; + /// false_var.enforce_equal(&Boolean::constant(false))?; + /// # Ok(()) + /// # } + /// ``` pub fn constant(b: bool) -> Self { Boolean::Constant(b) } /// Negates `self`. + /// + /// This *does not* create any new variables or constraints. + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// a.not().enforce_equal(&b)?; + /// b.not().enforce_equal(&a)?; + /// + /// a.not().enforce_equal(&Boolean::FALSE)?; + /// b.not().enforce_equal(&Boolean::TRUE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn not(&self) -> Self { match *self { Boolean::Constant(c) => Boolean::Constant(!c),

@@ -293,13 +357,34 @@ Boolean::Is(ref v) => Boolean::Not(v.clone()),

Boolean::Not(ref v) => Boolean::Is(v.clone()), } } -} -impl<F: Field> Boolean<F> { /// Outputs `self ^ other`. /// /// If at least one of `self` and `other` are constants, then this method /// *does not* create any constraints or variables. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// a.xor(&b)?.enforce_equal(&Boolean::TRUE)?; + /// b.xor(&a)?.enforce_equal(&Boolean::TRUE)?; + /// + /// a.xor(&a)?.enforce_equal(&Boolean::FALSE)?; + /// b.xor(&b)?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn xor<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> { use Boolean::*;

@@ -319,6 +404,29 @@ /// Outputs `self | other`.

/// /// If at least one of `self` and `other` are constants, then this method /// *does not* create any constraints or variables. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// a.or(&b)?.enforce_equal(&Boolean::TRUE)?; + /// b.or(&a)?.enforce_equal(&Boolean::TRUE)?; + /// + /// a.or(&a)?.enforce_equal(&Boolean::TRUE)?; + /// b.or(&b)?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn or<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> { use Boolean::*;

@@ -337,6 +445,29 @@ /// Outputs `self & other`.

/// /// If at least one of `self` and `other` are constants, then this method /// *does not* create any constraints or variables. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// a.and(&a)?.enforce_equal(&Boolean::TRUE)?; + /// + /// a.and(&b)?.enforce_equal(&Boolean::FALSE)?; + /// b.and(&a)?.enforce_equal(&Boolean::FALSE)?; + /// b.and(&b)?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn and<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> { use Boolean::*;

@@ -355,6 +486,27 @@ }

} /// Outputs `bits[0] & bits[1] & ... & bits.last().unwrap()`. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// let c = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// + /// Boolean::kary_and(&[a.clone(), b.clone(), c.clone()])?.enforce_equal(&Boolean::FALSE)?; + /// Boolean::kary_and(&[a.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn kary_and(bits: &[Self]) -> Result<Self, SynthesisError> { assert!(!bits.is_empty());

@@ -371,6 +523,28 @@ Ok(cur.expect("should not be 0"))

} /// Outputs `bits[0] | bits[1] | ... | bits.last().unwrap()`. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// let c = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// Boolean::kary_or(&[a.clone(), b.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// Boolean::kary_or(&[a.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// Boolean::kary_or(&[b.clone(), c.clone()])?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn kary_or(bits: &[Self]) -> Result<Self, SynthesisError> { assert!(!bits.is_empty());

@@ -387,6 +561,28 @@ Ok(cur.expect("should not be 0"))

} /// Outputs `(bits[0] & bits[1] & ... & bits.last().unwrap()).not()`. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// let c = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// + /// Boolean::kary_nand(&[a.clone(), b.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// Boolean::kary_nand(&[a.clone(), c.clone()])?.enforce_equal(&Boolean::FALSE)?; + /// Boolean::kary_nand(&[b.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn kary_nand(bits: &[Self]) -> Result<Self, SynthesisError> { Ok(Self::kary_and(bits)?.not())

@@ -489,6 +685,27 @@

/// Conditionally selects one of `first` and `second` based on the value of `self`: /// /// If `self.is_eq(&Boolean::TRUE)`, this outputs `first`; else, it outputs `second`. + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// let cond = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// + /// cond.select(&a, &b)?.enforce_equal(&Boolean::TRUE)?; + /// cond.select(&b, &a)?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs", skip(first, second))] pub fn select<T: CondSelectGadget<F>>( &self,
M r1cs-std/src/bits/mod.rs → r1cs-std/src/bits/mod.rs

@@ -28,7 +28,7 @@

/// Outputs a possibly non-unique bit-wise representation of `self`. /// /// If you're not absolutely certain that your usecase can get away with a - /// non-canonical representation, please use `self.to_bits(cs)` instead. + /// non-canonical representation, please use `self.to_bits()` instead. fn to_non_unique_bits_le(&self) -> Result<Vec<Boolean<F>>, SynthesisError> { self.to_bits_le() }
M r1cs-std/src/bits/uint8.rs → r1cs-std/src/bits/uint8.rs

@@ -12,7 +12,6 @@ #[derive(Clone, Debug)]

pub struct UInt8<F: Field> { /// Little-endian representation: least significant bit first pub(crate) bits: Vec<Boolean<F>>, - /// Little-endian representation: least significant bit first pub(crate) value: Option<u8>, }

@@ -39,6 +38,24 @@ }

impl<F: Field> UInt8<F> { /// Construct a constant vector of `UInt8` from a vector of `u8` + /// + /// This *does not* create any new variables or constraints. + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// let var = vec![UInt8::new_witness(cs.clone(), || Ok(2))?]; + /// + /// let constant = UInt8::constant_vec(&[2]); + /// var.enforce_equal(&constant)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn constant_vec(values: &[u8]) -> Vec<Self> { let mut result = Vec::new(); for value in values {

@@ -48,6 +65,25 @@ result

} /// Construct a constant `UInt8` from a `u8` + /// + /// This *does not* create new variables or constraints. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// let var = UInt8::new_witness(cs.clone(), || Ok(2))?; + /// + /// let constant = UInt8::constant(2); + /// var.enforce_equal(&constant)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn constant(value: u8) -> Self { let mut bits = Vec::with_capacity(8);

@@ -80,12 +116,30 @@ Ok(output_vec)

} /// Allocates a slice of `u8`'s as public inputs by first packing them into - /// `F` elements, (thus reducing the number of input allocations), - /// and then converts this list of `AllocatedFp<F>` variables back into - /// bytes. + /// elements of `F`, (thus reducing the number of input allocations), allocating + /// these elements as public inputs, and then converting these field variables + /// `FpVar<F>` variables back into bytes. /// /// From a user perspective, this trade-off adds constraints, but improves /// verifier time and verification key size. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// let two = UInt8::new_witness(cs.clone(), || Ok(2))?; + /// let var = vec![two.clone(); 32]; + /// + /// let c = UInt8::new_input_vec(cs.clone(), &[2; 32])?; + /// var.enforce_equal(&c)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn new_input_vec( cs: impl Into<Namespace<F>>, values: &[u8],

@@ -121,6 +175,30 @@ }

/// Converts a little-endian byte order representation of bits into a /// `UInt8`. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// let var = UInt8::new_witness(cs.clone(), || Ok(128))?; + /// + /// let f = Boolean::FALSE; + /// let t = Boolean::TRUE; + /// + /// // Construct [0, 0, 0, 0, 0, 0, 0, 1] + /// let mut bits = vec![f.clone(); 7]; + /// bits.push(t); + /// + /// let mut c = UInt8::from_bits_le(&bits); + /// var.enforce_equal(&c)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn from_bits_le(bits: &[Boolean<F>]) -> Self { assert_eq!(bits.len(), 8);

@@ -142,6 +220,24 @@ /// Outputs `self ^ other`.

/// /// If at least one of `self` and `other` are constants, then this method /// *does not* create any constraints or variables. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::<Fr>::new_ref(); + /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?; + /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?; + /// let c = UInt8::new_witness(cs.clone(), || Ok(1))?; + /// + /// a.xor(&b)?.enforce_equal(&c)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn xor(&self, other: &Self) -> Result<Self, SynthesisError> { let new_value = match (self.value, other.value) {
M r1cs-std/src/lib.rs → r1cs-std/src/lib.rs

@@ -112,7 +112,8 @@ R1CSVar,

}; } -/// This trait describes some core functionality that is common to high-level variables, such as `Boolean`s, `FieldVar`s, `GroupVar`s, etc. +/// 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;

@@ -175,8 +176,10 @@ self.ok_or(r1cs_core::SynthesisError::AssignmentMissing)

} } -/// Obtains the field variables +/// Specifies how to convert a variable of type `Self` to variables of +/// type `FpVar<ConstraintF>` pub trait ToConstraintFieldGadget<ConstraintF: algebra::PrimeField> { + /// Converts `self` to `FpVar<ConstraintF>` variables. fn to_constraint_field( &self, ) -> Result<Vec<crate::fields::fp::FpVar<ConstraintF>>, r1cs_core::SynthesisError>;