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

Starting expansion of ZKVM rustdocs #275

Merged
merged 15 commits into from
Sep 15, 2022
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ jobs:
- run: cargo check --benches
- run: cargo fmt --all -- --check
- run: cargo sort --workspace --check
- run: cargo doc
16 changes: 11 additions & 5 deletions risc0/zkvm/sdk/rust/build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ fn guest_packages(pkg: &Package) -> Vec<Package> {
.collect()
}

/// Returns all methods associated with the given riscv guest package.
/// Returns all methods associated with the given RISC-V guest package.
fn guest_methods<P>(pkg: &Package, out_dir: P) -> Vec<Risc0Method>
where
P: AsRef<Path>,
Expand Down Expand Up @@ -304,7 +304,7 @@ where
fs::rename(&tmp_dest_base, dest_base.as_ref()).unwrap();
}

// Builds a package that targets the riscv guest into the specified target
// Builds a package targeting the RISC-V guest into the specified target
// directory.
fn build_guest_package<P>(
pkg: &Package,
Expand Down Expand Up @@ -403,13 +403,19 @@ fn build_guest_package<P>(
/// Options defining how to embed a guest package in
/// [`embed_methods_with_options`].
pub struct GuestOptions {
/// The number of po2 entries to generate in the MethodID.
/// The number of entries in the MethodID.
///
/// By default, this value is set to `DEFAULT_METHOD_ID_LIMIT`.
///
/// Successive MethodID entries increase in size by powers of two, so
/// increasing `code_limit` by 1 effectively doubles the limit on code
/// size.
pub code_limit: u32,

/// Features for cargo to build the guest with.
/// Features provided to cargo for building the guest.
pub features: Vec<String>,

/// Enable standard library support
/// Flag for enabling standard library support.
pub std: bool,
}

Expand Down
10 changes: 6 additions & 4 deletions risc0/zkvm/sdk/rust/guest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

The RISC Zero ZKVM's guest-side RISC-V APIs.

Code that is validated by the [RISC Zero zkVM](risc0_zkvm) is run inside the guest. In the minimal case, an entrypoint (the guest's "`main`" function) must be provided by calling [entry]. The code executed when the entrypoint function is called is what will be proven by the zkVM and available for verification later. In almost all practical cases, the guest will want to read private input data using [env::read] and commit public output data using [env::commit]; additional I/O functionality is also available in [mod@env].
Code that is validated by the [RISC Zero zkVM](risc0_zkvm) is run inside the guest. In the minimal case, an entry point (the guest's "`main`" function) must be provided by calling [entry]. It will be proven that code was executed in the zkVM when the entry point function was called, and a receipt of this proof can be verified later.

In almost all practical cases, the guest will want to read private input data using [env::read] and commit public output data using [env::commit]; additional I/O functionality is also available in [mod@env].

[comment]: # (TODO: The following would ideally be a reference to the starter example guest code, not a copy of it)

For example[^starter-ex], the following guest code proves a number is composite by multiplying two unsigned integers, and panicking if either is `1` or if the multiplication overflows:
For example[^starter-ex], the following guest code proves a number is composite by multiplying two unsigned integers, and panicking if either value is `1` or if the multiplication overflows.
```
use risc0_zkvm_guest::env;

Expand All @@ -26,6 +28,6 @@ pub fn main() {
env::commit(&product);
}
```
Notice how [entry] is used to indicate the entrypoint, [env::read] is used to load the two factors, and [env::commit] is used to make their composite product publically available.
Notice that [entry] is used to indicate the entry point, [env::read] is used to load the two factors, and [env::commit] is used to make their composite product publicly available. (Had we wanted simply to share the result back to the host, without associating it with the proof of computation, we would have used the function [env::write]).

[^starter-ex]: The example is based on the [Risc Zero Rust Starter repository](https://github.com/risc0/risc0-rust-starter).
[^starter-ex]: The example is based on the [Risc Zero Rust Starter repository](https://github.com/risc0/risc0-rust-starter).
97 changes: 87 additions & 10 deletions risc0/zkvm/sdk/rust/guest/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ use crate::{align_up, memory_barrier, sha};

#[cfg(not(target_os = "zkvm"))]
// Bazel really wants to compile this file for the host too, so provide a stub.
/// Stub version of risc0_zkvm_platform::rt::host_sendrecv, re-exported for
/// easy access through the SDK.
/// This is a stub version of `risc0_zkvm_platform::rt::host_sendrecv`,
/// re-exported for easy access through the SDK.
pub fn host_sendrecv(_channel: u32, _buf: &[u8]) -> (&'static [u32], usize) {
unimplemented!()
}
Expand All @@ -57,7 +57,7 @@ unsafe impl<T: Send + Sync> Sync for Once<T> {}
pub struct Reader(Deserializer<'static>);

impl Reader {
/// Read private data from the host.
/// Reads private data from the host.
pub fn read<T: Deserialize<'static>>(&mut self) -> T {
T::deserialize(&mut self.0).unwrap()
}
Expand Down Expand Up @@ -95,8 +95,10 @@ pub(crate) fn finalize(result: *mut usize) {
ENV.get().finalize(result);
}

/// Exchanges data with the host, returning the data from the host
/// Exchanges data with the host, returning data from the host
/// as a slice of bytes.
/// See `env::write` for details on passing structured data to the
/// host.
pub fn send_recv(channel: u32, buf: &[u8]) -> &'static [u8] {
ENV.get().send_recv(channel, buf)
}
Expand All @@ -107,28 +109,103 @@ pub fn send_recv_as_u32(channel: u32, buf: &[u8]) -> (&'static [u32], usize) {
ENV.get().send_recv_as_u32(channel, buf)
}

/// Read private data from the host.
/// Reads private data from the host.
///
/// # Examples
/// Values are read in the order in which they are written by the host,
/// as in a queue. In the following example, `first_value` and `second_value`
/// have been shared consecutively by the host via
/// `prover.add_input_u32_slice()` or `prover.add_input_u8_slice()`.
///
/// ```rust, ignore
/// let first_value: u64 = env::read();
/// let second_value: u64 = env::read();
/// ```
/// For ease and clarity, we recommend sharing multiple values between guest and
/// host as a struct. In this example, we read in details about an overdue
/// library book.
/// ```rust, ignore
/// #[derive(Serialize, Deserialize, Debug)]
/// struct LibraryBookDetails {
/// book_id: u64,
/// overdue: bool
/// }
///
/// let book_info: LibraryBookDetails = from_slice(&receipt.get_journal_vec().unwrap()).unwrap();
/// ```
pub fn read<T: Deserialize<'static>>() -> T {
ENV.get().read()
}

/// Write private data to the host.
/// Writes private data to the host.
///
/// # Arguments
///
/// * `data` - serialized data to be made available in host-readable memory.
/// # Example
/// In this example, the value `42` is written and is then
/// accessible to the host via `prover.get_output()`.
/// ```rust, ignore
/// let integer_to_share: u32 = 42;
/// env::write(&integer_to_share);
/// ```
pub fn write<T: Serialize>(data: &T) {
ENV.get().write(data);
}

/// Commit public data to the journal.
/// Commits public data to the journal.
///
/// # Examples
/// In this example, we want to publicly share the results of a private
/// computation, so we commit the value to the journal.
/// ```rust, ignore
/// env::commit(&some_result);
/// ```
/// When committing values to the journal, keep in mind that journal contents
/// must be deserialized from a single vector. If multiple values are to be
/// committed, consider creating a commitment data struct.
/// In our [digital signature example](https://github.com/risc0/risc0-rust-examples/tree/main/digital-signature),
/// we commit message and signature together.
/// ```rust, ignore
/// pub struct SigningRequest {
/// pub passphrase: Passphrase,
/// pub msg: Message,
/// }
///
/// env::commit(&SignMessageCommit {
/// identity: *sha::digest(&request.passphrase.pass),
/// msg: request.msg,
/// });
/// ```
pub fn commit<T: Serialize>(data: &T) {
ENV.get().commit(data);
}

/// Returns the number of processor cycles that have occured since the guest
/// Returns the number of processor cycles that have occurred since the guest
/// began.
///
/// # Examples
/// ```rust, ignore
/// let count = get_cycle_count();
/// ```
/// This function can be used to note how many cycles have elapsed during a
3lkn marked this conversation as resolved.
Show resolved Hide resolved
/// guest operation:
/// ```
/// let count1 = get_cycle_count();
/// doSomething();
/// let count2 = get_cycle_count();
/// let cycles_elapsed = count2 - count1;
/// ```
pub fn get_cycle_count() -> usize {
ENV.get().get_cycle_count()
}

/// Print a message to the debug console.
///
/// # Example
/// ```
/// env::log("This is an example log message");
/// ```
pub fn log(msg: &str) {
// TODO: format! is expensive, replace with a better solution.
let msg = alloc_crate::format!("{}\0", msg);
Expand Down Expand Up @@ -193,7 +270,6 @@ impl Env {
let slice: &mut [u32] = unsafe {
slice::from_raw_parts_mut(memory::COMMIT.start() as _, memory::COMMIT.len_words())
};

// Write the full data out to the host
unsafe {
let desc = IoDescriptor {
Expand All @@ -206,7 +282,7 @@ impl Env {
}

// If the total proof message is small (<= 32 bytes), return it directly
// from the proof, otherwise SHA it and return the hash.
// from the proof. Otherwise, SHA it and return the hash.
if len_words <= 8 {
for i in 0..len_words {
unsafe {
Expand Down Expand Up @@ -236,6 +312,7 @@ impl Env {
sha::finalize();
}

/// Gets the current count of instruction cycles.
fn get_cycle_count(&self) -> usize {
unsafe { GPIO_CYCLECOUNT.as_ptr().write_volatile(0) }
match host_recv(1) {
Expand Down
10 changes: 5 additions & 5 deletions risc0/zkvm/sdk/rust/guest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ pub mod env;
/// Functions for computing SHA-256 hashes.
pub mod sha;

/// Faster than "sha", but delegates to host so should not be trusted
/// to prove anything.
/// `sha_insecure` is faster than `sha`, but delegates to the host, so it should
/// not be trusted to prove anything.
#[cfg(feature = "pure-prove")]
pub mod sha_insecure;

Expand All @@ -40,8 +40,8 @@ extern "C" {
fn _fault() -> !;
}

/// Aborts the guest with the given message. This message should
/// include a trailing \0.
/// Aborts the guest with the given message.
/// This message should include a trailing \0.
pub unsafe fn _fail(msg: &str) -> ! {
use risc0_zkvm_platform::io::GPIO_FAULT;
let ptr = msg.as_ptr();
Expand Down Expand Up @@ -85,7 +85,7 @@ macro_rules! standalone_handlers {
};
}

/// Used for defining a main entrypoint.
/// Used for defining a main entry point.
///
/// # Example
///
Expand Down
33 changes: 27 additions & 6 deletions risc0/zkvm/sdk/rust/guest/src/sha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ static CUR_DESC: CurDesc = CurDesc(UnsafeCell::new(0));

const END_MARKER: u8 = 0x80;

// Chunk size in words for optimized SHA to operate on; all SHA
// Chunk size in words on which the optimized SHA will operate; all SHA
// requests must be a multiple of this size.
const CHUNK_SIZE: usize = 64 / WORD_SIZE;

fn alloc_desc() -> *mut SHADescriptor {
// SAFETY: Single threaded and this is the only place we use CUR_DESC.
// SAFETY: Single-threaded and this is the only place we use CUR_DESC.
unsafe {
let cur_desc = CUR_DESC.0.get();
let ptr = (memory::SHA.start() as *mut SHADescriptor).add(*cur_desc);
Expand All @@ -53,7 +53,7 @@ fn alloc_desc() -> *mut SHADescriptor {

/// Computes a raw digest of the given slice. For compatibility with
/// the SHA specification, the data must already contain the end
/// marker and the trailer
/// marker and the trailer.
pub fn raw_digest(data: &[u32]) -> &'static Digest {
assert_eq!(data.len() % CHUNK_SIZE, 0);
// Allocate fresh memory that's guaranteed to be uninitialized so
Expand All @@ -67,7 +67,7 @@ pub fn raw_digest(data: &[u32]) -> &'static Digest {
}

// Computes a raw digest of the given slice, and stores the digest in
// the given pointer. The digest memory must be uninitilaized.
// the given pointer. The digest memory must be uninitialized.
pub(crate) unsafe fn raw_digest_to(data: &[u32], digest: *mut Digest) {
assert_eq!(data.len() % CHUNK_SIZE, 0);
let type_count = data.len() / CHUNK_SIZE;
Expand All @@ -86,8 +86,8 @@ pub(crate) unsafe fn raw_digest_to(data: &[u32], digest: *mut Digest) {
GPIO_SHA.as_ptr().write_volatile(desc_ptr);
}

// Calculates the number of words of capacity needed, including end
// marker and trailer, to take the SHA hash of len_bytes bytes.
// Calculates the capacity needed as a number of words, including end
// marker and trailer, in order to take the SHA hash of `len_bytes` bytes.
pub(crate) const fn compute_capacity_needed(len_bytes: usize) -> usize {
// Add one for end marker, round up, then 2 words for the 64-bit size.
let len_words = align_up(len_bytes + 1, WORD_SIZE) / WORD_SIZE + 2;
Expand Down Expand Up @@ -128,6 +128,27 @@ pub(crate) fn add_trailer(data: &mut [u32], len_bytes: usize, memtype: MemoryTyp
}

/// Computes the SHA256 digest of a serialized object.
/// Within the RISC Zero zkVM, this is useful in combination with calls to
/// publicly `commit` the SHA digest, in cases where it is desirable to
/// publicly associate data with guest program execution without revealing its
/// contents.
///
/// # Examples
/// ```rust, ignore
/// let message_digest: Digest = sha::digest(&message);
/// ```
/// In our in-progress [voting machine example](https://www.github.com/risc0/risc0-rust-examples/tree/main/voting-machine),
/// we publicly commit a SHA digest of private state parameters:
/// ```rust, ignore
/// env::commit(&FreezeVotingMachineCommit {
/// old_state: *sha::digest(&params.state),
/// new_state: *sha::digest(&result.state),
/// polls_open: polls_open,
/// voter_bitfield: voter_bitfield,
/// count: count,
/// });
/// ```

pub fn digest<T: Serialize>(val: &T) -> &'static Digest {
// If the object to be serialized is a plain old structure in memory, this
// should be a good guess for the allocation needed.
Expand Down
23 changes: 11 additions & 12 deletions risc0/zkvm/sdk/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//! A virtual machine to produces ZK proofs of computation
//! A virtual machine that produces zero-knowledge proofs of computation.
//!
//! The RISC Zero zkVM is a RISC-V virtual machine that produces
//! [zero-knowledge proofs](https://en.wikipedia.org/wiki/Zero-knowledge_proof)
//! of code it executes. By using the zkVM, a cryptographic
//! [Receipt](host::Receipt) is produced which anyone can verify was produced by
//! the zkVM's guest code. No additional information about the code execution
//! (such as, for example, the inputs provided) is revealed by publishing the
//! of code it executes. When programs are executed in the RISC Zero
//! zkVM, a cryptographic [Receipt](host::Receipt) is generated. Anyone with the
//! receipt can verify that it was produced via execution of the zkVM's guest
//! code. No additional information about code execution (such as which inputs
//! were provided to the guest) is revealed by publishing the
//! [Receipt](host::Receipt). A high-level overview of how the zkVM is
//! structured to accomplish this is available in our
//! [Overview of the zkVM](https://www.risczero.com/docs/explainers/zkvm/zkvm_overview)
//! explainer.
//! structured to accomplish this is available in our [Overview of the zkVM](https://www.risczero.com/docs/explainers/zkvm/zkvm_overview).
//!
//! Developers new to RISC Zero are encouraged to get started with our
//! Developers new to RISC Zero are encouraged to begin with our
3lkn marked this conversation as resolved.
Show resolved Hide resolved
//! [Risc Zero Rust Starter repository](https://github.com/risc0/risc0-rust-starter),
//! which provides an example of producing a zero-knowledge proof that a number
//! is composite, along with an introduction to key components of the RISC Zero
//! zkVM.
//! a minimal example program that demonstrates through zero-knowledge proof
//! that a number is composite. The example also introduces key components of
//! the RISC Zero zkVM.

#![cfg_attr(not(feature = "std"), no_std)]

Expand Down