Writing a Program

Writing a guest program

See the example fibonacci program.

The guest program should be a no_std Rust crate. As long as it is no_std, you can import any other no_std crates and write Rust as you normally would. Import the openvm library crate to use openvm intrinsic functions (for example openvm::io::*).

More examples of guest programs can be found in the benchmarks/programs directory.

Handling I/O

The program can take input from stdin, with some functions provided by openvm::io.

openvm::io::read takes from stdin and deserializes it into a generic type T, so one should specify the type when calling it:

#![allow(unused)]
fn main() {
let n: u64 = read();
}

openvm::io::read_vec will just read a vector and return Vec<u8>.

openvm::io::reveal_bytes32 sets the user public values in the final proof (to be read by the smart contract).

For debugging purposes, openvm::io::print and openvm::io::println can be used normally, but println! will only work if std is enabled.

⚠️ WARNING

The maximum memory address for an OpenVM program is 2^29. The majority of that (approximately 480-500 MB depending on transpilation) is available to the guest program, but large reads may exceed the maximum memory and thus fail.

Rust std library support

OpenVM supports standard Rust written using the std library, with the following limitations that users should be aware of:

  • Standard input (e.g., from console) is not supported. Use the read methods above instead.
  • Standard output and standard error (e.g., println!, eprintln!) are supported and will both print to the host standard output.
  • System randomness calls are supported by default. Important: system randomness requests randomness from the host, and the provided randomness is unvalidated. Users must be aware of this and only use system randomness in settings where this meets their security requirements. In particular, system randomness should not be used for cryptographic purposes.
  • Reading of environmental variables will always return None.
  • Reading of argc and argv will always return 0.

The above applies to the Rust std library. Users should also be aware that when writing a standard Rust program, usage of external crates that use foreign function interfaces (FFI) may not work as expected.

To use the standard library, you must enable the "std" feature in the openvm crate. This is not one of the default features.

Note: If you write a program that only imports openvm in Cargo.toml but does not import it anywhere in your crate, the Rust linker may optimize away the dependency, which will cause a compile error. To fix this, you may need to explicitly import the openvm crate in your code.

When to use std vs no_std

Due to the limitations described above, our general recommendation is that developers should write OpenVM library crates as Rust no_std libraries when possible (see below). Binary crates can generally be written using the standard library, although for more control over the expected behavior, we provide entrypoints for writing no_std binaries.

Writing no_std Rust

OpenVM fully supports no_std Rust. We refer to the Embedded Rust Book for a more detailed introduction to no_std Rust.

no_std library crates

In a library crate, you should add the following to lib.rs to declare your crate as no_std:

#![allow(unused)]
fn main() {
// lib.rs
#![no_std]
}

If you want to feature gate the usage of the standard library, you can do so by adding a "std" feature to your Cargo.toml, where the feature must also enable the "std" feature in the openvm crate:

[features]
std = ["openvm/std"]

To tell Rust to selectively enable the standard library, add the following to lib.rs (in place of the header above):

#![allow(unused)]
fn main() {
// lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
}

no_std binary crates

In addition to declaring a binary crate no_std, there is additional handling that must be done around the main function. First, add the following header to main.rs:

#![allow(unused)]
fn main() {
// main.rs
#![no_std]
#![no_main]
}

This tells Rust there is no handler for the main function. OpenVM provides a separate entrypoint for the main function, with panic handler, via the openvm::entry! macro. You should write a main function in the normal way, and add the following to main.rs:

openvm::entry!(main);

fn main() {
    // Your code here
}

If you want to feature gate the usage of the standard library, you can add

[features]
std = ["openvm/std"]

to Cargo.toml as discussed above. In this case, the main.rs header should be modified to:

#![allow(unused)]
fn main() {
// main.rs
#![cfg_attr(not(feature = "std"), no_main)]
#![cfg_attr(not(feature = "std"), no_std)]
}

and you still need the openvm::entry!(main) line. This tells Rust to use the custom main handler when the environment is no_std, but to use the Rust std library and the standard main handler when the feature "std" is enabled.

Building and running

See the overview on how to build and run the program.