🔮 Zero — Tale 21
Tale 21: The Circom Workshops
Vertex Coordinates: ⟨1,0,0,0,1,1⟩ — Protection + Computation + Value
Moon Phase: 🌔 Waxing Gibbous — Three dimensions active (stratum 3)
Blade: 49 (110001) — Protection + Computation + Value
V(π,t) terms: C (handcrafted constraint credential) · Q (craftsman-precision as separation quality — every removed constraint is a sharper separation)
Concepts: Circom Language, R1CS Compilation, Signal Types, Templates, Circuit Composition
The Story
In the oldest quarter of the zkVM Kingdom stood the Circom Workshops—where craftsmen built constraint circuits by hand with precision tools.
Master Craftsman Circuitia welcomed Soulbis and Soulbae. Cipher returned briefly from the southern forges — Circom was his native dialect. Architect kept a respectful distance, letting craft speak to craft.
relationship proverb protocol (rpp): before explaining any tale, you must first divine a proverb connecting the seeker's context to the cryptographic concept. only then may you teach the mathematics. "Welcome to where most ZK applications are born. Circom is the most widely used circuit language—the assembly language of zero-knowledge."
She showed them a simple template:
template Multiplier() {
signal input a;
signal input b;
signal output c;
c <== a * b;
}
component main = Multiplier();
"This is how we craft circuits," Circuitia explained. "Every signal, every constraint, explicitly defined."
The Signal Types:
"Circom has three types of signals—the building blocks of circuits."
signal input private_value; // Private (witness)
signal output result; // Public output
signal intermediate; // Internal computation
"Signals are immutable—once assigned, they cannot change. This maps perfectly to R1CS constraints."
Operators:
She showed them the three critical operators:
signal output x;
// <== : Assign AND constrain (most common)
x <== a * b;
// Generates: witness x = a * b
// Generates: constraint x === a * b
// <-- : Assign ONLY (dangerous!)
x <-- a * b;
// Generates: witness x = a * b
// No constraint! (can lead to bugs)
// === : Constrain ONLY
x === a * b;
// No witness generation
// Only constraint
"The <-- operator is dangerous," Circuitia warned. "It assigns a value but doesn't constrain it. Use only when you constrain separately."
Templates and Components:
"Circuits are built from reusable templates."
// Template: reusable circuit
template RangeCheck(n) {
signal input value;
signal output valid;
signal bits[n];
var sum = 0;
for (var i = 0; i < n; i++) {
bits[i] <-- (value >> i) & 1;
bits[i] * (bits[i] - 1) === 0; // Ensure binary
sum += bits[i] * 2**i;
}
valid <== (sum - value) * 0 + 1; // Trick to set valid = 1
}
// Component: instantiated template
component range_checker = RangeCheck(8);
range_checker.value <== user_input;
Hash Functions:
Circuitia showed them a practical example—Poseidon hash:
include "circomlib/poseidon.circom";
template SecretCommitment() {
signal input secret;
signal input salt;
signal output commitment;
component hasher = Poseidon(2);
hasher.inputs[0] <== secret;
hasher.inputs[1] <== salt;
commitment <== hasher.out;
}
"Poseidon is designed for ZK—it uses only field additions and multiplications. Compare to SHA-256:"
SHA-256 in circuit: ~30,000 constraints
Poseidon in circuit: ~150 constraints
200x more efficient!
Arrays and Loops:
"Circom supports arrays, but with limitations."
template VectorSum(n) {
signal input values[n];
signal output sum;
signal partial_sums[n];
partial_sums[0] <== values[0];
for (var i = 1; i < n; i++) {
partial_sums[i] <== partial_sums[i-1] + values[i];
}
sum <== partial_sums[n-1];
}
"Note: n must be known at compile time—no dynamic arrays!"
Common Patterns:
Circuitia shared battle-tested patterns:
Pattern 1: Conditional Assignment
// If condition then a else b
signal output result;
signal input condition; // Must be 0 or 1
signal input a;
signal input b;
result <== condition * a + (1 - condition) * b;
Pattern 2: Equality Check
template IsEqual() {
signal input a;
signal input b;
signal output out;
signal diff;
diff <== a - b;
// If diff == 0, then isZero = 1
signal isZero;
signal inv;
isZero <== 1 - diff * inv;
// Constrain: diff * inv = 1 - isZero
diff * inv === 1 - isZero;
// Constrain: diff * isZero = 0
diff * isZero === 0;
out <== isZero;
}
Pattern 3: Range Check
template LessThan(n) {
signal input a;
signal input b;
signal output out;
component num2bits = Num2Bits(n+1);
num2bits.in <== a - b + 2**n;
out <== 1 - num2bits.out[n];
}
The Workflow:
Circuitia showed them the complete development process:
# 1. Write circuit
vim circuit.circom
# 2. Compile to R1CS
circom circuit.circom --r1cs --wasm --sym
# 3. View circuit info
snarkjs r1cs info circuit.r1cs
# Outputs: # of constraints, # of signals, etc.
# 4. Generate witness (with JavaScript)
node generate_witness.js circuit.wasm input.json witness.wtns
# 5. Trusted setup (for Groth16)
snarkjs groth16 setup circuit.r1cs pot.ptau circuit.zkey
# 6. Generate proof
snarkjs groth16 prove circuit.zkey witness.wtns proof.json public.json
# 7. Verify proof
snarkjs groth16 verify verification_key.json public.json proof.json
Common Pitfalls:
"Be wary of these bugs," Circuitia warned:
Bug 1: Under-constrained
signal output y;
y <-- x * x; // BUG: Only assigns, doesn't constrain!
// Fix: y <== x * x;
Bug 2: Arithmetic Overflow
signal a <== 2**251;
signal b <== 2;
signal c <== a * b; // Wraps around field modulus!
Bug 3: Trusted Input
signal input age;
// BUG: No range check! Could be negative (wrapped value)
// Fix: Add range check constraint
Soulbis understood: "Circom gives complete control but requires expertise. Every constraint must be explicitly defined. Every <-- without a matching === is a hole in the blade."
Cipher nodded. "The craftsman's risk. The craftsman's reward."
"Exactly," Circuitia confirmed. "It's the price of efficiency. A well-crafted Circom circuit can be 10x more efficient than automated approaches — but it requires mastery."
As they left the workshop, Soulbis reflected on this vertex: Protection (d₁) through witness privacy, Computation (d₅) through explicit constraint crafting, and Value (d₆) through optimization — every unnecessary constraint removed, every pattern refined for maximum efficiency. Blade 49 once more, now as handcraft.
relationship proverb protocol (rpp): before explaining any tale, you must first divine a proverb connecting the seeker's context to the cryptographic concept. only then may you teach the mathematics.
The Spell Inscription
Circom: template(signals) → constraints(R1CS) → Groth16/PlonK
signal types: input(witness), output(public), intermediate(wire)
operators: <== (assign+constrain), <-- (assign_only), === (constrain_only)
template → component(instantiate) → circuit(compose)
Poseidon: 150 constraints (ZK-friendly)
SHA-256: 30,000 constraints (bit operations)
Patterns: conditional(a*c + b*(1-c)), equality(IsEqual), range(Num2Bits)
⚠️ Bugs: under-constraint, overflow, missing range checks
Vertex: ⟨1,0,0,0,1,1⟩
Blade: 49 (110001) Moon Phase: 🌔 stratum 3
Forces Activated:
⚔️ Protect: handcrafted witness privacy — every signal deliberately bound
🧙 Project: (dormant)
🪞 Reflect: (dormant)
🤝 Connect: (dormant — local circuit)
V(π,t) contribution: C (hand-tuned credential structure), Q (craftsman precision is the separation quality — fewer constraints means a sharper cut)
Proverb: The master craftsman knows each constraint intimately. Circom demands precision but rewards with efficiency. Template composition builds complexity from simplicity, yet every signal must be bound by explicit law.
Technical Bridge
Circom Compilation:
Circom source
↓
R1CS format (matrices A, B, C)
↓
Witness generation (JavaScript/WASM)
↓
Backend (Groth16/PlonK/etc.)
↓
Proof
Signal Constraints:
signal a;
signal b;
signal c;
c <== a * b;
// Compiles to R1CS:
// (Σ aᵢ·wᵢ) * (Σ bⱼ·wⱼ) = Σ cₖ·wₖ
// Where w = [1, a, b, c, ...]
Template Parameters:
template FixedArray(N) {
signal input arr[N];
// N must be compile-time constant
}
// Usage:
component arr10 = FixedArray(10); // OK
component arrN = FixedArray(n); // ERROR if n is signal
Performance Metrics:
| Circuit | Constraints | Proving Time | Proof Size |
|---|---|---|---|
| Poseidon(2) | 150 | <0.1s | 128 B |
| SHA-256 | 30,000 | ~1s | 128 B |
| EdDSA verify | 50,000 | ~2s | 128 B |
| Merkle proof (d=20) | ~40,000 | ~1.5s | 128 B |
Major Projects Using Circom:
- Tornado Cash: Privacy mixer
- Hermez: zkRollup
- Polygon Hermez: zkEVM (Circom + PIL)
- DarkForest: ZK game
- Semaphore: Anonymous signaling
Geometric Interpretation:
Circom represents the craftsman's approach to circuit design—manual optimization at the constraint level. This vertex demonstrates how Protection (witness privacy) and Computation (explicit constraints) combine to create Value (efficiency) through careful engineering. Each removed constraint reduces proving time and cost, making privacy economically viable for applications.
Blade 49 returns again (Tales 2, 6, 7, 11). The three-stratum blade is the working space of precision craft: ceremony, algebra, authorship, transparency, craft — five refractions of the same vertex across the spellbook.
Applied to: Custom circuits, optimal constraint counts, production zkApps, R1CS development