This tutorial guides you through creating, building, and deploying a zero-knowledge program using Bonsol on Solana. By the end, you'll understand how to create ZK proofs that can be verified on-chain.
Refer to the page for instructions on setting up a local environment.
Start the Local Validator
The validator script builds and deploys necessary Solana programs, including the Bonsol core program and an example callback program (not used in this tutorial).
For the purpose of local development, we will use a local HTTP server to host the ZK program data. This server stores everything in memory, so it will reset when the server is restarted. Open a new terminal and run:
$ cargo run -p local-zk-program-server
Compiling local-zk-program-server v0.4.5 (/home/ubuntu/bonsol/local-zk-program-server)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.56s
Running `target/debug/local-zk-program-server`
Server is running on 0.0.0.0:8080
Writing the ZK program
Let's examine the simple ZK program provided in the repo at bonsol/images/simple/src/main.rs.
# bonsol/images/simple/src/main.rs
use gjson::Kind;
use risc0_zkvm::{guest::{env, sha::Impl},sha::{Digest, Sha256}};
fn main() {
let mut public1 = Vec::new();
env::read_slice(&mut public1);
let publici1 = String::from_utf8(public1).unwrap();
let mut private2 = Vec::new();
env::read_slice(&mut private2);
let privatei2 = String::from_utf8(private2).unwrap();
let valid = gjson::valid(&publici1);
let mut res = 0;
if valid {
let val = gjson::get(&publici1, "attestation");
if val.kind() == Kind::String && val.str() == privatei2 {
res = 1;
}
}
let digest = Impl::hash_bytes(
&[
publici1.as_bytes(),
privatei2.as_bytes(),
].concat(),
);
env::commit_slice(digest.as_bytes());
env::commit_slice(&[res]);
}
This simple program demonstrates private input validation, where only the prover knows the private input, but anyone can verify the result. Here's how the program works:
Reads two inputs
public1: A JSON string with an "attestation" field
private2: A private string to compare against the attestation
Validates if
The public input is valid JSON
The "attestation" field in the JSON matches the private input
Outputs
A cryptographic digest of both inputs
A result (1 for match, 0 for no match)
Building the ZK program
Now that we understand the program, let's build it:
bonsol build --zk-program-path ./images/simple
This compiles the Rust code into a format compatible with the RISC Zero VM and generates a manifest.json file containing:
Next, deploy the program to the local ZK program server and register it on-chain:
$ bonsol deploy url \
--bucket bonsol \
--url http://localhost:8080 \
--post \
--manifest-path ./images/simple/manifest.json
Program available at URL https://localhost:8080/simple2-ec93e0a9592a2f00c177a7fce6ff191019740ff83f589e334153126c02f5772e
Deploying to Solana, which will cost real money. Are you sure you want to continue? (y/n)
y
ec93e0a9592a2f00c177a7fce6ff191019740ff83f589e334153126c02f5772e deployed
💡 Note: When deploying to mainnet, this operation costs SOL to register your program on-chain.
Creating and submitting an execution request
Edit the execution request
Locate the sample execution request template at bonsol/charts/input_files/simple_execution_request.json. Update the template with your specific imageId from the manifest.jsonfile:
Note: Keep this terminal window open as the validator needs to run throughout the tutorial.
Note: Keep this terminal window open as the prover node needs to run throughout the tutorial.
As explained in the page, provers on the network need to fetch the ZK programs and the input data used to generate the proof. The methods used to fetch these resources are stored on-chain by the ZK program developer.
Note: Keep this terminal window open as the prover node needs to run throughout the tutorial.
Important: Take note of the imageId as you'll need it for the next steps. This uniquely identifies your ZK program in the network.