⚠️ 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.
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 --releaseImportant: The default circom from the official repository does NOT support the --cvm flag. You must use the fork above.
-
Build this project
cargo build --release
-
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
-
Calculate witness using cvm-compile
Single-step approach (recommended):
./target/release/cvm-compile your_circuit_cvm/your_circuit.cvm \ --wtns witness.wtns \ --inputs inputs.jsonThis compiles the CVM assembly and immediately calculates the witness in one step.
The cvm-compile tool can operate in two modes:
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.jsonThis 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.wcdStep 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.wtnsBenefits 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
The test_circuits_vm2.sh script automates the complete workflow: compiling circuits, calculating witnesses, and validating results.
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:
- CVM witness (using this project)
- 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.
./test_circuits_vm2.sh [-h] [-l <include_path>] [-i <inputs_path>] [-f <features>] [-m <mode>] [-c <circom_flags>] [file1 ...]-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.jsonin 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:
directorsplit(default:direct) - direct: Single-step witness calculation using
cvm-compilewith--wtnsflag - 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
./test_circuits_vm2.sh test_circuits/circuit1.circomExpects inputs at: test_circuits/circuit1_inputs.json
./test_circuits_vm2.sh -i /path/to/custom_inputs.json test_circuits/circuit1.circom./test_circuits_vm2.sh test_circuits/circuit1.circom test_circuits/circuit2.circomEach circuit must have a corresponding <circuit_name>_inputs.json file in the same directory.
./test_circuits_vm2.shProcesses all .circom files in test_circuits/ directory with their default input files.
./test_circuits_vm2.sh -f parallel_components test_circuits/circuit9_authV2.circom./test_circuits_vm2.sh -m split test_circuits/circuit1.circom./test_circuits_vm2.sh -c "" test_circuits/circuit1.circom./test_circuits_vm2.sh \
-f parallel_components \
-m split \
-l ./custom_libs \
test_circuits/circuit9_authV2.circom- Builds the project with specified cargo features
- Compiles the circuit using circom with
--r1cs --cvm --wasmflags plus any additional flags from-c - Generates CVM witness using either direct or split mode
- Generates WASM witness using Node.js for comparison
- Validates both witnesses against R1CS constraints using
snarkjs wtns check - Compares witnesses byte-by-byte to ensure they match
- Exits with error if validation fails or witnesses don't match
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
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_vm2What 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.jsonThe 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>&1The 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.