ark-r1cs-std

repository·master·Indexed 41 days ago

https://github.com/arkworks-rs/r1cs-std

A 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.

Tokens
9.3K
Snippets
32
Records
40
Agent score
52%

What's inside ark-r1cs-std

  1. Install and build ark-r1cs-std

    master

    To use ark-r1cs-std, you need the Rust stable toolchain. Follow these steps to set up your environment and build the library from source:

    1. Install Rust: Use rustup to install the latest stable toolchain.
    2. Clone the repository: Download the source code from GitHub.
    3. Build: Use 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 --release
  2. Represent unsigned integers as bit sequences with `UInt`

    master

    The 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());
  3. Understand PairingVar associated types

    master

    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 TypeR1CS EquivalentDescription
    G1VarE::G1ProjectiveVariable representing an element of G1
    G2VarE::G2ProjectiveVariable representing an element of G2
    GTVarE::GTVariable representing an element of the target group GT
    G1PreparedVarE::G1PreparedCached precomputation for G1 to speed up pairings
    G2PreparedVarE::G2PreparedCached precomputation for G2 to speed up pairings
  4. Use the Boolean gadget for constraint system operations

    master

    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:

    • Creating constant booleans (TRUE, FALSE).
    • Converting bitwise representations (little-endian) of field elements into FpVar<F> variables.
    • Converting boolean vectors from raw bytes.
    • Extracting underlying 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))?;
  5. Available Field Variable Implementations

    master

    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$).
  6. Use AffineVar for non-zero elliptic curve points

    master

    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.

  7. Use the EqGadget trait to compare R1CS variables

    master

    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)?;
  8. Use ProjectiveVar for elliptic curve points

    master

    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:

    • Complete Addition: Supports adding any two points, including the identity (point at infinity).
    • Scalar Multiplication: Supports multiplying a point by a scalar (represented as a little-endian Boolean bit vector).
    • Coordinate Conversion: Can be converted to an 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())?;
  9. Understand AllocationMode for R1CS variables

    master

    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,
    }
  10. Use the `ark-r1cs-std` prelude for common gadgets

    master

    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::*;
  11. Allocate variables with inferred mode for hints

    master

    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)
    }