ZoKrates is a toolbox for zkSNARKs on Ethereum. It helps you use verifiable computation in your DApp, from the specification of your program in a high level language to generating proofs of computation to verifying those proofs in Solidity.

Background on zkSNARKs

Zero-knowledge proofs (ZKPs) are a family of probabilistic protocols, first described by Goldwasser, Micali and Rackoff in 1985.

One particular family of ZKPs is described as zero-knowledge Succinct Non-interactive ARguments of Knowledge, a.k.a. zkSNARKs. zkSNARKs are the most widely used zero-knowledge protocols, with the anonymous cryptocurrency Zcash and the smart-contract platform Ethereum among the notable early adopters.

For further details we refer the reader to some introductory material provided by the community: [1],[2], [3].


Ethereum runs computations on all nodes of the network, resulting in high costs, limits in complexity, and low privacy. zkSNARKs have been enabling to only verify computations on-chain for a fraction of the cost of running them, but are hard to grasp and work with.

ZoKrates bridges this gap. It helps you create off-chain programs and link them to the Ethereum blockchain, expanding the possibilities for your DApp.


ZoKrates is released under the GNU Lesser General Public License v3.

Getting Started


Remix online IDE

To write your first SNARK program, check out the ZoKrates plugin in the Remix online IDE!

One-line install

We provide a one-line install for Linux, MacOS and FreeBSD:

curl -LSfs | sh


ZoKrates is available on Dockerhub.

docker run -ti zokrates/zokrates /bin/bash

From there on, you can use the zokrates CLI.

From source

You can build ZoKrates from source with the following commands:

git clone
cd ZoKrates
cargo +nightly build --release
cd target/release

Hello ZoKrates!

First, create the text-file root.zok and implement your program. In this example, we will prove knowledge of the square root a of a number b:

def main(private field a, field b) -> (field):
  field result = if a * a == b then 1 else 0 fi
  return result

Some observations:

  • The keyword field is the basic type we use, which is an element of a given prime field.
  • The keyword private signals that we do not want to reveal this input, but still prove that we know its value.

Then run the different phases of the protocol:

# compile
zokrates compile -i root.zok
# perform the setup phase
zokrates setup
# execute the program
zokrates compute-witness -a 337 113569
# generate a proof of computation
zokrates generate-proof
# export a solidity verifier
zokrates export-verifier

The CLI commands are explained in more detail in the CLI reference.

Programming Concepts


Variables can have any name which does not start with a number. Underscores are not allowed in variable names. Variables are mutable, and always passed by value to functions.


Shadowing is not allowed.

def main() -> (field):
    field a = 2
    // field a = 3 <- not allowed
    for field i in 0..5 do
        // field a = 7 <- not allowed
    return a



Functions have their own scope

def foo() -> (field):
    // return myGlobal <- not allowed
    return 42

def main() -> (field):
    field myGlobal = 42
    return foo()


For-loops have their own scope

def main() -> (field):
    field a = 0
    for field i in 0..5 do
        a = a + i
    // return i <- not allowed
    return a


ZoKrates currently exposes two primitive types and two complex types:

Primitive Types


This is the most basic type in ZoKrates, and it represents a positive integer in [0, p - 1] where p is a (large) prime number.

The prime p is set to 21888242871839275222246405745257275088548364400416034343698204186575808495617 as imposed by the pairing curve supported by Ethereum.

While field values mostly behave like unsigned integers, one should keep in mind that they overflow at p and not some power of 2, so that we have:

def main() -> (field):
    field pMinusOne = 21888242871839275222246405745257275088548364400416034343698204186575808495616
    0 - 1 == pMinusOne
    return 1


ZoKrates has limited support for booleans, to the extent that they can only be used as the condition in if ... else ... endif expressions.

You can use them for equality and inequality checks between field values.

Note that while equality checks are cheap, inequality checks should be used wisely as they are orders of magnitude more expensive.

Complex Types

ZoKrates provides two complex types, Arrays and Structs.


ZoKrates supports static arrays, i.e., their length needs to be known at compile time. Arrays can contain elements of any type and have arbitrary dimensions.

The following example code shows examples of how to use arrays:

