Compiling Programs

Once you have written an SP1 program, you must compile it to an ELF file that can be executed in the zkVM. The cargo prove CLI tool (downloaded during installation) provides convenient commands for compiling SP1 programs.

Development Builds

WARNING: This may not generate a reproducible ELF which is necessary for verifying that your binary corresponds to given source code.

Use the reproducible build system for production builds.

To build a program while developing, simply run the following command in the crate that contains your SP1 program:

cargo prove build

This will compile the ELF that can be executed in the zkVM and put it in the file elf/riscv32im-succinct-zkvm-elf. The output from the command will look something like this:

[sp1]     Compiling version_check v0.9.4
[sp1]     Compiling proc-macro2 v1.0.86
[sp1]     Compiling unicode-ident v1.0.12
[sp1]     Compiling cfg-if v1.0.0
...
[sp1]     Compiling sp1-lib v1.0.1
[sp1]     Compiling sp1-zkvm v1.0.1
[sp1]     Compiling fibonacci-program v0.1.0 (/Users/username/Documents/fibonacci/program)
[sp1]      Finished `release` profile [optimized] target(s) in 8.33s

Under the hood, this CLI command calls cargo build with the riscv32im-succinct-zkvm-elf target and other required environment variables and flags. The logic for this command is defined in the sp1-build crate.

Advanced Build Options

You can pass additional arguments to the cargo prove build command to customize the build process, like configuring what features are enabled, customizing the output directory and more. To see all available options, run cargo prove build --help. Many of these options mirror the options available in the cargo build command.

Production Builds

For production builds of programs, you can build your program inside a Docker container which will generate a reproducible ELF on all platforms. To do so, just use the --docker flag and optionally the --tag flag with the release version you want to use (defaults to latest). For example:

cargo prove build --docker --tag v1.0.1

To verify that your build is reproducible, you can compute the SHA-512 hash of the ELF on different platforms and systems with:

$ shasum -a 512 elf/riscv32im-succinct-zkvm-elf
f9afb8caaef10de9a8aad484c4dd3bfa54ba7218f3fc245a20e8a03ed40b38c617e175328515968aecbd3c38c47b2ca034a99e6dbc928512894f20105b03a203

Build Script

If you want your program crate to be built automatically whenever you build/run your script crate, you can add a build.rs file inside of script/ (at the same level as Cargo.toml of your script crate) that utilizes the sp1-build crate:

fn main() {
    sp1_build::build_program(&format!("{}/../program", env!("CARGO_MANIFEST_DIR")));
}

The path passed in to build_program should point to the directory containing the Cargo.toml file for your program. Make sure to also add sp1-build as a build dependency in script/Cargo.toml:

[build-dependencies]
sp1-build = "2.0.0"

You will see output like the following from the build script if the program has changed, indicating that the program was rebuilt:

[fibonacci-script 0.1.0] cargo:rerun-if-changed=../program/src
[fibonacci-script 0.1.0] cargo:rerun-if-changed=../program/Cargo.toml
[fibonacci-script 0.1.0] cargo:rerun-if-changed=../program/Cargo.lock
[fibonacci-script 0.1.0] cargo:warning=fibonacci-program built at 2024-03-02 22:01:26
[fibonacci-script 0.1.0] [sp1]    Compiling fibonacci-program v0.1.0 (/Users/umaroy/Documents/fibonacci/program)
[fibonacci-script 0.1.0] [sp1]     Finished release [optimized] target(s) in 0.15s
warning: fibonacci-script@0.1.0: fibonacci-program built at 2024-03-02 22:01:26

The above output was generated by running RUST_LOG=info cargo run --release -vv for the script folder of the Fibonacci example.

Advanced Build Options

To configure the build process when using the sp1-build crate, you can pass a BuildArgs struct to to the build_program_with_args function. The build arguments are the same as the ones available from the cargo prove build command.

As an example, you could use the following code to build the Fibonacci example with the docker flag set to true and a custom output directory for the generated ELF:

use sp1_build::{build_program_with_args, BuildArgs};

fn main() {
    let args = BuildArgs {
        docker: true,
        output_directory: "./fibonacci-program".to_string(),
        ..Default::default()
    };
    build_program_with_args("../program", &args);
}

Note: If you want reproducible builds with the build.rs approach, you should use the docker flag and the build_program_with_args function, as shown in the example above.