# Groth16

A zero-knowledge proof allows a prover to validate that a statement is true without revealing any information about the inputs. For example, a prover can validate that they know the solution to a sudoku puzzle without revealing the solution.

Zero-knowledge succinct non-interactive argument of knowledge (zk-SNARKs) are a family of zero-knowledge proofs that are non-interactive, have succinct proof size and efficient verification time. An important and widely used variant of them is pairing-based zk-SNARKs like the Groth16 proof system, which is one of the most efficient and widely used.

The Move API in Sui enables you to verify any statement that can be expressed in a NP-complete language efficiently using Groth16 zk-SNARKs over either the BN254 or BLS12-381 elliptic curve constructions.

There are high-level languages for expressing these statements, such as Circom, used in the following example.

Groth16 requires a trusted setup for each circuit to generate the verification key. The API is not pinning any particular verification key and each user can generate their own parameters or use an existing verification to their apps.

## Usage

The following example demonstrates how to create a Groth16 proof from a statement written in Circom and then verify it using the Sui Move API. The API currently supports up to eight public inputs.

### Create circuit

The proof demonstrates that we know a secret input to a hash function which gives a certain public output.

`pragma circom 2.1.5;`

include "node_modules/circomlib/circuits/poseidon.circom";

template Main() {

component poseidon = Poseidon(1);

signal input in;

signal output digest;

poseidon.inputs[0] <== in;

digest <== poseidon.out;

}

component main = Main();

We use the Poseidon hash function which is a ZK-friendly hash function. Assuming that the circom compiler has been installed, the above circuit is compiled using the following command:

`circom main.circom --r1cs --wasm`

This outputs the constraints in R1CS format and the circuit in Wasm format.

### Generate proof

To generate a proof verifiable in Sui, you need to generate a witness. This example uses Arkworks' ark-circom Rust library. The code constructs a witness for the circuit and generates a proof for it for a given input. Finally, it verifies that the proof is correct.

`use ark_bn254::Bn254;`

use ark_circom::CircomBuilder;

use ark_circom::CircomConfig;

use ark_groth16::Groth16;

use ark_snark::SNARK;

fn main() {

// Load the WASM and R1CS for witness and proof generation

let cfg = CircomConfig::<Bn254>::new("main.wasm", "main.r1cs").unwrap();

// Insert our secret inputs as key value pairs. We insert a single input, namely the input to the hash function.

let mut builder = CircomBuilder::new(cfg);

builder.push_input("in", 7);

// Create an empty instance for setting it up

let circom = builder.setup();

// WARNING: The code below is just for debugging, and should instead use a verification key generated from a trusted setup.

// See for example https://docs.circom.io/getting-started/proving-circuits/#powers-of-tau.

let mut rng = rand::thread_rng();

let params =

Groth16::<Bn254>::generate_random_parameters_with_reduction(circom, &mut rng).unwrap();

let circom = builder.build().unwrap();

// There's only one public input, namely the hash digest.

let inputs = circom.get_public_inputs().unwrap();

// Generate the proof

let proof = Groth16::<Bn254>::prove(¶ms, circom, &mut rng).unwrap();

// Check that the proof is valid

let pvk = Groth16::<Bn254>::process_vk(¶ms.vk).unwrap();

let verified = Groth16::<Bn254>::verify_with_processed_vk(&pvk, &inputs, &proof).unwrap();

assert!(verified);

}

The proof shows that an input (7) which, when hashed with the Poseidon hash function, gives a certain output (which in this case is `inputs[0].to_string() = 7061949393491957813657776856458368574501817871421526214197139795307327923534`

).

### Verification in Sui

The API in Sui for verifying a proof expects a special processed verification key, where only a subset of the values are used. Ideally, computation for this prepared verification key happens only once per circuit. You can perform this processing using the `sui::groth16::prepare_verifying_key`

method of the Sui Move API with a serialization of the `params.vk`

value used previously.

The output of the `prepare_verifying_key`

function is a vector with four byte arrays, which corresponds to the `vk_gamma_abc_g1_bytes`

, `alpha_g1_beta_g2_bytes`

, `gamma_g2_neg_pc_bytes`

, `delta_g2_neg_pc_bytes`

.

To verify a proof, you also need two more inputs, `public_inputs_bytes`

and `proof_points_bytes`

, which contain the public inputs and the proof respectively. These are serializations of the `inputs`

and `proof`

values from the previous example, which you can compute in Rust as follows:

`let mut vk_bytes = Vec::new();`

params.vk.serialize_compressed(&mut vk_bytes).unwrap();

let mut public_inputs_bytes = Vec::new();

for i in 0..inputs.len() { // if there is more than one public input, serialize one by one

inputs[i].serialize_compressed(&mut inputs_bytes).unwrap();

}

let mut proof_points_bytes = Vec::new();

proof.serialize_compressed(&mut proof_points_bytes).unwrap();

The following example smart contract prepares a verification key and verifies the corresponding proof. This example uses the BN254 elliptic curve construction, which is given as the first parameter to the `prepare_verifying_key`

and `verify_groth16_proof`

functions. You can use the `bls12381`

function instead for BLS12-381 construction.

`module test::groth16_test {`

use sui::groth16;

use sui::event;

/// Event on whether the proof is verified

struct VerifiedEvent has copy, drop {

is_verified: bool,

}

public fun verify_proof(vk_bytes: vector<u8>, public_inputs_bytes: vector<u8>, proof_points_bytes: vector<u8>) {

let pvk = groth16::prepare_verifying_key(&groth16::bn254(), &vk_bytes);

let public_inputs = groth16::public_proof_inputs_from_bytes(public_inputs_bytes);

let proof_points = groth16::proof_points_from_bytes(proof_points_bytes);

event::emit(VerifiedEvent {is_verified: groth16::verify_groth16_proof(&groth16::bn254(), &pvk, &public_inputs, &proof_points)});

}

}