DEV Community

Cover image for ZK Speedrun: Noir, Circom, Zokrates in 15 Minutes
Ahmed Castro
Ahmed Castro

Posted on

ZK Speedrun: Noir, Circom, Zokrates in 15 Minutes

ℹ️ Are you a Circuit Breaker hacker? Join the Scroll telegram, find project ideas and join our telegram group by visiting our Circuit Breaker cheat sheet

Zokrates

ZK Zokrates Logo

Instalation

curl -LSfs get.zokrat.es | sh
export PATH=$PATH:/home/YOURUSERNAME/.zokrates/bin
Enter fullscreen mode Exit fullscreen mode

Generate a proof

Create the zokrates circuit file.

root.zok

def main(private field a, field b) {
    assert(a * a == b);
    return;
}
Enter fullscreen mode Exit fullscreen mode

Now compile it and generate a proof.

zokrates compile -i root.zok
zokrates setup
zokrates compute-witness -a 3 9
zokrates generate-proof
Enter fullscreen mode Exit fullscreen mode

The proof is now located in proof.json.

Verify the proof

Generate a solidity verifier.

zokrates export-verifier
Enter fullscreen mode Exit fullscreen mode

This will generate the verifier.sol contract. Now deploy it and pass it's address as constructor parameter to the following contract with custom solidity logic.

// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

library Pairing {
    struct G1Point {
        uint X;
        uint Y;
    }
    struct G2Point {
        uint[2] X;
        uint[2] Y;
    }
}

struct Proof {
    Pairing.G1Point a;
    Pairing.G2Point b;
    Pairing.G1Point c;
}

interface IZokratesVerifier {
    function verifyTx(Proof memory proof, uint[1] memory input) external view returns (bool r);
}

contract ZokratesCustomLogic {
    IZokratesVerifier zokratesVerifier;
    uint public publicInput;

    constructor(address zokratesVeriferAddress) {
        zokratesVerifier = IZokratesVerifier(zokratesVeriferAddress);
    }

    function sendProof(Proof memory proof, uint[1] memory input) public {
        // ZK verification
        zokratesVerifier.verifyTx(proof, input);

        // Your custom logic
        publicInput = input[0];
    }
}
Enter fullscreen mode Exit fullscreen mode

Noir

ZK Noir Logo

Instalation

On Linux

mkdir -p $HOME/.nargo/bin && \
curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.22.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \
tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \
echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \
source ~/.bashrc
Enter fullscreen mode Exit fullscreen mode

On MacOs Apple Silicon

mkdir -p $HOME/.nargo/bin && \
curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.22.0/nargo-aarch64-apple-darwin.tar.gz && \
tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \
echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \
source ~/.zshrc
Enter fullscreen mode Exit fullscreen mode

On MacOs Intel

mkdir -p $HOME/.nargo/bin && \
curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.22.0/nargo-x86_64-apple-darwin.tar.gz && \
tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \
echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \
source ~/.zshrc
Enter fullscreen mode Exit fullscreen mode

Generate a proof

Generate a new noir project and prepare it to receive the prover inputs.

nargo new hello_world
cd hello_world
nargo check
Enter fullscreen mode Exit fullscreen mode

Now put the inputs in Prover.toml and generate the proof.

Prover.toml

x = "1"
y = "2"
Enter fullscreen mode Exit fullscreen mode
nargo prove
Enter fullscreen mode Exit fullscreen mode

The proof is now located at proofs/hello_world.proof.

Verify the proof

Generate the solidity verifier.

nargo codegen-verifier
Enter fullscreen mode Exit fullscreen mode

The verifier contract is now located at contract/hello_world/plonk_vk.sol. Deploy it and send it's address as param to the following contract with custom logic.

// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

interface INoirVerifier {
    function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool);
}

contract NoirCustomLogic {
    INoirVerifier noirVerifier;
    uint public publicInput;

    constructor(address noirVeriferAddress) {
        noirVerifier = INoirVerifier(noirVeriferAddress);
    }

    function sendProof(bytes calldata _proof, bytes32[] calldata _publicInputs) public {
        // ZK verification
        noirVerifier.verify(_proof, _publicInputs);

        // Your custom logic
        publicInput = uint(_publicInputs[0]);
    }
}
Enter fullscreen mode Exit fullscreen mode

Circom

ZK Circom Logo

Installation

curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
git clone https://github.com/iden3/circom.git
cd circom
cargo build --release
cargo install --path circom
npm install -g snarkjs
Enter fullscreen mode Exit fullscreen mode

Generate a proof

Create the circom circuit file and the inputs that will be passed to it.

multiplier2.circom

pragma circom 2.0.0;

template Multiplier2() {
    signal input a;
    signal input b;
    signal output c;
    c <== a*b;
 }

 component main = Multiplier2();
Enter fullscreen mode Exit fullscreen mode

input.json

{"a": "3", "b": "11"}
Enter fullscreen mode Exit fullscreen mode

Now let's generate the proof.

circom multiplier2.circom --r1cs --wasm --sym --c
node multiplier2_js/generate_witness.js multiplier2_js/multiplier2.wasm input.json witness.wtns
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 multiplier2.r1cs pot12_final.ptau multiplier2_0000.zkey
snarkjs zkey contribute multiplier2_0000.zkey multiplier2_0001.zkey --name="1st Contributor Name" -v
snarkjs zkey export verificationkey multiplier2_0001.zkey verification_key.json
snarkjs groth16 prove multiplier2_0001.zkey witness.wtns proof.json public.json
snarkjs groth16 verify verification_key.json public.json proof.json
snarkjs generatecall
Enter fullscreen mode Exit fullscreen mode

The proof will be printed on the terminal.

Verify the proof

Generate the solidity verifier.

snarkjs zkey export solidityverifier multiplier2_0001.zkey verifier.sol
Enter fullscreen mode Exit fullscreen mode

This will generate the verifier.sol contract. Deploy it and passit as constructo parameter to the following custom logic contract.

// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

interface ICircomVerifier {
    function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[1] calldata _pubSignals) external view returns (bool);
}

contract CircomCustomLogic {
    ICircomVerifier circomVerifier;
    uint public publicInput;

    constructor(address circomVeriferAddress) {
        circomVerifier = ICircomVerifier(circomVeriferAddress);
    }

    function sendProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[1] calldata _pubSignals) public {
        // ZK verification
        circomVerifier.verifyProof(_pA, _pB, _pC, _pubSignals);

        // Your custom logic
        publicInput = _pubSignals[0];
    }
}
Enter fullscreen mode Exit fullscreen mode

For further information visit the Zokrates, Noir, and Circom official documentation.

Thank you for reading this article!

Follow me on dev.to and on Youtube for everything related to Blockchain development.

Top comments (0)