# Analysis of zk-Snark engineering principles based on Gnark ###### tags: `Public` Author: 0x60018 from [@OrbiterResearch](https://twitter.com/OrbiterResearch) ## 1. How to use Gnark Example: ```typescript Hash = mimc(PreImage) ``` *mimc* is the hash algorithm, providing *Hash* and *proof* to make the **verifier** be sure of the **prover** knows the *PreImage*. ### 1.1 Define the circuit ```typescript // CubicCircuit defines a simple circuit type CubicCircuit struct { PreImage frontend.Variable Hash frontend.Variable `gnark:",public"` } // Define declares the circuit constraints func (circuit *CubicCircuit) Define(api frontend.API) error { mimc, _ := mimc.NewMiMC(api) mimc.Write(circuit.PreImage) api.AssertIsEqual(circuit.Hash, mimc.Sum()) return nil } ``` Q1: Is there a more general Turing-complete language for writing circuits? Q2: Is that what the Cairo language does for this purpose? ### 1.2 Compile the circuit and generate R1CS constraints *Note: In addition to the BN254 curve, there are also curves such as BLS12–381.* Detailed comparison: https://docs.gnark.consensys.net/en/latest/Concepts/schemes_curves/ The mathematical rationale for generating R1CS constraints is complicated and still needs further research. ```typescript // Generate constraints using BN254 curves r1cs, err := frontend.Compile(ecc.BN254, r1cs.NewBuilder, &circuit) ``` ### 1.3 Generate ProvingKey, VerifyingKey ```typescript pk, vk, err := groth16.Setup(r1cs) ``` ### 1.4 Generates Solidity file of Verifier using VerifyingKey ```typescript verifySolidityPath := fmt.Sprintf("..%chardhat%ccontracts%cmimc_groth16.sol", os.PathSeparator, os.PathSeparator, os.PathSeparator) f, _ := os.OpenFile(verifySolidityPath, os.O_CREATE|os.O_WRONLY, 0666) defer f.Close() vk.ExportSolidity(f) ``` ### 1.5 Generate proof by applying Prove to PreImage, Hash ```typescript assignment := &CubicCircuit{PreImage: preImage, Hash: hash} witness, _ := frontend.NewWitness(assignment, ecc.BN254) publicWitness, _ := witness.Public() proof, err := groth16.Prove(r1cs, pk, witness) ``` ### 1.6 Save proof and publicWitness ```typescript // Save publicWitness publicWitnessPath := fmt.Sprintf("..%chardhat%ctest%cmimc_public_witness.json", os.PathSeparator, os.PathSeparator, os.PathSeparator) publicWitnessFile, _ := os.OpenFile(publicWitnessPath, os.O_CREATE|os.O_WRONLY, 0666) defer publicWitnessFile.Close() publicWitnessJson, _ := publicWitness.MarshalJSON() publicWitnessFile.Write(publicWitnessJson) // Save proof proofPath := fmt.Sprintf("..%chardhat%ctest%cmimc.proof", os.PathSeparator, os.PathSeparator, os.PathSeparator) proofFile, _ := os.OpenFile(proofPath, os.O_CREATE|os.O_WRONLY, 0666) defer proofFile.Close() proof.WriteRawTo(proofFile) ``` ### 1.7 Send proof and publicWitness to the contract for verification ```typescript import { ethers } from "hardhat"; import { Signer, BigNumber, Contract, ContractFactory } from "ethers"; // import "ethers"; import { expect } from "chai"; import fs from "fs-extra"; import path from "path"; describe("mimc", function () { let accounts: Signer[]; let contractFactory: ContractFactory; let verifierContract: Contract; before(async function () { accounts = await ethers.getSigners(); contractFactory = await ethers.getContractFactory("Verifier"); }); it("deploy", async function () { verifierContract = await contractFactory.deploy(); }); it("verifyProof", async function () { const fpSize = 4 * 8; const inputBuffer = await fs.readFile( path.resolve(__dirname, "mimc_public_witness.input") ); const input = JSON.parse(inputBuffer.toString()).Hash; console.warn(input); const proofBuffer = await fs.readFile( path.resolve(__dirname, "mimc.proof") ); const a: Buffer[] = []; a[0] = proofBuffer.slice(fpSize * 0, fpSize * 1); a[1] = proofBuffer.slice(fpSize * 1, fpSize * 2); const b: Buffer[][] = [[], []]; b[0][0] = proofBuffer.slice(fpSize * 2, fpSize * 3); b[0][1] = proofBuffer.slice(fpSize * 3, fpSize * 4); b[1][0] = proofBuffer.slice(fpSize * 4, fpSize * 5); b[1][1] = proofBuffer.slice(fpSize * 5, fpSize * 6); const c: Buffer[] = []; c[0] = proofBuffer.slice(fpSize * 6, fpSize * 7); c[1] = proofBuffer.slice(fpSize * 7, fpSize * 8); const resp = await verifierContract.verifyProof(a, b, c, [input]); expect(resp).to.be.true; }); }); ``` In the above example, we can see that only **proof** and **public input** (Hash value) are submitted off-chain. The contract can verify whether the value of **PreImage** is known off-chain, which can realize privacy proof. But there are still some issues to be resolved: **Q1**: Split the proof into a, b, and c. The function is to check that the elements in a, b, and c cannot be greater than or equal to PRIME_Q (value: 21888242871839275222246405745257275088696311157297823662689037894645226208583) during contract verification. What is the mathematical principle? Rules for R1CS constraints? **Q2**: Proof generation in the above example needs to be generated by **go language**, so the proof cannot be generated on the web client. How can this be achieved using **TypeScript**? Can it be imported from gnark or is there another library that implements it? Is there a reference to *Tornado.cash*? A2: Currently, [snarkjs](https://github.com/iden3/snarkjs) and [zokrates](https://github.com/Zokrates/ZoKrates) use *JavaScript* to implement the *zk-snark* function, and *Tornado.cash* uses *snarkjs* Q3: How to implement zk-rollup with zk-snark technology? or a merkle tree proof circuit? What is the relationship between merkle and zk-rollup (or no relationship at all?) A3: *zk-rollup* uses *merkle tree* to store **user amount, public key, nonce value** and other information. The Operator converts the transaction into a state and generates a zk-snark proof to submit to the L1 contract for verification and change the user state (simple version) ## 2. Gnark source code structure The following structural analysis is based on Gnark v0.7.1 ![](https://hackmd.io/_uploads/ryyK4gotj.png) **Q**: The operation functions provided under *frontend => api.go* are limited, and Turing-complete zk programming cannot be provided for the time being. Is **circom** more complete?