zkProver
The design paradigm at Polygon has shifted to developing a zeroknowledge virtual machine that emulates the Ethereum Virtual Machine (EVM).
Proving and verification of transactions in Polygon zkEVM are all handled by a zeroknowledge prover component called the zkProver. All the rules for a transaction to be valid are implemented and enforced in the zkProver.
The zkProver performs complex mathematical computations in the form of polynomials and assembly language which are later verified on a smart contract. Those rules could be seen as constraints that a transaction must follow in order to be able to modify the state tree or the exit tree.
Info
zkProver is a component of the Polygon zkEVM which is solely responsible for Proving.
Interaction with node and database¶
The zkProver mainly interacts with two components, i.e. the Node and the database (DB). Hence, before diving deeper into other components, we must understand the flow of control between zkProver, the Node, and database. Here is a diagram to explain the process clearly.
As depicted in the flow diagram above, the whole interaction works out in 4 steps.

The node sends the content of Merkle trees to the database to be stored there.

The node then sends the input transactions to the zkProver.

The zkProver accesses the database and fetches the info needed to produce verifiable proofs of the transactions sent by the Node. This information consists of the Merkle roots, the keys and hashes of relevant siblings, and more.

The zkProver then generates the proofs of transactions, and sends these proofs back to the Node.
However, this is really the tip of the iceberg in terms of what the zkProver does. There is a lot more detail involved in how the zkProver actually creates these verifiable proofs of transactions. It will be revealed while we dig deeper into state machines below.
State machines¶
The zkProver follows modularity of design to the extent that, except for a few components, it is mainly a cluster of state machines. It has a total of thirteen (13) state machines;

The Main state machine.

Secondary state machines; Binary SM, Storage SM, Memory SM, Arithmetic SM, Keccak Function SM, PoseidonG SM.

Auxiliary state machines; PaddingPG SM, PaddingKK SM, Bits2Field SM, Memory Align SM, Byte4 SM, ROM SM.
Due to the modular design of zkProver, the Main state machine can delegate as many of tasks as possible to other specialist state machines. This heavily improves the efficiency of Main SM.
Secondary state machines¶
The Main SM executor directly instructs each of the secondary state machines by sending appropriate instructions called Actions, depicted in the below diagram.
The grey boxes are not state machines but indicate Actions, which are specific instructions from the Main state machine to the relevant Secondary state machine. These instructions dictate how a state should transition in a state machine. However, every Action, whether from the generic Main SM or the specific SM, must be supported with a proof that it was correctly executed.
There are some natural dependencies between;

The Storage state machine which uses merkle Trees and the Poseidon state machine, which is needed for computing hash values of all nodes in the Storage’s Merkle Trees.

