The ZK-VM
Now that you understand what Ligetron is, let's explore how it works as a zero-knowledge virtual machine. Unlike traditional VMs that simply execute code, Ligetron executes your programs while simultaneously generating cryptographic proofs of correct execution, proving computation was performed correctly without revealing private data.
Instance and Witness
When building zero-knowledge applications, you'll work with two types of inputs: public data that everyone can see, and private data that only the prover knows. Think of the public data as the "claim" being made, and the private data as the "secret evidence" that proves it. For example, in age verification: the public claim is "I am over 18 years old," and the private evidence is your actual birthdate.
In zero-knowledge proof literature, these are called instance and witness:
- Instance: All public data known to both prover and verifier, including the program (WASM binary), public inputs (e.g., today's date, a hash value, a threshold), and other public parameters
- Witness: Private inputs known only to the prover (e.g., your birthdate, a password, account balances)
Crucially, your program code doesn't specify which inputs belong to the instance, and which belong to the witness (you may have noticed this in the toy example). Privacy is configured at runtime using the private-indices parameter when running the prover, allowing the same program to be used with different privacy configurations. When running the verifier, the witness can be replaced with placeholder values since the verifier only needs the instance and the proof itself.
WebAssembly as an Intermediate Representation
Ligetron uses WebAssembly (WASM) as its intermediate representation (IR). You write programs in C++ or Rust, compile them to WASM using standard toolchains, and Ligetron executes the WASM binary while generating zero-knowledge proofs. As your program runs, Ligetron automatically generates mathematical constraints on-the-fly internally, which form the basis of the cryptographic proof.
Why WebAssembly? WASM strikes the right balance: low-level enough to connect efficiently with the proof system, yet high-level enough to retain important semantic information like scoping and control flow. This enables efficient memory management and allows the proof system to generate constraints smoothly during program execution.
LigeroWASM: Supported WASM Subset
Ligetron supports a subset of WebAssembly tailored for zero-knowledge proof generation, which we call LigeroWASM. To avoid confusion: LigeroWASM refers to the intermediate representation format and execution environment, not the implementation language of Ligetron itself.
Supported operations:
- Integer operations: i32/i64 types and arithmetic operations
- Control flow: Conditional branches, loops, function calls
- Memory operations: Linear memory access for data structures and arrays
Not supported:
- Floating-point operations (f32/f64)
- SIMD instructions (v128)
- Multi-threading
Toolchain requirements:
Your compiler must target wasm32-wasi-preview1 (also known as wasm32-wasip1). Note that while this compilation target supports floating-point and SIMD operations, your program code should only use integer operations since LigeroWASM's execution environment does not execute floating-point or SIMD instructions.
- C++: Use Emscripten or WASI SDK
- Rust: Use
cargo build --target wasm32-wasip1
Ligetron provides an SDK with API functions (assert_one(), assert_zero(), etc.) and cryptographic libraries (Poseidon, EdDSA) for both C++ and Rust. See Generating Constraints with the API for details.
The Execution Pipeline
Now that you understand how Ligetron uses WASM as its intermediate representation, let's see how it transforms your program into a zero-knowledge proof. We'll examine this from three perspectives: the complete end-to-end flow, how the prover generates proofs, and how the verifier checks them.
Complete End-to-End Flow
The journey from WASM program to verified proof:
Key insight: The prover executes your program three times, once in each stage for a different purpose (commit, constrain, sample). This is what gives Ligetron its space efficiency: at any point in time when generating the proof, only a small portion of the witness matrix needs to be kept in memory, and once that portion is no longer needed, it can be garbage collected.
Prover and Verifier
- Prover
- Verifier
The prover generates proofs through three distinct stages:
What each stage does:
-
Commit (Stage 1): Records witness values as the program executes, encodes them, then hashes each column and builds a Merkle tree commitment.
-
Constrain (Stage 2): Re-executes the program to generate constraint polynomials (code, linear, quadratic) encoding all operations, then hashes them to create a challenge seed.
-
Sample (Stage 3): Executes the program a third time, uses the challenge seed to randomly select sample points, then generates Merkle proofs at those points.
After all three stages complete, the prover validates that all constraints are satisfied before outputting the final proof.
The verifier checks the proof through five main steps:
Why verification is fast: The verifier only checks sampled points (not all values) and deterministically recreates the challenge from the seed, making verification orders of magnitude faster than proof generation.
Generating Constraints with the API
As your WASM program executes, you use API functions to define what properties you want to prove. Ligetron provides two levels of API depending on your needs:
High-Level API: For Program Logic
For most applications, use the simple assertion functions that work directly on boolean expressions and integer values:
// Example: Prove edit distance is less than 5
int distance = calculate_distance(string_a, string_b);
assert_one(distance < 5); // Proves: distance < 5
// Example: Prove two values are equal
assert_one(computed_hash == expected_hash);
// Example: Prove a value is zero
int balance_check = total_in - total_out;
assert_zero(balance_check); // Proves: balance_check = 0
Available functions:
assert_one(condition)- Proves the condition evaluates to true (equals 1)assert_zero(value)- Proves the value equals zerooblivious_if(cond, a, b)- Oblivious selection: returnsaifcond, elseboblivious_min(a, b)/oblivious_max(a, b)- Oblivious comparison
These high-level functions work with regular C++ types and expressions. Behind the scenes, they generate constraints using the BN254 scalar field (Fr), but you don't need to manage field elements explicitly.
Cryptographic Libraries
For cryptographic operations (hashing, signatures, elliptic curves), Ligetron provides ready-to-use libraries that handle field arithmetic internally:
// Example: Poseidon hash using the SDK library
#include <ligetron/poseidon.h>
bn254fr_class input(input_string);
bn254fr_class reference(expected_hash);
poseidon_context<poseidon_permx5_254bit_5>::global_init();
poseidon_context<poseidon_permx5_254bit_5> ctx;
ctx.digest_update(input);
ctx.digest_final();
bn254fr_class::assert_equal(ctx.state[0], reference); // Prove: hash matches
// Example: EdDSA signature verification
#include <ligetron/eddsa.h>
#include <ligetron/poseidon2.h>
bn254fr_class message = 42;
jubjub_point public_key { ... };
eddsa_signature signature { ... };
bn254fr_class digest;
auto *ctx = poseidon2_bn254_context_new();
poseidon2_bn254_digest_update(ctx, message);
poseidon2_bn254_digest_final(ctx, digest);
eddsa_verify(signature, public_key, digest); // Proves: signature is valid
Available cryptographic libraries:
poseidon/poseidon2- Zero-knowledge friendly hash functionseddsa- EdDSA signature verification on Baby JubJub curvebabyjubjub- Elliptic curve point operationsbn254fr_class- C++ wrapper for field elements (RAII, no manual memory management)
When to use cryptographic libraries:
- Verifying signatures in zero-knowledge
- Computing cryptographic hashes (Poseidon, SHA256)
- Implementing privacy-preserving authentication
- Building protocols that need elliptic curve operations
When to use high-level API:
- Regular program logic and computations
- Boolean conditions and comparisons
- Non-cryptographic zero-knowledge applications
The API functions above generate different types of constraints that affect proof generation time and size. To learn what constraints are, how they're generated from WASM operations, and how to optimize performance, see Performance and Parameters.
Deployment Options
Ligetron supports flexible deployment for different environments:
- Native builds: Via Dawn (Metal/Vulkan/DirectX 12) for server-side proving
- Web builds: Via WebGPU API in browsers with zero installation
Both platforms enable billion-gate proofs on commodity hardware, including the first billion-gate proofs running directly in browsers.
See the Installation Guide for setup instructions.