def main() -> (field):
    field[3] a = [1, 2, 3] // initialize a field array with field values
    a[2] = 4               // set a member to a value
    field[4] b = [42; 4]   // initialize an array of 4 values all equal to 42
    field[4] c = [...a, 4] // initialize an array copying values from `a`, followed by 4
    field[2] d = a[1..3]   // initialize an array copying a slice from `a`
    bool[3] e = [true, true || false, true] // initialize a boolean array
    return a[0] + b[1] + c[2]

Declaration and Initialization

An array is defined by appending [] to a type literal representing the type of the array's elements.

Initialization always needs to happen in the same statement as a declaration, unless the array is declared within a function's signature.

For initialization, a list of comma-separated values is provided within brackets [].

ZoKrates offers a special shorthand syntax to initialize an array with a constant value: [value;repetitions]

The following code provides examples for declaration and initialization:

    field[3] a = [1, 2, 3] // initialize a field array with field values
    bool[13] b = [false; 13] // initialize a bool array with value false

Multidimensional Arrays

As an array can contain any type of elements, it can contain arrays again. There is a special syntax to declare such multi-dimensional arrays, i.e., arrays of arrays. To declare an array of an inner array, i.e., and an array of elements of a type, prepend brackets [size] to the declaration of the inner array. In summary, this leads to the following scheme for array declarations: data_type[size of 1st dimension][size of 2nd dimension]. Consider the following example:

def main() -> (field):

    // Array of two elements of array of 3 elements
    field[2][3] a = [[1, 2, 3],[4, 5, 6]]

    field[3] b = a[0] // should be [1, 2, 3]

    // allowed access [0..2][0..3]
    return a[1][2]

Spreads and Slices

ZoKrates provides some syntactic sugar to retrieve subsets of arrays.


The spread operator ... applied to an array copies the elements of the existing array. This can be used to conveniently compose new arrays, as shown in the following example:

field[3] a = [1, 2, 3]
field[4] c = [...a, 4] // initialize an array copying values from `a`, followed by 4

An array can also be assigned to by creating a copy of a subset of an existing array. This operation is called slicing, and the following example shows how to slice in ZoKrates:

field[3] a = [1, 2, 3]
field[2] b = a[1..3]   // initialize an array copying a slice from `a`


A struct is a composite datatype representing a named collection of variables. The contained variables can be of any type.

The following code shows an example of how to use structs.

