Skip to content

Compile a Circuit

Many ZK libraries and languages work with Sign Protocol; choosing one is up to you. We recommend a library that can generate a Solidity verifier from your compiled circuit. This tutorial uses Circom.

First, follow Circom's installation docs. If you are new to Circom, read through their sample circuit. This tutorial uses a non-trivial example circuit, which you can find here. The circom file should contain the following:

pragma circom 2.0.0;

include "circomlib/circuits/sha256/sha256.circom";
include "circomlib/circuits/bitify.circom";

/**
 * Wrapper around SHA256 to support bytes as input instead of bits
 * @param  N   The number of input bytes
 * @input  in  The input bytes
 * @output out The SHA256 output of the n input bytes, in bytes
 *
 * SOURCE: https://github.com/celer-network/zk-benchmark/blob/main/circom/circuits/sha256/sha256_bytes.circom
 */
template Sha256Bytes(N) {
  signal input in[N];
  signal output out[32];

  // convert input bytes to bits
  component byte_to_bits[N];
  for (var i = 0; i < N; i++) {
    byte_to_bits[i] = Num2Bits(8);
    byte_to_bits[i].in <== in[i];
  }

  // sha256 over bits
  component sha256 = Sha256(N*8);
  for (var i = 0; i < N; i++) {
    for (var j = 0; j < 8; j++) {
      sha256.in[i*8+j] <== byte_to_bits[i].out[7-j];
    }
  }

  // convert output bytes to bits
  component bits_to_bytes[32];
  for (var i = 0; i < 32; i++) {
    bits_to_bytes[i] = Bits2Num(8);
    for (var j = 0; j < 8; j++) {
      bits_to_bytes[i].in[7-j] <== sha256.out[i*8+j];
    }
    out[i] <== bits_to_bytes[i].out;
  }
}

template Main(N) {
    signal input in[N];
    signal input hash[32];
    signal output out[32];

    component sha256 = Sha256Bytes(N);
    sha256.in <== in;
    out <== sha256.out;

    for (var i = 0; i < 32; i++) {
        out[i] === hash[i];
    }

    log("start ================");
    for (var i = 0; i < 32; i++) {
        log(out[i]);
    }
    log("finish ================");
}

// render this file before compilation
component main = Main(64);

This sha256 circuit takes two input signals: an array of size 64 and an array of size 32. The first array is hashed with sha256, and the output (hash) signal must equal the provided hash.

Compile your circuit

Compile your circuit with the circom command, which comes with your local Circom installation:

> circom sha256.circom --r1cs --wasm --sym

This compiles your source code into the required files, including the r1cs constraint system file and the wasm files for generating a witness. The command flags are described here. You do not need a witness to generate a smart contract verifier; witnesses are used to create proofs, which your schema hook verifies.

Generate a Witness

In your output directory (likely named sha256_js), create a file named input.json with the following contents:

{
  "in": [
    20, 207, 123, 94, 84, 118, 123, 180, 191, 195, 21, 180, 82, 88, 185, 223,
    207, 101, 103, 247, 201, 113, 113, 87, 111, 84, 191, 243, 87, 47, 134, 143,
    20, 207, 123, 94, 84, 118, 123, 180, 191, 195, 21, 180, 82, 88, 185, 223,
    207, 101, 103, 247, 201, 113, 113, 87, 111, 84, 191, 243, 87, 47, 134, 143
  ],
  "hash": [
    29, 131, 153, 39, 255, 114, 11, 253, 116, 101, 97, 195, 155, 112, 35, 76,
    226, 32, 120, 129, 165, 186, 145, 10, 192, 28, 49, 244, 143, 179, 101, 236
  ]
}

From your build directory, generate a witness:

> node generate_witness.js sha256.wasm input.json witness.wtns

Generating a verifier contract

This section follows Circom's documentation here, which explains each step. Run the following commands to complete the Powers of Tau and Phase 2 requirements:

> snarkjs powersoftau new bn128 17 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 sha256.r1cs pot12_final.ptau sha256_0000.zkey
> snarkjs zkey contribute sha256_0000.zkey sha256_0001.zkey --name="1st Contributor Name" -v
> snarkjs zkey export verificationkey sha256_0001.zkey verification_key.json

Then generate a smart contract verifier:

> snarkjs zkey export solidityverifier sha256_0001.zkey verifier.sol

This generates verifier.sol in your root project directory. To help build a call to verifyProof(), run:

> snarkjs generatecall

The next page integrates verifier.sol with a schema hook.