Each of the hashing state machines, Keccak Function SM and the PoseidonG SM, and their respective padding state machines, i.e. the PaddingKK SM and the PaddingPG SM.
Two novel languages for zkProver¶
The zkProver is the most complex module of zkEVM. It required development of two new programming languages to implement the needed elements; The ZeroKnowledge Assembly language and the Polynomial Identity Language.
It is not surprising that the zkProver uses a language specifically created for the firmware and another for the hardware because adopting the state machines paradigm requires moving from highlevel programming to lowlevel programming.
These two languages, zkASM and PIL, were designed mindful of prospects for broader adoption outside Polygon zkEVM.
Zeroknowledge assembly¶
As an Assembly language, the ZeroKnowledge Assembly (or zkASM) language is specially designed to map instructions from the zkProver’s Main state machine to other state machines. In case of the state machines with firmware, zkASM is the Interpreter for the firmware.
Prescriptive assembly codes are generated by zkASM codes using instructions from the Main state machine to specify how a given SM Executor must carry out calculations. The Executor’s strict adherence to the zkASM codes’ logic and conventions makes computation verification simple.
Polynomial identity language¶
The Polynomial identity language (or PIL) is especially designed for the zkProver. Almost all state machines express computations in terms of polynomials. Therefore, state transitions in state machines must satisfy computationspecific polynomial identities.
Polygon zkEVM is creating the most effective solution to solve the blockchain trilemma of privacy, security, and scalability. And its context is an efficient zeroknowledge commitment scheme. The most reliable and effective commitment schemes tilldate are the Polynomial Commitment Schemes.
Hence, it was convenient to transform calculations into some polynomial language, where verification essentially comes down to verifying whether execution fulfils specific polynomial identities. All PIL codes, in the zkProver’s state machines, therefore form the very DNA of the verifier code.
Microprocessor state machines¶
There are two microprocessortype state machines; the Main SM and the Storage SM. These two state machines have the Firmware and the Hardware part. It is worth noting that each of these Microprocessor SM has its own ROM.
The Firmware part runs the zkASM language to set up the logic and rules, which are expressed in JSON format and stored in a ROM. The JSONfile is then parsed to the specific SM Executor, which executes Storage Actions in compliance with the rules and logic in the JSONfile.
The Hardware component, which uses the Polynomial Identity Language (PIL), defines constraints (or polynomial identities), expresses them in JSON format, and stores them in the accompanying JSONfile. These constraints are parsed to the specific SM Executor, because all computations must be executed in conformance to the polynomial identities.
Info
Although the Main SM and the Storage SM have the same look and feel, they differ considerably.
For example, the Storage SM specialises with execution of Storage Actions (also called SMT Actions), whilst the Main SM is responsible for a wider range of Actions. Nevertheless, the Main SM delegates most of these Actions to specialist state machines. Also, the Storage SM remains secondary as it receives instructions from the Main SM, and not conversely.
Hashing in the zkProver¶
There are two secondary state machines specialising with Hashing ; The Keccak state machine, and the Poseidon state machine. Each of them is an “automised” version of its standard cryptographic hash function.
Keccak state machine¶
The Keccak SM is a gates state machine. It has a set of logic gates (the hardware) and a set of connections between the gates (the logic). It’s a secondary state machine composed of the Keccak SM Hash Generator and the Keccak PIL code, where the latter is for validation purposes.
Poseidon state machine¶
The POSEIDON hash function has been publicised as a zkSTARKfriendly hash function. It is newer than the Keccak hash and thus still under the scrutiny of cryptanalysts. As such, it is bestsuited for the zkProver context.
The Poseidon SM is very straightforward if someone is familiar with the internal mechanism of the original Poseidon hash function.
The hash function’s permutation process translates readily to the state transitions of the POSEIDON state machine. The hash function’s twelve (12) input elements, the nonlinear substitution layers (the Sboxes) and the linear diffusion layers (the MDS matrices), are directly implemented in the state machine.
Although a secondary state machine, the POSEIDON SM receives instructions from both the Main SM and the Storage SM. It has both the executor part and an internal PIL code (a set of verification rules), written in the PIL language.
Proving executioncorrectness¶
The zkProver’s state machines are designed to execute programs, as well as to guarantee that these programs are correctly executed.
Each secondary state machine therefore consists of its own executor and a PIL program that can be used to check correct execution of all the instructions coming from the Main SM Executor.
Here is a stepbystep outline of how the system achieves proof / verification of transactions:
 Represent a given computation as a state machine (SM).
 Express the state changes of the SM as polynomials.
 Capture traces of state changes, called execution traces, as rows of a lookup table.
 Form polynomial identities and/or constraints that these state transitions satisfy.
 Prover uses a specific polynomial commitment scheme to commit and prove knowledge of the committed polynomials.
 Plookup is one of the ways to check if the Prover’s committed polynomials produce correct traces.
While the polynomial constraints are written in the PIL language, the instructions are initially written in zkASM but subsequently expressed and stored in JSON format. Although not all verification involves a Plookup, the diagram below, briefly illustrates the wide role Plookup plays in the zkProver.
Components of zkProver¶
For the sake of simplicity, one can think of the zkProver as being composed of the following four components;

