Example RISC Zero programs. See README inside each folder on instructions for more details.
An example Data Availability contract is deployed at 0x2Bcbfc02AAa1dB9798179902DeE48F268C8DD3CC
on Optimism. It has been configured to verify data from the erc20-counter-multiple-blocks
program as described below.
This section describes the process for deploying a new Data Availability contract for an entity as well as writing RISC0 programs to verify uploaded data.
The general procedure for this is as follows:
- Write a RISC0 program that, given raw data, calculates changes in the objective function over a given timespan
- Deploy a new Data Availability contract associated with the RISC0 program you've written
- Upload (untrusted) data to the Data Availability contract for a given timestamp
- Use the RISC0 program from above to generate a proof of the data's correctness
- Upload the generated proof to the contract for verification
Each Data Availability contract allows uploaders to submit objective function data for users at arbitrary timestamps; internally, the contract maintains a "signature" of this data for each timestamp through a custom hashing function. After an uploader has uploaded all the data they wish to upload for a particular timestamp, they may optionally verify the data for that timestamp. Each Data Availability contract is associated with a RISC0 Image ID, which is a signature which uniquely specifies a RISC0 program. This RISC0 program is passed raw EVM event data, performs some calculations to determine the objective function values for all relevant users, and then generates a proof journal containing the hash of the calculated data as well as the timestamp associated with the data.
This proof journal is then submitted to the contract for verification, and the provided hash is cross-checked against the hash stored in the contract for the given timestamp. The proof itself is then verified against the on-chain RISC0 contracts. If the hashes match, and the proof verifies correctly, we can be sure that the data previously uploaded to the contract has a signature which matches the signature computed by our RISC0 program (since the Data Availability contract will only positively verify proofs submitted from a RISC0 program whose Image ID matches the one we previously set).
The first step in deploying a Data Availability contract is writing the RISC0 program, since we need to know the Image ID of our program
before deploying a Data Availability contract. See the erc20-counter-multiple-blocks
directory for a complete example of a suitable RISC0 program.
This example fetches a chunk of ERC20 transfers for a token on the Celo network across a specified block range, and computes a proof of the transfer count
of the token per user. It can be invoked in dev mode as follows, from the host
directory:
RISC0_DEV_MODE=true RUST_LOG=info cargo run --release -- --start-block=34304880 --end-block=34304900 --token-address=0x471EcE3750Da237f93B8E339c536989b8978a438
In dev mode, a false proof will be generated, which takes significantly less time than generating a real proof.
If the --verify
argument is passed, the script will also attempt to upload the proof to a Data Availability contract deployed on the Optimism network.
In this case, the following arguments also need to be provided:
optimisim_rpc_url
data_contract_address
eth_private_key
(can also be passed asETH_PRIVATE_KEY
env var)
Each RISC0 program consists of two parts, the host and the guest. The host program can be found in erc20-multiple-blocks/host/src/main.rs
, and the guest
program can be found in erc20-multiple-blocks/methods/guest/src/main.rs
. The host is responsible for fetching the raw data (in this case from Hypersync),
passing it to the guest program, and uploading the proof to the Data Availability contract. The guest program is responsible for performing computation
on the input data and generating the hash.
To adapt this program into a new one, there are essentially two steps:
- Updating the host program to fetch the data required for your computation
- Updating the guest program to compute the correct objective function values based on the raw data
Copy the erc20-counter-multiple-blocks
directory, give it a new name, and update the package names throughout to reference the new name.
Within the guest program, the hash calculation will remain identical; the only difference will be how the mapping from user -> objective function values is
calculated.
Once you've written your RISC0 program, build the project by running cargo build
in the root directory of your project. This will generate
the Image ID of the guest program in contracts/ImageID.sol
.
Once you have the Image ID for the guest program, you can deploy the Data Availability contract. In the divvi-protocol-v0
repo, invoke
the data-availability:deploy
task as follows:
yarn hardhat data-availability:deploy --network op \
--owner-address <DESIRED_OWNER_ADDRESS> \
--image-id <IMAGE_ID> \
--verifier-address 0x0b144e07a0826182b6b59788c34b32bfa86fb711
Fill in the desired owner address and Image ID from the previous step. The verifier address is the address of the RISC0 verifier contract on Optimisim, which does not change for deploys on the same network.
Now that the contract is deployed, you can upload unverified data to it. In the divvi-protocol-v0
repo, see the scripts/dataAvailability/getTokenTransfers.ts
file for
an example that calculates the same data as the example RISC0 program referenced throughout this doc. This script computes the number of ERC20 token transfers for a given
token on Celo over a block range, and optionally uploads it to a Data Availability contract. To run this script with the same parameters as the example RISC0 program
execution from above, you can run:
yarn ts-node scripts/dataAvailability/getTokenTransfers.ts --token-address 0x471EcE3750Da237f93B8E339c536989b8978a438 \
--network celo-mainnet
--start-block 34304880
--end-block 34304900
--output-file out.csv
--data-availability-address=<DATA_AVAILABILITY_CONTRACT_ADDRESS>
--upload
Make sure you expose a MNEMONIC
environment variable to use for uploading the data. The account associated with this mnemonic must have the UPLOADER_ROLE
on the contract.
For a new Data Availability contract, you'll need to copy the general pattern used in the example script.
Now that the contract has been deployed and we've uploaded some unverified data, we can generate a proof for the data and upload it to the contract for verification. In the host directory of your RISC0 project, (assuming the parameters for your host program are the same as the example) we can run:
RUST_LOG=info cargo run --release -- \
--start-block=34304880 \
--end-block=34304900 \
--token-address=0x471EcE3750Da237f93B8E339c536989b8978a438 \
--verify \
--optimisim-rpc-url=https://mainnet.optimism.io \
--data-contract-address=<DATA_AVAILABILITY_CONTRACT_ADDRESS> \
--eth-private-key=<ETH_PRIVATE_KEY>
Note that the eth-private-key
argument is in fact a private key, and not a mnemonic. Again, the account associated with the private key must have the UPLOADER_ROLE
on the contract.
Running this will generate a proof as explained above, and upload it to the contract. The proof journal will attest to both the timestamp of the data as well as the data hash. The contract will verify these against the stored data, and then proceed to verify the proof seal itself using the on-chain RISC0 verifier contracts. This process will take a bit of time to complete. Once this is done, the data has been verified on-chain, and the data for the proven timestamp can no longer be modified on-chain.