struct Bar {
    field[2] c
    bool d

struct Foo {
    Bar a
    bool b

def main() -> (Foo):
    Foo[2] f = [Foo { a: Bar { c: [0, 0], d: false }, b: true}, Foo { a: Bar {c: [0, 0], d: false}, b: true}]
    f[0].a.c = [42, 43]
    return f[0]


Before a struct data type can be used, it needs to be defined. A struct definition starts with the struct keyword followed by a name. Afterwards, a new-line separated list of variables is declared in curly braces {}. For example:

struct Point {
    field x
    field y

Declaration and Initialization

Initialization of a variable of a struct type always needs to happen in the same statement as a declaration, unless the struct-typed variable is declared within a function's signature.

The following example shows declaration and initialization of a variable of the Point struct type:

struct Point {
    field x
    field y

def main() -> (Point):
    Point p = Point {x: 1, y: 0}
    return p


The variables within a struct instance, the so called members, can be accessed through the . operator as shown in the following extended example:

struct Point {
    field x
    field y

def main(field a) -> (Point):
    Point p = Point {x: 1, y: 0}
    p.x = a
    p.y = p.x
    return p


A function has to be declared at the top level before it is called.

def foo() -> (field):
    return 1

def main() -> (field):
    return foo()

A function's signature has to be explicitly provided. Functions can return many values by providing them as a comma-separated list.

def main() -> (field, field[3]):
    return 1, [2, 3, 4]


When defining a variable as the return value of a function, types are optional:

def foo() -> (field, field):
    return 21, 42

def main() -> (field):
    a, b = foo()
    return 1

If there is any ambiguity, providing the types of some of the assigned variables is necessary.

def foo() -> (field, field[3]):
    return 1, [2, 3, 4]

def foo() -> (field, field):
    return 1, 2

def main() -> (field):
    a, field[3] b = foo()
    return 1

Control Flow

ZoKrates provide a single thread of execution with two control flow constructs.

Function calls

Function calls can help make programs clearer and more modular. However, using function calls is not always zero-cost, so deep call chains should be avoided.

Arguments are passed by value.

def incr(field a) -> (field):
    a = a + 1
    return a

def main() -> (field):
    field x = 1
    field res = incr(x)
    x == 1 // x has not changed
    return 1

If expressions

An if-expression allows you to branch your code depending on a condition.

def main(field x) -> (field):
  field y = if x + 2 == 3 then 1 else 5 fi
  return y

The condition supports <, <=, >, >=, ==, which can be combined with the boolean operators &&, || and !.

When it comes to inequality checks, there is a caveat: when executing a < b, both a and b will be asserted to be strictly lower than the biggest power of 2 lower than p/2. This means that a and b are both asserted to be between 0 and 2**252 - 1. The same applies to other inequality checks.

For loops

For loops are available with the following syntax:

def main() -> (field):
    field res = 0
    for field i in 0..4 do
        res = res + i
    return res

The bounds have to be known at compile-time, so only constants are allowed. For-loops define their own scope.


You can separate your code into multiple ZoKrates files using import statements to import symbols, ignoring the .zok extension of the imported file.

Import syntax

Symbol selection

The preferred way to import a symbol is by module and name:

from "./path/to/my/module" import MySymbol

// `MySymbol` is now in scope.


The as keyword enables renaming symbols:

from "./path/to/my/module" import MySymbol as MyAlias

// `MySymbol` is now in scope under the alias MyAlias.


The legacy way to import a symbol is by only specifying a module:

import "./path/to/my/module"

In this case, the name of the symbol is assumed to be main and the alias is assumed to be the module's filename so that the above is equivalent to

from "./path/to/my/module" import main as module

// `main` is now in scope under the alias `module`.

Note that this legacy method is likely to become deprecated, so it is recommended to use the preferred way instead.


Two types of symbols can be imported


Functions are imported by name. If many functions have the same name but different signatures, all of them get imported, and which one to use in a particular call is inferred.

User-defined types

User-defined types declared with the struct keyword are imported by name.

Relative Imports

You can import a resource in the same folder directly, like this:

from "./mycode" import foo

There also is a handy syntax to import from the parent directory:

from "../mycode" import foo

Also, imports further up the file-system are supported:

from "../../../mycode" import foo

Absolute Imports

Absolute imports don't start with ./ or ../ in the path and are used to import components from the ZoKrates standard library. Please check the according section for more details.


Comments can be added with double-slashes.

def main() -> (field):
    field a = 42 // this is an end of line comment
    // this is a full line comment
    return a

Standard library

ZoKrates comes with a number of reusable components which are defined at ./stdlib/ in the ZoKrates root repository. In order to import the standard library as described in the imports section the $ZOKRATES_HOME environment variable needs to be set to the stdlib folder. The standard library is solely based on the ZoKrates DSL and can be easily extended.

The following section highlights a subset of available imports:



import "hashes/sha256/512Padded.zok"

A function that takes 2 field[256] arrays as inputs and returns their sha256 compression function as an array of 256 field elements.


import "hashes/sha256/512bit.zok"

A function that takes 2 field[256] arrays as inputs and returns their sha256 compression function as an array of 256 field elements. The difference with sha256 is that no padding is added at the end of the message, which makes it more efficient but also less compatible with Solidity.

There also is support for 2-round (1024-bit input) and 3-round (1536-bit input) variants, using hashes/1024bit.zok and hashes/1536bit.zok respectively.


import "hashes/sha256/512bitPacked.zok"

A function that takes an array of 4 field elements as inputs, unpacks each of them to 128 bits (big-endian), concatenates them and applies sha256. It then returns an array of 2 field elements, each representing 128 bits of the result.

Public-key Cryptography

Proof of private-key ownership

import "ecc/proofOfOwnership.zok"

Verifies match of a given public/private keypair. Checks if the following equation holds for the provided keypair: pk = sk*G where G is the chosen base point of the subgroup and * denotes scalar multiplication in the subgroup.

Signature verification

import "signatures/verifyEddsa.zok"

Verifies an EdDSA Signature. Checks the correctness of a given EdDSA Signature (R,S) for the provided public key A and message (M0, M1). Check out this python repository for tooling to create valid signatures.

Packing / Unpacking


import "utils/pack/pack128"

Packs 128 field elements as one.


import "utils/pack/unpack128"

Unpacks a field element to 128 field elements.


import "utils/pack/pack256"

Packs 256 field elements as one. Overflows can occur.


import "utils/pack/nonStrictUnpack256"

Unpacks a field element into 256 field elements. Uniqueness of the output is not guaranteed.

ZoKrates Reference

The reference covers the details of various areas of ZoKrates.

Command Line Tool

ZoKrates provides a command line interface. You can see an overview of the available subcommands by running


For each command, you can get the list of expected arguments using --help.


zokrates compile -i /path/to/add.zok

Compiles a .zok source code file into ZoKrates internal representation of arithmetic circuits.

Creates a compiled binary file at ./out. Unless the --light flag is set, a human-readable .ztf file is generated, which displays the compilation output in ZoKrates Text Format.


zokrates compute-witness -a 1 2 3

Computes a witness for the compiled program found at ./out and computes arguments of the program. A witness is a valid assignment of the variables, including the results of the computation. Arguments of the program are passed as a space-separated list with the -a flag, or over stdin with the --stdin flag.

With the --abi flag, arguments are passed in the ZoKrates JSON ABI format described here:

cat arguments.json | zokrates compute-witness --stdin --abi

Creates a witness file at ./witness


zokrates setup

Generates a trusted setup for the compiled program found at ./out.

Creates a proving key and a verifying key at ./proving.key and ./verifying.key. These keys are derived from a source of randomness, commonly referred to as "toxic waste". Anyone having access to the source of randomness can produce fake proofs that will be accepted by a verifier following the protocol.


zokrates export-verifier

Using the verifying key at ./verifying.key, generates a Solidity contract that contains the generated verification key and a public function to verify a solution to the compiled program at ./out.

Creates a verifier contract at ./verifier.sol.


zokrates generate-proof

Using the proving key at ./proving.key, generates a proof for a computation of the compiled program ./out resulting in ./witness.

Returns the proof, for example:

A = 0x45582d7906c967b1fd1cac0aad3efefa526e4cd888b8ecb5907b46c2eb1f781, 0x8158089a63a6aafa4afc3bbfd5ebf392e5ef61d0c5faf2e2445c9112450f29c
A_p = 0x5e4fe0bfa79a571b8918138ee5d7b3d0ad394c9bb8f7d2e1549f7e3c3bab7e9, 0x1708b5ba3d138e433406c792f679ae6902fc9f7c6131305a9a5f826dbe2d71fb
B = [0x34f5c5b7518597452e55a69bf9171a63837a98a1c1c1870b610b2cfe79c4573, 0x18e56afd179d67960db838a8fdb128eb78d5dd2c1ffcd564f9d0dada928ed71f], [0xf160ea8d2dc33b564a45c0998309b4bf5a050cc8f6288793b7401b37d1eb1a2, 0x23ade8ba2c64300b5ff90e18641516407054a21179829252fd87f1bd61a3be34]
B_p = 0xc88b87d45f90da42b9c455da16dad76996ef5b1e859a4f0db7dcef4f7e3b2fd, 0x20ed7c62dd8c6c47506e6db1d4837daa42ae80b931227153054539dcbf6f3778
C = 0x2c230cbffbcb6211d2cf8f434df291a413721e3bef5ada4030d532d14b6ea504, 0x21421565f75429d0922c8cf00b68e4da23c61670e787ce6a5de14a5a86ebdcb0
C_p = 0xce11fe724ce1ce183c15c4f5405d9607d6c769422aa9f62f4868478324a2f5, 0x1e585b35ed22ef32fd70ef960818f1514d1dd94b3517c127e782de24173c69f9
H = 0x2306e74a1a7e318d2d3c40cbea708b0e0b91cd1548c9db6261fc2bd815740978, 0xde538e4e99b0e20e84cdbbd3bc08c37bca0af21edd67faf52bc4027a9b00f7c
K = 0x1868436121f271e9fbf78a8f75bb4077e2d4f208891793fd5b468afc3b05c0e4, 0x1021c3ecb15c3fd7340d4eb5bf446e1ad457020e4f8b7cc82f8af64507a35fbe

Passed to the verifier contract, this proof can be checked. For example, using web3, a call would look like the following:<verifier contract address>).verifyTx(A, A_p, B, B_p, C, C_p, H, K, [...publicInputs, ...outputs])

Where A, ..., K are defined as above (adding brackets and quotes: A = ["0x123", "0x345"]), publicInputs are the public inputs supplied to witness generation and outputs are the results of the computation.

Proving schemes

ZoKrates supports different proving schemes. All of the available schemes rely on the ALT_BN128 curve, which means that they're all compatible with Ethereum.

We identify the schemes by the reference to the paper that introduced them. Currently the options available are:

Name Paper CLI flag Requires libsnark
PGHR13 Here --proving-scheme pghr13 Yes
G16 Here --proving-scheme g16 No
GM17 Here --proving-scheme gm17 Yes

The default proving scheme is G16.

When not using the default, the CLI flag has to be provided for the following commands:

