Developers

Verifying RISC Zero Execution In A Stellar Smart Contract

Author

James Bachini

Publishing date

RISC Zero provides an execution environment where we can compute large amounts of data off-chain and then verify the output in a Stellar smart contract. This has the potential to expand the boundaries of decentralized computing, so let’s open up a console window and get verifying.

You’ll need a Unix-based environment. If you are on Windows, try Windows Subsystem for Linux. You’ll also need Rust installed and the stellar CLI.

Let’s install RISC Zero and set up an example program to verify.
curl -L https://risczero.com/install | bash

rzup install

Once we have that up and running, let’s clone the repository at:

git clone https://github.com/jamesbachini/RiscZero-Experiments.git

Within this repository is a RISC Zero application called greater_than that has two parts:

  • The guest crate is the code that will run inside the zkVM
  • The host crate is the code that orchestrates proof generation on your machine

Let’s take a look at the main function within the guest program that runs on the zkVM host:

https://github.com/jamesbachini/RiscZero-Experiments/blob/main/greater_than/methods/guest/src/main.rs

fn main() {

let a: u32 = env::read();

let b: u32 = env::read();

if !(a > b) {

panic!("Condition failed: a is not greater than b"); } env::commit(&1u32);

}

Here we are reading two u32 numeric variables and then checking that a is not greater than b. If it is, we panic; if not, we send back a 1.

We then have the host application to run this, which can be found here: https://github.com/jamesbachini/RiscZero-Experiments/blob/main/greater_than/host/src/main.rs

This will create a proof and output 3 values when run: println!("Seal (hex): {}", hex::encode(seal));println!("Image ID (hex): {}", hex::encode(image_id.as_bytes()));println!("Journal SHA-256 (hex): {}", hex::encode(journal_sha256.as_bytes()));

It’s these values that we need to verify the proof within a Stellar smart contract. Let’s fire up dockerd in the background and then run the code.

sudo dockerd &

RISC0_DEV_MODE=0 cargo run --release

After a couple of minutes, we should get an output something like this:

We now have everything we need to verify the proof in a Stellar smart contract.

Let’s go ahead and clone the Nethermind risc0 verifier contract from here:https://github.com/NethermindEth/stellar-risc0-verifier/

git clone https://github.com/NethermindEth/stellar-risc0-verifier.git

Now let’s build and deploy the contract using the Stellar CLI. We will be deploying to Futurenet as, at the time of writing, the bn254 functions required were not yet rolled out to the Stellar Testnet or Mainnet (launched January 22nd 2026).

Note you’ll need some XLM to send the transactions, which you can get for Futurenet/Testnet here: https://lab.stellar.org/account/fund?$=network$id=futurenet

stellar contract build

stellar contract deploy --wasm target/wasm32v1-none/release/groth16_verifier.wasm --source james --network futurenet

Once deployed, we will be provided with a contractId that we can use to invoke function calls. We will call the selector function, which returns a 4 byte hex code. We will later copy and paste this 4 byte hex code in front of the seal.

stellar contract invoke --network futurenet --id CBY3GOBGQXDGRR4K2KYJO2UOXDW5NRW6UKIQHUBNBNU2V3BXQBXGTVX7 --source-account james -- selector

"73c457ba"
Let’s use this in the main verify function, passing in the sha256 hash of the journal, the image id and the seal with the selector added to the beginning:

stellar contract invoke --network futurenet --id CBY3GOBGQXDGRR4K2KYJO2UOXDW5NRW6UKIQHUBNBNU2V3BXQBXGTVX7 --source-account james --send=yes -- verify --journal 67abdd721024f0ff4e0b3f4c2fc13bc5bad42d0b7851d456d88d203d15aaa450 --image_id 9dee6122483cff892f04913c0d37f8ef1732f438551e204e11e9db56e27d3cd1 --seal 73c457ba0b6ef023f4714919283813c3163a85572756dd49f6413d6f2f8b91c59d6b6e3f1d6b36499f83c4cf1c51834fbd3cd67e116ad054a5497639c876eac501766e9007381ac060c447f25e4e848884533a7782524ce45b5b8aec784a8a201343d30b2da48ab647833bbe10418347723d678ea9267e51e62eb2d2397a4383759251842be67dd0fd700cb76a72d0e7a6e7d8e61376e88840bb5150013a949fe8ff2dd5252afef0ec3b47241cb0bbdf963571c20fbaca1858abd4175c8b6fef5a15fe881ac9a2871d734d27700603ce2c785c5f46f52c7a0196404475361496600a1f9604950eb367003d4c301a45eb32926e1ab9900b155058cc4af1f7392c11b159ec


If the Groth16 proof verifies, you’ll get a null response and no panics. This is the Stellar CLI’s way of printing () (unit) as the verify function returns Result<(),VerifierError>

This basic example should get you up and running. The beauty of this is that you could prove in a single transaction a complex computation involving megabytes of data, which would be impossible to do directly on-chain.

That computation could open up new logic within a decentralized smart contract, creating new possibilities for builders creating the next generation of zero knowledge technology on Stellar.

For more information, check out the following resources: