mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-23 12:13:48 +01:00
Document r1cs-std
This commit is contained in:
@@ -254,6 +254,10 @@ impl<F: Field> Boolean<F> {
|
||||
/// Returns the constrant `false`.
|
||||
pub const FALSE: Self = Boolean::Constant(false);
|
||||
|
||||
/// Constructs a `LinearCombination` from `Self`'s variables.
|
||||
///
|
||||
/// * `Boolean::Is(v) => lc!() + v.variable()`
|
||||
/// * `Boolean::Not(v) => lc!() + Variable::One - v.variable()`
|
||||
pub fn lc(&self) -> LinearCombination<F> {
|
||||
match self {
|
||||
Boolean::Constant(false) => lc!(),
|
||||
@@ -263,7 +267,9 @@ impl<F: Field> Boolean<F> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a boolean vector from a vector of u8
|
||||
/// Constructs a `Boolean` vector from a slice of constant `u8`.
|
||||
///
|
||||
/// This *does not* create any new variables.
|
||||
pub fn constant_vec_from_bytes(values: &[u8]) -> Vec<Self> {
|
||||
let mut input_bits = vec![];
|
||||
for input_byte in values {
|
||||
@@ -274,12 +280,12 @@ impl<F: Field> Boolean<F> {
|
||||
input_bits
|
||||
}
|
||||
|
||||
/// Construct a boolean from a known constant
|
||||
/// Constructs a constant `Boolean` with value `b`.
|
||||
pub fn constant(b: bool) -> Self {
|
||||
Boolean::Constant(b)
|
||||
}
|
||||
|
||||
/// Return a negated interpretation of this boolean.
|
||||
/// Negates `self`.
|
||||
pub fn not(&self) -> Self {
|
||||
match *self {
|
||||
Boolean::Constant(c) => Boolean::Constant(!c),
|
||||
@@ -290,11 +296,14 @@ impl<F: Field> Boolean<F> {
|
||||
}
|
||||
|
||||
impl<F: Field> Boolean<F> {
|
||||
/// Perform XOR over two boolean operands
|
||||
/// Outputs `self ^ other`.
|
||||
///
|
||||
/// If at least one of `self` and `other` are constants, then this method
|
||||
/// *does not* create any constraints or variables.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn xor<'a>(&'a self, b: &'a Self) -> Result<Self, SynthesisError> {
|
||||
pub fn xor<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> {
|
||||
use Boolean::*;
|
||||
match (self, b) {
|
||||
match (self, other) {
|
||||
(&Constant(false), x) | (x, &Constant(false)) => Ok(x.clone()),
|
||||
(&Constant(true), x) | (x, &Constant(true)) => Ok(x.not()),
|
||||
// a XOR (NOT b) = NOT(a XOR b)
|
||||
@@ -306,11 +315,14 @@ impl<F: Field> Boolean<F> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform OR over two boolean operands
|
||||
/// Outputs `self | other`.
|
||||
///
|
||||
/// If at least one of `self` and `other` are constants, then this method
|
||||
/// *does not* create any constraints or variables.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn or<'a>(&'a self, b: &'a Self) -> Result<Self, SynthesisError> {
|
||||
pub fn or<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> {
|
||||
use Boolean::*;
|
||||
match (self, b) {
|
||||
match (self, other) {
|
||||
(&Constant(false), x) | (x, &Constant(false)) => Ok(x.clone()),
|
||||
(&Constant(true), _) | (_, &Constant(true)) => Ok(Constant(true)),
|
||||
// a OR b = NOT ((NOT a) AND b)
|
||||
@@ -321,11 +333,14 @@ impl<F: Field> Boolean<F> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform AND over two boolean operands
|
||||
/// Outputs `self & other`.
|
||||
///
|
||||
/// If at least one of `self` and `other` are constants, then this method
|
||||
/// *does not* create any constraints or variables.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn and<'a>(&'a self, b: &'a Self) -> Result<Self, SynthesisError> {
|
||||
pub fn and<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> {
|
||||
use Boolean::*;
|
||||
match (self, b) {
|
||||
match (self, other) {
|
||||
// false AND x is always false
|
||||
(&Constant(false), _) | (_, &Constant(false)) => Ok(Constant(false)),
|
||||
// true AND x is always x
|
||||
@@ -339,6 +354,7 @@ impl<F: Field> Boolean<F> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs `bits[0] & bits[1] & ... & bits.last().unwrap()`.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn kary_and(bits: &[Self]) -> Result<Self, SynthesisError> {
|
||||
assert!(!bits.is_empty());
|
||||
@@ -354,6 +370,7 @@ impl<F: Field> Boolean<F> {
|
||||
Ok(cur.expect("should not be 0"))
|
||||
}
|
||||
|
||||
/// Outputs `bits[0] | bits[1] | ... | bits.last().unwrap()`.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn kary_or(bits: &[Self]) -> Result<Self, SynthesisError> {
|
||||
assert!(!bits.is_empty());
|
||||
@@ -369,12 +386,15 @@ impl<F: Field> Boolean<F> {
|
||||
Ok(cur.expect("should not be 0"))
|
||||
}
|
||||
|
||||
/// Outputs `(bits[0] & bits[1] & ... & bits.last().unwrap()).not()`.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn kary_nand(bits: &[Self]) -> Result<Self, SynthesisError> {
|
||||
Ok(Self::kary_and(bits)?.not())
|
||||
}
|
||||
|
||||
/// Assert that at least one input is false.
|
||||
/// Enforces that `Self::kary_nand(bits).is_eq(&Boolean::TRUE)`.
|
||||
///
|
||||
/// Informally, this means that at least one element in `bits` must be `false`.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn enforce_kary_nand(bits: &[Self]) -> Result<(), SynthesisError> {
|
||||
use Boolean::*;
|
||||
@@ -392,7 +412,7 @@ impl<F: Field> Boolean<F> {
|
||||
|
||||
/// Enforces that `bits`, when interpreted as a integer, is less than `F::characteristic()`,
|
||||
/// That is, interpret bits as a little-endian integer, and enforce that this integer
|
||||
/// is "in the field F".
|
||||
/// is "in the field Z_p", where `p = F::characteristic()` .
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn enforce_in_field_le(bits: &[Self]) -> Result<(), SynthesisError> {
|
||||
// `bits` < F::characteristic() <==> `bits` <= F::characteristic() -1
|
||||
@@ -466,6 +486,9 @@ impl<F: Field> Boolean<F> {
|
||||
Ok(current_run)
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
#[tracing::instrument(target = "r1cs", skip(first, second))]
|
||||
pub fn select<T: CondSelectGadget<F>>(
|
||||
&self,
|
||||
|
||||
@@ -5,15 +5,20 @@ use crate::{
|
||||
use algebra::Field;
|
||||
use r1cs_core::SynthesisError;
|
||||
|
||||
/// This module contains `Boolean`, a R1CS equivalent of the `bool` type.
|
||||
pub mod boolean;
|
||||
/// This module contains `UInt8`, a R1CS equivalent of the `u8` type.
|
||||
pub mod uint8;
|
||||
/// This module contains a macro for generating `UIntN` types, which are R1CS equivalents of
|
||||
/// `N`-bit unsigned integers.
|
||||
#[macro_use]
|
||||
pub mod uint;
|
||||
|
||||
make_uint!(UInt16, 16, u16, uint16);
|
||||
make_uint!(UInt32, 32, u32, uint32);
|
||||
make_uint!(UInt64, 64, u64, uint64);
|
||||
make_uint!(UInt16, 16, u16, uint16, "16");
|
||||
make_uint!(UInt32, 32, u32, uint32, "32");
|
||||
make_uint!(UInt64, 64, u64, uint64, "64");
|
||||
|
||||
/// Specifies constraints for conversion to a little-endian bit representation of `self`.
|
||||
pub trait ToBitsGadget<F: Field> {
|
||||
/// Outputs the canonical little-endian bit-wise representation of `self`.
|
||||
///
|
||||
@@ -70,6 +75,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies constraints for conversion to a little-endian byte representation of `self`.
|
||||
pub trait ToBytesGadget<F: Field> {
|
||||
/// Outputs a canonical, little-endian, byte decomposition of `self`.
|
||||
///
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
macro_rules! make_uint {
|
||||
($name:ident, $size:expr, $native:ident, $mod_name:ident) => {
|
||||
($name:ident, $size:expr, $native:ident, $mod_name:ident, $native_doc_name:expr) => {
|
||||
#[doc = "This module contains a `UInt"]
|
||||
#[doc = $native_doc_name]
|
||||
#[doc = "`, a R1CS equivalent of the `u"]
|
||||
#[doc = $native_doc_name]
|
||||
#[doc = "`type."]
|
||||
pub mod $mod_name {
|
||||
use algebra::{Field, FpParameters, PrimeField};
|
||||
use core::borrow::Borrow;
|
||||
@@ -15,8 +20,14 @@ macro_rules! make_uint {
|
||||
Assignment, Vec,
|
||||
};
|
||||
|
||||
/// Represents an interpretation of `Boolean` objects as an
|
||||
/// unsigned integer.
|
||||
#[doc = "This struct represent an unsigned"]
|
||||
#[doc = $native_doc_name]
|
||||
#[doc = "-bit integer as a sequence of "]
|
||||
#[doc = $native_doc_name]
|
||||
#[doc = " `Boolean`s\n"]
|
||||
#[doc = "This is the R1CS equivalent of the native `u"]
|
||||
#[doc = $native_doc_name]
|
||||
#[doc = "` unsigned integer type."]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct $name<F: Field> {
|
||||
// Least significant bit first
|
||||
@@ -46,7 +57,11 @@ macro_rules! make_uint {
|
||||
}
|
||||
|
||||
impl<F: Field> $name<F> {
|
||||
/// Construct a constant `$name` from a `$native`
|
||||
#[doc = "Construct a constant `UInt"]
|
||||
#[doc = $native_doc_name]
|
||||
#[doc = "` from the native `u"]
|
||||
#[doc = $native_doc_name]
|
||||
#[doc = "` type."]
|
||||
pub fn constant(value: $native) -> Self {
|
||||
let mut bits = Vec::with_capacity($size);
|
||||
|
||||
@@ -67,13 +82,18 @@ macro_rules! make_uint {
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns this `$name` into its little-endian byte order representation.
|
||||
/// Turns `self` into the underlying little-endian bits.
|
||||
pub fn to_bits_le(&self) -> Vec<Boolean<F>> {
|
||||
self.bits.clone()
|
||||
}
|
||||
|
||||
/// Converts a little-endian byte order representation of bits into a
|
||||
/// `$name`.
|
||||
/// Construct `Self` from a slice of `Boolean`s.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if `bits.len() != u
|
||||
#[doc($native_doc_name)]
|
||||
#[doc("`.")]
|
||||
pub fn from_bits_le(bits: &[Boolean<F>]) -> Self {
|
||||
assert_eq!(bits.len(), $size);
|
||||
|
||||
@@ -105,6 +125,7 @@ macro_rules! make_uint {
|
||||
Self { value, bits }
|
||||
}
|
||||
|
||||
/// Rotates `self` to the right by `by` steps, wrapping around.
|
||||
#[tracing::instrument(target = "r1cs", skip(self))]
|
||||
pub fn rotr(&self, by: usize) -> Self {
|
||||
let by = by % $size;
|
||||
@@ -126,8 +147,11 @@ macro_rules! make_uint {
|
||||
}
|
||||
}
|
||||
|
||||
/// XOR this `$name` with another `$name`
|
||||
#[tracing::instrument(target = "r1cs", skip(self))]
|
||||
/// Outputs `self ^ other`.
|
||||
///
|
||||
/// If at least one of `self` and `other` are constants, then this method
|
||||
/// *does not* create any constraints or variables.
|
||||
#[tracing::instrument(target = "r1cs", skip(self, other))]
|
||||
pub fn xor(&self, other: &Self) -> Result<Self, SynthesisError> {
|
||||
let new_value = match (self.value, other.value) {
|
||||
(Some(a), Some(b)) => Some(a ^ b),
|
||||
@@ -147,8 +171,10 @@ macro_rules! make_uint {
|
||||
})
|
||||
}
|
||||
|
||||
/// Perform modular addition of several `$name` objects.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
/// Perform modular addition of `operands`.
|
||||
///
|
||||
/// The user must ensure that overflow does not occur.
|
||||
#[tracing::instrument(target = "r1cs", skip(operands))]
|
||||
pub fn addmany(operands: &[Self]) -> Result<Self, SynthesisError>
|
||||
where
|
||||
F: PrimeField,
|
||||
|
||||
@@ -64,6 +64,7 @@ impl<F: Field> UInt8<F> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a slice of `u8`'s as private witnesses.
|
||||
pub fn new_witness_vec(
|
||||
cs: impl Into<Namespace<F>>,
|
||||
values: &[impl Into<Option<u8>> + Copy],
|
||||
@@ -78,10 +79,13 @@ impl<F: Field> UInt8<F> {
|
||||
Ok(output_vec)
|
||||
}
|
||||
|
||||
/// Allocates a vector of `u8`'s by first converting (chunks of) them to
|
||||
/// `ConstraintF` elements, (thus reducing the number of input allocations),
|
||||
/// and then converts this list of `ConstraintF` gadgets back 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.
|
||||
///
|
||||
/// From a user perspective, this trade-off adds constraints, but improves
|
||||
/// verifier time and verification key size.
|
||||
pub fn new_input_vec(
|
||||
cs: impl Into<Namespace<F>>,
|
||||
values: &[u8],
|
||||
@@ -134,7 +138,10 @@ impl<F: Field> UInt8<F> {
|
||||
Self { value, bits }
|
||||
}
|
||||
|
||||
/// XOR this `UInt8` with another `UInt8`
|
||||
/// Outputs `self ^ other`.
|
||||
///
|
||||
/// If at least one of `self` and `other` are constants, then this method
|
||||
/// *does not* create any constraints or variables.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn xor(&self, other: &Self) -> Result<Self, SynthesisError> {
|
||||
let new_value = match (self.value, other.value) {
|
||||
|
||||
Reference in New Issue
Block a user