Key components and tools
zkLLVM Components
The architecture of zkLLVM is modular and extendable. It can be divided into two main components.
Frontends
Frontends parse high-level languages, generate abstract syntax trees (ASTs), and output intermediate representations (IRs) of circuits. Presently, zkLLVM supports C++ and Rust as frontends based on clang
and rustc
, respectively.
clang
is taken from circifier
, which is an extended fork of the original LLVM project. In turn, rustc
is contained within rslang
, which is a similarly extended version of the main source code repository for Rust.
As a result, in comparison to the vanilla LLVM project frontends, zkLLVM frontends generate IRs that are extended with custom types and instructions.
Assigner
assigner
transforms an IR into two products.
- The assignment table
- Arithmetization-specific constraints
Constraints flatten a computation into an arithmetization (e.g., R1CS or PLONK). An arithmitization is a set of constraints imposed by a proof system input type.
It is only necessary to generate constraints once per every circuit/input type pair. In contrast, the assignment table needs to be regenerated whenever dynamic inputs change.
Together, these products ensure that the circuit can be proven with dynamic inputs.
Click here to learn more about assigner extendibility
The core of assigner
is an LLVM IR parser, which can be ported into any programming language. To access its current implementation, click here. This implementation is written in C++ and supports only the PLONK arithmetization.
assigner
can be extended to support any other arithmetization. This process involves implementing the LLVM IR parser and following these steps.
- Find a suitable components library for the chosen arithmetization (such as halo2 for Rust)
- Use the components library to implement assignment table generation
- Use the components library to implement constraints generation and serialization
Note that the zkLLVM frontends output IRs that are modified compared to the IRs generated by the vanilla LLVM project. As a result, any external tool that uses the original LLVMIRParser
from LLVM will be unable to parse IRs from zkLLVM.
When extending assigner
, simply reusing an external parser will not work: it will need to be adapted to fully parse the modified IRs supplied by frontends.
Proof system components
There are two additional components provided by a proof system rather than zkLLVM.
- A prover used for generating circuit-specific proofs
- A verifier used for verifying circuit-specific proofs
By default, zkLLVM uses the circuit components from the blueprint module and the PLONKish arithmetization supported by the Placeholder proof system.
To adapt zkLLVM to other arithmetizations and proof systems, extend the assigner
component as described above.
Depending on the proof system, a verifier can be located either on-chain or off-chain.
Note that the same arithmetization can be used by several different proof systems. As a result, constraints generated by assigner
for one arithmetization can be freely reused across various proof systems.