This project is allows to generate a Zero Knowledge Proof to be used with BitVMX. For this we have some tools that allows to split the three phases of a ZKP.
- Setup
- Proving
- Verification
This library is currently under development and may not be fully stable. It is not production-ready, has not been audited, and future updates may introduce breaking changes without preserving backward compatibility.
This repo contains a dummy example of a ZKP using RISC0. The proof is inside methods/guest/src
Inside host/src is the enviornment that allows to get the information required for the setup, execute the proof and verifiy it.
The first proof is a Stark, that is later converted into a Snark (groth16).
Currently RISC0 support for Groth16 is only available on x86/x64. Also some of the scripts are aimed to run in linux, and Docker is required to be installed.
This steps where tested on WSL (Ubuntu 20.04) on Windows 11 and also in Azure Standard E4as v4
To make docker to be accesible from wsl follow this instructions
clone git@github.com:FairgateLabs/rust-bitvmx-zk-proof.git
sudo snap install docker
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Option: (1) standard installation
bash (or logout/login to update paths)
sudo apt-get update
sudo apt -y install build-essential
sudo apt -y install pkg-config libssl-dev
curl -L https://risczero.com/install | bash
source ~/.bashrc (to update path)
cargo install cargo-binstall
cargo binstall cargo-risczero --version 2.0.1
rzup install
Running this command will, among other things, builds the guest program.
cargo build --release
Check these resources to better understand the remaining of this section:
- Risc0 article on embed_methods
- build.rs: that generates the
./target/<debug|release>/build/methods-xxx/out/methods.rsfile. This file contains some constants representing the image_id and elf for the guest code (more in this below) - lib.rs: that includes the
methods.rsfile mentioned above, making its constants available to import
During the build process, Risc0 generates the guest's unique and secure identifier (image_id), which is injected in the mentioned methods.rs file as the constant <PKG_NAME>_ID. Using the included guest_sample as an example, the image_id will be made available through the constant BITVMX_ID, since the package name is bitvmx.
During the build process, Risc0 also generates the guest's ELF file, which is injected in the mentioned methods.rs file as the constants <PKG_NAME>_PATH (the elf file path) and <PKG_NAME>_ELF (elf bytes). Using also guest_sample as an example, the elf will be made available through the constants BITVMX_PATH and BITVMX_ELF.
We can check or use the generated constants in two ways:
- We can manually check them in the generated
methods.rs(./target/<debug|release>/build/methods-xxx/out/methods.rs). If you have built theguest_sample, then amethods.rsshould exist containing its image_id and elf constants. - We can define the guest crate as a dependency on a client crate and import the constant. Using the included
guest_sampleas an example, we should define it as a dependency and import the constant withuse guest_sample::<CONSTANT_NAME>;
To dump the guest's image_id to a file for later usage, we can serialize it with cli_serde::serialize_image_id(<PKG_NAME>_ID) (from cli_serde crate in this repository) and then use the output of this call as parameter for the following command:
cargo run --release --bin host -- dump-id -i <serialized_image_id> -o image_id.json
The following command will use the identifier and the expected result as the journal parameter (in this case, the bytes of a 1 in u32 representation)
cargo run --release --bin verifier -- generate-claim -i image_id.json --journal 1,0,0,0
The first step is to generate the stark proof, passing the expected input written in a file (TODO: maybe we can make this more generic so a file is not needed for basic types and alike). Serialising the data or not depends on the use case, and it is the client who decides: it should be the one that, when needed, serialises the data when building the input and provides a guest that deserializes it when reading from env.
To generate the STARK proof, run the following command where:
- <input_file> is the input to the guest program
- <elf_file_path> is the path to the ELF file generated by Risc0 as explained above
cargo run --release --bin host -- prove-stark --input <input_file> --elf <elf_file_path> --output stark-proof.bin
For example, to test the guest_sample provided:
- create an <input_file> with just a number written on it: any input below 100 will output a journal with 1, and zero otherwise. The guest code is not deserialising, so no need to serialize the input.
- find the
guest_sampleELF path as explained above
The second step is to generate the snark proof for the stark proof (for the stark-proof.bin generated on the previous step)
Check running docker works fine. In that case run this command:
cargo run --release --bin host -- prove-snark --input stark-proof.bin --json output.json
If not, try runnign it in this way:
sudo RISC0_WORK_DIR=./ RUST_LOG=debug ./target/release/host prove-snark --input stark-proof.bin --output snark-seal.json
cargo run --release --bin verifier -- verify -i image_id.json --journal 1,0,0,0 --seal snark-seal.json
We have two ways to prepare the proof for its program execution
If the proof will be inserted in the constants.h directly run:
cargo run --release --bin verifier -- template-setup --image-id image_id.json --template ../bitvmx-zk-verifier/templates/constants_template.h -o intermediate.h
and
cargo run --release --bin verifier -- template-proof --journal 1,0,0,0 --seal snark-seal.json -t intermediate.h -o constants.h
Now take note of the path to the constants.h file, as it will be used later on.
If the proof will be provided as input to the program:
cargo run --release --bin verifier -- template-setup --image-id image_id.json --template ../bitvmx-zk-verifier/templates/constants_template.h -o constants.h --zero-proof
and
cargo run --release --bin verifier -- proof-as-input --journal 1,0,0,0 --seal snark-seal.json
Now take note of the printed hex string and the path to the constants.h file, as they will be used later on.
To execute the program, we have to follow two other README files:
- bitvmx-docker-riscv32 to create the Verifier's ELF (
zkverifier-new-mul.elf) that will be run in the BitVMX-CPU. NOTE: if I'm not mistaken, this is a one-time step, no need to re-run it for future program executions - BitVMX-CPU to run the Verifier's ELF (
zkverifier-new-mul.elf) in the CPU with the input that we built in the previous Execution Preparation step.
Below are the steps as a reference, but bear in mind that the up-to-date instructions are in the respective README files mentioned above.
- Clone the BitVMX-CPU repository.
- In
BitVMX-CPUrepo, let's set the verifier:- if not done yet, initialise
docker-riscv32submodule (it points to bitvmx-docker-riscv32) - from
docker-riscv32submodule run./docker-build.sh. This step takes a while, but it is run just once, no need to re-run it again, neither for this Template (image_id) nor for a new one. - copy
constants.shgenerated before todocker-riscv32/verifier, it will be used bydocker-riscv32/verifier/build.shand internally by bitvmx-docker-riscv32. - from
docker-riscv32submodule run./docker-run.sh verifier verifier/build.sh --with-mul. This will create thezkverifier-new-mul.elffile in thedocker-riscv32/verifier/buildfolder. This step is necessary only once for each Template (image_id).
- if not done yet, initialise
- Now depending on the input preparation you did on the Execution Preparation step, you have to run the following:
- Template Proof. Since the input is already in the
constants.hfile, we can directly run the emulator without input.- within
BitVMX-CPUrepository run:cargo run --release -p emulator execute --elf ./docker-riscv32/verifier/build/zkverifier-new-mul.elf --no-hash --debug
- within
- Proof as Input. Now we will use the hex generated on Proof to Input Hex step as input, because
constants.hdoes not contain it.- within
BitVMX-CPUrepository run:cargo run --release -p emulator execute --elf ./docker-riscv32/verifier/build/zkverifier-new-mul.elf --input <output-from-proof-to-input-hex-step> --no-hash --debug
- within
- Template Proof. Since the input is already in the
This project is licensed under the MIT License - see LICENSE file for details.
This repository is a component of the BitVMX Ecosystem, an open platform for disputable computation secured by Bitcoin.
You can find the index of all BitVMX open-source components at FairgateLabs/BitVMX.