Browse Source

Add doctests for `Boolean` and `UInt8`.

master
Pratyush Mishra 4 years ago
parent
commit
13332bcc1d
5 changed files with 351 additions and 31 deletions
  1. +8
    -4
      r1cs-std/src/alloc.rs
  2. +237
    -20
      r1cs-std/src/bits/boolean.rs
  3. +1
    -1
      r1cs-std/src/bits/mod.rs
  4. +100
    -4
      r1cs-std/src/bits/uint8.rs
  5. +5
    -2
      r1cs-std/src/lib.rs

+ 8
- 4
r1cs-std/src/alloc.rs

@ -40,7 +40,7 @@ where
Self: Sized, Self: Sized,
V: ?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`. /// The mode of allocation is decided by `mode`.
fn new_variable<T: Borrow<V>>( fn new_variable<T: Borrow<V>>(
cs: impl Into<Namespace<F>>, cs: impl Into<Namespace<F>>,
@ -48,7 +48,9 @@ where
mode: AllocationMode, mode: AllocationMode,
) -> Result<Self, SynthesisError>; ) -> 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))] #[tracing::instrument(target = "r1cs", skip(cs, t))]
fn new_constant( fn new_constant(
cs: impl Into<Namespace<F>>, cs: impl Into<Namespace<F>>,
@ -57,7 +59,7 @@ where
Self::new_variable(cs, || Ok(t), AllocationMode::Constant) 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))] #[tracing::instrument(target = "r1cs", skip(cs, f))]
fn new_input<T: Borrow<V>>( fn new_input<T: Borrow<V>>(
cs: impl Into<Namespace<F>>, cs: impl Into<Namespace<F>>,
@ -66,7 +68,7 @@ where
Self::new_variable(cs, f, AllocationMode::Input) 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))] #[tracing::instrument(target = "r1cs", skip(cs, f))]
fn new_witness<T: Borrow<V>>( fn new_witness<T: Borrow<V>>(
cs: impl Into<Namespace<F>>, cs: impl Into<Namespace<F>>,
@ -76,6 +78,8 @@ where
} }
} }
/// 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> { impl<I, F: Field, A: AllocVar<I, F>> AllocVar<[I], F> for Vec<A> {
fn new_variable<T: Borrow<[I]>>( fn new_variable<T: Borrow<[I]>>(
cs: impl Into<Namespace<F>>, cs: impl Into<Namespace<F>>,

+ 237
- 20
r1cs-std/src/bits/boolean.rs

@ -6,6 +6,10 @@ use r1cs_core::{lc, ConstraintSystemRef, LinearCombination, Namespace, Synthesis
/// Represents a variable in the constraint system which is guaranteed /// Represents a variable in the constraint system which is guaranteed
/// to be either zero or one. /// 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)] #[derive(Clone, Debug, Eq, PartialEq)]
#[must_use] #[must_use]
pub struct AllocatedBit<F: Field> { pub struct AllocatedBit<F: Field> {
@ -156,10 +160,7 @@ impl AllocatedBit {
} }
impl<F: Field> AllocVar<bool, F> for AllocatedBit<F> { 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. /// (instance or witness), with a booleanity check.
/// ///
/// N.B.: we could omit the booleanity check when allocating `self` /// N.B.: we could omit the booleanity check when allocating `self`
@ -215,16 +216,16 @@ impl CondSelectGadget for AllocatedBit {
} }
} }
/// 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)] #[derive(Clone, Debug, Eq, PartialEq)]
#[must_use] #[must_use]
pub enum Boolean<F: Field> { pub enum Boolean<F: Field> {
/// Existential view of the boolean variable
/// Existential view of the boolean variable.
Is(AllocatedBit<F>), Is(AllocatedBit<F>),
/// Negated view of the boolean variable
/// Negated view of the boolean variable.
Not(AllocatedBit<F>), Not(AllocatedBit<F>),
/// Constant (not an allocated variable)
/// Constant (not an allocated variable).
Constant(bool), Constant(bool),
} }
@ -248,14 +249,17 @@ impl R1CSVar for Boolean {
} }
impl<F: Field> Boolean<F> { impl<F: Field> Boolean<F> {
/// Returns the constrant `true`.
/// The constant `true`.
pub const TRUE: Self = Boolean::Constant(true); pub const TRUE: Self = Boolean::Constant(true);
/// Returns the constrant `false`.
/// The constant `false`.
pub const FALSE: Self = Boolean::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::Is(v) => lc!() + v.variable()`
/// * `Boolean::Not(v) => lc!() + Variable::One - v.variable()` /// * `Boolean::Not(v) => lc!() + Variable::One - v.variable()`
pub fn lc(&self) -> LinearCombination<F> { pub fn lc(&self) -> LinearCombination<F> {
@ -268,24 +272,84 @@ impl Boolean {
} }
/// Constructs a `Boolean` vector from a slice of constant `u8`. /// 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 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::*;
/// ///
/// This *does not* create any new variables.
/// 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> { 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`. /// 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 { pub fn constant(b: bool) -> Self {
Boolean::Constant(b) Boolean::Constant(b)
} }
/// Negates `self`. /// 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 { pub fn not(&self) -> Self {
match *self { match *self {
Boolean::Constant(c) => Boolean::Constant(!c), Boolean::Constant(c) => Boolean::Constant(!c),
@ -293,13 +357,34 @@ impl Boolean {
Boolean::Not(ref v) => Boolean::Is(v.clone()), Boolean::Not(ref v) => Boolean::Is(v.clone()),
} }
} }
}
impl<F: Field> Boolean<F> {
/// Outputs `self ^ other`. /// Outputs `self ^ other`.
/// ///
/// If at least one of `self` and `other` are constants, then this method /// If at least one of `self` and `other` are constants, then this method
/// *does not* create any constraints or variables. /// *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")] #[tracing::instrument(target = "r1cs")]
pub fn xor<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> { pub fn xor<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> {
use Boolean::*; use Boolean::*;
@ -319,6 +404,29 @@ impl Boolean {
/// ///
/// If at least one of `self` and `other` are constants, then this method /// If at least one of `self` and `other` are constants, then this method
/// *does not* create any constraints or variables. /// *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")] #[tracing::instrument(target = "r1cs")]
pub fn or<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> { pub fn or<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> {
use Boolean::*; use Boolean::*;
@ -337,6 +445,29 @@ impl Boolean {
/// ///
/// If at least one of `self` and `other` are constants, then this method /// If at least one of `self` and `other` are constants, then this method
/// *does not* create any constraints or variables. /// *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")] #[tracing::instrument(target = "r1cs")]
pub fn and<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> { pub fn and<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> {
use Boolean::*; use Boolean::*;
@ -355,6 +486,27 @@ impl Boolean {
} }
/// Outputs `bits[0] & bits[1] & ... & bits.last().unwrap()`. /// 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")] #[tracing::instrument(target = "r1cs")]
pub fn kary_and(bits: &[Self]) -> Result<Self, SynthesisError> { pub fn kary_and(bits: &[Self]) -> Result<Self, SynthesisError> {
assert!(!bits.is_empty()); assert!(!bits.is_empty());
@ -371,6 +523,28 @@ impl Boolean {
} }
/// Outputs `bits[0] | bits[1] | ... | bits.last().unwrap()`. /// 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")] #[tracing::instrument(target = "r1cs")]
pub fn kary_or(bits: &[Self]) -> Result<Self, SynthesisError> { pub fn kary_or(bits: &[Self]) -> Result<Self, SynthesisError> {
assert!(!bits.is_empty()); assert!(!bits.is_empty());
@ -387,6 +561,28 @@ impl Boolean {
} }
/// Outputs `(bits[0] & bits[1] & ... & bits.last().unwrap()).not()`. /// 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")] #[tracing::instrument(target = "r1cs")]
pub fn kary_nand(bits: &[Self]) -> Result<Self, SynthesisError> { pub fn kary_nand(bits: &[Self]) -> Result<Self, SynthesisError> {
Ok(Self::kary_and(bits)?.not()) Ok(Self::kary_and(bits)?.not())
@ -489,6 +685,27 @@ impl Boolean {
/// Conditionally selects one of `first` and `second` based on the value of `self`: /// 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`. /// 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))] #[tracing::instrument(target = "r1cs", skip(first, second))]
pub fn select<T: CondSelectGadget<F>>( pub fn select<T: CondSelectGadget<F>>(
&self, &self,

+ 1
- 1
r1cs-std/src/bits/mod.rs

@ -28,7 +28,7 @@ pub trait ToBitsGadget {
/// Outputs a possibly non-unique bit-wise representation of `self`. /// Outputs a possibly non-unique bit-wise representation of `self`.
/// ///
/// If you're not absolutely certain that your usecase can get away with a /// 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> { fn to_non_unique_bits_le(&self) -> Result<Vec<Boolean<F>>, SynthesisError> {
self.to_bits_le() self.to_bits_le()
} }

+ 100
- 4
r1cs-std/src/bits/uint8.rs

@ -12,7 +12,6 @@ use core::borrow::Borrow;
pub struct UInt8<F: Field> { pub struct UInt8<F: Field> {
/// Little-endian representation: least significant bit first /// Little-endian representation: least significant bit first
pub(crate) bits: Vec<Boolean<F>>, pub(crate) bits: Vec<Boolean<F>>,
/// Little-endian representation: least significant bit first
pub(crate) value: Option<u8>, pub(crate) value: Option<u8>,
} }
@ -39,6 +38,24 @@ impl R1CSVar for UInt8 {
impl<F: Field> UInt8<F> { impl<F: Field> UInt8<F> {
/// Construct a constant vector of `UInt8` from a vector of `u8` /// 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> { pub fn constant_vec(values: &[u8]) -> Vec<Self> {
let mut result = Vec::new(); let mut result = Vec::new();
for value in values { for value in values {
@ -48,6 +65,25 @@ impl UInt8 {
} }
/// Construct a constant `UInt8` from a `u8` /// 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 { pub fn constant(value: u8) -> Self {
let mut bits = Vec::with_capacity(8); let mut bits = Vec::with_capacity(8);
@ -80,12 +116,30 @@ impl UInt8 {
} }
/// Allocates a slice of `u8`'s as public inputs by first packing them into /// 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 /// From a user perspective, this trade-off adds constraints, but improves
/// verifier time and verification key size. /// 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( pub fn new_input_vec(
cs: impl Into<Namespace<F>>, cs: impl Into<Namespace<F>>,
values: &[u8], values: &[u8],
@ -121,6 +175,30 @@ impl UInt8 {
/// Converts a little-endian byte order representation of bits into a /// Converts a little-endian byte order representation of bits into a
/// `UInt8`. /// `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")] #[tracing::instrument(target = "r1cs")]
pub fn from_bits_le(bits: &[Boolean<F>]) -> Self { pub fn from_bits_le(bits: &[Boolean<F>]) -> Self {
assert_eq!(bits.len(), 8); assert_eq!(bits.len(), 8);
@ -142,6 +220,24 @@ impl UInt8 {
/// ///
/// If at least one of `self` and `other` are constants, then this method /// If at least one of `self` and `other` are constants, then this method
/// *does not* create any constraints or variables. /// *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")] #[tracing::instrument(target = "r1cs")]
pub fn xor(&self, other: &Self) -> Result<Self, SynthesisError> { pub fn xor(&self, other: &Self) -> Result<Self, SynthesisError> {
let new_value = match (self.value, other.value) { let new_value = match (self.value, other.value) {

+ 5
- 2
r1cs-std/src/lib.rs

@ -112,7 +112,8 @@ 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.
/// 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> { pub trait R1CSVar<F: Field> {
/// The type of the "native" value that `Self` represents in the constraint system. /// The type of the "native" value that `Self` represents in the constraint system.
type Value: core::fmt::Debug + Eq + Clone; type Value: core::fmt::Debug + Eq + Clone;
@ -175,8 +176,10 @@ impl Assignment for Option {
} }
} }
/// 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> { pub trait ToConstraintFieldGadget<ConstraintF: algebra::PrimeField> {
/// Converts `self` to `FpVar<ConstraintF>` variables.
fn to_constraint_field( fn to_constraint_field(
&self, &self,
) -> Result<Vec<crate::fields::fp::FpVar<ConstraintF>>, r1cs_core::SynthesisError>; ) -> Result<Vec<crate::fields::fp::FpVar<ConstraintF>>, r1cs_core::SynthesisError>;

Loading…
Cancel
Save