  • setup
  • export-verifier
  • generate-proof

Supporting backends

As shown in the table above, the PGHR13 and GM17schemes require libsnark as a backend, while G16 uses bellman, which is included as the default backend.

To include libsnark in the build, compile ZoKrates from source with the libsnark feature:

cargo +nightly -Z package-features build --release --package zokrates_cli --features="libsnark"

Note, that this is only tested for Linux. If you are on another OS, consider using our Docker container, which includes a libsnark installation.

G16 malleability

When using G16, developers should pay attention to the fact that an attacker, seeing a valid proof, can very easily generate a different but still valid proof. Therefore, depending on the use case, making sure on chain that the same proof cannot be submitted twice may not be enough to guarantee that attackers cannot replay proofs. Mechanisms to solve this issue include:

  • signed proofs
  • nullifiers
  • usage of an ethereum address as a public input to the program
  • usage of non-malleable schemes such as GM17


Passed to the verifier contract, this proof can be checked. For example, using web3, a call would look like the following:<verifier contract address>).verifyTx(A, A_p, B, B_p, C, C_p, H, K, [...publicInputs, ...outputs])

Where A, ..., K are defined as above (adding brackets and quotes: A = ["0x123", "0x345"]), publicInputs are the public inputs supplied to witness generation and outputs are the results of the computation.


ZIR is the intermediate representation ZoKrates uses to represent programs. It is close to R1CS but still encapsulates witness generation.

Note that ZIR is still in development and can change without notice.

When generating R1CS constraints, very large numbers are often used, which can make reading ZIR hard for humans. To mitigate this, ZIR applies an isomorphism when displaying field elements: they are shown as members of the interval [- (p - 1)/2, (p - 1)/2]. In other words, the following mapping is used:

