AI OCR Example
Building Zero-Knowledge Applications with Bonsol
This guide walks you through building applications that leverage zero-knowledge proofs on Solana using the Bonsol framework. We'll use an OCR (Optical Character Recognition) example that runs a machine learning model off-chain while maintaining cryptographic accountability on-chain.
How Bonsol Works
Bonsol enables you to move computation off-chain while keeping full transparency and verification on-chain:
Your app calls your Solana program with input data
Your program schedules a ZK job by calling the Bonsol program
Bonsol provers pick up the job, download your ZK program image, execute it, and generate a proof
Bonsol verifies the proof and calls your program back with the results
Your program processes the verified results on-chain
This architecture lets you run expensive computations (like ML inference, complex algorithms, or data processing) off-chain while maintaining the security guarantees of on-chain execution.
Architecture Overview

Step 1: Building the ZK Program
Your ZK program runs inside the RISC0 zkVM. It reads inputs, performs computation, and commits outputs that will be verified on-chain.
Basic Structure
OCR Example
In our OCR example, we read a 28×28 pixel image (packed as bits), run it through a neural network, and output class probabilities:
Key points:
Use
env::read_slice()to read input dataUse
env::commit_slice()to output results that will be available on-chainKeep your program focused and efficient while you're generating proofs of execution
1.1: Building and Deploying
For development, start a local image server:
Your program gets a unique image ID (SHA256 hash) that you'll reference on-chain.
Step 2: Writing the Solana Program
Your Solana program acts as the orchestrator as it receives requests from users and schedules ZK jobs with Bonsol.
2.1: Scheduling a ZK Execution
Key parameters:
execution_id: Unique identifier for this execution (used to derive PDAs)
image_id: The SHA256 hash of your deployed ZK program
InputRef::public(): Marks data as public input (there's also
InputRef::private())tip: Incentivizes provers to pick up your job quickly
CallbackConfig: Tells Bonsol how to call your program back with results
Authorized provers: A list of the public keys of provers that are authorized to run this job. It's recommended to use only trusted/performing provers.
2.2: Receiving Results via Callback
Important: The callback is only invoked if the proof verification succeeds, in production you should verify that callback was called from our program.
2.3: Account Structure
Step 3: Building the Client
The client coordinates PDAs, submits transactions, and polls for results.
3.1: Deriving Program-Derived Addresses
To schedule a ZK job you typically need 3 accounts:
the execution request account: this is where the execution request will be stored, and later picked up by provers
the deployment account: this is the account that informs provers about your program’s deployment (where to download it from, what inputs it takes, etc.).
the result account: This is optional but you’ll typically want to store your ZK program’s output somewhere.
3.2: Submitting an Execution Request
After all the accounts are derived, we schedule a new job calling our program (which in turn will call Bonsol’s program).
3.3: Polling for Results
Since ZK proof generation happens off-chain asynchronously, you need to poll for results:
Complete Flow Example
The entire flow includes the following steps:
User draws digit on canvas → Exports as 98-byte bit array
Client calls
doOcr()→ Creates execution request on-chainSolana program calls Bonsol → Schedules ZK job with image data
Off-chain prover:
Picks up job from Bonsol program
Downloads ZK program image
Executes ML inference in zkVM
Generates proof
Submits proof + output to Bonsol
Bonsol verifies proof → Calls your program's callback
Callback stores results → Writes to
ai_resultaccountClient polls and reads → Displays recognized digit

Best Practices
Execution IDs: Use unique, descriptive identifiers. Include timestamps and user info to avoid collisions:
Tips: Higher tips incentivize faster proof generation. Balance cost vs. speed based on your use case.
Input Data: Keep inputs small when possible, as they’re committed to on-chain transactions.
For large data, consider using hashes and storing data off-chain.
Error Handling: Always handle cases where proofs time out or fail. Provide feedback to users.
Testing: Use devnet for development. Start a local image server for rapid iteration.
Summary
Bonsol lets you build powerful Solana applications that leverage off-chain computation while maintaining on-chain verification:
Write ZK programs that perform complex computations
Deploy them to accessible image servers
Schedule executions from your Solana program
Receive verified results via callbacks
Build UIs that poll for results asynchronously
This pattern works for ML inference, complex algorithms, data processing, and any computation too expensive for on-chain execution but requiring cryptographic accountability.
For now, our proof of concept demo only works with numbers since training was done on the MNIST dataset with numbers only. To expand into other use cases like verifying characters, you simply have to train the AI model with a dataset such as the EMNIST dataset which includes letters, both lowercase and uppercase.
Last updated