Welcome to the technical guide
1) Setting up the Circuit in Circom ( The Wizards setting up the cave magic)
// cave_circuit.circom
pragma circom 2.1.4;
// This circuit represents our magical Cave of Secrets
template CaveOfSecrets() {
// Private Input: Alice's secret word (represented as a number)
signal input secretWord;
// Public Input: The magical cave's entrance hash (known to everyone)
signal input publicEntranceHash;
// The proof that Alice can exit correctly (output signal)
signal output validPath;
// The Magic Chamber's verification
signal magicChamberCheck;
// Magical transformation in the chamber
magicChamberCheck <== secretWord * secretWord;
// Verify the path - if they're equal, validPath will be 1
validPath <== IsEqual()([magicChamberCheck, publicEntranceHash]);
}
// Helper template to check equality
template IsEqual() {
signal input in[2];
signal output out;
signal diff;
diff <== in[0] - in[1];
out <== IsZero()(diff);
}
// Helper template to check if a number is zero
template IsZero() {
signal input in;
signal output out;
signal inv;
// if input is 0, output should be 1
// if input is not 0, output should be 0
inv <-- in != 0 ? 1/in : 0;
out <-- in == 0 ? 1 : 0;
// Constrain the output to be binary
out * (out - 1) === 0;
// Constrain the output to be correct
in * inv === 1 - out;
}
component main = CaveOfSecrets();
# Create a new directory
mkdir cave_circuit
cd cave_circuit
# Save the circuit code as cave_circuit.circom
# Save the input as input.json
# Compile the circuit
circom cave_circuit.circom --r1cs --wasm --sym
# Generate the witness
node cave_circuit_js/generate_witness.js cave_circuit_js/cave_circuit.wasm input.json witness.wtns
2) Now generate witness
node cave_circuit_js/generate_witness.js cave_circuit_js/cave_circuit.wasm input.json witness.wtns
3) Create a input.json ( Wizards Putting the Secret Word)
{
"secretWord": "7",
"publicEntranceHash": "49"
}
Project Structure at this point should look
cave_circuit/
├── cave_circuit.circom
├── input.json
├── cave_circuit.r1cs
├── cave_circuit.sym
└── cave_circuit_js/
├── cave_circuit.wasm
├── generate_witness.js
└── ... (other generated files)
3) Adding the magical Essence
1) First, install snarkjs globally:
npm install -g snarkjs
2) Powers of Tau Ceremony (Phase 1):
# Start a new Powers of Tau ceremony
snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
# Contribute to the ceremony (like adding magical essence)
snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contribution" -v
# Prepare for Phase 2 (finalizing the magical foundation)
snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v
3) Circuit-Specific Setup (Phase 2):
# Setup the circuit-specific keys (creating the cave's special properties)
snarkjs groth16 setup cave_circuit.r1cs pot12_final.ptau cave_circuit_0000.zkey
# Add contribution to the circuit setup (strengthening the cave's magic)
snarkjs zkey contribute cave_circuit_0000.zkey cave_circuit_0001.zkey --name="1st Contributor" -v
# Export the verification key (creating Bob's verification tool)
snarkjs zkey export verificationkey cave_circuit_0001.zkey verification_key.json
4) Generate and Verify Proof:
# Generate the proof (Alice demonstrating her knowledge)
snarkjs groth16 prove cave_circuit_0001.zkey witness.wtns proof.json public.json
# Verify the proof (Bob checking Alice's claim)
snarkjs groth16 verify verification_key.json public.json proof.json
If everything works correctly, you should see:
[INFO] snarkJS: OK!
Complete project structure
cave_circuit/
├── cave_circuit.circom
├── input.json
├── cave_circuit.r1cs
├── cave_circuit.sym
├── cave_circuit_js/
├── pot12_0000.ptau
├── pot12_0001.ptau
├── pot12_final.ptau
├── cave_circuit_0000.zkey
├── cave_circuit_0001.zkey
├── verification_key.json
├── proof.json
├── public.json
└── witness.wtns
This means:
The cave's magic is properly set up
Alice successfully proved she knows the secret
Bob verified the proof without learning the secret
Common issues to watch out for:
Make sure you're in the correct directory
Run commands in the exact order shown
Wait for each command to complete before running the next
Check that witness.wtns was generated correctly before proving
make sure you have circom installed
Each file represents:
cave_circuit.r1cs
: The cave's magical blueprintpot12_final.ptau
: The fundamental magical energy of the cavewitness.wtns
: Alice's actual journey through the caveverification_key.json
: Bob's special tools to verify proofsproof.json
: The magical evidence of Alice's successful journeypublic.json
: Publicly visible information about the proof
Bonus Part: Verifying Proofs On-Chain
Run the command to automatically generate a Solidity contract file containing the Circuit
# Generate Solidity verifier
snarkjs zkey export solidityverifier cave_circuit_0001.zkey verifier.sol
A verifier.sol file would be created after this command
Then we generate call data to verify proof on-chain
# Generate call data for the contract
snarkjs zkey export soliditycalldata public.json proof.json
The Call data should look like
Copy the call data , deploy the contract(verifier.sol) and the paste the call data in the function
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[1] calldata _pubSignals)
And you should get a true output
Github Repo of Project : https://github.com/MansoorButt/Zk-Circom