Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve groth16 encode API #179

Merged
merged 11 commits into from
Aug 7, 2024
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [Linux, macOS]
feature: [default]
device: [cpu]
include:
- os: Linux
feature: default
device: cpu
- os: Linux
feature: cuda
device: nvidia_rtx_a5000
- os: macOS
feature: metal
feature: default
device: apple_m2_pro
env:
FEATURE: ${{ matrix.feature }}
Expand Down
6 changes: 3 additions & 3 deletions contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ repository = { workspace = true }
anyhow = "1.0"

[dependencies]
alloy-sol-types = { workspace = true }
alloy = { workspace = true, features = ["sol-types", "contract"] }
anyhow = { workspace = true }
ethers = { version = "2.0", features = ["rustls", "ws"] }
risc0-zkvm = { workspace = true }

[dev-dependencies]
hex = "0.4"
regex = "1.10"
tokio = { version = "1", features = ["macros", "rt"] }
tokio = { workspace = true, features = ["macros", "rt"] }

[lib]
doctest = false
Expand Down
41 changes: 0 additions & 41 deletions contracts/build.rs

This file was deleted.

20 changes: 13 additions & 7 deletions contracts/src/groth16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use alloy_sol_types::SolValue;
use alloy::sol_types::SolValue;
use anyhow::Result;
use risc0_zkvm::{sha::Digestible, Groth16ReceiptVerifierParameters};

