Skip to content

Latest commit

 

History

History
306 lines (215 loc) · 8.89 KB

File metadata and controls

306 lines (215 loc) · 8.89 KB

CVM (Circom Virtual Machine) - Witness Calculation Guide

⚠️ Development Status: The CVM feature is not production ready and is currently in active development. Users are welcome to file bug reports if they encounter circuits where it does not work correctly.

Quick Start

Prerequisites

You need a special fork of circom that supports the --cvm flag:

git clone https://github.com/costa-group/circom_cvm
cd circom_cvm
cargo build --release

Important: The default circom from the official repository does NOT support the --cvm flag. You must use the fork above.

Basic Workflow

  1. Build this project

    cargo build --release
  2. Compile your circuit with circom to generate CVM assembly

    circom --cvm your_circuit.circom

    This generates:

    • your_circuit_cvm/your_circuit.cvm - CVM assembly file
  3. Calculate witness using cvm-compile

    Single-step approach (recommended):

    ./target/release/cvm-compile your_circuit_cvm/your_circuit.cvm \
        --wtns witness.wtns \
        --inputs inputs.json

    This compiles the CVM assembly and immediately calculates the witness in one step.


Detailed Documentation

cvm-compile

The cvm-compile tool can operate in two modes:

Mode 1: Direct witness calculation (single-step)

Compile CVM assembly and calculate witness in one command:

./target/release/cvm-compile <cvm_file> --wtns <output.wtns> --inputs <inputs.json>

Arguments:

  • <cvm_file> - Path to the CVM assembly file (generated by circom with --cvm flag)

Options:

  • --wtns <output.wtns> - Output file for the calculated witness
  • --inputs <inputs.json> - JSON file with circuit inputs (required when --wtns is used)
  • -h, --help - Display help message

Example:

./target/release/cvm-compile circuit_cvm/circuit.cvm \
    --wtns witness.wtns \
    --inputs inputs.json

Mode 2: Two-step compilation (compile then execute)

This mode is useful when you want to reuse the compiled bytecode multiple times with different inputs.

Step 1: Compile to bytecode

./target/release/cvm-compile <cvm_file> -o <circuit.wcd>

Arguments:

  • <cvm_file> - Path to the CVM assembly file

Options:

  • -o <circuit.wcd> - Output file for compiled bytecode

Example:

./target/release/cvm-compile circuit_cvm/circuit.cvm -o circuit_bc.wcd

Step 2: Execute bytecode to calculate witness

./target/release/calc-witness <wcd_file> <inputs_json> <wtns_file>

Arguments:

  • <wcd_file> - Path to the compiled bytecode file (.wcd)
  • <inputs_json> - JSON file containing circuit inputs
  • <wtns_file> - Output file where the witness will be saved

Options:

  • -h, --help - Display help message

Example:

./target/release/calc-witness circuit_bc.wcd inputs.json witness.wtns

Benefits of two-step mode:

  • Faster execution when calculating witness multiple times with different inputs
  • Compiled bytecode can be distributed and used without the original CVM assembly
  • Integration with Rust library for programmatic witness generation

test_circuits_vm2.sh - Automated Testing Script

The test_circuits_vm2.sh script automates the complete workflow: compiling circuits, calculating witnesses, and validating results.

Requirements

