An example of how to use RISC Zero's zkVM to protect an Ethereum smart contract
Caution
This example is outdated. You should instead be looking at risc0-ethereum.
The EvenNumber
contract wraps a uint256
and provides set()
and get()
functions. The contract guarantees that the wrapped number is always even.
To enforce this guarantee, the contract's set()
function requires a ZK proof that the new value is even. The proof is generated by running even-guests/is-even
from within the RISC Zero zkVM.
An example of how to tie everything together is given by the even-cli
tool, which gives us a way to invoke the contract's set()
function from the command line. This tool:
- Uses Bonsai to generate the ZK proof.
- Sends a transaction (with the appropriate calldata) to the contract via an RPC provider.
Example usage is given below.
You'll need foundry and risc0.
$ git clone https://github.com/intoverflow/risc0-on-eth.git
$ cd risc0-on-eth
$ git submodule update --init --recursive
$ forge test -vvvvv
$ anvil -a 1
...
Available Accounts
==================
(0) "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" (10000.000000000000000000 ETH)
Private Keys
==================
(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
...
Listening on 127.0.0.1:8545
We now save our private key into an environment variable. This will allow us to deploy our contracts and submit transactions.
$ export ETH_WALLET_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
First we build the guest:
$ cargo risczero build --manifest-path even-guests/is-even/Cargo.toml
...
ELFs ready at:
ImageID: b5738a9099282bf37dc46cac14865b81dd9dd230467b6c50a78e668458b886b5 - "target/riscv-guest/riscv32im-risc0-zkvm-elf/docker/is_even/is-even"
Once the guest has been built, we can deploy it to Bonsai. This requires that BONSAI_API_URL
and BONSAI_API_KEY
are set:
$ cargo run --release -- \
deploy \
--guest-elf=target/riscv-guest/riscv32im-risc0-zkvm-elf/docker/is_even/is-even
b5738a9099282bf37dc46cac14865b81dd9dd230467b6c50a78e668458b886b5
On success, the tool outputs the guest's Image ID. We save this value to an environment variable. This will allow us to deploy our contracts.
$ export GUEST_IMAGE_ID=b5738a9099282bf37dc46cac14865b81dd9dd230467b6c50a78e668458b886b5
We can optionally test the guest deployment (and our environment variables) at this time:
$ cargo run --release -- \
test-vector \
--image-id=${GUEST_IMAGE_ID} \
--number=12345678
This command given above fetches a Snark receipt from Bonsai and print its contents. The output can be used to create unit tests for the EvenNumber
contract.
This requires that GUEST_IMAGE_ID
and ETH_WALLET_PRIVATE_KEY
are set:
$ forge script --rpc-url http://localhost:8545 --broadcast script/Deploy.s.sol
...
== Logs ==
Guest Image ID is
0xb5738a9099282bf37dc46cac14865b81dd9dd230467b6c50a78e668458b886b5
Deployed RiscZeroGroth16Verifier to 0x5FbDB2315678afecb367f032d93F642f64180aa3
Deployed EvenNumber to 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
...
First, let's query the contract's state:
$ cast call --rpc-url http://localhost:8545 \
0xe7f1725e7734ce288f8367e1bb143e90bb3f0512 \
'get()(uint256)'
0
Now let's update its state. We can do this by submitting a transaction. This requires that BONSAI_API_URL
, BONSAI_API_KEY
, and ETH_WALLET_PRIVATE_KEY
are set:
$ cargo run --release -- \
send-tx \
--image-id=${GUEST_IMAGE_ID} \
--chain-id=31337 \
--rpc-url=http://localhost:8545 \
--contract=e7f1725E7734CE288F8367e1Bb143E90bb3F0512 \
--number=12345678
Lastly, let's query the state one more time to see that the value has been updated:
$ cast call --rpc-url http://localhost:8545 \
0xe7f1725e7734ce288f8367e1bb143e90bb3f0512 \
'get()(uint256)'
12345678