ZoKrates tutorial with Truffle

A step-by-step walkthrough for a simple ZoKrates integration with Truffle

Extropy.IO
Sep 26, 2018 · 7 min read

A zero-knowledge proof is a way for a prover to convince a verifier that they know some secret information without revealing the details of what it is. In a traditional interactive zero-knowledge protocol, several rounds of exchanges between the prover and the verifier are needed before a verifier can be convinced. A zk-SNARK is essentially a non-interactive zero-knowledge protocol that allows this exchange between a prover and a verifier to be very short. To implement a zk-SNARK in Ethereum, you can use the ZoKrates toolbox. With ZoKrates, you can create a smart contract to act as the verifier. You can also use it to generate your proof that you know some secret information and convince the verifier of this. Instead of convincing a verifier over some kind of online channel, you simply publish the fact that you convinced the verifying smart contract to the Ethereum network.

Getting Started

This article will act as a tutorial for setting up ZoKrates and integrating your zk-SNARK with your Truffle environment. You will need to have truffle and docker installed before proceeding.

It is also recommended that you read this article to get a primer on zk-SNARKs and their role in Ethereum. In that article, the functions of the Generator, Prover and the Verifier are outlined — these are going to be the points of reference for the following sections.

To start off, create a new working directory and run truffle init to create a new Truffle environment.

Generator

As the Generator, you’re going to run the trusted setup for the zk-SNARK to generate the verifying key and the proving key. The key generation function takes in a secret value as an extra parameter, and you are trusted with the important task of deleting this secret value after it’s used so that people can’t generate false proofs down the line. Luckily, ZoKrates will handle this for you.

Running the generator function

You will run the generator off-chain to produce the verifying key vk and the proving key pk. The vk will be hardcoded into the generated smart contract, and the pk will be a file you will need to share with provers. To get started, you will need to start up ZoKrates:

docker run -ti kyroy/zokrates /bin/bash

In the container you need to cd ZoKrates/ before proceeding.

The function used to generate vk and pk requires a program that's modelled as an arithmetic circuit, and a secret value. For the setup to be a 'trusted setup' you need to delete the secret value, but as discussed earlier ZoKrates handles this for you. So the only thing you need to worry about is the program, which you need to provide in the form of a .code file. You can either use one of the example files in the ZoKrates examples/ directory, or create your own. To model the program as an arithmetic circuit, you need ZoKrates to compile it:

./target/release/zokrates compile -i FILE_NAME.code

This creates two files, out which is used in the next step, and out.code which you can read if you want. Now you need to run the setup phase:

./target/release/zokrates setup

No arguments are required as it picks up the out file automatically. This creates verification.key and proving.key, the proving.key file is what you will need to share to provers as the pk. It also creates a file called variables.inf which provides details of the constraints for the original program. Finally, to create the actual smart contract:

./target/release/zokrates export-verifier

Again, no arguments are required as it picks up the verification.key file automatically. This creates verifier.sol, which has the vk hardcoded into the Verifier contract. It has a function verifyTx which is used by a Prover (by passing their generated proof) to verify that their proof satisfies the original program.

Exporting the required files

The whole fun of having a verifying smart contract for a zero-knowledge proof is for a prover to be able to show off that they know some secret information, so for the contract to be of any use, it will need to be deployed to either the main net or a test net. To start, we need to get the required files out of the Docker container and into the Truffle environment. The required files are the verifier.sol file to deploy the contract to the chosen network, the proving.key file so provers can generate proofs, the out file so provers can compute a witness, and the variables.inf file which is also needed to generate a proof. In a separate terminal window or tab, run docker ps to get the container id that ZoKrates is in. To get the files in the right directories:

docker cp CONTAINER_ID:root/ZoKrates/verifier.sol ./contracts
docker cp CONTAINER_ID:root/ZoKrates/proving.key ./
docker cp CONTAINER_ID:root/ZoKrates/out ./
docker cp CONTAINER_ID:root/ZoKrates/variables.inf ./

This is assuming you started up ZoKrates in your Truffle working directory in the first place. You can now close the Docker environment by running exit.

Deploying the smart contract

Set up your migrations/2_deploy_contracts.js and truffle-config.js files for deployment to whichever network you want to put the Verifieron. For this tutorial we’ll use Rinkeby, and an easy way to deploy it is by using Infura through Truffle. With the configuration finished, run:

truffle migrate --network rinkeby

And that’s you done! Now for a prover to use the Verifier contract, you need to share the verifier.json artifact so they can interact with it through truffle-contract or truffle console (or just share the ABI and address for non-Truffle interaction), the proving.key and variables.inf files so they can generate their proof and the out file so they can compute their witness.

Prover

As the prover, you have found a contract Verifier which provides a verifyTx function, taking in a proof as a parameter which you know you can generate — because you know some secret information to satisfy the original program generated in the setup! You want to generate this proof that you know the secret information, so you obtain the verifier.json Truffle artifact (or ABI and contract address), the pk in the file proving.key, the variables.inf file and the out file which represents the original program as an arithmetic circuit.

Importing required files to ZoKrates

As before, to start ZoKrates run:

