SEI ZK Series Part 3 - Creating a zk snark circuit
June 17th, 2025

Creating Your First Zero-Knowledge Circuit: A Beginner's Guide

Introduction

Zero-knowledge proofs (ZKPs) are cryptographic methods that allow one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any additional information beyond the validity of the statement itself. This tutorial will guide you through creating your first ZK circuit using Circom, one of the most popular languages for writing ZK circuits.

Prerequisites

Before we begin, make sure you have the following installed:

  • Node.js (v14 or higher)

  • npm (Node Package Manager)

  • Git

Setting Up Your Development Environment

  1. First, let's install the Circom compiler and snarkjs:
npm install -g circom snarkjs
  1. Create a new project directory and initialize it:
mkdir my-first-zk-circuit
cd my-first-zk-circuit
npm init -y

Understanding the Basics

What is a ZK Circuit?

A ZK circuit is a program that defines a computational problem that can be proven without revealing the inputs. Think of it as a mathematical function that:

  1. Takes some inputs (private and public)

  2. Performs computations

  3. Produces outputs that can be verified

Key Components of a ZK Circuit

  1. Signals: These are the inputs and outputs of your circuit

    • Private signals: Known only to the prover

    • Public signals: Known to both prover and verifier

  2. Constraints: These are the mathematical relationships that must be satisfied

    • They define the valid computations in your circuit

    • They ensure the prover is following the rules

Creating Your First Circuit

Let's create a simple circuit that proves knowledge of a number that, when multiplied by itself, equals a given public number. This is a basic example that demonstrates the core concepts.

  1. Create a new file called multiplier.circom:
pragma circom 2.0.0;

template Multiplier() {
    // Declaration of signals
    signal private input a;
    signal public output b;

    // Constraints
    b <== a * a;
}

component main = Multiplier();

Let's break down this circuit:

  • pragma circom 2.0.0; - Specifies the Circom version

  • template Multiplier() - Defines our circuit template

  • signal private input a; - Declares a private input signal

  • signal public output b; - Declares a public output signal

  • b <== a * a; - Defines the constraint (b must equal a squared)

Compiling and Testing the Circuit

  1. Compile the circuit:
circom multiplier.circom --r1cs --wasm --sym
  1. Generate the proving and verification keys:
snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contribution" -v
snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v
snarkjs groth16 setup multiplier.r1cs pot12_final.ptau multiplier_0000.zkey
snarkjs zkey contribute multiplier_0000.zkey multiplier_0001.zkey --name="First contribution" -v
snarkjs zkey export verificationkey multiplier_0001.zkey verification_key.json
  1. Create a test input file input.json:
{
  "a": 5
}
  1. Generate the proof:
snarkjs groth16 prove multiplier_0001.zkey witness.wtns proof.json public.json
  1. Verify the proof:
snarkjs groth16 verify verification_key.json public.json proof.json

Understanding What Happened

Let's break down what we just did:

  1. Circuit Definition: We created a circuit that proves knowledge of a number that, when squared, equals a given output.

  2. Compilation: The circuit was compiled into a format that can be used to generate and verify proofs.

  3. Trusted Setup: We performed a trusted setup to generate the proving and verification keys.

  4. Proof Generation: We generated a proof that we know a number (5) that, when squared, equals 25.

  5. Verification: We verified that the proof is valid without revealing the original number.

Best Practices

  1. Security:

    • Always keep private inputs secure

    • Use appropriate bit lengths for your signals

    • Be careful with arithmetic overflow

  2. Performance:

    • Minimize the number of constraints

    • Use efficient circuit designs

    • Consider the trade-off between circuit size and security

  3. Testing:

    • Test your circuit with various inputs

    • Include edge cases

    • Verify the constraints work as expected

Common Pitfalls to Avoid

  1. Arithmetic Overflow: Always consider the maximum values your signals can take

  2. Incorrect Constraints: Double-check your mathematical relationships

  3. Inefficient Designs: Keep your circuits as simple as possible

  4. Security Assumptions: Don't make assumptions about the security of your inputs

Next Steps

Now that you've created your first ZK circuit, here are some suggestions for further learning:

  1. Try creating more complex circuits

  2. Learn about different proving systems (Groth16, PLONK, etc.)

  3. Explore optimization techniques

  4. Study real-world applications of ZK proofs

  5. Join the ZK community and contribute to open-source projects

Resources

Conclusion

Congratulations! You've created your first ZK circuit. This is just the beginning of your journey into the world of zero-knowledge proofs. Remember that ZK technology is still evolving, and there's always more to learn. Keep experimenting, building, and contributing to the community.

Happy coding!

Subscribe to 4undRaiser
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from 4undRaiser

Skeleton

Skeleton

Skeleton