SnarkVM’s most distinctive architectural feature is its dual type system: every operation exists in both console (plaintext) and circuit (constraint-generating) forms. This design enables the same program to run in native mode for fast evaluation and in circuit mode for proof generation.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/provablehq/snarkvm/llms.txt
Use this file to discover all available pages before exploring further.
The Two Worlds
Console Types: Native Execution
Console types insnarkvm-console-types execute natively on the CPU using standard Rust field arithmetic:
- Fast native arithmetic (nanoseconds per operation)
- Direct field element manipulation
- No constraint generation overhead
- Used for transaction verification and plaintext evaluation
Circuit Types: Constraint Generation
Circuit types insnarkvm-circuit-types generate R1CS constraints for proof generation:
- Each operation generates R1CS constraints
- Variables tracked in constraint system
- Three modes:
Constant,Public,Private - Used for proof generation during program execution
Synchronization Requirement
FromAGENTS.md:25:
console / circuit sync requirement:
- These crate families must stay in sync. Same structure, same API.
- When modifying one, check the other.
- Test circuit equivalence by comparing constraint counts.
Why Synchronization Matters
Consider a transaction that transfers tokens:- Local Execution: User runs console types to verify the transaction works
- Proof Generation: Synthesizer runs circuit types to generate a SNARK proof
- Network Verification: Validators run console types to verify the proof
- User sees valid transaction locally
- Proof generation succeeds with different values
- Validators reject the transaction
- Result: Consensus fork and network partition
Type Correspondence
Every console type has an exact circuit equivalent:| Console Type | Circuit Type | Purpose |
|---|---|---|
console::Field<E> | circuit::Field<E> | Base field elements |
console::Group<E> | circuit::Group<E> | Elliptic curve points |
console::Scalar<E> | circuit::Scalar<E> | Scalar field elements |
console::Boolean<E> | circuit::Boolean<E> | Boolean values |
console::Address<E> | circuit::Address<E> | Account addresses |
console::U8<E> | circuit::U8<E> | 8-bit unsigned integers |
console::I32<E> | circuit::I32<E> | 32-bit signed integers |
console::StringType<E> | circuit::StringType<E> | UTF-8 strings |
Module Mirroring
Both crate families have identical structure:Inject and Eject Traits
Circuit types implement two key traits for converting between console and circuit representations:Inject: Console → Circuit
Mode::Constant: Value is compile-time constant, no variable allocatedMode::Public: Value is public input, allocated as public variableMode::Private: Value is private witness, allocated as private variable
circuit/types/field/src/lib.rs:67):
Eject: Circuit → Console
circuit/types/field/src/lib.rs:76):
Constraint Generation
Linear Combinations
Circuit types internally represent values as linear combinations of variables:2*a + 3*b + 5 is represented as:
R1CS Constraints
Operations enforce constraints in the formA * B = C where A, B, C are linear combinations:
c = a * b generates:
Operation Examples
Addition
Console (fromconsole/types/field/src/arithmetic.rs):
circuit/types/field/src/add.rs):
Field addition is “free” in circuits - it doesn’t generate constraints because linear combinations can be combined directly.
Multiplication
Console (fromconsole/types/field/src/arithmetic.rs):
circuit/types/field/src/mul.rs):
Field multiplication generates 1 R1CS constraint. This is the fundamental cost unit for circuit complexity.
Comparison
Console (fromconsole/types/field/src/compare.rs):
circuit/types/field/src/equal.rs):
- Compute
diff = a - b - Check
diff == 0using inverse computation - Generates multiple constraints for zero-check
Testing Equivalence
Circuit types include tests that verify equivalence with console types:- Circuit operations produce same results as console
- All input combinations are tested
- Edge cases (zero, max, negative) are covered
Program Execution Flow
Performance Implications
Console Execution
- Speed: Native CPU instructions, ~nanoseconds per operation
- Use Cases: Transaction validation, blockchain state queries, proof verification
- Memory: Minimal overhead, direct field elements
Circuit Execution
- Speed: Constraint generation ~microseconds, proving ~milliseconds to seconds
- Use Cases: Proof generation for transactions and programs
- Memory: Stores entire constraint system (can be gigabytes for large programs)
Best Practices
For SnarkVM Contributors
- Always modify both: When changing console types, update circuit types identically
- Test equivalence: Add tests comparing console and circuit outputs
- Document constraints: Note how many constraints each operation generates
- Avoid divergence: Never add console-only or circuit-only behavior
For Aleo Program Developers
- Understand cost model: Multiplications are expensive, additions are free
- Minimize constraints: Refactor to reduce circuit operations
- Test locally: Use console mode for rapid iteration
- Profile circuits: Measure constraint counts for performance tuning
Common Pitfalls
Other issues to avoid:- Floating point: No floating point types exist; use fixed-point field arithmetic
- Overflow: Integer types wrap by default; use checked operations for safety
- Randomness: Circuit randomness must come from witness, not generated internally
- Serialization: Ensure deterministic serialization for consensus
Next Steps
Zero-Knowledge Proofs
Learn how circuits become SNARKs
Architecture
Explore the full crate structure