/risc0-on-eth

Use RISC Zero's zkVM to protect an Ethereum smart contract

Primary LanguageRustMIT LicenseMIT

risc0-on-eth

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.

Get the tools

You'll need foundry and risc0.

Clone the repo and fetch the dependencies

$ git clone https://github.com/intoverflow/risc0-on-eth.git
$ cd risc0-on-eth
$ git submodule update --init --recursive

Test with forge

$ forge test -vvvvv

Test with anvil

Start the anvil service

$ 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

Build the guest and deploy to Bonsai

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.

Deploy the contracts

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

...

Interact with the contract

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