  • elements in [0, (p - 1)/2] map to themselves
  • elements in [(p + 1)/2, p - 1] map to themselves minus p

Therefore, instead of writing p - 1 as:


... in ZIR, we simply write:


ZoKrates ABI

In order to interact programatically with compiled ZoKrates programs, ZoKrates supports passing arguments using an ABI.

To illustrate this, we'll use the following example program:

struct Bar {
    field a

struct Foo {
    field a
    Bar b

def main(private Foo foo, bool[2] bar, field num) -> (field):
    return 42

ABI specification

When compiling a program, an ABI specification is generated and describes the interface of the program.

In this example, the ABI specification is:

    "inputs": [
            "name": "foo",
            "public": false,
            "type": "struct",
            "components": [ 
                    "name": "a",
                    "type": "field"
                    "name": "b",
                    "type": "struct",
                    "components": [
                            "name": "a",
                            "type": "field"
            "name": "bar",
            "public": "true",
            "type": "array",
            "components": {
                "size": 2,
                "type": "bool"
            "name": "num",
            "public": "true",
            "type": "field"
    "outputs": [
            "type": "field"

ABI input format

When executing a program, arguments can be passed as a JSON object of the following form:

        "a": "42",
            "a": "42"

Note that field elements are passed as JSON strings in order to support arbitrary large numbers.


Before running any tests, make sure your ZOKRATES_HOME environment variable is set correctly. It has to point to zokrates_stdlib/stdlib/

Unit tests

In ZoKrates, unit tests comprise of

  • internal tests for all zokrates crates
  • compilation tests for all examples in zokrates_cli/examples. These tests only ensure that the examples compile.
  • compilation + witness-computation tests. These tests compile the test cases, compute a witness and compare the result with a pre-defined expected result. Such test cases exist for
    • The zokrates_core crate in zokrates_core_test/tests
    • The zokrates_stdlib crate in zokrates_stdlib/tests

Unit tests can be executed with the following command:

cargo test --release

Integration tests

Integration tests are excluded from cargo test by default. They are defined in the zokrates_cli crate in and use the test cases specified in zokrates_cli/tests/code.

Before running integration tests:

  1. Make sure your $ZOKRATES_HOME is set correctly

  2. You have solc installed and in your $PATH.

    Solc can conveniently be installed through npm by running

    npm install -g solc
  3. You have an Ethereum node running on localhost with a JSON-RPC interface on the default port 8545 (http://localhost:8545).

Integration tests can then be run with the following command:

cargo test --release -- --ignored

If you want to run unit and integrations tests together, run the following command:

cargo test --release & cargo test --release -- --ignored

Tutorial: Proving knowledge of a hash preimage

Let's jump into ZoKrates by working through a hands-on project together!

We'll implement an operation that's very typical in blockchain use-cases: proving knowledge of the preimage for a given hash digest. In particular, we'll show how ZoKrates and the Ethereum blockchain can be used to allow a prover, let's call her Peggy, to demonstrate beyond any reasonable doubt to a verifier, let's call him Victor, that she knows a hash preimage for a digest chosen by Victor, without revealing what the preimage is.


Make sure you have followed the instructions in the Getting Started chapter and are able to run the "Hello World" example described there.

Computing a Hash using ZoKrates

We will start this tutorial by using ZoKrates to compute the hash for an arbitrarily chosen preimage, being the number 5 in this example.

First, we create a new file named hashexample.zok with the following content:

import "hashes/sha256/512bitPacked" as sha256packed

def main(private field a, private field b, private field c, private field d) -> (field[2]):
    h = sha256packed([a, b, c, d])
    return h

The first line imports the sha256packed function from the ZoKrates standard library.

sha256packed is a SHA256 implementation that is optimized for the use in the ZoKrates DSL. Here is how it works: We want to pass 512 bits of input to SHA256. However, a field value can only hold 254 bits due to the size of the underlying prime field we are using. As a consequence, we use four field elements, each one encoding 128 bits, to represent our input. The four elements are then concatenated in ZoKrates and passed to SHA256. Given that the resulting hash is 256 bit long, we split it in two and return each value as a 128 bit number.

In case you are interested in an example that is fully compliant with existing SHA256 implementations in Python or Solidity, you can have a look at this blog post.

Our code is really just using the sha256packed, returning the computed hash.

Having our problem described in ZoKrates' DSL, we can now continue using ZoKrates for the rest of our workflow.

First, we compile the program into an arithmetic circuit using the compile command.

./zokrates compile -i hashexample.zok

As a next step we can create a witness file using the following command:

./zokrates compute-witness -a 0 0 0 5

Using the flag -a we pass arguments to the program. Recall that our goal is to compute the hash for the number 5. Consequently we set a, b and c to 0 and d to 5.

Still here? Great! At this point, we can check the witness file for the return values:

grep '~out' witness

which should lead to the following output:

~out_0 263561599766550617289250058199814760685
~out_1 65303172752238645975888084098459749904

Hence, by concatenating the outputs as 128 bit numbers, we arrive at the following value as the hash for our selected pre-image : 0xc6481e22c5ff4164af680b8cfaa5e8ed3120eeff89c4f307c4a6faaae059ce10

Prove knowledge of pre-image

For now, we have seen that we can compute a hash using ZoKrates.

Let's recall our goal: Peggy wants to prove that she knows a preimage for a digest chosen by Victor, without revealing what the preimage is. Without loss of generality, let's now assume that Victor chooses the digest to be the one we found in our example above.

To make it work, the two parties have to follow their roles in the protocol:

First, Victor has to specify what hash he is interested in. Therefore, we have to adjust the zkSNARK circuit, compiled by ZoKrates, such that in addition to computing the digest, it also validates it against the digest of interest, provided by Victor. This leads to the following update for hashexample.zok:

import "hashes/sha256/512bitPacked" as sha256packed

def main(private field a, private field b, private field c, private field d) -> (field):
    h = sha256packed([a, b, c, d])
    h[0] == 263561599766550617289250058199814760685
    h[1] == 65303172752238645975888084098459749904
    return 1

Note that we now compare the result of sha256packed with the hard-coded correct solution defined by Victor. The lines which we added are treated as assertions: the verifier will not accept a proof where these constraints were not satisfied. Clearly, this program only returns 1 if all of the computed bits are equal.

So, having defined the program, Victor is now ready to compile the code:

./zokrates compile -i hashexample.zok

Based on that Victor can run the setup phase and export a verifier smart contract as a Solidity file:

./zokrates setup
./zokrates export-verifier

setup creates a verifiation.key file and a proving.key file. Victor gives the proving key to Peggy.

export-verifier creates a verifier.sol contract that contains our verification key and a function verifyTx. Victor deploys this smart contract to the Ethereum network.

Peggy provides the correct pre-image as an argument to the program.

./zokrates compute-witness -a 0 0 0 5

Finally, Peggy can run the command to construct the proof:

./zokrates generate-proof

As the inputs were declared as private in the program, they do not appear in the proof thanks to the zero-knowledge property of the protocol.

ZoKrates creates a file, proof.json, consisting of the three elliptic curve points that make up the zkSNARKs proof. The verifyTx function in the smart contract deployed by Victor accepts these three values, along with an array of public inputs. The array of public inputs consists of:

  • any public inputs to the main function, declared without the private keyword
  • the return values of the ZoKrates function

In the example we're considering, all inputs are private and there is a single return value of 1, hence Peggy has to define her public input array as follows: [1].

Peggy can then submit her proof by calling verifyTx.

Victor monitors the verification smart contract for the Verified event, which is emitted upon successful verification of a transaction. As soon as he observes the event triggered by a transaction from Peggy's public address, he can be sure that Peggy has a valid pre-image for the hash he set in the smart contract.


At this point, you've successfully ran you first zkSNARK on the Ethereum blockchain. Congratulations!

Remember that in this example only two parties were involved. This special case makes it easy to deal with the trust assumptions of zkSNARKs: only Victor was interested in verifying the claim by Peggy, hence he can trust his execution of the setup phase.

In general, multiple parties may be interested in verifying the correctness of Peggy's statement. For example, in the zero-knowledge based cryptocurrency Zcash, each node needs to be able to validate the correctness of transactions. In order to generalize the setup phase to these multi-party use-cases a tricky process, commonly referred to as "trusted setup" or "ceremony" needs to be conducted.

ZoKrates would welcome ideas to add support for such ceremonies!


You can get JavaScript bindings for ZoKrates by running

npm install zokrates-js


Function Description
initialize Loads binding wasm module and returns a promise with ZoKrates provider
compile Compiles source code into ZoKrates internal representation of arithmetic circuits
computeWitness Computes a valid assignment of the variables, which include the results of the computation
setup Generates a trusted setup for the compiled program
exportSolidityVerifier Generates a Solidity contract which contains the generated verification key and a public function to verify a solution to the compiled program
generateProof Generates a proof for a computation of the compiled program


import { initialize } from 'zokrates-js';

function importResolver(location, path) {
  // implement your resolving logic here
  return { 
    source: "def main() -> (): return", 
    location: path 

initialize().then((zokratesProvider) => {
    // we have to initialize the wasm module before calling api functions
    zokratesProvider.compile("def main(private field a) -> (field): return a", "main", importResolver)