/// ABI encoding of the seal.
pub fn abi_encode(seal: Vec<u8>) -> Result<Vec<u8>> {
pub fn abi_encode(seal: impl AsRef<[u8]>) -> Result<Vec<u8>> {
Ok(encode(seal)?.abi_encode())
}

/// encoding of the seal with selector.
pub fn encode(seal: Vec<u8>) -> Result<Vec<u8>> {
/// Encoding of a Groth16 seal by prefixing it with the verifier selector.
///
/// The verifier selector is determined from the first 4 bytes of the hash of the verifier
/// parameters including the Groth16 verification key and the control IDs that commit to the RISC
/// Zero circuits.
///
/// NOTE: Selector value of the current zkVM version is used. If you need to use a selector from a
/// different version of the zkVM, use the [encode_seal] method instead.
pub fn encode(seal: impl AsRef<[u8]>) -> Result<Vec<u8>> {
let verifier_parameters_digest = Groth16ReceiptVerifierParameters::default().digest();
let selector = &verifier_parameters_digest.as_bytes()[..4];
// Create a new vector with the capacity to hold both selector and seal
let mut selector_seal = Vec::with_capacity(selector.len() + seal.len());
let mut selector_seal = Vec::with_capacity(selector.len() + seal.as_ref().len());
selector_seal.extend_from_slice(selector);
selector_seal.extend_from_slice(&seal);
selector_seal.extend_from_slice(seal.as_ref());

Ok(selector_seal)
}

#[cfg(test)]
mod tests {
use anyhow::anyhow;
use ethers::utils::hex;
use regex::Regex;

use super::*;
Expand Down
44 changes: 35 additions & 9 deletions contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,41 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use ethers::prelude::*;

abigen!(
IRiscZeroVerifier,
"$OUT_DIR/IRiscZeroVerifier.sol/IRiscZeroVerifier.json"
);
abigen!(
RiscZeroGroth16Verifier,
"$OUT_DIR/RiscZeroGroth16Verifier.sol/RiscZeroGroth16Verifier.json"
alloy::sol!(
#![sol(rpc, all_derives)]
"src/IRiscZeroVerifier.sol"
);

pub mod groth16;

use anyhow::{bail, Result};
use risc0_zkvm::{sha::Digestible, InnerReceipt};

/// Encode the seal of the given receipt for use with EVM smart contract verifiers.
///
/// Appends the verifier selector, determined from the first 4 bytes of the verifier parameters
/// including the Groth16 verification key and the control IDs that commit to the RISC Zero
/// circuits.
pub fn encode_seal(receipt: &risc0_zkvm::Receipt) -> Result<Vec<u8>> {
let seal = match receipt.inner.clone() {
InnerReceipt::Fake(receipt) => {
let seal = receipt.claim.digest().as_bytes().to_vec();
let selector = &[0u8; 4];
// Create a new vector with the capacity to hold both selector and seal
let mut selector_seal = Vec::with_capacity(selector.len() + seal.len());
selector_seal.extend_from_slice(selector);
selector_seal.extend_from_slice(&seal);
selector_seal
}
InnerReceipt::Groth16(receipt) => {
let selector = &receipt.verifier_parameters.as_bytes()[..4];
// Create a new vector with the capacity to hold both selector and seal
let mut selector_seal = Vec::with_capacity(selector.len() + receipt.seal.len());
selector_seal.extend_from_slice(selector);
selector_seal.extend_from_slice(receipt.seal.as_ref());
selector_seal
}
_ => bail!("Unsupported receipt type"),
};
Ok(seal)
}
6 changes: 2 additions & 4 deletions examples/erc20-counter/apps/src/bin/publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use alloy_primitives::Address;
use anyhow::{ensure, Context, Result};
use clap::Parser;
use erc20_counter_methods::BALANCE_OF_ELF;
use risc0_ethereum_contracts::groth16::encode;
use risc0_ethereum_contracts::encode_seal;
use risc0_steel::{
ethereum::{EthEvmEnv, ETH_SEPOLIA_CHAIN_SPEC},
host::BlockNumberOrTag,
Expand Down Expand Up @@ -134,13 +134,11 @@ async fn main() -> Result<()> {
.await?
.context("failed to create proof")?;
let receipt = prove_info.receipt;
let seal = encode_seal(&receipt)?;

// Create an alloy instance of the Counter contract.
let contract = ICounter::new(args.contract, provider);

// Encode the groth16 seal with the selector and call the increment function of the contract.
let seal = encode(receipt.inner.groth16()?.seal.clone())?;

// Call the increment function of the contract and wait for confirmation.
println!(
"Sending Tx calling {} Function of {:#}...",
Expand Down
7 changes: 3 additions & 4 deletions examples/erc20-counter/script/DeployCounter.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pragma solidity ^0.8.20;
import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";
import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol";
import {ControlID, RiscZeroGroth16Verifier} from "risc0/groth16/RiscZeroGroth16Verifier.sol";
import {RiscZeroCheats} from "risc0/test/RiscZeroCheats.sol";

import {Counter} from "../contracts/Counter.sol";
import {ERC20} from "../contracts/ERC20.sol";
Expand All @@ -30,7 +30,7 @@ import {ERC20} from "../contracts/ERC20.sol";
///
/// See the Foundry documentation for more information about Solidity scripts.
/// https://book.getfoundry.sh/tutorials/solidity-scripting
contract CounterrDeploy is Script {
contract CounterDeploy is Script, RiscZeroCheats {
function run() external {
uint256 deployerKey = uint256(vm.envBytes32("ETH_WALLET_PRIVATE_KEY"));

Expand All @@ -39,8 +39,7 @@ contract CounterrDeploy is Script {
ERC20 toyken = new ERC20("TOYKEN", "TOY", 0);
console2.log("Deployed ERC20 TOYKEN to", address(toyken));

IRiscZeroVerifier verifier = new RiscZeroGroth16Verifier(ControlID.CONTROL_ROOT, ControlID.BN254_CONTROL_ID);
console2.log("Deployed RiscZeroGroth16Verifier to", address(verifier));
IRiscZeroVerifier verifier = deployRiscZeroVerifier();

Counter counter = new Counter(verifier, address(toyken));
console2.log("Deployed Counter to", address(counter));
Expand Down
2 changes: 1 addition & 1 deletion ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ homepage = { workspace = true }
repository = { workspace = true }

[dependencies]
alloy = { workspace = true }
anyhow = { workspace = true }
clap = { version = "4.5", features = ["derive", "env"] }
ethers = { version = "2.0" }
hex = { version = "0.4" }
risc0-ethereum-contracts = { workspace = true }
risc0-zkvm = { workspace = true, features = ["client"] }
23 changes: 6 additions & 17 deletions ffi/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@

use std::io::Write;

use alloy::{primitives::Bytes, sol_types::SolValue};
use anyhow::{Context, Result};
use clap::Parser;
use ethers::abi::Token;
use risc0_ethereum_contracts::groth16::encode;
use risc0_zkvm::{
default_prover, is_dev_mode, sha::Digestible, ExecutorEnv, ProverOpts, VerifierContext,
};
use risc0_ethereum_contracts::encode_seal;
use risc0_zkvm::{default_prover, ExecutorEnv, ProverOpts, VerifierContext};

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
Expand Down Expand Up @@ -54,8 +52,8 @@ pub fn main() -> Result<()> {
fn prove_ffi(elf_path: String, input: Vec<u8>) -> Result<()> {
let elf = std::fs::read(elf_path).unwrap();
let (journal, seal) = prove(&elf, &input)?;
let calldata = vec![Token::Bytes(journal), Token::Bytes(seal)];
let output = hex::encode(ethers::abi::encode(&calldata));
let calldata = vec![Bytes(journal.into()), Bytes(seal.into())];
let output = hex::encode(calldata.abi_encode());

// Forge test FFI calls expect hex encoded bytes sent to stdout
print!("{output}");
Expand All @@ -77,15 +75,6 @@ fn prove(elf: &[u8], input: &[u8]) -> Result<(Vec<u8>, Vec<u8>)> {
.receipt;

let journal = receipt.clone().journal.bytes;

let seal = match is_dev_mode() {
true => {
let mut seal = Vec::new();
seal.extend(vec![0u8; 4]);
seal.extend(receipt.claim()?.digest().as_bytes());
seal
}
false => encode(receipt.inner.groth16()?.seal.clone())?,
};
let seal = encode_seal(&receipt)?;
Ok((journal, seal))
}