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

feat(raiko): generalized build pipeline for ZkVMs guests #133

Merged
merged 44 commits into from
May 4, 2024

Conversation

CeciliaZ030
Copy link
Contributor

@CeciliaZ030 CeciliaZ030 commented Apr 29, 2024

Problem:

  • Each ZkVM has a different build pipeline and elf loading workflow. It's difficult to manage the pipeline with upstream updates, plus that we even have to make PR just to use desirable features / flags
  • We want to compile unittests directly with #[test], which means we need to compile Rust's test harness into risv32 elf
  • Also we want to customize the compilation of benchmarks.

Solution

I'm gonna make the build pipeline myself so that I don't want to bother about the upstream, and also to test and benchmark my code however i want. also. Here are some of the design considerations:

  • In embedded system compilation, you need the correct target & toolchain for cargo and rustc, under the hood you might need to swap out gcc or llvm, which means you need to set a bunch of flags and args that go into the compilers.

  • Currently most ZkVM build the guest in build.rs, and gen proofs & verify in main.rs, which means these are the processes actually being run:

    1. compiling build.rs, resulting in binary target/debug/build/hello-world-b4166fd/build-script-build
    2. run build-script-build, which invokes another cargo process that builds the guest ELF; here's how the command can look like:
    cd ../guest & CC="/home/ubuntu/.local/share/cargo-risczero/cpp/bin/riscv32-unknown-elf-gcc" CFLAGS_riscv32im_risc0_zkvm_elf="-march=rv32im -nostdlib" RUSTC="/home/ubuntu/.rustup/toolchains/risc0/bin/rustc" "cargo" "test" "--target" "riscv32im-risc0-zkvm-elf" "--no-run" "--manifest-path" "/home/ubuntu/xxx/hello-world/methods/guest/Cargo.toml" "--target-dir" "/home/ubuntu/xxx/hello-world/methods/guest" "--release"
    
    1. the ELF is produced, we enter main.rs which loads the ELF into our VM runtime and generates a proof.

I'm making the build stage explicit, which means having a builder crate to replace build.rs. Within the pipeline, there are two steps: 1) configure the right flags and env to generate a correct command 2) execute the command and place the ELFs in some user-specified directories. Resulting file structure:

example
├── Cargo.lock
├── Cargo.toml
├── builder
│   ├── Cargo.toml
│   └── src
├── driver
│   ├── Cargo.toml
│   └── src
├── guest
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── methods
│   ├── src
│   └── target
└── target

Workflow

Build the ELFs

$ cd builder & cargo build --features <zkvm>

Run the ELFs

$ cd driver & cargo +nightly run --release --features <zkvm>

Builder

The resultant pipeline starts with parsing the Cargo manifest, then setup the Builder with the right flags.

let meta = parse_metadata(project);
let mut builder = GuestBuilder::new(&meta, "riscv32im-risc0-zkvm-elf", "risc0")
    .rust_flags(&[
        "passes=loweratomic",
        "link-arg=-Ttext=0x00200800",
        "link-arg=--fatal-warnings",
        "panic=abort",
    ])
    .cc_compiler(
        risc0_data()
            .unwrap()
            .join("cpp/bin/riscv32-unknown-elf-gcc"),
    )
    .c_flags(&["-march=rv32im", "-nostdlib"]);

The executor runs the desired action (e.g. cargo build or cargo test) and finally place the ELF to specified dest directory.

let executor = if !test {
    builder.build_command(profile, bins)
} else {
    builder.test_command(profile, bins)
};

executor
    .execute()
    .expect("Execution failed")
    .risc0_placement(dest)
    .expect("Failed to export Ris0 artifacts");

Test Harness

I implemented a light-weight harness to build test suits and run them in the ELF's main. The main with normal cargo test compilation is an alternative entrypoint is created that collide with the ZkVM entrypoint. The resulted test workflow looks like this:

// In program/src/main.rs
#![no_main]
sp1_zkvm::entrypoint!(main);
use harness::{zk_suits, zk_test, TestSuite};
pub fn main() {
    println!("Hello from example");

    #[cfg(test)]
    zk_suits!(test_ok, test_fail);
}

