Skip to content

Commit

Permalink
Add missing docs/test to the Risc0 adapter (#920)
Browse files Browse the repository at this point in the history
* Document risc0 adapter

* add test for adapter

* Update adapters/risc0/README.md

Co-authored-by: Nikolai Golub <nikolai@sovlabs.io>

* Code review feedback

---------

Co-authored-by: Blazej Kolad <blazejkolad@gmail.com>
Co-authored-by: Nikolai Golub <nikolai@sovlabs.io>
  • Loading branch information
3 people authored Oct 10, 2023
1 parent 41a3408 commit 2ce4d78
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 5 deletions.
4 changes: 4 additions & 0 deletions adapters/risc0/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ sov-rollup-interface = { path = "../../rollup-interface", version = "0.2" }
default = []
native = ["risc0-zkvm/prove", "dep:risc0-zkp", "dep:risc0-circuit-rv32im"]
bench = ["once_cell", "parking_lot", "native", "sov-zk-cycle-utils/native"]

[[test]]
name = "native"
required-features = ["native"]
6 changes: 3 additions & 3 deletions adapters/risc0/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Risc0 Adapter

This package adapts Risc0 version 0.14 to work as a ZKVM for the Sovereign SDK.
This package adapts Risc0 version 0.18 to work as a zkVM for the Sovereign SDK.

## Limitations

Since recursion is not included in the 0.14 release, this adapter is currently limited - individual "slots" may
be proven, but those proofs cannot be recursively combined to facilitate bridging or ultra-fast sync.
Since in-VM recursion is not included in the 0.18 release, this adapter is currently limited. Individual "slots" may
be proven, but those proofs cannot be recursively combined to facilitate bridging or ultra-fast sync ("user recursion" is not supported).

## Warning

Expand Down
9 changes: 9 additions & 0 deletions adapters/risc0/src/guest.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! This module implements the `ZkvmGuest` trait for the RISC0 VM.
#[cfg(not(target_os = "zkvm"))]
use std::ops::DerefMut;

Expand Down Expand Up @@ -64,6 +65,10 @@ impl WordRead for Hints {
}
}

