[Num2FourBits Circuit] Circom and SnarkJS

[Num2FourBits Circuit] Circom and SnarkJS
none 0.0 0

Introduction

Zero-knowledge proofs (ZKPs) enable privacy-preserving verification of information, and Circom is a popular tool for building ZK circuits. In this tutorial, we’ll create a simple circuit that takes a number as input and output the four bit binary representation of the number . This hands-on approach will introduce you to key concepts like defining inputs, constraints, and generating proofs. By the end, you’ll have a solid foundation to start building more complex zero-knowledge circuits using Circom.

Revisiting Key Concepts

  1. Prover and Verifier: The prover generates a proof that the verifier can use to confirm a statement’s validity without learning any details.
  2. Proof: A piece of cryptographic evidence that convinces the verifier.
  3. Witness: Hidden input data used to construct the proof.
  4. Circuit: A set of logical steps used to create a proof. It defines how the inputs relate to the outputs.
  5. Constraint System: Rules combining inputs using mathematical and cryptographic operations.
  6. Trusted Setup: A one-time process to generate cryptographic keys for the prover and verifier to use.

The combination of these elements allows zero-knowledge proofs to provide privacy, security, and verification without revealing sensitive information.

To learn more, read this introduction

Introduction to Zero-Knowledge Proofs

Tools and Environment Setups.

To build and run ZKPs, you’ll need the following tools installed on your system:
Note: you can use zkREPL as a faster and easier web based editor with built in circom compiler.

  1. Node.js: JavaScript runtime to run SnarkJS scripts.
  2. Rust: Used to compile Circom.
  3. Circom: A specialized programming language for ZK circuits.
  4. SnarkJS: A JavaScript library for generating and verifying ZKP proofs.

Step 1: Install Prerequisites

For installation and project setup, check out Part1 of this section in the series.

Building the Circuit

Create a file called num2fourbits in the circuits/ folder.

num2fourbits.circom

pragma circom 2.1.6;

template Num2FourBits () {

    // declaratio of signals
    signal input x;


    signal output b0;
    signal output b1;
    signal output b2;
    signal output b3;


    b0 <-- x % 2;
    b1 <-- (x - b0) / 2 % 2;
    b2 <-- (x - 2 * b1 - b0) / 2 % 2;
    b3 <-- (x - 4 * b2 - 2 * b1 - b0) / 2 % 2;

    b0 * (b0 - 1) === 0;
    b1 * (b1 - 1) === 0;
    b2 * (b2 - 1) === 0;
    b3 * (b3 - 1) === 0;

    1*b0 + 2*b1 + 4*b2 + 8*b3 === x;
   
      
}

component main { public [ x ] } = Num2FourBits();

Explanation of Key Elements

In this circuit, we define a template called Num2FourBits, which is used to convert an input number x into a 4-bit binary representation. Here’s an explanation of the key components:

1. Signals Declaration:

  • signal input x;: This is the input signal x, which is the number that will be converted into binary.
  • signal output b0, b1, b2, b3;: These are the output signals representing the 4 bits of the binary representation of x. Each of these corresponds to one bit in the 4-bit binary number.

2. Binary Conversion Logic:

The goal of the circuit is to represent the number x as a sum of powers of 2. The circuit breaks down the number x into its binary digits (b0, b1, b2, and b3):

  • b0 <-- x % 2;: This computes the least significant bit (LSB) by taking the remainder of x when divided by 2 (x % 2).
  • b1 <-- (x - b0) / 2 % 2;: This calculates the second bit by subtracting b0 from x, then dividing by 2, and taking the remainder when divided by 2. This isolates the second bit.
  • b2 <-- (x - 2 * b1 - b0) / 2 % 2;: This isolates the third bit by subtracting 2 * b1 + b0 from x, then dividing by 2 and taking the remainder when divided by 2.
  • b3 <-- (x - 4 * b2 - 2 * b1 - b0) / 2 % 2;: This isolates the fourth bit by subtracting 4 * b2 + 2 * b1 + b0 from x, then dividing by 2 and taking the remainder when divided by 2.

3. Constraints on the Binary Values:

The following constraints ensure that the outputs b0, b1, b2, and b3 are binary values (either 0 or 1):

  • b0 * (b0 - 1) === 0;: This ensures that b0 is either 0 or 1.
  • Similarly, the other lines ensure that b1, b2, and b3 are also either 0 or 1.

4. Reconstruction of x:

  • 1 * b0 + 2 * b1 + 4 * b2 + 8 * b3 === x;: This constraint ensures that the sum of the binary digits (each multiplied by its corresponding power of 2) exactly reconstructs the original number x. This is a key check to verify that the binary decomposition is correct.

Compiling the Circuit

Run the following command to compile the circuit:

circom circuits/num2fourbits.circom --r1cs --wasm --sym -o output/

Let’s look at each file:

  • –r1cs: Generates the Rank 1 Constraint System (R1CS) file.

  • –wasm: Outputs a WebAssembly file for computation.

  • –sym: Outputs a symbol file for debugging.

  • -o output/: Specifies the output directory.

Check for the following files in output/:

  • num2fourbits.r1cs: The constraint system file.

  • num2fourbits.wasm: The WebAssembly file.

  • num2fourbits.sym: The symbol file for debugging.

Generating Trusted Setup

Generate the keys for the prover and verifier:

wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_10.ptau -O outputs/pot10.ptau
snarkjs groth16 setup outputs/num2fourbits.r1cs outputs/pot10.ptau outputs/num2fourbits_final.zkey

Export the Verification Key

snarkjs zkey export verificationkey outputs/num2fourbits_final.zkey outputs/verification_key.json

Creating Inputs

Create an input file input/input.json with the following content:

{
    "x": "3"
}

These Inputs will be used to generate the proof.

Generating the Proof

  1. Generate Witness

snarkjs groth16 prove outputs/num2fourbits_final.zkey outputs/witness.wtns outputs/proof.json outputs/public.json
  1. Generate the Proof

snarkjs groth16 prove outputs/num2fourbits_final.zkey outputs/witness.wtns outputs/proof.json outputs/public.json
  1. Verify the Proof

snarkjs groth16 verify outputs/verification_key.json outputs/public.json outputs/proof.json

If everything is setup correctly, you should see:

snarkJS: OK!

Summary

  1. Design the Circuit: Create the logic of the proof in a Circom file.

  2. Compile the Circuit: Convert the logic to constraint systems and WebAssembly files.

  3. Generate Trusted Setup: Produce keys for the prover and verifier.

  4. Input Data: Provide the inputs for the computation.

  5. Generate Witness: Produce intermediate computation results.

  6. Generate Proof: Create a proof from the witness.

  7. Verify Proof: Use the verifier to check the proof’s validity.

Conclusion

Congratulations! You’ve completed a hands-on implementation of zero-knowledge proofs. This process may seem complex initially, but as you create more advanced circuits, you’ll see how useful ZKPs can be for privacy-preserving computation.

We encourage you to continue your exploration of topics on zk proofs. If you’re interested in diving deeper, you can follow up on the pathway here Zero-Knowledge Proofs on the Celo Blockchain: A Comprehensive Tutorial Series - Pathways - Celo Academy

Resources

Circom docs
SnarkJS
0xparc Circom Workshop

About the author

I’m Jonathan Iheme, A full stack block-chain Developer from Nigeria. With a great passion for Zero Knowledge Technology.

linkedIn
Twitter