build: add shared guest program build

This commit is contained in:
Ricardo Guilherme Schmidt 2026-07-02 02:08:27 -03:00 committed by r4bbit
parent 0a120bd42c
commit 38008ace30
No known key found for this signature in database
GPG Key ID: E95F1E9447DC91A9
11 changed files with 201 additions and 7 deletions

4
.dockerignore Normal file
View File

@ -0,0 +1,4 @@
.git
target
programs/*/methods/guest/target
node_modules

View File

@ -125,6 +125,24 @@ jobs:
env:
RISC0_DEV_MODE: 1
build-programs:
name: Build Guest Programs
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- name: Build guest program binaries
run: make build-programs
- name: Check guest program binaries
run: |
set -eu
for manifest in programs/*/methods/guest/Cargo.toml; do
program="$(basename "$(dirname "$(dirname "$(dirname "${manifest}")")")")"
test -s "target/guest/${program}.bin"
done
check-idl:
name: Check IDL
runs-on: ubuntu-latest

2
.gitignore vendored
View File

@ -1,4 +1,2 @@
target/
token/methods/guest/target/
amm/methods/guest/target/
*.bin

View File

@ -32,11 +32,27 @@ RISC0_DEV_MODE=1 cargo test -p twap_oracle_program
# Format
make fmt
# Build the guest ZK binary (requires risc0 toolchain)
# Build all guest ZK binaries
make build-programs
# Build one guest directly only when debugging a single guest build
cargo risczero build --manifest-path programs/<program>/methods/guest/Cargo.toml
```
Built binaries output to: `<program>/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/<program>.bin`
`make build-programs` uses `scripts/build-guests.sh` and
`scripts/build-guests.Dockerfile` to compile every guest crate in one Docker
BuildKit build. Cargo git, registry, and target artifacts are shared through
BuildKit cache mounts. Each raw guest ELF is packaged into the deployable RISC
Zero `.bin` format, and only those final program binaries are exported.
Built binaries output to:
`target/guest/<program>.bin`
Use `RISC0_DOCKER_CONTAINER_TAG` to override the guest builder image tag. Use
`RISC0_BUILD_CACHE_ID` to isolate BuildKit caches for clean benchmarks or
parallel experiments. Use `RISC0_DOCKER_BUILD_NETWORK` to opt in to a custom
Docker build network mode, such as `host`, when the default Docker build
network is not sufficient.
## IDL Generation
@ -77,11 +93,14 @@ strip = "symbols"
That profile is part of program identity. After every release build, inspect the binary and update every value that depends on the ImageID before submitting transactions: deployed program IDs, client/config files, PDA-derived account addresses, AMM `token_program_id` and `twap_oracle_program_id`, and ATA `token_program_id` inputs. Do not mix raw or old ImageIDs with release-profile binaries.
```bash
# Build all guest binaries with the shared BuildKit cache
make build-programs
# Deploy a program binary to the sequencer
wallet deploy-program <path-to-binary>
wallet deploy-program target/guest/<program>.bin
# Inspect the ProgramId of a built binary
spel inspect <path-to-binary>
spel inspect target/guest/<program>.bin
```
## Workspace Structure

9
Cargo.lock generated
View File

@ -3133,6 +3133,15 @@ dependencies = [
"serde",
]
[[package]]
name = "risc0-packager"
version = "0.1.0"
dependencies = [
"anyhow",
"risc0-binfmt",
"risc0-build",
]
[[package]]
name = "risc0-zkos-v1compat"
version = "2.2.2"

View File

@ -17,6 +17,7 @@ members = [
"programs/stablecoin/methods",
"programs/integration_tests",
"tools/idl-gen",
"tools/risc0-packager",
]
exclude = [
"programs/token/methods/guest",

View File

@ -1,4 +1,7 @@
.PHONY: clippy clippy-guest clippy-all test integration-test fmt idl
.PHONY: build-programs clippy clippy-guest clippy-all test integration-test fmt idl
build-programs:
./scripts/build-guests.sh
clippy:
RISC0_SKIP_BUILD=1 cargo clippy --workspace --all-targets -- -D warnings

View File

@ -0,0 +1,44 @@
# syntax=docker/dockerfile:1.7
ARG RISC0_DOCKER_CONTAINER_TAG=r0.1.88.0
FROM risczero/risc0-guest-builder:${RISC0_DOCKER_CONTAINER_TAG} AS build
ARG RISC0_BUILD_CACHE_ID=lez-programs-risc0-guests
WORKDIR /src
COPY . .
ENV CARGO_TARGET_DIR=/src/target/risc0-guests
ENV RISC0_FEATURE_bigint2=""
ENV CC_riscv32im_risc0_zkvm_elf=/root/.risc0/cpp/bin/riscv32-unknown-elf-gcc
ENV CFLAGS_riscv32im_risc0_zkvm_elf="-march=rv32im -nostdlib"
RUN --mount=type=cache,id=${RISC0_BUILD_CACHE_ID}-cargo-git,sharing=locked,target=/root/.cargo/git \
--mount=type=cache,id=${RISC0_BUILD_CACHE_ID}-cargo-registry,sharing=locked,target=/root/.cargo/registry \
--mount=type=cache,id=${RISC0_BUILD_CACHE_ID}-target,sharing=locked,target=/src/target/risc0-guests <<'EOF'
set -eu
target_triple="riscv32im-risc0-zkvm-elf"
programs="amm ata stablecoin token twap_oracle"
unit_separator="$(printf '\037')"
guest_rustflags="-C${unit_separator}passes=lower-atomic${unit_separator}-C${unit_separator}link-arg=-Ttext=0x00200800${unit_separator}-C${unit_separator}link-arg=--fatal-warnings${unit_separator}-C${unit_separator}panic=abort${unit_separator}--cfg${unit_separator}getrandom_backend=\"custom\""
export CARGO_ENCODED_RUSTFLAGS="${guest_rustflags}"
for program in ${programs}; do
manifest="programs/${program}/methods/guest/Cargo.toml"
echo "==> Building ${program}"
cargo +risc0 build --release --locked --target "${target_triple}" --manifest-path "${manifest}"
done
mkdir -p /guest-output
unset CARGO_ENCODED_RUSTFLAGS
cargo +risc0 build --locked -p risc0-packager
packager="${CARGO_TARGET_DIR}/debug/risc0-packager"
for program in ${programs}; do
elf="${CARGO_TARGET_DIR}/${target_triple}/release/${program}"
"${packager}" "${elf}" "/guest-output/${program}.bin"
done
EOF
FROM scratch AS export
COPY --from=build /guest-output /

34
scripts/build-guests.sh Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
risc0_tag="${RISC0_DOCKER_CONTAINER_TAG:-r0.1.88.0}"
cache_id="${RISC0_BUILD_CACHE_ID:-lez-programs-risc0-guests}"
network_mode="${RISC0_DOCKER_BUILD_NETWORK:-}"
staging_dir="$(mktemp -d)"
cleanup() {
rm -rf "${staging_dir}"
}
trap cleanup EXIT
network_args=()
if [[ -n "${network_mode}" ]]; then
network_args=(--network "${network_mode}")
fi
DOCKER_BUILDKIT=1 docker build ${network_args[@]+"${network_args[@]}"} \
--build-arg "RISC0_DOCKER_CONTAINER_TAG=${risc0_tag}" \
--build-arg "RISC0_BUILD_CACHE_ID=${cache_id}" \
--output="${staging_dir}" \
-f "${repo_root}/scripts/build-guests.Dockerfile" \
"${repo_root}"
out_dir="${repo_root}/target/guest"
rm -rf "${out_dir}"
mkdir -p "${out_dir}"
for program in amm ata stablecoin token twap_oracle; do
rm -rf "${repo_root}/programs/${program}/methods/guest/target"
install -m 0644 "${staging_dir}/${program}.bin" "${out_dir}/${program}.bin"
done

View File

@ -0,0 +1,12 @@
[package]
name = "risc0-packager"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1"
risc0-binfmt = "=3.0.4"
risc0-build = "=3.0.5"
[lints]
workspace = true

View File

@ -0,0 +1,52 @@
use std::{env, ffi::OsString, fs, path::PathBuf};
use anyhow::{bail, Context, Result};
use risc0_binfmt::ProgramBinary;
fn main() -> Result<()> {
let mut args = env::args_os();
let command = args
.next()
.unwrap_or_else(|| OsString::from("risc0-packager"));
let input = required_arg(&mut args, &command, "guest ELF")?;
let output = required_arg(&mut args, &command, "output .bin")?;
if args.next().is_some() {
bail!(
"usage: {} <guest-elf> <output-bin>",
command.to_string_lossy()
);
}
if let Some(parent) = output
.parent()
.filter(|parent| !parent.as_os_str().is_empty())
{
fs::create_dir_all(parent)
.with_context(|| format!("failed to create {}", parent.display()))?;
}
let user_elf =
fs::read(&input).with_context(|| format!("failed to read {}", input.display()))?;
let kernel_elf = risc0_build::GuestOptions::default().kernel();
let binary = ProgramBinary::new(&user_elf, &kernel_elf);
fs::write(&output, binary.encode())
.with_context(|| format!("failed to write {}", output.display()))?;
Ok(())
}
fn required_arg(
args: &mut impl Iterator<Item = OsString>,
command: &OsString,
name: &str,
) -> Result<PathBuf> {
match args.next() {
Some(value) => Ok(PathBuf::from(value)),
None => bail!(
"missing {name}; usage: {} <guest-elf> <output-bin>",
command.to_string_lossy()
),
}
}