/// A guest for the RISC0 VM. When running in the Risc0 environment, this struct
/// implements the `ZkvmGuest` trait in terms of Risc0's env::read and env::commit functions.
/// When running in any other environment, the struct uses interior mutability to emulate
/// the same functionality.
#[derive(Default)]
pub struct Risc0Guest {
#[cfg(not(target_os = "zkvm"))]
Expand All @@ -73,10 +78,14 @@ pub struct Risc0Guest {
}

impl Risc0Guest {
/// Constructs a new Risc0 Guest
pub fn new() -> Self {
Self::default()
}

/// Constructs a new Risc0 Guest with the provided hints.
///
/// This function is only available outside of Risc0's environment.
#[cfg(not(target_os = "zkvm"))]
pub fn with_hints(hints: Vec<u32>) -> Self {
Self {
Expand Down
7 changes: 7 additions & 0 deletions adapters/risc0/src/host.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! This module implements the `ZkvmHost` trait for the RISC0 VM.
use std::sync::Mutex;

use risc0_zkvm::serde::to_vec;
Expand All @@ -13,6 +14,8 @@ use crate::guest::Risc0Guest;
use crate::metrics::metrics_callback;
use crate::Risc0MethodId;

/// A Risc0Host stores a binary to execute in the Risc0 VM, and accumulates hints to be
/// provided to its execution.
pub struct Risc0Host<'a> {
env: Mutex<Vec<u32>>,
elf: &'a [u8],
Expand All @@ -36,6 +39,7 @@ fn add_benchmarking_callbacks(mut env: ExecutorEnvBuilder<'_>) -> ExecutorEnvBui
}

impl<'a> Risc0Host<'a> {
/// Create a new Risc0Host to prove the given binary.
pub fn new(elf: &'a [u8]) -> Self {
Self {
env: Default::default(),
Expand Down Expand Up @@ -107,6 +111,7 @@ impl<'host> Zkvm for Risc0Host<'host> {
}
}

/// A verifier for Risc0 proofs.
pub struct Risc0Verifier;

impl Zkvm for Risc0Verifier {
Expand Down Expand Up @@ -150,6 +155,8 @@ fn verify_from_slice<'a>(
/// data. This allows us to avoid one unnecessary copy during proof verification.
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Risc0Proof<'a> {
/// The cryptographic data certifying the execution of the program.
pub receipt: InnerReceipt,
/// The public outputs produced by the program execution.
pub journal: &'a [u8],
}
7 changes: 7 additions & 0 deletions adapters/risc0/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#![deny(missing_docs)]
//! # RISC0 Adapter
//!
//! This crate contains an adapter allowing the Risc0 to be used as a proof system for
//! Sovereign SDK rollups.
use risc0_zkvm::sha::Digest;
use serde::{Deserialize, Serialize};
use sov_rollup_interface::zk::Matches;
Expand All @@ -9,6 +14,8 @@ pub mod host;
#[cfg(feature = "bench")]
pub mod metrics;

/// Uniquely identifies a Risc0 binary. Roughly equivalent to
/// the hash of the ELF file.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Risc0MethodId([u32; 8]);

Expand Down
15 changes: 13 additions & 2 deletions adapters/risc0/src/metrics.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
//! Defines utilities for collecting runtime metrics from inside a Risc0 VM
use std::collections::HashMap;

use anyhow::Context;
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use risc0_zkvm::Bytes;

/// A global hashmap mapping metric names to their values.
pub static GLOBAL_HASHMAP: Lazy<Mutex<HashMap<String, (u64, u64)>>> =
Lazy::new(|| Mutex::new(HashMap::new()));

pub fn add_value(metric: String, value: u64) {
/// Increments the requested metric by the given value, creating a
/// new entry in the global map if necessary.
fn add_value(metric: String, value: u64) {
let mut hashmap = GLOBAL_HASHMAP.lock();
hashmap
.entry(metric)
Expand All @@ -19,7 +23,9 @@ pub fn add_value(metric: String, value: u64) {
.or_insert((value, 1));
}

pub fn deserialize_custom(serialized: Bytes) -> Result<(String, u64), anyhow::Error> {
/// Deserialize a `Bytes` into a null-separated `(String, u64)` tuple. This function
/// expects its arguments to match the format of arguments to Risc0's io callbacks.
fn deserialize_custom(serialized: Bytes) -> Result<(String, u64), anyhow::Error> {
let null_pos = serialized
.iter()
.position(|&b| b == 0)
Expand All @@ -31,6 +37,11 @@ pub fn deserialize_custom(serialized: Bytes) -> Result<(String, u64), anyhow::Er
Ok((string, size))
}

/// A custom callback for extracting metrics from the Risc0 zkvm.
///
/// When the "bench" feature is enabled, this callback is registered as a syscall
/// in the Risc0 VM and invoked whenever a function annotated with the [`sov-zk-cycle-utils::cycle_tracker`]
/// macro is invoked.
pub fn metrics_callback(input: risc0_zkvm::Bytes) -> Result<Bytes, anyhow::Error> {
let met_tuple = deserialize_custom(input)?;
add_value(met_tuple.0, met_tuple.1);
Expand Down
24 changes: 24 additions & 0 deletions adapters/risc0/tests/native.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use serde::{Deserialize, Serialize};
use sov_risc0_adapter::host::Risc0Host;
use sov_rollup_interface::zk::{ZkvmGuest, ZkvmHost};

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestStruct {
ints: Vec<i32>,
string: String,
}

#[test]
fn test_hints_roundtrip() {
let hint = TestStruct {
ints: vec![1, 2, 3, 4, 5],
string: "hello".to_string(),
};
let mut host = Risc0Host::new(&[]);

host.add_hint(&hint);

let guest = host.simulate_with_hints();
let received = guest.read_from_host();
assert_eq!(hint, received);
}

0 comments on commit 2ce4d78

Please sign in to comment.