Run tests for ark-r1cs-std
masterThe library includes unit tests for each of its provided crates. To verify the implementation, run the following command from the root of the repository:
cargo testrepository·master·Indexed 41 days ago
https://github.com/arkworks-rs/r1cs-stdA standard library for Rank-1 Constraint System (R1CS) gadgets for zkSNARKs, providing efficient implementations for bit operations, finite field arithmetic, elliptic curve arithmetic, and pairings within zero-knowledge proofs. Version 0.5.0.
The library includes unit tests for each of its provided crates. To verify the implementation, run the following command from the root of the repository:
cargo testTo use ark-r1cs-std, you need the Rust stable toolchain. Follow these steps to set up your environment and build the library from source:
rustup to install the latest stable toolchain.cargo to compile the library in release mode.WARNING: This library is an academic proof-of-concept prototype and has not received careful code review. It is NOT ready for production use.
# Install the stable toolchain
rustup install stable
# Clone and build
git clone https://github.com/arkworks-rs/r1cs-std.git
cd r1cs-std
cargo build --releaseThe UInt<N, T, F> struct represents an unsigned N-bit integer as a sequence of N [Boolean] variables. This abstraction allows for bitwise operations and arithmetic within an R1CS constraint system.
Key characteristics:
N: The number of bits.T: The underlying native unsigned integer type (must implement PrimUInt).F: The scalar field type.Because it is composed of bits, operations on UInt will typically involve constraints on each individual bit.
// Example of using UInt8 (a UInt with N=8) in a constraint system
use ark_test_curves::bls12_381::Fr;
use ark_relations::gr1cs::ConstraintSystem;
use ark_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());When implementing or using PairingVar<E>, you must work with specific R1CS variable types that correspond to the elliptic curve groups. These types are defined as associated types within the trait:
| Associated Type | R1CS Equivalent | Description |
|---|---|---|
G1Var | E::G1Projective | Variable representing an element of G1 |
G2Var | E::G2Projective | Variable representing an element of G2 |
GTVar | E::GT | Variable representing an element of the target group GT |
G1PreparedVar | E::G1Prepared | Cached precomputation for G1 to speed up pairings |
G2PreparedVar | E::G2Prepared | Cached precomputation for G2 to speed up pairings |
The Boolean<F> type represents a boolean value in a constraint system, ensuring it is either zero or one. It can represent either a variable (Boolean::Var(AllocatedBool<F>)) or a constant (Boolean::Constant(bool)). This abstraction allows you to perform boolean logic and conversions within a R1CS constraint system.
Key capabilities include:
TRUE, FALSE).FpVar<F> variables.Variable or LinearCombination representations for use in arithmetic constraints.use ark_r1cs_std::prelude::*;n
use ark_test_curves::bls12_381::Fr;
// Example: Creating and enforcing constant booleans
let cs = ConstraintSystem::<Fr>::new_ref();
let t = Boolean::<Fr>::TRUE;
let f = Boolean::<Fr>::FALSE;
t.enforce_equal(&Boolean::TRUE)?;
f.enforce_equal(&Boolean::constant(false))?;The ark-r1cs-std crate provides several specialized modules for implementing R1CS equivalents of various ark-ff field types. Depending on your circuit requirements, you can use the following modules:
fp: Prime field variables (equivalent to ark_ff::Fp*).quadratic_extension: Quadratic extension field variables.cubic_extension: Cubic extension field variables.fp2, fp3, fp4, fp6_2over3, fp6_3over2: Tower extension field variables (degree 2, 3, 4, and 6).fp12: Degree-12 tower extension field variables.emulated_fp: Emulated prime field variables (emulates Fp arithmetic using Fq operations where $p \neq q$).The AffineVar gadget represents a curve point in affine coordinates $(x, y)$ with an explicit infinity boolean flag.
Warning: This module is generally unsafe for general R1CS use because it can synthesize unsatisfiable or underconstrained constraint systems if the represented point is equal to zero. Use ProjectiveVar instead whenever possible.
It is primarily useful for representing points that are guaranteed to be non-zero or for specific interface requirements.
The EqGadget<F> trait provides a standardized way to compare two R1CS variables (or collections of variables) for equality or inequality within a circuit. It can be implemented for individual variables, slices [T], Vec<T>, and fixed-size arrays [T; N], provided the underlying type T implements EqGadget<F> and GR1CSVar<F>.
Key methods include:
is_eq(&self, other: &Self): Returns a Boolean<F> representing whether the values are equal.is_neq(&self, other: &Self): Returns a Boolean<F> representing whether the values are not equal.enforce_equal(&self, other: &Self): Immediately enforces that the two variables are equal by adding constraints to the circuit.enforce_not_equal(&self, other: &Self): Immediately enforces that the two variables are not equal.conditional_enforce_equal(&self, other: &Self, should_enforce: &Boolean<F>): Enforces equality only if the should_enforce boolean variable is true.use crate::prelude::*;
// Assuming `var_a` and `var_b` are types implementing EqGadget
// such as Boolean or other R1CS variables.
// 1. Get a boolean variable representing equality
let is_equal: Boolean<F> = var_a.is_eq(&var_b)?;
// 2. Enforce equality directly
var_a.enforce_equal(&var_b)?;
// 3. Enforce equality conditionally based on another boolean variable
var_a.conditional_enforce_equal(&var_b, &condition_var)?;When working with elliptic curve points on Short Weierstrass curves in R1CS, ProjectiveVar is the recommended gadget. It uses projective coordinates $(x, y, z)$ and implements complete addition formulae, making it safer and more robust than affine representations which can lead to unsatisfiable or underconstrained systems if a point is equal to zero.
Key features include:
Boolean bit vector).AffineVar representation.// Example of creating a ProjectiveVar (conceptual)
let point = ProjectiveVar::new(x_var, y_var, z_var);
// Converting to affine form
let affine_point = point.to_affine()?;
// Scalar multiplication
let scalar_bits = vec![bit1, bit2, ...];
let result = point.scalar_mul_le(scalar_bits.iter())?;When allocating variables in a ConstraintSystem, you must specify an AllocationMode. This determines how the variable is treated within the circuit:
AllocationMode::Constant: The variable is treated as a constant. No new Variables are generated in the ConstraintSystem, and no constraints are created. Use this for values that are fixed and known.AllocationMode::Input: The variable is allocated as a public input to the ConstraintSystem.AllocationMode::Witness: The variable is allocated as a private witness to the ConstraintSystem.pub enum AllocationMode {
Constant = 0,
Input = 1,
Witness = 2,
}The prelude module provides a convenient way to import the most commonly used gadgets and traits for programming rank-1 constraint systems. By importing the prelude, you gain access to variable allocation utilities, boolean gadgets, integer types (UInt8 through UInt128), field and group operations, and conversion traits like ToBitsGadget and ToBytesGadget.
use ark_r1cs_std::prelude::*;To avoid boilerplate when creating non-deterministic advice (hints), use new_variable_with_inferred_mode. This method infers the mode from the ConstraintSystem associated with the provided Namespace. If the Namespace is empty (no ConstraintSystem), it allocates a Constant; if it contains a ConstraintSystem, it allocates a Witness.
Example of computing division using inferred mode for a hint variable:
use ark_ff::PrimeField;
use ark_r1cs_std::{alloc::AllocVar, fields::{fp::FpVar, FieldVar}, GR1CSVar};
use ark_relations::gr1cs::SynthesisError;
fn div<F: PrimeField>(x_var: &FpVar<F>, y_var: &FpVar<F>) -> Result<FpVar<F>, SynthesisError> {
// Get the constraint system from existing variables if available
let cs = x_var.cs().or(y_var.cs());
// z_var will be a Witness if x_var or y_var are witnesses, otherwise a Constant
let z_var = FpVar::new_variable_with_inferred_mode(cs, || Ok(x_var.value()? / y_var.value()?))?;
// Verify the relationship: z * y = x
z_var.mul_equals(y_var, x_var)?;
Ok(z_var)
}use ark_ff::PrimeField;
use ark_r1cs_std::{alloc::AllocVar, fields::{fp::FpVar, FieldVar}, GR1CSVar};
use ark_relations::gr1cs::SynthesisError;
fn div<F: PrimeField>(x_var: &FpVar<F>, y_var: &FpVar<F>) -> Result<FpVar<F>, SynthesisError> {
let cs = x_var.cs().or(y_var.cs());
let z_var = FpVar::new_variable_with_inferred_mode(cs, || Ok(x_var.value()? / y_var.value()?))?;
z_var.mul_equals(y_var, x_var)?;
Ok(z_var)
}