#[test]
pub fn test_ok() {
    assert_eq!(1, 1);
}

#[test]
pub fn test_fail() {
    assert_eq!(1, 2);
}

After you write tests in the guest program, compile them:

// In build.rs
pipeline::sp1::tests("../example-sp1", &["example", "foo"]);

This will gives you binaries equivalent to cargo test --no-run --bin example -- bin foo. Then in the host:

#[test]
fn test_example() {
    // Generate the proof for the given program.
    let mut client = ProverClient::new();
    let stdin = SP1Stdin::new();
    let (pk, vk) = client.setup(TEST_EXAMPLE);
    let proof = client.prove(&pk, stdin).expect("proving failed");
    client.verify(&proof, &vk).expect("verification failed");
}

@CeciliaZ030 CeciliaZ030 changed the title Universal build pipeline for ZkVM Universal build pipeline for ZkVMs guests Apr 29, 2024
@CeciliaZ030 CeciliaZ030 changed the title Universal build pipeline for ZkVMs guests Generalized build pipeline for ZkVMs guests Apr 29, 2024
@CeciliaZ030 CeciliaZ030 changed the title Generalized build pipeline for ZkVMs guests feat(raiko): generalized build pipeline for ZkVMs guests May 2, 2024
pipeline/src/lib.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@Brechtpd Brechtpd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be merging this one already, but a few things to follow up in future PRs:

  • Update CI/scripts to use the same scripts/makefile (with perhaps extra flags for special CI logic).
  • maybe gitignoring the generated files because they are big and potentially hard to keep up to date (though difficulties with CI right now with not checking them in so will have to see for this)
  • some other minor things

The makefile approach already available locally for building so people can already get used to that, but still needs to be extended for installing everything as well.

@Brechtpd Brechtpd merged commit 9cebd36 into taiko/unstable May 4, 2024
8 checks passed
@Brechtpd Brechtpd deleted the build-pipeline branch May 4, 2024 23:33
@CeciliaZ030
Copy link
Contributor Author

CeciliaZ030 commented May 5, 2024

  • some other minor things

? dude what's the point of making this a point lol

@Brechtpd
Copy link
Contributor

Brechtpd commented May 5, 2024

  • some other minor things

? dude what's the point of making this a point lol

Minor things that I can't be bothered to type out. 🦆

quangtuyen88 pushed a commit to quangtuyen88/raiko that referenced this pull request May 6, 2024
update path for chmod default.json

Added step install nodejs

feat(raiko): enable kzg blob check (taikoxyz#148)

* feat(raiko): enable kzg blob check

* update build script

* update ci

* fix ci

* keep fixing ci

* keep fixing ci - sgx docker build

* fix ci risc0

* update ci env

feat(raiko): generalized build pipeline for ZkVMs guests (taikoxyz#133)

* init + some taiko_util -> util

* pipeline

* example

* r0 bins & tests done

* exmple-sp1 done

* gen proof with unittests

* test harness for sp1 doen + split the builder and driver

* make example a workspace

* change dest param

* harness upgrade

* image id compute

* cleanup

* pipeline api change

* r0 switch builder-driver

* r0 switch builder-driver

* sp1 prover -> driver rename

* sp1 builder

* cargo check passed

* name changes

* commented out sp1 & ris0 flags in irrelavent setup

* fixes + makefile

* update

* clean up

* update CI r0

* update sp1

* add builder to workspace

* sp1 CI permission denied

* add example test in guest + clippy

* add test to CI

* fmt & fix CI bug

* test with --release + toolchain

* fix

* fix docker

* add CC support for c-kzg

* trait Pipeline impl in specific builder

* fix ci

* download & verify riscv-gcc-prebuilt

* Update pipeline/src/lib.rs

* update script

* Updated readme + fix local building

* cargo: -> cargo::

---------

Co-authored-by: Brechtpd <Brechtp.devos@gmail.com>

fix(lib): temporarily disable kzg check in sgx/sp1 provers (taikoxyz#157)

* temporarily disable kzg check in sgx/sp1 provers

* fix typo

fast on-chain register + change path before clone repo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants