mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-10 16:01:28 +01:00
Add doctests for Boolean and UInt8.
This commit is contained in:
@@ -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 @@ where
|
||||
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 @@ where
|
||||
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 @@ where
|
||||
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 @@ 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> {
|
||||
fn new_variable<T: Borrow<[I]>>(
|
||||
cs: impl Into<Namespace<F>>,
|
||||
|
||||
@@ -6,6 +6,10 @@ use r1cs_core::{lc, ConstraintSystemRef, LinearCombination, Namespace, Synthesis
|
||||
|
||||
/// 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> 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.
|
||||
///
|
||||
/// N.B.: we could omit the booleanity check when allocating `self`
|
||||
@@ -215,16 +216,16 @@ impl<F: Field> CondSelectGadget<F> for AllocatedBit<F> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> R1CSVar<F> for Boolean<F> {
|
||||
}
|
||||
|
||||
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 @@ impl<F: Field> Boolean<F> {
|
||||
}
|
||||
|
||||
/// 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 @@ impl<F: Field> Boolean<F> {
|
||||
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 @@ impl<F: Field> Boolean<F> {
|
||||
///
|
||||
/// 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 @@ impl<F: Field> Boolean<F> {
|
||||
///
|
||||
/// 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 @@ impl<F: Field> Boolean<F> {
|
||||
}
|
||||
|
||||
/// 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 @@ impl<F: Field> Boolean<F> {
|
||||
}
|
||||
|
||||
/// 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 @@ impl<F: Field> Boolean<F> {
|
||||
}
|
||||
|
||||
/// 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 @@ impl<F: Field> Boolean<F> {
|
||||
/// 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,
|
||||
|
||||
@@ -28,7 +28,7 @@ pub trait ToBitsGadget<F: Field> {
|
||||
/// 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()
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use core::borrow::Borrow;
|
||||
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> R1CSVar<F> for UInt8<F> {
|
||||
|
||||
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 @@ impl<F: Field> UInt8<F> {
|
||||
}
|
||||
|
||||
/// 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 @@ impl<F: Field> UInt8<F> {
|
||||
}
|
||||
|
||||
/// 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 @@ impl<F: Field> UInt8<F> {
|
||||
|
||||
/// 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 @@ impl<F: Field> UInt8<F> {
|
||||
///
|
||||
/// 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) {
|
||||
|
||||
@@ -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> {
|
||||
/// The type of the "native" value that `Self` represents in the constraint system.
|
||||
type Value: core::fmt::Debug + Eq + Clone;
|
||||
@@ -175,8 +176,10 @@ impl<T> Assignment<T> for Option<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>;
|
||||
|
||||
Reference in New Issue
Block a user