ZoKrates Language Concepts

Share on facebook
Share on twitter
Share on pinterest
Share on linkedin

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.

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.

Variables Types

ZoKrates currently exposes two primitive types and two complex types:

Primitive

field Type

It represents a positive integer in range from 0 to p – 1, where p is the prime number imposed by the pairing curve supported by Ethereum, which is equal to:

21888242871839275222246405745257275088548364400416034343698204186575808495617

field values are unsigned integers which goes to 1 while they exceed the maximum value p or be negative

bool Type

ZoKrates has limited support for booleans, this type can only be used as the condition in if … else … endif expressions.

Complex

array Type

ZoKrates supports static arrays, so that its length needs to be delcared at compile time. Arrays can contain elements of any type and have arbitrary dimensions.

An array can be defined by appending [ ] to a type. Initialization always needs to happen in the same statement as a declaration, unless the array is declared within a function’s signature

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

Here some examples which provide a correct use of the array:

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
    return a[0] + b[1]

To retrieve subsets of arrays ZoKrates provides some tools like the spread operator.

spread operator […]

When this apoerator is applied to an array, it copies the elements of the existing array into a new one. Here an example

field[3] a = [4, 22, 13]
field[4] b = [...a, 7] // initialize an array of 4 values
// three values copied from array a (4,22,13)
// the last value is a integer (7)
slice operator [a..b]

An array can be created by a copy of a subset of an existing array using the operator [a..b] where a is the right bound and b the left one. This operation is called slicing. Here an example:

field[1] a = [13, 42, 53]
field[2] b = a[1..3]   // initialize an array
// copying a slice from 1 element to the 3

multidimensional arrays

An array can contain arrays in itself and so have multi dimension. Each dimension is declare by chaining brackets.

data_type[1st dimension][2nd][3rd]

to declare our values we have to open the brackets each time we have a dimension so in the case of

field[2][3]

we will have 2 values in the first dimension

first dimension   elements of 1st dimension
      ↓                    ↓    ↓
field[2][3] \\ 2 values [  a  , b  ]

and 3 values inside each previous dimension

  second dimension      elements of 2nd dimension
         ↓                 ↓  ↓  ↓     ↓  ↓  ↓
field[2][3] \\ 3 values [[ a, b, c], [ d, e, f]]

the final code will be

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]

struct Type

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

struct data needs to be defined starting 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
}

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. Next step is to initialize the struct type (if you know json syntax, it is very similar), here an example:

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

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

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

Statement

Functions

A function is an istance inside which occurs operations. It has input values and output. In ZoKrates functions require consistent outpus not void

def foo(input type) -> (output type):
    return something

here a real case without any input and an output with field type:

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

Functions can return many values by providing them as a comma-separated list.

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

When you define a variable as the return value of a function you don’t need to specify the type

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

def main() -> (field):
    a, b = foo() // type specification not required
    return 1

but if there are functions with same name but different output it is necessary to specify of variables

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() // we are calling first foo function
   return 1

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.

Conditions

if statement

ZoKrates use if statement to manage conditions allowing to branch different piece of code

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 !.

¡PAY ATTENTION! When executing inequality check like a < b, both a and b will be asserted to be between 0 and (2^252) – 1

Loops

for statement

Loops take in account input values and run into a limit doing your code. loop statement must be closed by keyword end for. Look this pseudo code

for type variable in limit do
    code here
end for

here an example:

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

Scope

Syntax is python-like and works like main new high level program language

Scope in functions

Each function has its own scope. Mind the scope, external variable cannot be called

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

You have to declare it inside your function or get it from input

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

Scope in loop

ZoKrates respects basic rules for scope. Loop has its own scope and like every day, you cannot call a component of its scope outside of it

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

Comments

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

Imports

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

from "./path/to/my/module" import MySymbol
// `MySymbol` is now in scope.

Aliasing

The as keyword enables renaming symbols.

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

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

Legacy

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

All the information are taken from zokrates.github.io

Condividi
Share on facebook
Share on twitter
Share on pinterest
Share on linkedin

Chi è Nils Lewin

Mi chiamo Nicola Bombaci alias Nils Lewin, sono un ingegnere informatico con la passione per la musica e l’audio. Durante la mia crescita professionale ho deciso di fondere questi due mondi apparentemente diversi tra loro.
Articoli Correlati

Lascia una risposta

Iscriviti alla newsletter

Resta sempre aggiornato sulle ultime novità