diff --git a/Dockerfile.sp1 b/Dockerfile.sp1 new file mode 100644 index 00000000000..84c72be3e49 --- /dev/null +++ b/Dockerfile.sp1 @@ -0,0 +1,154 @@ +# Stage 0: Build WASM brotli using emsdk (runs in parallel via BuildKit) +FROM debian:bookworm-slim AS brotli-wasm-builder +WORKDIR /workspace +RUN apt-get update && \ + apt-get install -y cmake make git lbzip2 python3 xz-utils && \ + git clone https://github.com/emscripten-core/emsdk.git && \ + cd emsdk && \ + ./emsdk install 3.1.7 && \ + ./emsdk activate 3.1.7 +COPY scripts/build-brotli.sh scripts/ +COPY brotli brotli +RUN cd emsdk && . ./emsdk_env.sh && cd .. && ./scripts/build-brotli.sh -w -t /workspace/install/ + +FROM scratch AS brotli-wasm-export +COPY --from=brotli-wasm-builder /workspace/install/ / + +# Stage 1: Full SP1 build environment and build +FROM ubuntu:24.04 AS sp1-builder +WORKDIR /workspace + +ENV DEBIAN_FRONTEND=noninteractive + +# System packages (including LLVM 15 from Ubuntu's own repos) +RUN apt-get update && apt-get install -y \ + build-essential cmake git curl wget pkg-config \ + clang-14 lld-14 wabt protobuf-compiler \ + llvm-15-dev libclang-common-15-dev \ + libstdc++-12-dev zlib1g-dev \ + libzstd-dev libxml2-dev libffi-dev \ + software-properties-common gpg \ + python3 xz-utils \ + ca-certificates && \ + ln -s /usr/bin/wasm-ld-14 /usr/local/bin/wasm-ld && \ + rm -rf /var/lib/apt/lists/* + +# LLVM 21 (for wasmer LLVM backend used by sp1-builder) +RUN curl --retry 3 --proto '=https' --tlsv1.2 -sSfL \ + https://github.com/wasmerio/llvm-custom-builds/releases/download/21.x/llvm-linux-amd64.tar.xz \ + -o /tmp/llvm.tar.xz && \ + mkdir -p /opt/llvm-21 && \ + tar xf /tmp/llvm.tar.xz --strip-components=1 -C /opt/llvm-21 && \ + rm /tmp/llvm.tar.xz +ENV LLVM_SYS_211_PREFIX=/opt/llvm-21 +ENV PATH="/opt/llvm-21/bin:${PATH}" + +# Node.js 24 + yarn +RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \ + apt-get install -y nodejs && \ + npm install -g yarn && \ + rm -rf /var/lib/apt/lists/* + +# Foundry +RUN curl -L https://foundry.paradigm.xyz | bash && \ + /root/.foundry/bin/foundryup -i 1.0.0 +ENV PATH="/root/.foundry/bin:${PATH}" + +# Go 1.25 +RUN curl -L https://golang.org/dl/go1.25.8.linux-amd64.tar.gz | tar -C /usr/local -xzf - +ENV PATH="/usr/local/go/bin:${PATH}" +ENV GOPATH="/root/go" +ENV PATH="${GOPATH}/bin:${PATH}" + +# Rust 1.93.0 +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ + sh -s -- -y --default-toolchain 1.93.0 --target x86_64-unknown-linux-gnu,wasm32-unknown-unknown,wasm32-wasip1 +ENV PATH="/root/.cargo/bin:${PATH}" + +# cbindgen +RUN cargo install --force cbindgen --version 0.29.2 + +# SP1 toolchain (succinct Rust toolchain + RISC-V C compiler) +RUN curl -L https://sp1up.succinct.xyz | bash && \ + /root/.sp1/bin/sp1up -v v6.0.0-beta.1 && \ + /root/.sp1/bin/sp1up -c +ENV PATH="/root/.sp1/bin:${PATH}" +ENV RISCV_GNU_TOOLCHAIN="/root/.sp1/riscv" + +# -- Dependency layers (for better caching) -- + +# Go module download +COPY go.mod go.sum ./ +COPY go-ethereum/go.mod go-ethereum/go.sum go-ethereum/ +RUN go mod download + +# Copy full repo +COPY . ./ + +# WASM brotli from stage 0 +COPY --from=brotli-wasm-export / target/ + +# -- Build steps -- + +# Build local brotli +RUN ./scripts/build-brotli.sh -l + +# Build solidity contracts + go bindings +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-solidity + +# Build replay environment and test dependencies +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-replay-env test-go-deps + +# Build SP1 brotli (cross-compile for RISC-V) +RUN cp crates/sp1/brotli_cmake_patch.txt brotli/CMakeLists.txt && \ + rm -rf target/build-sp1/brotli target/lib-sp1 && \ + mkdir -p target/build-sp1/brotli && \ + cd target/build-sp1/brotli && \ + cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ + -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \ + -DCMAKE_SYSTEM_NAME=Generic \ + -DCMAKE_C_COMPILER="$RISCV_GNU_TOOLCHAIN/bin/riscv64-unknown-elf-gcc" \ + -DCMAKE_C_FLAGS="-march=rv64im -mabi=lp64 -DBROTLI_BUILD_PORTABLE -mcmodel=medany -ffunction-sections -fdata-sections -fPIC" \ + -DCMAKE_AR="$RISCV_GNU_TOOLCHAIN/bin/riscv64-unknown-elf-ar" \ + -DCMAKE_RANLIB="$RISCV_GNU_TOOLCHAIN/bin/riscv64-unknown-elf-ranlib" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/workspace/target/lib-sp1 \ + -DBROTLI_DISABLE_TESTS=ON \ + /workspace/brotli && \ + make && make install + +# Build SP1 replay.wasm +RUN mkdir -p target/sp1 && \ + GOOS=wasip1 GOARCH=wasm go build -tags sp1 -o target/sp1/replay.wasm ./cmd/replay/... + +# Record test block data +RUN rm -rf system_tests/test-data && \ + go test -run TestProgramStorage ./system_tests/ -- \ + -recordBlockInputs.WithBaseDir="$(pwd)"/system_tests/test-data \ + -recordBlockInputs.WithTimestampDirEnabled=false \ + -recordBlockInputs.enable=true && \ + cp system_tests/test-data/TestProgramStorage/*.json target/sp1/ + +# Build SP1 program (bootloaded ELF) and SP1 runner +ENV SP1_ZKVM_MAX_MEMORY=1099511627776 +RUN cd crates/sp1 && \ + cargo run --release -p sp1-builder -- \ + --replay-wasm /workspace/target/sp1/replay.wasm \ + --output-folder /workspace/target/sp1 + +RUN cargo build --release -p sp1-runner + +# Copy final artifacts +RUN cp target/elf-compilation/riscv64im-succinct-zkvm-elf/release/stylus-compiler-program target/sp1/ && \ + cp target/release/sp1-runner target/sp1/ + +# Stage 2: Minimal runtime image +FROM ubuntu:24.04 AS sp1-runner +RUN apt-get update && \ + apt-get install -y --no-install-recommends ca-certificates libstdc++6 && \ + rm -rf /var/lib/apt/lists/* +WORKDIR /app +COPY --from=sp1-builder /workspace/target/sp1/ ./ +COPY --from=sp1-builder /workspace/target/bin/jit /usr/local/bin/jit +COPY --from=sp1-builder /workspace/target/machines/latest/replay.wasm /app/machines/latest/replay.wasm +ENTRYPOINT ["/app/sp1-runner"] diff --git a/Dockerfile.sp1.dockerignore b/Dockerfile.sp1.dockerignore new file mode 100644 index 00000000000..fea31ea70d1 --- /dev/null +++ b/Dockerfile.sp1.dockerignore @@ -0,0 +1,46 @@ +**/.github +.make +**/.dockerignore +**/Dockerfile +**/.gitignore +**/.git +**/.gitmodules +go-ethereum/tests +contracts/build +contracts/cache/ +safe-smart-account/build/ +solgen/go +**/node_modules + +target/**/* +!target/machines +!target/machines/* +!target/machines/**/* +brotli/buildfiles/**/* + +# these are used by environment outside the docker: +nitro-testnode/**/* + +# Arbitrator ignores +crates/tools/module_roots +crates/tools/pricer + +# Rust outputs +crates/stylus/tests/*/target/ +crates/wasm-testsuite/target/ +crates/wasm-libraries/target/ +crates/tools/wasmer/target/ +crates/tools/wasm-tools/ +crates/tools/pricers/ +crates/tools/module_roots/ +crates/tools/stylus_benchmark +crates/langs/rust/target/ +crates/langs/bf/target/ + +# Compiled files +**/*.o +**/*.a +*.wasm + +# external tools and IDEs +.vscode diff --git a/crates/sp1/README.md b/crates/sp1/README.md index 71a0f8b76df..1d5274c8acd 100644 --- a/crates/sp1/README.md +++ b/crates/sp1/README.md @@ -56,6 +56,18 @@ stderr: WARNING: Using insecure random number generator. stdout: Validation succeeds with hash 624b2d504238ba9fe94ad3e19d1036a51894bc209b7f0ead1331d22005d40178 ``` +To generate and verify a ZK proof for the block (for benchmarking), use `--mode prove`: + +```bash +$ RUST_LOG=info ./target/sp1/sp1-runner \ + --program target/sp1/dumped_replay_wasm.elf \ + --stylus-compiler-program target/sp1/stylus-compiler-program \ + --block-file target/sp1/block_inputs_7.json \ + --mode prove +``` + +This will log the time spent on proving key generation, proof generation, and proof verification separately. + You can tweak `RUST_LOG` for more logs(e.g., running cycles and running time): ```bash diff --git a/crates/sp1/build.sh b/crates/sp1/build.sh index 4dc73d115cf..c365cd5c0e7 100755 --- a/crates/sp1/build.sh +++ b/crates/sp1/build.sh @@ -55,6 +55,7 @@ cp system_tests/test-data/TestProgramStorage/*.json target/sp1/ cd "$SCRIPT_DIR" # Bump SP1's maximum heap memory size export SP1_ZKVM_MAX_MEMORY=1099511627776 +export RISCV_GNU_TOOLCHAIN="$HOME/.sp1/riscv" # Build SP1 program and run bootloading process cargo run --release -p sp1-builder -- --replay-wasm "$OUTPUT_DIR"/replay.wasm --output-folder "$OUTPUT_DIR" # Build the SP1 runner diff --git a/crates/sp1/runner/src/main.rs b/crates/sp1/runner/src/main.rs index c322d72cb89..6feb7d95bc9 100644 --- a/crates/sp1/runner/src/main.rs +++ b/crates/sp1/runner/src/main.rs @@ -2,7 +2,7 @@ use std::{ops::Deref, sync::Arc, time::SystemTime}; use clap::{ArgAction, Parser, ValueEnum}; use sp1_core_executor::{MinimalExecutor, Program}; -use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin}; +use sp1_sdk::{Elf, Prover, ProverClient, ProvingKey, SP1Stdin}; use validation::{ValidationInput, ValidationRequest}; #[derive(Debug, Parser)] @@ -43,6 +43,9 @@ enum Mode { /// Normal mode Normal, + + /// Prove mode: generates and verifies a ZK proof + Prove, } #[tokio::main] @@ -109,6 +112,37 @@ async fn main() { report.exit_code as i32 } + Mode::Prove => { + let client = ProverClient::builder().cpu().build().await; + + let a = SystemTime::now(); + let pk = client.setup(program_elf).await.expect("failed to setup ELF"); + let b = SystemTime::now(); + tracing::info!( + "Setup completed, pk generation time: {:?}", + b.duration_since(a).unwrap(), + ); + + let a = SystemTime::now(); + let proof = client.prove(&pk, stdin).await.expect("failed to generate proof"); + let b = SystemTime::now(); + tracing::info!( + "Proof generated, proving time: {:?}", + b.duration_since(a).unwrap(), + ); + + let a = SystemTime::now(); + client + .verify(&proof, pk.verifying_key(), None) + .expect("failed to verify proof"); + let b = SystemTime::now(); + tracing::info!( + "Proof verified successfully, verification time: {:?}", + b.duration_since(a).unwrap(), + ); + + 0 + } }; std::process::exit(exit_code);