The script requires the following binaries in your PATH:

  • circom (from https://github.com/costa-group/circom_cvm) - Compiles circuits to CVM assembly
  • snarkjs - Validates witness against R1CS constraints
  • cargo - Builds this project
  • node - Runs WASM witness generator for comparison
  • cmp - Compares witness files

The script generates two witnesses:

  1. CVM witness (using this project)
  2. WASM witness (using circom's JavaScript generator)

It then validates both witnesses against the R1CS file using snarkjs wtns check and compares them to ensure they match. For this validation, the script compiles circuits with --r1cs and --wasm flags in addition to --cvm.

Usage

./test_circuits_vm2.sh [-h] [-l <include_path>] [-i <inputs_path>] [-f <features>] [-m <mode>] [-c <circom_flags>] [file1 ...]

Options

-l <include_path>

  • Path to include directory for circuit dependencies
  • Can be specified multiple times for multiple include paths
  • Default: test_deps/circomlib/circuits

-i <inputs_path>

  • Path to custom inputs JSON file
  • Requires exactly one circuit file argument
  • Default: Uses <circuit_name>_inputs.json in the same directory as the circuit

-f <features>

  • Comma-separated list of cargo features to enable during build
  • Examples: debug_vm2, parallel_components, debug_vm2,parallel_components

-m <mode>

  • Execution mode: direct or split (default: direct)
  • direct: Single-step witness calculation using cvm-compile with --wtns flag
  • split: Two-step process - compile to bytecode, then execute

-c <circom_flags>

  • Additional flags to pass to circom compiler
  • Default: --cvm_multi_assign
  • Use empty string to disable: -c ""
  • Can specify multiple flags: -c "--cvm_multi_assign --other-flag"

-h

  • Print usage information and exit

Examples

Test single circuit with default inputs

./test_circuits_vm2.sh test_circuits/circuit1.circom

Expects inputs at: test_circuits/circuit1_inputs.json

Test single circuit with custom inputs

./test_circuits_vm2.sh -i /path/to/custom_inputs.json test_circuits/circuit1.circom

Test multiple circuits with default inputs

./test_circuits_vm2.sh test_circuits/circuit1.circom test_circuits/circuit2.circom

Each circuit must have a corresponding <circuit_name>_inputs.json file in the same directory.

Test all circuits in test_circuits/ directory

./test_circuits_vm2.sh

Processes all .circom files in test_circuits/ directory with their default input files.

Build with specific cargo features

./test_circuits_vm2.sh -f parallel_components test_circuits/circuit9_authV2.circom

Use split mode (two-step compilation)

./test_circuits_vm2.sh -m split test_circuits/circuit1.circom

Disable cvm_multi_assign flag

./test_circuits_vm2.sh -c "" test_circuits/circuit1.circom

Complex example with multiple options

./test_circuits_vm2.sh \
    -f parallel_components \
    -m split \
    -l ./custom_libs \
    test_circuits/circuit9_authV2.circom

What the Script Does

  1. Builds the project with specified cargo features
  2. Compiles the circuit using circom with --r1cs --cvm --wasm flags plus any additional flags from -c
  3. Generates CVM witness using either direct or split mode
  4. Generates WASM witness using Node.js for comparison
  5. Validates both witnesses against R1CS constraints using snarkjs wtns check
  6. Compares witnesses byte-by-byte to ensure they match
  7. Exits with error if validation fails or witnesses don't match

Output Files

All output files are created in test_working_dir_vm2/:

  • <circuit_name>.r1cs - Constraint system
  • <circuit_name>.wtns - CVM-generated witness
  • <circuit_name>.wtns2 - WASM-generated witness
  • <circuit_name>_bc2.wcd - Compiled bytecode (in split mode)
  • <circuit_name>_cvm/ - CVM assembly directory
  • <circuit_name>_js/ - JavaScript/WASM generator

Debugging CVM Execution

debug_vm2 Feature

The cvm-compile tool supports a debug_vm2 cargo feature that enables detailed tracing information during witness calculation. This is useful for understanding problems with CVM execution.

Building with debug_vm2:

cargo build --release --features debug_vm2

What it does:

When enabled, the debug_vm2 feature prints detailed tracing information to stdout during witness calculation, including:

  • Bytecode instructions being executed
  • Component information and state
  • Signal assignments and calculations
  • Stack and memory operations
  • Other low-level execution details

Example usage:

# Build with debug feature
cargo build --release --features debug_vm2

# Run with debug output
./target/release/cvm-compile circuit_cvm/circuit.cvm \
    --wtns witness.wtns \
    --inputs inputs.json

The debug output will be printed to stdout alongside the normal execution, providing insight into each step of the witness calculation process.

Note: The debug output can be very verbose for large circuits. Consider redirecting output to a file for analysis:

./target/release/cvm-compile circuit_cvm/circuit.cvm \
    --wtns witness.wtns \
    --inputs inputs.json > debug.log 2>&1

Integration with Rust Library

The compiled bytecode (.wcd files) can be used programmatically in Rust applications by linking against the circom-witnesscalc library. This allows witness generation to be embedded in other tools and services without spawning separate processes.

See the library documentation for API details.