The Executor or the Main state machine executor.

The STARK Recursion Component.

The CIRCOM library.

The zkSNARK Prover.
In the nutshell, the zkProver uses these four components to generates verifiable proofs. As a result, the constraints that each proposed batch must meet are polynomial constraints or polynomial identities. All valid batches must satisfy specific polynomial constraints.
The executor¶
The Executor or Main state machine Executor handles the execution of the zkEVM. This is where EVM Bytecodes are interpreted using a new zeroknowledge Assembly language (or zkASM), specially developed by the Polygon zkEVM team.
It takes as inputs; the transactions, the old and the new states, the ChainID of the Sequencer, to mention a few. The executor also needs;
 The PIL, which is the list of polynomials, the list of the registers.
 The ROM, which stores the list of instructions pertaining to execution.
The Executor sets up the polynomial constraints that every valid batch of transactions must satisfy. Another language, specially developed by the team, called Polynomial Identity Language (or PIL), is used to encode all the polynomial constraints.
The Executor executes all instructions on top of the PIL hardware and generates the committed polynomials; which are the state machine cycles, or a list of all the states. It also generates some public data, which forms part of the input to the zkSNARK verifier.
STARK recursion component¶
Once the Main state machine Executor has converted transactions and related data to committed polynomials, the STARK Recursion component takes the following inputs;
 The committed polynomials.
 The constant polynomials.
 Scripts, which are lists of instructions.
These are taken in order to generate a zkSTARK proof. In an effort to facilitate fast zkSTARK proving, the STARK Recursion Component utilises Fast ReedSolomon Interactive Oracle Proofs of Proximity (RSIOPP), also referred to as FRI, for each zkproof.
The component is referred to as the STARK Recursion, because;

It actually produces several zkSTARK proofs.

Collates them into bundles of a few zkSTARK proofs.

Produces a further zkSTARK proof for each bundle.

The resulting zkSTARK proofs of the bundles are also collated and proved with only one zkSTARK proof.
This way, hundreds of zkSTARK proofs are represented and proved with only one zkSTARK proof.
CIRCOM library¶
The single zkSTARK proof produced by the STARK Recursion Component is the input to a CIRCOM component.
The original CIRCOM paper describes it as both a circuits programming language to define Arithmetic circuits, and a compiler that generates two things;
 A file containing a set of Rank1 Constraints System (or R1CS) constraints associated with an Arithmetic circuit.
 A program (written either in C++ or WebAssembly) for computing a valid assignment to all wires of the Arithmetic circuit, called a witness.
As implemented in the zkProver, CIRCOM takes as input a zkSTARK proof in order to perform two tasks;
 Defining and creating an Arithmetic circuit corresponding to the input zkSTARK proof. This Arithmetic circuit is expressed in terms of its equivalent R1CS.
 Generating the witness, which is in fact a set of valid input, intermediate and output values of the circuit wires satisfying the above R1CS.
zkSNARK prover¶
The last component of the zkProver is the zkSNARK Prover, in particular, Rapid SNARK.
Rapid SNARK is a zkSNARK proof generator, written in C++ and Intel Assembly, which is very fast in generating proofs of CIRCOM’s outputs. With regards to the zkProver, the Rapid SNARK takes as inputs
 The witness from CIRCOM.
 The STARK verifier data, which dictates how the Rapid SNARK must process the data, and then generate a zkSNARK proof.
Strategy to achieving succinctness¶
zkSTARK proofs are used because of their speed, and they require no trusted setup. They are however much larger compared to zkSNARK proofs. It is for this reason, and the succinctness of the zkSNARKs, that the zkProver uses a zkSNARK to attest to the correctness of the zkSTARK proofs. zkSNARKs are therefore published as the validity proofs to state changes. This strategy has huge benefits as it results in gas costs reducing from 5M to 350K.