docker run -ti kyroy/zokrates /bin/bash

In the container you need to run cd Zokrates/ before proceeding.

Before you can generate your proof you need to compute a witness, in which you enter the public arguments as well as your secret information in order to satisfy the proof. First, you’ll need to import the required files to the Docker container. In a separate terminal window or tab, run docker ps to get the container id that ZoKrates is in, then run:

docker cp ./out CONTAINER_ID:root/ZoKrates/
docker cp ./proving.key CONTAINER_ID:root/ZoKrates/
docker cp ./variables.inf CONTAINER_ID:root/ZoKrates/

Now to compute the witness, in the Docker container run:

./target/release/zokrates compute-witness -a [arg0 arg1 .. argN]

Where you input your arguments as needed as a space separated list. Note that the newer version of ZoKrates takes the secret information (private parameters) as arguments here, whereas in the older version you would enter an interactive environment to input private values. This creates a file witness which will be used in the next step.

Generate your proof

Now with your computed witness and the pk available, you can generate your proof. To do this, run:

./target/release/zokrates generate-proof

No arguments are required as it picks up the witness and proving.key files automatically, as well as the variables.inffile. ZoKrates doesn't actually generate a proof file here — instead it will spit out some output that looks like this:

A = 0x5743c0dd166bacb272bf191c57c62a7f9e6fa5350a75ec10d04e951988044e0, 0x1a7c371ef983a24a64f6e0a0895699dd3ee47291a02582ad01575d6f40937389
A_p = 0x1727fe01690803f370b0529bb867d538f21a0243e01498bd7d75e5581adf1543, 0x103e9cc0800f170b7fd56563397e4423e92ada4885fc5775edbad2ee0182c5a6
B = [0x1042ffb222d04811503565dd0383e394dbf14123cc59e7a96a4e38724c54e410, 0xf90d20c5a22207f2194aa31a27050ed3163e140e10b683b60c05d8f4b90838f], [0x28711042902b5a93688396fd3d335a99ee2f3ac23b678b2b16972a7794fa58f5, 0x26c0a26e2690ffea68b3add058e817b126d25fc8f76184701a392241cd95e94c]
B_p = 0x3a6788801c11e76a999762e6ce9553829921269e59bbc0daec5f05e360e6c8f, 0x7046c0a74b42d7711918521ef9894e2e7816f53aad3730cb0e6eb26e76a3830
C = 0x1a3f691edfac16abfd8eaa44cdf72208e5fdcfea1c851314e37fff46d89def46, 0x5ca8c10703d97653a90c231244037f6da778a60e7b90c866fc9e8234cba4719
C_p = 0x21f34e4b0c6d82c8fe018a81dab2fa7791addc41307a909bcede640043f9211d, 0x24d412695575320871eac309e74198de60c229fe3693706395c859f64de24157
H = 0x241dbdaa080c3fcbaa2421eec29e8118486b1eee7e769b295564fe1af86bb141, 0x13ba9419b8445c675c727bf6201faa79be892bc078e24f46aae20ac0f49fb4b3
K = 0x1f758dd7c493067b7d288fb37028d6d3f8fe0117a66cf51c40f327defcf4a0cf, 0x11f9990afa4b102507da488d481d5e0cf7fb95c89e5c5c038ee8da2978b4a063

Verify your proof

These are the eight variables that make up a zk-SNARKs proof. You can now pass these eight variables into the verifyTx function in the verifier contract (using the truffle-contract abstraction of the contract using the verifier.json artifact, or whichever mechanism you see fit). If you do have the verifier.json artifact, you can easily verify your proof through truffle console. To start up the Truffle console run:

truffle console --network rinkeby

If the verifier.json file is correct, you should be able to run Verifier.deployed() in the console and it will spit out the artifiact details. Unfortunately, the 8 proof values need to be defined as string arrays, so you need to wrap each value in quotation marks and then wrap those in an array. For example:

truffle(rinkeby)> let A = ["0x5743c0dd166bacb272bf191c57c62a7f9e6fa5350a75ec10d04e951988044e0", "0x1a7c371ef983a24a64f6e0a0895699dd3ee47291a02582ad01575d6f40937389"]

And do this A through K. The verifyTx function actually takes 9 parameters, and this 9th parameter I is an array with 2 elements in it of the form [...publicInputs, ...outputs]. Say the public input was 5 and you want verifyTx to output 1, you would define I as

truffle(rinkeby)> let I = [5,1]

Finally, to validate your proof to the network, you run:

truffle(rinkeby)> Verifier.deployed().then(instance => { return instance.verifyTx(A, A_p, B, B_p, C, C_p, H, K, I) })

Note, if you have trouble at this step and a TypeError is raised, see this issue. If the Verified event is emitted, then you have completed the zero knowledge proof! The role of the Verifier would simply be to observe this transaction, and note that the Verified event was emitted, so the prover must know the secret information to satisfy the original program.



If you would like to arrange a consultation, or would like to hire us to develop a bespoke blockchain solution, visit our website and get in touch.

Extropy.IO

_

Extropy.IO

Written by

Oxford-based blockchain and zero knowledge consultancy and auditing firm

Extropy.IO

_

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade