From c01d575e8932deb2f600a2bd3902e9018a2f29b1 Mon Sep 17 00:00:00 2001 From: John Guibas Date: Sun, 5 May 2024 22:07:43 -0700 Subject: [PATCH] chore: sdk tweaks (#653) Co-authored-by: Uma Roy --- .github/workflows/groth16.yml | 40 -- .github/workflows/tests.yml | 93 +--- Cargo.lock | 5 + core/src/io.rs | 19 +- core/src/stark/machine.rs | 5 +- .../weierstrass/weierstrass_decompress.rs | 5 +- core/src/utils/buffer.rs | 2 +- core/src/utils/prove.rs | 3 +- ec_add_test.sh | 1 - prover/Cargo.toml | 11 +- prover/scripts/e2e.rs | 4 +- prover/scripts/fibonacci_groth16.rs | 2 +- prover/scripts/fibonacci_sweep.rs | 2 +- prover/scripts/install_groth16.rs | 8 + prover/scripts/tendermint_sweep.rs | 2 +- prover/src/build.rs | 57 +- prover/src/install.rs | 112 ++++ prover/src/lib.rs | 94 ++-- prover/src/verify.rs | 8 +- recursion/core/src/runtime/mod.rs | 1 - sdk/src/lib.rs | 515 ++++++++++++++---- sdk/src/local.rs | 135 ----- sdk/src/mock.rs | 80 --- sdk/src/provers/local.rs | 131 +++++ sdk/src/provers/mock.rs | 85 +++ sdk/src/provers/mod.rs | 77 +++ sdk/src/{ => provers}/network.rs | 58 +- sdk/src/provers/utils.rs | 22 + sdk/src/utils.rs | 30 - 29 files changed, 1014 insertions(+), 593 deletions(-) delete mode 100644 .github/workflows/groth16.yml delete mode 100644 ec_add_test.sh create mode 100644 prover/scripts/install_groth16.rs create mode 100644 prover/src/install.rs delete mode 100644 sdk/src/local.rs delete mode 100644 sdk/src/mock.rs create mode 100644 sdk/src/provers/local.rs create mode 100644 sdk/src/provers/mock.rs create mode 100644 sdk/src/provers/mod.rs rename sdk/src/{ => provers}/network.rs (86%) create mode 100644 sdk/src/provers/utils.rs delete mode 100644 sdk/src/utils.rs diff --git a/.github/workflows/groth16.yml b/.github/workflows/groth16.yml deleted file mode 100644 index 4eec12da85..0000000000 --- a/.github/workflows/groth16.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Gnark Tests - -on: - push: - branches: [main] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - groth16: - name: Groth16 - runs-on: warp-ubuntu-latest-arm64-8x - env: - CARGO_NET_GIT_FETCH_WITH_CLI: "true" - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Setup CI - uses: ./.github/actions/setup - with: - pull_token: ${{ secrets.PULL_TOKEN }} - - - name: Install sp1 toolchain - run: | - curl -L https://sp1.succinct.xyz | bash - echo "/root/.sp1/bin" >> $GITHUB_PATH - /root/.sp1/bin/sp1up - - - name: Run make groth16 - run: make groth16 - working-directory: prover - env: - RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 -Ctarget-cpu=native - RUST_LOG: "info" - RUST_BACKTRACE: "1" - FRI_QUERIES: "1" - SP1_DEV_WRAPPER: "false" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3c528ac4fb..350dcb3a45 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,7 +36,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: -p sp1-core -p sp1-recursion-compiler -p sp1-recursion-program -p sp1-recursion-circuit --release + args: -p sp1-core -p sp1-recursion-compiler -p sp1-recursion-program -p sp1-recursion-circuit -p sp1-sdk --release env: RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 -C target-cpu=native RUST_LOG: 1 @@ -44,97 +44,6 @@ jobs: CARGO_INCREMENTAL: 1 FRI_QUERIES: 1 - e2e: - name: End to end with toolchain installation. - runs-on: warp-ubuntu-latest-arm64-8x - env: - CARGO_NET_GIT_FETCH_WITH_CLI: "true" - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Setup CI - uses: ./.github/actions/setup - with: - pull_token: ${{ secrets.PULL_TOKEN }} - - - name: Install sp1 toolchain - run: | - curl -L https://sp1.succinct.xyz | bash - echo "/root/.sp1/bin" >> $GITHUB_PATH - /root/.sp1/bin/sp1up - - - name: Run Fibonacci example main.rs - uses: actions-rs/cargo@v1 - with: - command: run - args: --release --manifest-path examples/fibonacci/script/Cargo.toml --bin fibonacci-script - env: - RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 -Ctarget-cpu=native - RUST_LOG: 1 - RUST_BACKTRACE: 1 - CARGO_INCREMENTAL: 1 - - - name: Run Fibonacci example compressed.rs - uses: actions-rs/cargo@v1 - with: - command: run - args: --release --manifest-path examples/fibonacci/script/Cargo.toml --bin compressed - env: - RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 -Ctarget-cpu=native - RUST_LOG: 1 - RUST_BACKTRACE: 1 - CARGO_INCREMENTAL: 1 - - # - name: Run Fibonacci example groth16.rs - # uses: actions-rs/cargo@v1 - # with: - # command: run - # args: --release --manifest-path examples/fibonacci/script/Cargo.toml --bin groth16 - # env: - # RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 -Ctarget-cpu=native - # RUST_LOG: 1 - # RUST_BACKTRACE: 1 - # CARGO_INCREMENTAL: 1 - - - name: Run cargo prove new - run: | - cargo prove new cargo-prove-test - cd cargo-prove-test - cd script - cargo run --release - - misc: - name: Miscellaneous - runs-on: warp-ubuntu-latest-arm64-8x - env: - CARGO_NET_GIT_FETCH_WITH_CLI: "true" - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Setup CI - uses: ./.github/actions/setup - with: - pull_token: ${{ secrets.PULL_TOKEN }} - - - name: Run cargo test with no default features - uses: actions-rs/cargo@v1 - with: - command: test - args: -p sp1-core --release --no-default-features --features debug -- cpu::trace::tests::generate_trace - env: - RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 -Ctarget-cpu=native - RUST_LOG: 1 - RUST_BACKTRACE: 1 - CARGO_INCREMENTAL: 1 - - - name: Install sp1 toolchain - run: | - curl -L https://sp1.succinct.xyz | bash - echo "/root/.sp1/bin" >> $GITHUB_PATH - /root/.sp1/bin/sp1up - lints: name: Formatting & Clippy runs-on: warp-ubuntu-latest-arm64-8x diff --git a/Cargo.lock b/Cargo.lock index 294472e4f4..dd57acc917 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4547,7 +4547,10 @@ dependencies = [ "backtrace", "bincode", "clap", + "dirs 5.0.1", + "futures", "hex", + "indicatif 0.17.8", "itertools 0.12.1", "p3-baby-bear", "p3-bn254-fr", @@ -4555,6 +4558,7 @@ dependencies = [ "p3-commit", "p3-field", "rayon", + "reqwest 0.11.27", "serde", "serde_json", "sha2", @@ -4568,6 +4572,7 @@ dependencies = [ "sp1-recursion-program", "subtle-encoding", "tempfile", + "tokio", "tracing", "tracing-appender", "tracing-subscriber", diff --git a/core/src/io.rs b/core/src/io.rs index dde8720a16..9820b76286 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -5,7 +5,7 @@ use crate::{ use serde::{de::DeserializeOwned, Deserialize, Serialize}; /// Standard input for the prover. -#[derive(Serialize, Deserialize, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SP1Stdin { /// Input stored as a vec of vec of bytes. It's stored this way because the read syscall reads /// a vec of bytes at a time. @@ -18,10 +18,9 @@ pub struct SP1Stdin { } /// Public values for the prover. -#[derive(Serialize, Deserialize, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SP1PublicValues { - // TODO: fix - pub buffer: Buffer, + buffer: Buffer, } impl SP1Stdin { @@ -97,6 +96,14 @@ impl SP1PublicValues { } } + pub fn as_slice(&self) -> &[u8] { + self.buffer.data.as_slice() + } + + pub fn to_vec(&self) -> Vec { + self.buffer.data.clone() + } + /// Read a value from the buffer. pub fn read(&mut self) -> T { self.buffer.read() @@ -116,10 +123,6 @@ impl SP1PublicValues { pub fn write_slice(&mut self, slice: &[u8]) { self.buffer.write_slice(slice); } - - pub fn to_vec(self) -> Vec { - self.buffer.data - } } impl AsRef<[u8]> for SP1PublicValues { diff --git a/core/src/stark/machine.rs b/core/src/stark/machine.rs index 193d683b58..84ce7b6d6c 100644 --- a/core/src/stark/machine.rs +++ b/core/src/stark/machine.rs @@ -266,7 +266,6 @@ impl>> StarkMachine { /// /// Given a proving key `pk` and a matching execution record `record`, this function generates /// a STARK proof that the execution record is valid. - #[instrument("prove", level = "info", skip_all)] pub fn prove>( &self, pk: &StarkProvingKey, @@ -279,10 +278,10 @@ impl>> StarkMachine { + for<'a> Air> + for<'a> Air, SC::Challenge>>, { - let shards = tracing::info_span!("shard execution record") + let shards = tracing::info_span!("shard_record") .in_scope(|| self.shard(record, &::Config::default())); - tracing::info_span!("generate shard proofs") + tracing::info_span!("prove_shards") .in_scope(|| P::prove_shards(self, pk, shards, challenger)) } diff --git a/core/src/syscall/precompiles/weierstrass/weierstrass_decompress.rs b/core/src/syscall/precompiles/weierstrass/weierstrass_decompress.rs index 7ac449f569..04e3e125dd 100644 --- a/core/src/syscall/precompiles/weierstrass/weierstrass_decompress.rs +++ b/core/src/syscall/precompiles/weierstrass/weierstrass_decompress.rs @@ -387,9 +387,10 @@ mod tests { let inputs = SP1Stdin::from(&compressed); - let mut proof = run_test_io(Program::from(SECP256K1_DECOMPRESS_ELF), inputs).unwrap(); + let mut public_values = + run_test_io(Program::from(SECP256K1_DECOMPRESS_ELF), inputs).unwrap(); let mut result = [0; 65]; - proof.buffer.read_slice(&mut result); + public_values.read_slice(&mut result); assert_eq!(result, decompressed); } } diff --git a/core/src/utils/buffer.rs b/core/src/utils/buffer.rs index f591e4e3c3..c4a2a926bb 100644 --- a/core/src/utils/buffer.rs +++ b/core/src/utils/buffer.rs @@ -1,7 +1,7 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; /// A buffer of serializable/deserializable objects. -#[derive(Serialize, Deserialize, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Buffer { pub data: Vec, #[serde(skip)] diff --git a/core/src/utils/prove.rs b/core/src/utils/prove.rs index 1e692db435..7cd796fcbb 100644 --- a/core/src/utils/prove.rs +++ b/core/src/utils/prove.rs @@ -305,8 +305,7 @@ where // Prove the program. let start = Instant::now(); let cycles = runtime.state.global_clk; - let proof = tracing::info_span!("prove") - .in_scope(|| machine.prove::>(&pk, runtime.record, &mut challenger)); + let proof = machine.prove::>(&pk, runtime.record, &mut challenger); let time = start.elapsed().as_millis(); let nb_bytes = bincode::serialize(&proof).unwrap().len(); diff --git a/ec_add_test.sh b/ec_add_test.sh deleted file mode 100644 index ca5578ba88..0000000000 --- a/ec_add_test.sh +++ /dev/null @@ -1 +0,0 @@ -RUST_LOG=debug cargo test --package sp1-core --lib -- syscall::precompiles::weierstrass::weierstrass_add::tests::test_secp256k1_add_simple --exact --nocapture diff --git a/prover/Cargo.toml b/prover/Cargo.toml index cbfd6436e3..66b7a9a5d7 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -32,8 +32,13 @@ sha2 = "0.10.8" hex = "0.4.3" anyhow = "1.0.82" size = "0.4.1" -subtle-encoding = "0.5.1" +dirs = "5.0.1" tempfile = "3.10.1" +tokio = { version = "1.36.0", features = ["full"] } +reqwest = { version = "0.11.25", features = ["rustls-tls", "trust-dns", "stream"] } +indicatif = "0.17.8" +futures = "0.3.30" +subtle-encoding = "0.5.1" [[bin]] name = "fibonacci_sweep" @@ -59,5 +64,9 @@ path = "scripts/test_groth16_verification.rs" name = "build_groth16" path = "scripts/build_groth16.rs" +[[bin]] +name = "install_groth16" +path = "scripts/install_groth16.rs" + [features] neon = ["sp1-core/neon"] diff --git a/prover/scripts/e2e.rs b/prover/scripts/e2e.rs index f8ffe74ed8..397ceb1d82 100644 --- a/prover/scripts/e2e.rs +++ b/prover/scripts/e2e.rs @@ -39,10 +39,10 @@ pub fn main() { let core_proof = prover.prove_core(&pk, &stdin); tracing::info!("reduce"); - let reduced_proof = prover.reduce(&vk, core_proof.proof, vec![]); + let reduced_proof = prover.compress(&vk, core_proof, vec![]); tracing::info!("compress"); - let compressed_proof = prover.compress(&vk, reduced_proof); + let compressed_proof = prover.shrink(&vk, reduced_proof); tracing::info!("wrap"); let wrapped_proof = prover.wrap_bn254(&vk, compressed_proof); diff --git a/prover/scripts/fibonacci_groth16.rs b/prover/scripts/fibonacci_groth16.rs index 898d8174c5..480ac2af6c 100644 --- a/prover/scripts/fibonacci_groth16.rs +++ b/prover/scripts/fibonacci_groth16.rs @@ -68,7 +68,7 @@ fn main() { tracing::info!("proving inner"); let recursion_proving_start = Instant::now(); - let _ = prover.reduce(&vk, proof.proof, vec![]); + let _ = prover.compress(&vk, proof, vec![]); let recursion_proving_duration = recursion_proving_start.elapsed().as_secs_f64(); tracing::info!("recursion_proving_duration={}", recursion_proving_duration); } diff --git a/prover/scripts/fibonacci_sweep.rs b/prover/scripts/fibonacci_sweep.rs index 3bd8bbd335..c93f7950b2 100644 --- a/prover/scripts/fibonacci_sweep.rs +++ b/prover/scripts/fibonacci_sweep.rs @@ -69,7 +69,7 @@ fn main() { let leaf_proving_duration = leaf_proving_start.elapsed().as_secs_f64(); let recursion_proving_start = Instant::now(); - let _ = prover.reduce(&vk, proof.proof, vec![]); + let _ = prover.compress(&vk, proof, vec![]); let recursion_proving_duration = recursion_proving_start.elapsed().as_secs_f64(); lines.push(format!( diff --git a/prover/scripts/install_groth16.rs b/prover/scripts/install_groth16.rs new file mode 100644 index 0000000000..712ef69cb1 --- /dev/null +++ b/prover/scripts/install_groth16.rs @@ -0,0 +1,8 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +use sp1_prover::install; + +pub fn main() { + install::groth16_artifacts(); +} diff --git a/prover/scripts/tendermint_sweep.rs b/prover/scripts/tendermint_sweep.rs index e929bc4221..bff6050725 100644 --- a/prover/scripts/tendermint_sweep.rs +++ b/prover/scripts/tendermint_sweep.rs @@ -69,7 +69,7 @@ fn main() { let leaf_proving_duration = leaf_proving_start.elapsed().as_secs_f64(); let recursion_proving_start = Instant::now(); - let _ = prover.reduce(&vk, proof.proof, vec![]); + let _ = prover.compress(&vk, proof, vec![]); let recursion_proving_duration = recursion_proving_start.elapsed().as_secs_f64(); lines.push(format!( diff --git a/prover/src/build.rs b/prover/src/build.rs index 7bfd29e24b..25975f9f21 100644 --- a/prover/src/build.rs +++ b/prover/src/build.rs @@ -1,19 +1,30 @@ -//! This module contains functions for building the groth16 and plonk circuits. These are in -//! sp1_prover because they require a dummy proof to be generated during the build process. - use std::path::PathBuf; +use p3_baby_bear::BabyBear; use sp1_core::stark::StarkVerifyingKey; use sp1_core::{io::SP1Stdin, stark::ShardProof}; -use sp1_recursion_circuit::stark::build_wrap_circuit; -use sp1_recursion_circuit::witness::Witnessable; -use sp1_recursion_compiler::ir::Witness; +pub use sp1_recursion_circuit::stark::build_wrap_circuit; +pub use sp1_recursion_circuit::witness::Witnessable; +pub use sp1_recursion_compiler::ir::Witness; use sp1_recursion_compiler::{config::OuterConfig, constraints::Constraint}; +use sp1_recursion_core::air::RecursionPublicValues; use sp1_recursion_gnark_ffi::plonk_bn254::PlonkBn254Prover; use sp1_recursion_gnark_ffi::Groth16Prover; +use crate::utils::{babybear_bytes_to_bn254, babybears_to_bn254, words_to_bytes}; use crate::{OuterSC, SP1Prover}; +/// Build the groth16 artifacts to the given directory for the given verification key and template +/// proof. +pub fn groth16_artifacts( + wrap_vk: &StarkVerifyingKey, + wrapped_proof: &ShardProof, + build_dir: PathBuf, +) { + let (constraints, witness) = build_constraints(wrap_vk, wrapped_proof); + Groth16Prover::build(constraints, witness, build_dir); +} + /// Generate a dummy proof that we can use to build the circuit. We need this to know the shape of /// the proof. fn dummy_proof() -> (StarkVerifyingKey, ShardProof) { @@ -26,33 +37,43 @@ fn dummy_proof() -> (StarkVerifyingKey, ShardProof) { let (pk, vk) = prover.setup(elf); tracing::info!("prove core"); - let stdin = SP1Stdin::new(); + let mut stdin = SP1Stdin::new(); + stdin.write(&500u32); let core_proof = prover.prove_core(&pk, &stdin); - tracing::info!("reduce"); - let reduced_proof = prover.reduce(&vk, core_proof.proof, vec![]); - tracing::info!("compress"); - let compressed_proof = prover.compress(&vk, reduced_proof); + let compressed_proof = prover.compress(&vk, core_proof, vec![]); + + tracing::info!("shrink"); + let shrink_proof = prover.shrink(&vk, compressed_proof); tracing::info!("wrap"); - let wrapped_proof = prover.wrap_bn254(&vk, compressed_proof); + let wrapped_proof = prover.wrap_bn254(&vk, shrink_proof); (prover.wrap_vk, wrapped_proof) } /// Build the verifier constraints and template witness for the circuit. -fn build_circuit( - wrap_vk: StarkVerifyingKey, - wrapped_proof: ShardProof, +fn build_constraints( + wrap_vk: &StarkVerifyingKey, + wrapped_proof: &ShardProof, ) -> (Vec, Witness) { tracing::info!("building verifier constraints"); let constraints = tracing::info_span!("wrap circuit") - .in_scope(|| build_wrap_circuit(&wrap_vk, wrapped_proof.clone())); + .in_scope(|| build_wrap_circuit(wrap_vk, wrapped_proof.clone())); + + let pv = RecursionPublicValues::from_vec(wrapped_proof.public_values.clone()); + let vkey_hash = babybears_to_bn254(&pv.sp1_vk_digest); + let committed_values_digest_bytes: [BabyBear; 32] = words_to_bytes(&pv.committed_value_digest) + .try_into() + .unwrap(); + let committed_values_digest = babybear_bytes_to_bn254(&committed_values_digest_bytes); tracing::info!("building template witness"); let mut witness = Witness::default(); wrapped_proof.write(&mut witness); + witness.write_commited_values_digest(committed_values_digest); + witness.write_vkey_hash(vkey_hash); (constraints, witness) } @@ -69,7 +90,7 @@ pub fn build_groth16_artifacts(build_dir: PathBuf) { std::env::set_var("RECONSTRUCT_COMMITMENTS", "false"); let (wrap_vk, wrapped_proof) = dummy_proof(); - let (constraints, witness) = build_circuit(wrap_vk, wrapped_proof); + let (constraints, witness) = build_constraints(&wrap_vk, &wrapped_proof); tracing::info!("sanity check gnark test"); Groth16Prover::test(constraints.clone(), witness.clone()); @@ -89,7 +110,7 @@ pub fn build_plonk_artifacts(build_dir: PathBuf) { std::env::set_var("RECONSTRUCT_COMMITMENTS", "false"); let (wrap_vk, wrapped_proof) = dummy_proof(); - let (constraints, witness) = build_circuit(wrap_vk, wrapped_proof); + let (constraints, witness) = build_constraints(&wrap_vk, &wrapped_proof); mkdirs(&build_dir); diff --git a/prover/src/install.rs b/prover/src/install.rs new file mode 100644 index 0000000000..95a18dd618 --- /dev/null +++ b/prover/src/install.rs @@ -0,0 +1,112 @@ +use std::{cmp::min, io::Write, path::PathBuf, process::Command}; + +use futures::StreamExt; +use indicatif::{ProgressBar, ProgressStyle}; +use reqwest::Client; + +/// The base URL for the S3 bucket containing the groth16 artifacts. +pub const GROTH16_ARTIFACTS_URL_BASE: &str = "https://sp1-circuits.s3-us-east-2.amazonaws.com"; + +/// The current version of the groth16 artifacts. +pub const GROTH16_ARTIFACTS_VERSION: &str = "1"; + +/// The base URL for the S3 bucket containing the plonk bn254 artifacts. +pub const PLONK_BN254_ARTIFACTS_URL_BASE: &str = "https://sp1-circuits.s3-us-east-2.amazonaws.com"; + +/// The current version of the plonk bn254 artifacts. +pub const PLONK_BN254_ARTIFACTS_VERSION: &str = "1"; + +/// Install the latest groth16 artifacts. +/// +/// This function will download the latest groth16 artifacts from the S3 bucket and extract them to +/// the directory specified by [groth16_artifacts_dir()]. +pub fn groth16_artifacts() { + let build_dir = groth16_artifacts_dir(); + + // If build directory already exists, skip the download. + if build_dir.exists() { + tracing::info!("groth16 artifacts already seem to exist at {}. if you want to re-download them, delete the directory", build_dir.display()); + return; + } + + // Create the build directory. + std::fs::create_dir_all(&build_dir).expect("failed to create build directory"); + + // Download the artifacts. + let download_url = format!( + "{}/groth16/{}.tar.gz", + GROTH16_ARTIFACTS_URL_BASE, GROTH16_ARTIFACTS_VERSION + ); + let mut artifacts_tar_gz_file = + tempfile::NamedTempFile::new().expect("failed to create tempfile"); + let rt = tokio::runtime::Runtime::new().expect("failed to create tokio runtime"); + let client = Client::builder() + .build() + .expect("failed to create reqwest client"); + rt.block_on(download_file( + &client, + &download_url, + &mut artifacts_tar_gz_file, + )) + .expect("failed to download file"); + + // Extract the tarball to the build directory. + let mut res = Command::new("tar") + .args([ + "-Pxzf", + artifacts_tar_gz_file.path().to_str().unwrap(), + "-C", + build_dir.to_str().unwrap(), + ]) + .spawn() + .expect("failed to extract tarball"); + res.wait().unwrap(); +} + +/// The directory where the groth16 artifacts will be stored based on [GROTH16_ARTIFACTS_VERSION] +/// and [GROTH16_ARTIFACTS_URL_BASE]. +pub fn groth16_artifacts_dir() -> PathBuf { + dirs::home_dir() + .unwrap() + .join(".sp1") + .join("circuits") + .join(format!("groth16-{}", GROTH16_ARTIFACTS_VERSION)) +} + +/// Download the file with a progress bar that indicates the progress. +pub async fn download_file( + client: &Client, + url: &str, + file: &mut tempfile::NamedTempFile, +) -> std::result::Result<(), String> { + let res = client + .get(url) + .send() + .await + .or(Err(format!("Failed to GET from '{}'", &url)))?; + + let total_size = res + .content_length() + .ok_or(format!("Failed to get content length from '{}'", &url))?; + + let pb = ProgressBar::new(total_size); + pb.set_style(ProgressStyle::default_bar() + .template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})").unwrap() + .progress_chars("#>-")); + println!("downloading groth16 artifacts {}", url); + + let mut downloaded: u64 = 0; + let mut stream = res.bytes_stream(); + while let Some(item) = stream.next().await { + let chunk = item.or(Err("Error while downloading file"))?; + file.write_all(&chunk) + .or(Err("Error while writing to file"))?; + let new = min(downloaded + (chunk.len() as u64), total_size); + downloaded = new; + pb.set_position(new); + } + + let msg = format!("Downloaded {} to {:?}", url, file); + pb.finish_with_message(msg); + Ok(()) +} diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 7389206f7d..84b2876340 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -13,11 +13,11 @@ #![allow(clippy::new_without_default)] pub mod build; +pub mod install; mod types; pub mod utils; mod verify; -use crate::utils::RECONSTRUCT_COMMITMENTS_ENV_VAR; use p3_baby_bear::BabyBear; use p3_challenger::CanObserve; use p3_field::AbstractField; @@ -68,6 +68,7 @@ use crate::types::ReduceState; use crate::utils::get_chip_quotient_data; use crate::utils::get_preprocessed_data; use crate::utils::get_sorted_indices; +use crate::utils::RECONSTRUCT_COMMITMENTS_ENV_VAR; /// The configuration for the core prover. pub type CoreSC = BabyBearPoseidon2; @@ -87,16 +88,16 @@ pub struct SP1Prover { pub recursion_setup_program: RecursionProgram, /// The proving key for the reduce step. - pub reduce_pk: StarkProvingKey, + pub compress_pk: StarkProvingKey, /// The verification key for the reduce step. - pub reduce_vk: StarkVerifyingKey, + pub compress_vk: StarkVerifyingKey, - /// The proving key for the compress step. - pub compress_pk: StarkProvingKey, + /// The proving key for the shrink step. + pub shrink_pk: StarkProvingKey, - /// The verification key for the compress step. - pub compress_vk: StarkVerifyingKey, + /// The verification key for the shrink step. + pub shrink_vk: StarkVerifyingKey, /// The proving key for the wrap step. pub wrap_pk: StarkProvingKey, @@ -108,11 +109,11 @@ pub struct SP1Prover { pub core_machine: StarkMachine::Val>>, /// The machine used for proving the reduce step. - pub reduce_machine: + pub compress_machine: StarkMachine::Val>>, /// The machine used for proving the compress step. - pub compress_machine: + pub shrink_machine: StarkMachine::Val>>, /// The machine used for proving the wrapping step. @@ -126,28 +127,28 @@ impl SP1Prover { pub fn new() -> Self { let recursion_setup_program = ReduceProgram::setup(); let recursion_program = ReduceProgram::build(); - let (reduce_pk, reduce_vk) = - RecursionAirWideDeg3::machine(InnerSC::default()).setup(&recursion_program); let (compress_pk, compress_vk) = + RecursionAirWideDeg3::machine(InnerSC::default()).setup(&recursion_program); + let (shrink_pk, shrink_vk) = RecursionAirSkinnyDeg7::machine(InnerSC::compressed()).setup(&recursion_program); let (wrap_pk, wrap_vk) = RecursionAirSkinnyDeg7::machine(OuterSC::default()).setup(&recursion_program); let core_machine = RiscvAir::machine(CoreSC::default()); - let reduce_machine = RecursionAirWideDeg3::machine(InnerSC::default()); - let compress_machine = RecursionAirSkinnyDeg7::machine(InnerSC::compressed()); + let compress_machine = RecursionAirWideDeg3::machine(InnerSC::default()); + let shrink_machine = RecursionAirSkinnyDeg7::machine(InnerSC::compressed()); let wrap_machine = RecursionAirSkinnyDeg7::machine(OuterSC::default()); Self { recursion_setup_program, recursion_program, - reduce_pk, - reduce_vk, compress_pk, compress_vk, + shrink_pk, + shrink_vk, wrap_pk, wrap_vk, core_machine, - reduce_machine, compress_machine, + shrink_machine, wrap_machine, } } @@ -216,12 +217,12 @@ impl SP1Prover { } } - /// Reduce shards proofs to a single shard proof using the recursion prover. - #[instrument(name = "reduce", level = "info", skip_all)] - pub fn reduce( + /// Compress shards proofs to a single shard proof using the recursion prover. + #[instrument(name = "compress", level = "info", skip_all)] + pub fn compress( &self, vk: &SP1VerifyingKey, - proof: SP1CoreProofData, + proof: SP1ProofWithMetadata, mut deferred_proofs: Vec>, ) -> SP1ReduceProof { // Observe all commitments and public values. @@ -231,7 +232,7 @@ impl SP1Prover { // challenger was correct. let mut core_challenger = self.core_machine.config().challenger(); vk.vk.observe_into(&mut core_challenger); - for shard_proof in proof.0.iter() { + for shard_proof in proof.proof.0.iter() { core_challenger.observe(shard_proof.commitment.main_commit); core_challenger.observe_slice( &shard_proof.public_values.to_vec()[0..self.core_machine.num_pv_elts()], @@ -240,6 +241,7 @@ impl SP1Prover { // Map the existing shards to a self-reducing type of proof (i.e. Reduce: T[] -> T). let mut reduce_proofs = proof + .proof .0 .into_iter() .map(|proof| SP1ReduceProofWrapper::Core(SP1ReduceProof { proof })) @@ -269,7 +271,7 @@ impl SP1Prover { let config = InnerSC::default(); self.verify_batch( config, - &self.reduce_pk, + &self.compress_pk, vk, core_challenger, reconstruct_challenger, @@ -349,7 +351,7 @@ impl SP1Prover { let config = InnerSC::default(); let proof = self.verify_batch( config, - &self.reduce_pk, + &self.compress_pk, vk, sp1_challenger.clone(), reconstruct_challenger, @@ -397,7 +399,7 @@ impl SP1Prover { let config = InnerSC::default(); self.verify_batch::( config, - &self.reduce_pk, + &self.compress_pk, vk, sp1_challenger.clone(), reconstruct_challenger.clone(), @@ -465,9 +467,9 @@ impl SP1Prover { } SP1ReduceProofWrapper::Recursive(reduce_proof) => { if verifying_compressed_proof { - get_chip_quotient_data(&self.compress_machine, &reduce_proof.proof) + get_chip_quotient_data(&self.shrink_machine, &reduce_proof.proof) } else { - get_chip_quotient_data(&self.reduce_machine, &reduce_proof.proof) + get_chip_quotient_data(&self.compress_machine, &reduce_proof.proof) } } }) @@ -480,9 +482,9 @@ impl SP1Prover { } SP1ReduceProofWrapper::Recursive(reduce_proof) => { if verifying_compressed_proof { - get_sorted_indices(&self.compress_machine, &reduce_proof.proof) + get_sorted_indices(&self.shrink_machine, &reduce_proof.proof) } else { - get_sorted_indices(&self.reduce_machine, &reduce_proof.proof) + get_sorted_indices(&self.compress_machine, &reduce_proof.proof) } } }) @@ -490,18 +492,18 @@ impl SP1Prover { let (prep_sorted_indices, prep_domains): (Vec, Vec>) = get_preprocessed_data(&self.core_machine, &core_vk.vk); let (reduce_prep_sorted_indices, reduce_prep_domains): (Vec, Vec>) = - get_preprocessed_data(&self.reduce_machine, &self.reduce_vk); + get_preprocessed_data(&self.compress_machine, &self.compress_vk); let (compress_prep_sorted_indices, compress_prep_domains): ( Vec, Vec>, - ) = get_preprocessed_data(&self.compress_machine, &self.compress_vk); + ) = get_preprocessed_data(&self.shrink_machine, &self.shrink_vk); let deferred_sorted_indices: Vec> = deferred_proofs .iter() - .map(|proof| get_sorted_indices(&self.reduce_machine, proof)) + .map(|proof| get_sorted_indices(&self.compress_machine, proof)) .collect(); let deferred_chip_quotient_data: Vec> = deferred_proofs .iter() - .map(|p| get_chip_quotient_data(&self.reduce_machine, p)) + .map(|p| get_chip_quotient_data(&self.compress_machine, p)) .collect(); // Convert the inputs into a witness stream. @@ -518,8 +520,8 @@ impl SP1Prover { witness_stream.extend(compress_prep_sorted_indices.write()); witness_stream.extend(Hintable::write(&compress_prep_domains)); witness_stream.extend(core_vk.vk.write()); - witness_stream.extend(self.reduce_vk.write()); witness_stream.extend(self.compress_vk.write()); + witness_stream.extend(self.shrink_vk.write()); witness_stream.extend(state.committed_values_digest.write()); witness_stream.extend(state.deferred_proofs_digest.write()); witness_stream.extend(Hintable::write(&state.start_pc)); @@ -610,9 +612,9 @@ impl SP1Prover { SP1ReduceProof { proof } } - /// Wrap a reduce proof into a STARK proven over a SNARK-friendly field. - #[instrument(name = "compress", level = "info", skip_all)] - pub fn compress( + /// Shrink a compressed proof into a STARK proven over a SNARK-friendly field. + #[instrument(name = "shrink", level = "info", skip_all)] + pub fn shrink( &self, vk: &SP1VerifyingKey, reduced_proof: SP1ReduceProof, @@ -629,7 +631,7 @@ impl SP1Prover { let config = InnerSC::compressed(); self.verify_batch::( config, - &self.compress_pk, + &self.shrink_pk, vk, core_challenger, reconstruct_challenger, @@ -759,11 +761,11 @@ mod tests { tracing::info!("verify core"); prover.verify(&core_proof.proof, &vk).unwrap(); - tracing::info!("reduce"); - let reduced_proof = prover.reduce(&vk, core_proof.proof, vec![]); + tracing::info!("compress"); + let compressed_proof = prover.compress(&vk, core_proof, vec![]); tracing::info!("wrap bn254"); - let wrapped_bn254_proof = prover.wrap_bn254(&vk, reduced_proof); + let wrapped_bn254_proof = prover.wrap_bn254(&vk, compressed_proof); tracing::info!("groth16"); prover.wrap_groth16(wrapped_bn254_proof, PathBuf::from("build")); @@ -796,7 +798,7 @@ mod tests { stdin.write(&1usize); stdin.write(&vec![0u8, 0, 0]); let deferred_proof_1 = prover.prove_core(&keccak_pk, &stdin); - let pv_1 = deferred_proof_1.public_values.buffer.data.clone(); + let pv_1 = deferred_proof_1.public_values.as_slice().to_vec().clone(); println!("proof 1 pv: {:?}", hex::encode(pv_1.clone())); let pv_digest_1 = deferred_proof_1.proof.0[0].public_values[..32] .iter() @@ -812,7 +814,7 @@ mod tests { stdin.write(&vec![2, 3, 4]); stdin.write(&vec![5, 6, 7]); let deferred_proof_2 = prover.prove_core(&keccak_pk, &stdin); - let pv_2 = deferred_proof_2.public_values.buffer.data.clone(); + let pv_2 = deferred_proof_2.public_values.as_slice().to_vec().clone(); println!("proof 2 pv: {:?}", hex::encode(pv_2.clone())); let pv_digest_2 = deferred_proof_2.proof.0[0].public_values[..32] .iter() @@ -822,11 +824,11 @@ mod tests { // Generate recursive proof of first subproof println!("reduce subproof 1"); - let deferred_reduce_1 = prover.reduce(&keccak_vk, deferred_proof_1.proof, vec![]); + let deferred_reduce_1 = prover.compress(&keccak_vk, deferred_proof_1, vec![]); // Generate recursive proof of second subproof println!("reduce subproof 2"); - let deferred_reduce_2 = prover.reduce(&keccak_vk, deferred_proof_2.proof, vec![]); + let deferred_reduce_2 = prover.compress(&keccak_vk, deferred_proof_2, vec![]); // Run verify program with keccak vkey, subproofs, and their committed values let mut stdin = SP1Stdin::new(); @@ -854,9 +856,9 @@ mod tests { // Generate recursive proof of verify program println!("proving verify program (recursion)"); - let verify_reduce = prover.reduce( + let verify_reduce = prover.compress( &verify_vk, - verify_proof.proof.clone(), + verify_proof, vec![ deferred_reduce_1.proof, deferred_reduce_2.proof.clone(), diff --git a/prover/src/verify.rs b/prover/src/verify.rs index 6cb9062457..64c895fc9d 100644 --- a/prover/src/verify.rs +++ b/prover/src/verify.rs @@ -90,12 +90,12 @@ impl SP1Prover { proof: &SP1ReducedProofData, vk: &SP1VerifyingKey, ) -> Result<(), ProgramVerificationError> { - let mut challenger = self.reduce_machine.config().challenger(); + let mut challenger = self.compress_machine.config().challenger(); let machine_proof = MachineProof { shard_proofs: vec![proof.0.clone()], }; - self.reduce_machine - .verify(&self.reduce_vk, &machine_proof, &mut challenger)?; + self.compress_machine + .verify(&self.compress_vk, &machine_proof, &mut challenger)?; // Validate public values let public_values = RecursionPublicValues::from_vec(proof.0.public_values.clone()); @@ -116,7 +116,7 @@ impl SP1Prover { } // Verify that the reduce program is the one we are expecting. - let recursion_vkey_hash = self.reduce_vk.hash(); + let recursion_vkey_hash = self.compress_vk.hash(); if public_values.recursion_vk_digest != recursion_vkey_hash { return Err(ProgramVerificationError::InvalidPublicValues( "recursion vk hash mismatch", diff --git a/recursion/core/src/runtime/mod.rs b/recursion/core/src/runtime/mod.rs index a3dac6bf7c..91bb1ae3cb 100644 --- a/recursion/core/src/runtime/mod.rs +++ b/recursion/core/src/runtime/mod.rs @@ -213,7 +213,6 @@ where tracing::debug!("Extension Operations: {}", self.nb_ext_ops); tracing::debug!("Memory Operations: {}", self.nb_memory_ops); tracing::debug!("Branch Operations: {}", self.nb_branch_ops); - tracing::debug!("\nCycle Tracker Statistics:"); for (name, entry) in self.cycle_tracker.iter().sorted_by_key(|(name, _)| *name) { tracing::debug!("> {}: {}", name, entry.cumulative_cycles); } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 56eed67b9d..dffc74e606 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -1,50 +1,80 @@ +//! # SP1 SDK +//! +//! A library for interacting with the SP1 RISC-V zkVM. +//! +//! Visit the [Getting Started](https://succinctlabs.github.io/sp1/getting-started.html) section +//! in the official SP1 documentation for a quick start guide. + #![allow(incomplete_features)] #![feature(generic_const_exprs)] + pub mod proto { - #[rustfmt::skip] - #[allow(clippy::all)] pub mod network; } pub mod artifacts; pub mod auth; pub mod client; -pub mod local; -pub mod mock; -pub mod network; -pub mod utils; +pub mod provers; +pub mod utils { + pub use sp1_core::utils::setup_logger; +} + +use std::{env, fmt::Debug, fs::File, path::Path}; use anyhow::{Ok, Result}; -use artifacts::WrapCircuitType; -use local::LocalProver; -use mock::MockProver; -use network::NetworkProver; +pub use provers::{LocalProver, MockProver, NetworkProver, Prover}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use sp1_core::stark::ShardProof; pub use sp1_prover::{ - CoreSC, SP1CoreProofData, SP1Prover, SP1ProvingKey, SP1PublicValues, SP1Stdin, SP1VerifyingKey, + CoreSC, Groth16Proof, InnerSC, PlonkBn254Proof, SP1CoreProof, SP1Prover, SP1ProvingKey, + SP1PublicValues, SP1Stdin, SP1VerifyingKey, }; -use sp1_prover::{ - SP1CoreProof, SP1Groth16Proof, SP1Groth16ProofData, SP1PlonkProof, SP1PlonkProofData, - SP1ProofWithMetadata, SP1ReducedProof, -}; -use std::env; - -use serde::{Deserialize, Serialize}; -/// A client that can prove RISCV ELFs and verify those proofs. +/// A client for interacting with SP1. pub struct ProverClient { + /// The underlying prover implementation. pub prover: Box, } -impl Default for ProverClient { - fn default() -> Self { - Self::new() - } +/// A proof generated with SP1. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound(serialize = "P: Serialize + Debug + Clone"))] +#[serde(bound(deserialize = "P: DeserializeOwned + Debug + Clone"))] +pub struct SP1ProofWithPublicValues

{ + pub proof: P, + pub stdin: SP1Stdin, + pub public_values: SP1PublicValues, } +/// A [SP1ProofWithPublicValues] generated with [ProverClient::prove]. +pub type SP1Proof = SP1ProofWithPublicValues>>; + +/// A [SP1ProofWithPublicValues] generated with [ProverClient::prove_compressed]. +pub type SP1CompressedProof = SP1ProofWithPublicValues>; + +/// A [SP1ProofWithPublicValues] generated with [ProverClient::prove_groth16]. +pub type SP1Groth16Proof = SP1ProofWithPublicValues; + +/// A [SP1ProofWithPublicValues] generated with [ProverClient::prove_plonk]. +pub type SP1PlonkProof = SP1ProofWithPublicValues; + impl ProverClient { - /// Creates a new ProverClient with the prover set to either prove locally or prove via the - /// prover network based on the SP1_PROVER environment variable. + /// Creates a new [ProverClient]. + /// + /// Setting the `SP1_PROVER` enviroment variable can change the prover used under the hood. + /// - `local` (default): Uses [LocalProver]. Recommended for proving end-to-end locally. + /// - `mock`: Uses [MockProver]. Recommended for testing and development. + /// - `remote`: Uses [NetworkProver]. Recommended for outsourcing proof generation to an RPC. + /// + /// ### Examples + /// + /// ``` + /// use sp1_sdk::ProverClient; + /// + /// std::env::set_var("SP1_PROVER", "local"); + /// let client = ProverClient::new(); + /// ``` pub fn new() -> Self { - dotenv::dotenv().ok(); match env::var("SP1_PROVER") .unwrap_or("local".to_string()) .to_lowercase() @@ -59,116 +89,417 @@ impl ProverClient { "network" => Self { prover: Box::new(NetworkProver::new()), }, - _ => panic!("Invalid SP1_PROVER value"), + _ => panic!( + "invalid value for SP1_PROVER enviroment variable: expected 'local', 'mock', or 'remote'" + ), } } - pub fn new_groth16() -> Self { - dotenv::dotenv().ok(); - match env::var("SP1_PROVER") - .unwrap_or("local".to_string()) - .to_lowercase() - .as_str() - { - "mock" => Self { - prover: Box::new(MockProver::new()), - }, - "local" => { - let prover = LocalProver::new(); - prover.initialize_circuit(WrapCircuitType::Groth16); - Self { - prover: Box::new(prover), - } - } - "remote" => Self { - prover: Box::new(NetworkProver::new()), - }, - _ => panic!("Invalid SP1_PROVER value"), + /// Creates a new [ProverClient] with the mock prover. + /// + /// Recommended for testing and development. You can also use [ProverClient::new] to set the + /// prover to `mock` with the `SP1_PROVER` enviroment variable. + /// + /// ### Examples + /// + /// ``` + /// use sp1_sdk::ProverClient; + /// + /// let client = ProverClient::mock(); + /// ``` + pub fn mock() -> Self { + Self { + prover: Box::new(MockProver::new()), } } - /// Executes the elf with the given inputs and returns the output. - pub fn execute(elf: &[u8], stdin: SP1Stdin) -> Result { + /// Creates a new [ProverClient] with the local prover. + /// + /// Recommended for proving end-to-end locally. You can also use [ProverClient::new] to set the + /// prover to `local` with the `SP1_PROVER` enviroment variable. + /// + /// ### Examples + /// + /// ``` + /// use sp1_sdk::ProverClient; + /// + /// let client = ProverClient::local(); + /// ``` + pub fn local() -> Self { + Self { + prover: Box::new(LocalProver::new()), + } + } + + /// Creates a new [ProverClient] with the network prover. + /// + /// Recommended for outsourcing proof generation to an RPC. You can also use [ProverClient::new] + /// + /// ### Examples + /// + /// ``` + /// use sp1_sdk::ProverClient; + /// + /// let client = ProverClient::remote(); + /// ``` + pub fn remote() -> Self { + Self { + prover: Box::new(NetworkProver::new()), + } + } + + /// Executes the given program on the given input (without generating a proof). + /// + /// Returns the public values of the program after it has been executed. + /// + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// // Load the program. + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// + /// // Initialize the prover client. + /// let client = ProverClient::new(); + /// + /// // Setup the inputs. + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// + /// // Execute the program on the inputs. + /// let public_values = client.execute(elf, stdin).unwrap(); + /// ``` + pub fn execute(&self, elf: &[u8], stdin: SP1Stdin) -> Result { Ok(SP1Prover::execute(elf, &stdin)) } + /// Setup a program to be proven and verified by the SP1 RISC-V zkVM by computing the proving + /// and verifying keys. + /// + /// The proving key and verifying key essentially embed the program, as well as other auxiliary + /// data (such as lookup tables) that are used to prove the program's correctness. + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// let client = ProverClient::new(); + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// let (pk, vk) = client.setup(elf); + /// ``` pub fn setup(&self, elf: &[u8]) -> (SP1ProvingKey, SP1VerifyingKey) { self.prover.setup(elf) } - /// Proves the execution of the given elf with the given inputs. - pub fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + /// Proves the execution of the given program with the given input in the default mode. + /// + /// Returns a proof of the program's execution. By default the proof generated will not be + /// compressed to constant size. To create a more succinct proof, use the [Self::prove_compressed], + /// [Self::prove_groth16], or [Self::prove_plonk] methods. + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// // Load the program. + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// + /// // Initialize the prover client. + /// let client = ProverClient::new(); + /// + /// // Setup the program. + /// let (pk, vk) = client.setup(elf); + /// + /// // Setup the inputs. + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// + /// // Generate the proof. + /// let proof = client.prove(&pk, stdin).unwrap(); + /// ``` + pub fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { self.prover.prove(pk, stdin) } - /// Generates a compressed proof for the given elf and stdin. - pub fn prove_compressed(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - self.prover.prove_reduced(pk, stdin) + /// Proves the execution of the given program with the given input in the compressed mode. + /// + /// Returns a compressed proof of the program's execution. The compressed proof is a succinct + /// proof that is of constant size and friendly for recursion and off-chain verification. + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// // Load the program. + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// + /// // Initialize the prover client. + /// let client = ProverClient::new(); + /// + /// // Setup the program. + /// let (pk, vk) = client.setup(elf); + /// + /// // Setup the inputs. + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// + /// // Generate the proof. + /// let proof = client.prove_compressed(&pk, stdin).unwrap(); + /// ``` + pub fn prove_compressed( + &self, + pk: &SP1ProvingKey, + stdin: SP1Stdin, + ) -> Result { + self.prover.prove_compressed(pk, stdin) } + /// Proves the execution of the given program with the given input in the groth16 mode. + /// + /// Returns a proof of the program's execution in the groth16 format. The proof is a succinct + /// proof that is of constant size and friendly for on-chain verification. + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// // Load the program. + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// + /// // Initialize the prover client. + /// let client = ProverClient::new(); + /// + /// // Setup the program. + /// let (pk, vk) = client.setup(elf); + /// + /// // Setup the inputs. + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// + /// // Generate the proof. + /// let proof = client.prove_groth16(&pk, stdin).unwrap(); + /// ``` /// Generates a groth16 proof, verifiable onchain, of the given elf and stdin. pub fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { self.prover.prove_groth16(pk, stdin) } - /// Generates a PLONK proof, verifiable onchain, of the given elf and stdin. + /// Proves the execution of the given program with the given input in the plonk mode. + /// + /// Returns a proof of the program's execution in the plonk format. The proof is a succinct + /// proof that is of constant size and friendly for on-chain verification. + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// // Load the program. + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// + /// // Initialize the prover client. + /// let client = ProverClient::new(); + /// + /// // Setup the program. + /// let (pk, vk) = client.setup(elf); + /// + /// // Setup the inputs. + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// + /// // Generate the proof. + /// let proof = client.prove_plonk(&pk, stdin).unwrap(); + /// ``` pub fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { self.prover.prove_plonk(pk, stdin) } - /// Verifies the given proof is valid and matches the given vkey. - pub fn verify(&self, proof: &SP1CoreProof, vkey: &SP1VerifyingKey) -> Result<()> { + /// Verifies that the given proof is valid and matches the given verification key produced by + /// [Self::setup]. + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// let client = ProverClient::new(); + /// let (pk, vk) = client.setup(elf); + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// let proof = client.prove(&pk, stdin).unwrap(); + /// client.verify(&proof, &vk).unwrap(); + /// ``` + pub fn verify(&self, proof: &SP1Proof, vkey: &SP1VerifyingKey) -> Result<()> { self.prover.verify(proof, vkey) } - /// Verifies the given compressed proof is valid and matches the given vkey. - pub fn verify_compressed(&self, proof: &SP1ReducedProof, vkey: &SP1VerifyingKey) -> Result<()> { - self.prover.verify_reduced(proof, vkey) - } - - /// Verifies the given groth16 proof is valid and matches the given vkey. - pub fn verify_plonk(&self, proof: &SP1PlonkProof, vkey: &SP1VerifyingKey) -> Result<()> { - self.prover.verify_plonk(proof, vkey) + /// Verifies that the given compressed proof is valid and matches the given verification key + /// produced by [Self::setup]. + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// // Load the program. + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// + /// // Initialize the prover client. + /// let client = ProverClient::new(); + /// + /// // Setup the program. + /// let (pk, vk) = client.setup(elf); + /// + /// // Setup the inputs. + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// + /// // Generate the proof. + /// let proof = client.prove_compressed(&pk, stdin).unwrap(); + /// client.verify_compressed(&proof, &vk).unwrap(); + /// ``` + pub fn verify_compressed( + &self, + proof: &SP1CompressedProof, + vkey: &SP1VerifyingKey, + ) -> Result<()> { + self.prover.verify_compressed(proof, vkey) } - /// Verifies the given groth16 proof is valid and matches the given vkey. + /// Verifies that the given groth16 proof is valid and matches the given verification key + /// produced by [Self::setup]. + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// // Load the program. + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// + /// // Initialize the prover client. + /// let client = ProverClient::new(); + /// + /// // Setup the program. + /// let (pk, vk) = client.setup(elf); + /// + /// // Setup the inputs. + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// + /// // Generate the proof. + /// let proof = client.prove_groth16(&pk, stdin).unwrap(); + /// + /// // Verify the proof. + /// client.verify_groth16(&proof, &vk).unwrap(); + /// ``` pub fn verify_groth16(&self, proof: &SP1Groth16Proof, vkey: &SP1VerifyingKey) -> Result<()> { self.prover.verify_groth16(proof, vkey) } + + /// Verifies that the given plonk proof is valid and matches the given verification key + /// produced by [Self::setup]. + /// + /// ### Examples + /// ``` + /// use sp1_sdk::{ProverClient, SP1Stdin}; + /// + /// // Load the program. + /// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + /// + /// // Initialize the prover client. + /// let client = ProverClient::new(); + /// + /// // Setup the program. + /// let (pk, vk) = client.setup(elf); + /// + /// // Setup the inputs. + /// let mut stdin = SP1Stdin::new(); + /// stdin.write(&10usize); + /// + /// // Generate the proof. + /// let proof = client.prove_plonk(&pk, stdin).unwrap(); + /// + /// // Verify the proof. + /// client.verify_plonk(&proof, &vk).unwrap(); + /// ``` + pub fn verify_plonk(&self, proof: &SP1PlonkProof, vkey: &SP1VerifyingKey) -> Result<()> { + self.prover.verify_plonk(proof, vkey) + } } -#[derive(Serialize, Deserialize)] -pub struct ProofStatistics { - pub cycle_count: u64, - pub cost: u64, - pub total_time: u64, - pub latency: u64, +impl Default for ProverClient { + fn default() -> Self { + Self::new() + } } -pub trait Prover: Send + Sync { - fn setup(&self, elf: &[u8]) -> (SP1ProvingKey, SP1VerifyingKey); - /// Prove the execution of a RISCV ELF with the given inputs. - fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result; +impl SP1ProofWithPublicValues

{ + /// Saves the proof to a path. + pub fn save(&self, path: impl AsRef) -> Result<()> { + bincode::serialize_into(File::create(path).expect("failed to open file"), self) + .map_err(Into::into) + } - /// Given an SP1 program and input, generate a reduced proof of its execution. Reduced proofs - /// are constant size and can be verified inside of SP1. - fn prove_reduced(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result; + /// Loads a proof from a path. + pub fn load(path: impl AsRef) -> Result { + bincode::deserialize_from(File::open(path).expect("failed to open file")) + .map_err(Into::into) + } +} - /// Given an SP1 program and input, generate a PLONK proof that can be verified on-chain. - fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result; +#[cfg(test)] +mod tests { - /// Given an SP1 program and input, generate a Groth16 proof that can be verified on-chain. - fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result; + use crate::{utils, ProverClient, SP1Stdin}; - /// Verify that an SP1 proof is valid given its vkey and metadata. - fn verify(&self, proof: &SP1CoreProof, vkey: &SP1VerifyingKey) -> Result<()>; + #[test] + fn test_execute() { + utils::setup_logger(); + let client = ProverClient::local(); + let elf = + include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + let mut stdin = SP1Stdin::new(); + stdin.write(&10usize); + let _ = client.execute(elf, stdin).unwrap(); + } - /// Verify that a compressed SP1 proof is valid given its vkey and metadata. - fn verify_reduced(&self, proof: &SP1ReducedProof, vkey: &SP1VerifyingKey) -> Result<()>; + #[test] + fn test_e2e_prove_local() { + utils::setup_logger(); + let client = ProverClient::local(); + let elf = + include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + let (pk, vk) = client.setup(elf); + let mut stdin = SP1Stdin::new(); + stdin.write(&10usize); + let proof = client.prove(&pk, stdin).unwrap(); + client.verify(&proof, &vk).unwrap(); + } - /// Verify that a SP1 PLONK proof is valid given its vkey and metadata. - fn verify_plonk(&self, proof: &SP1PlonkProof, vkey: &SP1VerifyingKey) -> Result<()>; + #[test] + fn test_e2e_prove_groth16() { + utils::setup_logger(); + let client = ProverClient::local(); + let elf = + include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + let (pk, vk) = client.setup(elf); + let mut stdin = SP1Stdin::new(); + stdin.write(&10usize); + let proof = client.prove_groth16(&pk, stdin).unwrap(); + client.verify_groth16(&proof, &vk).unwrap(); + } - /// Verify that a SP1 Groth16 proof is valid given its vkey and metadata. - fn verify_groth16(&self, proof: &SP1Groth16Proof, vkey: &SP1VerifyingKey) -> Result<()>; + #[test] + fn test_e2e_prove_mock() { + utils::setup_logger(); + let client = ProverClient::mock(); + let elf = + include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); + let (pk, vk) = client.setup(elf); + let mut stdin = SP1Stdin::new(); + stdin.write(&10usize); + let proof = client.prove(&pk, stdin).unwrap(); + client.verify(&proof, &vk).unwrap(); + } } diff --git a/sdk/src/local.rs b/sdk/src/local.rs deleted file mode 100644 index e86cc809f9..0000000000 --- a/sdk/src/local.rs +++ /dev/null @@ -1,135 +0,0 @@ -#![allow(unused_variables)] - -use std::path::PathBuf; - -use crate::{ - artifacts::{ - build_circuit_artifacts, get_artifacts_dir, get_dev_mode, install_circuit_artifacts, - WrapCircuitType, - }, - utils::EnvVarGuard, - Prover, SP1Groth16ProofData, SP1PlonkProofData, SP1ProofWithMetadata, SP1ProvingKey, - SP1VerifyingKey, -}; -use anyhow::Result; -use sp1_prover::{ - SP1CoreProof, SP1Groth16Proof, SP1PlonkProof, SP1Prover, SP1ReducedProof, SP1ReducedProofData, - SP1Stdin, -}; - -pub struct LocalProver { - pub(crate) prover: SP1Prover, -} - -impl Default for LocalProver { - fn default() -> Self { - Self::new() - } -} - -impl LocalProver { - pub fn new() -> Self { - let prover = SP1Prover::new(); - Self { prover } - } - - /// Initialize circuit artifacts by installing or building if in dev mode. - pub(crate) fn initialize_circuit(&self, circuit_type: WrapCircuitType) -> PathBuf { - let is_dev_mode = get_dev_mode(); - let artifacts_dir = get_artifacts_dir(circuit_type, is_dev_mode); - - if !artifacts_dir.exists() { - log::info!("First time initializing circuit artifacts"); - } - - if is_dev_mode { - build_circuit_artifacts(circuit_type, false, Some(artifacts_dir.clone())) - .expect("Failed to build circuit artifacts.") - } else { - install_circuit_artifacts( - WrapCircuitType::Groth16, - false, - Some(artifacts_dir.clone()), - None, - ) - .expect("Failed to install circuit artifacts"); - } - artifacts_dir - } -} - -impl Prover for LocalProver { - fn setup(&self, elf: &[u8]) -> (SP1ProvingKey, SP1VerifyingKey) { - self.prover.setup(elf) - } - - fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - let proof = self.prover.prove_core(pk, &stdin); - Ok(proof) - } - - fn prove_reduced(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - let proof = self.prover.prove_core(pk, &stdin); - let deferred_proofs = stdin.proofs.iter().map(|p| p.0.clone()).collect(); - let public_values = proof.public_values.clone(); - let _guard = EnvVarGuard::new("RECONSTRUCT_COMMITMENTS", "false"); - let reduce_proof = self.prover.reduce(&pk.vk, proof.proof, deferred_proofs); - Ok(SP1ReducedProof { - proof: SP1ReducedProofData(reduce_proof.proof), - stdin, - public_values, - }) - } - - fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - let artifacts_dir = self.initialize_circuit(WrapCircuitType::Groth16); - let proof = self.prover.prove_core(pk, &stdin); - let deferred_proofs = stdin.proofs.iter().map(|p| p.0.clone()).collect(); - let public_values = proof.public_values.clone(); - let _guard = EnvVarGuard::new("RECONSTRUCT_COMMITMENTS", "false"); - let reduce_proof = self.prover.reduce(&pk.vk, proof.proof, deferred_proofs); - let compress_proof = self.prover.compress(&pk.vk, reduce_proof); - let outer_proof = self.prover.wrap_bn254(&pk.vk, compress_proof); - let proof = self.prover.wrap_groth16(outer_proof, artifacts_dir); - Ok(SP1Groth16Proof { - proof: SP1Groth16ProofData(proof), - stdin, - public_values, - }) - } - - fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - let artifacts_dir = self.initialize_circuit(WrapCircuitType::Plonk); - let proof = self.prover.prove_core(pk, &stdin); - let deferred_proofs = stdin.proofs.iter().map(|p| p.0.clone()).collect(); - let public_values = proof.public_values.clone(); - let _guard = EnvVarGuard::new("RECONSTRUCT_COMMITMENTS", "false"); - let reduce_proof = self.prover.reduce(&pk.vk, proof.proof, deferred_proofs); - let compress_proof = self.prover.compress(&pk.vk, reduce_proof); - let outer_proof = self.prover.wrap_bn254(&pk.vk, compress_proof); - let proof = self.prover.wrap_plonk(outer_proof, artifacts_dir); - Ok(SP1ProofWithMetadata { - proof: SP1PlonkProofData(proof), - stdin, - public_values, - }) - } - - fn verify(&self, proof: &SP1CoreProof, vkey: &SP1VerifyingKey) -> Result<()> { - self.prover.verify(&proof.proof, vkey).map_err(|e| e.into()) - } - - fn verify_reduced(&self, proof: &SP1ReducedProof, vkey: &SP1VerifyingKey) -> Result<()> { - self.prover - .verify_reduced(&proof.proof, vkey) - .map_err(|e| e.into()) - } - - fn verify_groth16(&self, proof: &SP1Groth16Proof, vkey: &SP1VerifyingKey) -> Result<()> { - todo!() - } - - fn verify_plonk(&self, proof: &SP1PlonkProof, vkey: &SP1VerifyingKey) -> Result<()> { - todo!() - } -} diff --git a/sdk/src/mock.rs b/sdk/src/mock.rs deleted file mode 100644 index 26ed29cea9..0000000000 --- a/sdk/src/mock.rs +++ /dev/null @@ -1,80 +0,0 @@ -#![allow(unused_variables)] -use crate::{ - Prover, SP1Groth16ProofData, SP1PlonkProofData, SP1ProofWithMetadata, SP1ProvingKey, - SP1VerifyingKey, -}; -use anyhow::Result; -use sp1_prover::{ - Groth16Proof, PlonkBn254Proof, SP1CoreProof, SP1CoreProofData, SP1Groth16Proof, SP1PlonkProof, - SP1Prover, SP1ReducedProof, SP1Stdin, -}; - -pub struct MockProver { - pub(crate) prover: SP1Prover, -} - -impl Default for MockProver { - fn default() -> Self { - Self::new() - } -} - -impl MockProver { - pub fn new() -> Self { - let prover = SP1Prover::new(); - Self { prover } - } -} - -impl Prover for MockProver { - fn setup(&self, elf: &[u8]) -> (SP1ProvingKey, SP1VerifyingKey) { - self.prover.setup(elf) - } - - fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - let public_values = SP1Prover::execute(&pk.elf, &stdin); - Ok(SP1ProofWithMetadata { - proof: SP1CoreProofData(vec![]), - stdin, - public_values, - }) - } - - fn prove_reduced(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - unimplemented!() - } - - fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - let public_values = SP1Prover::execute(&pk.elf, &stdin); - Ok(SP1ProofWithMetadata { - proof: SP1PlonkProofData(PlonkBn254Proof::default()), - stdin, - public_values, - }) - } - - fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - let public_values = SP1Prover::execute(&pk.elf, &stdin); - Ok(SP1ProofWithMetadata { - proof: SP1Groth16ProofData(Groth16Proof::default()), - stdin, - public_values, - }) - } - - fn verify(&self, proof: &SP1CoreProof, vkey: &SP1VerifyingKey) -> Result<()> { - Ok(()) - } - - fn verify_reduced(&self, proof: &SP1ReducedProof, vkey: &SP1VerifyingKey) -> Result<()> { - Ok(()) - } - - fn verify_groth16(&self, proof: &SP1Groth16Proof, vkey: &SP1VerifyingKey) -> Result<()> { - Ok(()) - } - - fn verify_plonk(&self, proof: &SP1PlonkProof, vkey: &SP1VerifyingKey) -> Result<()> { - Ok(()) - } -} diff --git a/sdk/src/provers/local.rs b/sdk/src/provers/local.rs new file mode 100644 index 0000000000..5e5676981f --- /dev/null +++ b/sdk/src/provers/local.rs @@ -0,0 +1,131 @@ +use anyhow::Result; +use sp1_prover::{SP1Prover, SP1Stdin}; + +use super::utils; +use crate::{ + Prover, SP1CompressedProof, SP1Groth16Proof, SP1PlonkProof, SP1Proof, SP1ProofWithPublicValues, + SP1ProvingKey, SP1VerifyingKey, +}; + +/// An implementation of [crate::ProverClient] that can generate end-to-end proofs locally. +pub struct LocalProver { + prover: SP1Prover, +} + +impl LocalProver { + /// Creates a new [LocalProver]. + pub fn new() -> Self { + let prover = SP1Prover::new(); + Self { prover } + } +} + +impl Prover for LocalProver { + fn id(&self) -> String { + "local".to_string() + } + + fn setup(&self, elf: &[u8]) -> (SP1ProvingKey, SP1VerifyingKey) { + self.prover.setup(elf) + } + + fn sp1_prover(&self) -> &SP1Prover { + &self.prover + } + + fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + let proof = self.prover.prove_core(pk, &stdin); + Ok(SP1ProofWithPublicValues { + proof: proof.proof.0, + stdin: proof.stdin, + public_values: proof.public_values, + }) + } + + fn prove_compressed(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + let proof = self.prover.prove_core(pk, &stdin); + let deferred_proofs = stdin.proofs.iter().map(|p| p.0.clone()).collect(); + let public_values = proof.public_values.clone(); + let reduce_proof = self.prover.compress(&pk.vk, proof, deferred_proofs); + Ok(SP1CompressedProof { + proof: reduce_proof.proof, + stdin, + public_values, + }) + } + + fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + let proof = self.prover.prove_core(pk, &stdin); + let deferred_proofs = stdin.proofs.iter().map(|p| p.0.clone()).collect(); + let public_values = proof.public_values.clone(); + let reduce_proof = self.prover.compress(&pk.vk, proof, deferred_proofs); + let compress_proof = self.prover.shrink(&pk.vk, reduce_proof); + let outer_proof = self.prover.wrap_bn254(&pk.vk, compress_proof); + + // If `SP1_GROTH16_DEV_MODE` is enabled, we will compile a smaller version of the final + // circuit and rebuild it for every proof. + // + // This is useful for development and testing purposes, as it allows us to test the + // end-to-end proving without having to wait for the circuit to compile or download. + let artifacts_dir = if utils::groth16_dev_mode() { + tracing::debug!("proving groth16 inside development mode"); + let build_dir = tempfile::tempdir() + .expect("failed to create temporary directory") + .into_path(); + if let Err(err) = std::fs::create_dir_all(&build_dir) { + panic!( + "failed to create build directory for groth16 artifacts: {}", + err + ); + } + sp1_prover::build::groth16_artifacts( + &self.prover.wrap_vk, + &outer_proof, + build_dir.clone(), + ); + build_dir + } + // If `SP1_GROTH16_ARTIFACTS_DIR` is set, we will use the artifacts from that directory. + // + // This is useful for when you want to test the production circuit and have a local build + // available for development purposes. + else if let Some(artifacts_dir) = utils::groth16_artifacts_dir() { + artifacts_dir + } + // Otherwise, assume this is an official release and download the artifacts from the + // official download url. + else { + sp1_prover::install::groth16_artifacts(); + sp1_prover::install::groth16_artifacts_dir() + }; + + let proof = self.prover.wrap_groth16(outer_proof, artifacts_dir); + Ok(SP1ProofWithPublicValues { + proof, + stdin, + public_values, + }) + } + + fn prove_plonk(&self, _pk: &SP1ProvingKey, _stdin: SP1Stdin) -> Result { + // let proof = self.prover.prove_core(pk, &stdin); + // let deferred_proofs = stdin.proofs.iter().map(|p| p.0.clone()).collect(); + // let public_values = proof.public_values.clone(); + // let reduce_proof = self.prover.compress(&pk.vk, proof, deferred_proofs); + // let compress_proof = self.prover.shrink(&pk.vk, reduce_proof); + // let outer_proof = self.prover.wrap_bn254(&pk.vk, compress_proof); + // let proof = self.prover.wrap_plonk(outer_proof, artifacts_dir); + // Ok(SP1ProofWithPublicValues { + // proof, + // stdin, + // public_values, + // }) + todo!() + } +} + +impl Default for LocalProver { + fn default() -> Self { + Self::new() + } +} diff --git a/sdk/src/provers/mock.rs b/sdk/src/provers/mock.rs new file mode 100644 index 0000000000..ffea8ae55a --- /dev/null +++ b/sdk/src/provers/mock.rs @@ -0,0 +1,85 @@ +#![allow(unused_variables)] +use crate::{ + Prover, SP1CompressedProof, SP1Groth16Proof, SP1PlonkProof, SP1Proof, SP1ProofWithPublicValues, + SP1ProvingKey, SP1VerifyingKey, +}; +use anyhow::Result; +use sp1_prover::{SP1Prover, SP1Stdin}; + +/// An implementation of [crate::ProverClient] that can generate mock proofs. +pub struct MockProver { + pub(crate) prover: SP1Prover, +} + +impl MockProver { + /// Creates a new [MockProver]. + pub fn new() -> Self { + let prover = SP1Prover::new(); + Self { prover } + } +} + +impl Prover for MockProver { + fn id(&self) -> String { + "mock".to_string() + } + + fn setup(&self, elf: &[u8]) -> (SP1ProvingKey, SP1VerifyingKey) { + self.prover.setup(elf) + } + + fn sp1_prover(&self) -> &SP1Prover { + unimplemented!("MockProver does not support SP1Prover") + } + + fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + let public_values = SP1Prover::execute(&pk.elf, &stdin); + Ok(SP1ProofWithPublicValues { + proof: vec![], + stdin, + public_values, + }) + } + + fn prove_compressed( + &self, + _pk: &SP1ProvingKey, + _stdin: SP1Stdin, + ) -> Result { + unimplemented!() + } + + fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + todo!() + } + + fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + todo!() + } + + fn verify(&self, _proof: &SP1Proof, _vkey: &SP1VerifyingKey) -> Result<()> { + Ok(()) + } + + fn verify_compressed( + &self, + _proof: &SP1CompressedProof, + _vkey: &SP1VerifyingKey, + ) -> Result<()> { + Ok(()) + } + + fn verify_groth16(&self, _proof: &SP1Groth16Proof, _vkey: &SP1VerifyingKey) -> Result<()> { + Ok(()) + } + + fn verify_plonk(&self, _proof: &SP1PlonkProof, _vkey: &SP1VerifyingKey) -> Result<()> { + Ok(()) + } +} + +impl Default for MockProver { + fn default() -> Self { + Self::new() + } +} diff --git a/sdk/src/provers/mod.rs b/sdk/src/provers/mod.rs new file mode 100644 index 0000000000..cbd5906805 --- /dev/null +++ b/sdk/src/provers/mod.rs @@ -0,0 +1,77 @@ +mod local; +mod mock; +mod network; +mod utils; + +use crate::{SP1CompressedProof, SP1Groth16Proof, SP1PlonkProof, SP1Proof}; +use anyhow::Result; +pub use local::LocalProver; +pub use mock::MockProver; +pub use network::NetworkProver; +use sha2::{Digest, Sha256}; +use sp1_core::air::PublicValues; +use sp1_core::stark::MachineProof; +use sp1_core::stark::StarkGenericConfig; +use sp1_prover::SP1Prover; +use sp1_prover::{SP1ProvingKey, SP1Stdin, SP1VerifyingKey}; + +/// An implementation of [crate::ProverClient]. +pub trait Prover: Send + Sync { + fn id(&self) -> String; + + fn sp1_prover(&self) -> &SP1Prover; + + fn setup(&self, elf: &[u8]) -> (SP1ProvingKey, SP1VerifyingKey); + + /// Prove the execution of a RISCV ELF with the given inputs. + fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result; + + /// Generate a compressed proof of the execution of a RISCV ELF with the given inputs. + fn prove_compressed(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result; + + /// Given an SP1 program and input, generate a Groth16 proof that can be verified on-chain. + fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result; + + /// Given an SP1 program and input, generate a PLONK proof that can be verified on-chain. + fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result; + + /// Verify that an SP1 proof is valid given its vkey and metadata. + fn verify(&self, proof: &SP1Proof, vkey: &SP1VerifyingKey) -> Result<()> { + let pv = PublicValues::from_vec(proof.proof[0].public_values.clone()); + let pv_digest: [u8; 32] = Sha256::digest(proof.public_values.as_slice()).into(); + if pv_digest != *pv.commit_digest_bytes() { + return Err(anyhow::anyhow!("Public values digest mismatch")); + } + let machine_proof = MachineProof { + shard_proofs: proof.proof.clone(), + }; + let sp1_prover = self.sp1_prover(); + let mut challenger = sp1_prover.core_machine.config().challenger(); + Ok(sp1_prover + .core_machine + .verify(&vkey.vk, &machine_proof, &mut challenger)?) + } + + /// Verify that a compressed SP1 proof is valid given its vkey and metadata. + fn verify_compressed(&self, proof: &SP1CompressedProof, vkey: &SP1VerifyingKey) -> Result<()> { + // TODO: implement verification of the digest of the public values matching + let sp1_prover = self.sp1_prover(); + let machine_proof = MachineProof { + shard_proofs: vec![proof.proof.clone()], + }; + let mut challenger = sp1_prover.compress_machine.config().challenger(); + Ok(sp1_prover + .compress_machine + .verify(&vkey.vk, &machine_proof, &mut challenger)?) + } + + /// Verify that a SP1 Groth16 proof is valid given its vkey and metadata. + fn verify_groth16(&self, _proof: &SP1Groth16Proof, _vkey: &SP1VerifyingKey) -> Result<()> { + Ok(()) + } + + /// Verify that a SP1 PLONK proof is valid given its vkey and metadata. + fn verify_plonk(&self, _proof: &SP1PlonkProof, _vkey: &SP1VerifyingKey) -> Result<()> { + Ok(()) + } +} diff --git a/sdk/src/network.rs b/sdk/src/provers/network.rs similarity index 86% rename from sdk/src/network.rs rename to sdk/src/provers/network.rs index 21e510a915..17e9c5b301 100644 --- a/sdk/src/network.rs +++ b/sdk/src/provers/network.rs @@ -1,33 +1,29 @@ -#![allow(unused_variables)] use std::{env, time::Duration}; use crate::proto::network::ProofMode; use crate::{ client::NetworkClient, - local::LocalProver, proto::network::{ProofStatus, TransactionStatus}, Prover, }; +use crate::{ + SP1CompressedProof, SP1Groth16Proof, SP1PlonkProof, SP1Proof, SP1ProvingKey, SP1VerifyingKey, +}; use anyhow::{Context, Result}; use serde::de::DeserializeOwned; -use sp1_prover::{ - SP1CoreProof, SP1Groth16Proof, SP1PlonkProof, SP1Prover, SP1ProvingKey, SP1ReducedProof, - SP1Stdin, SP1VerifyingKey, -}; +use sp1_prover::{SP1Prover, SP1Stdin}; use tokio::{runtime, time::sleep}; +use super::LocalProver; + +/// An implementation of [crate::ProverClient] that can generate proofs on a remote RPC server. pub struct NetworkProver { client: NetworkClient, local_prover: LocalProver, } -impl Default for NetworkProver { - fn default() -> Self { - Self::new() - } -} - impl NetworkProver { + /// Creates a new [NetworkProver]. pub fn new() -> Self { let private_key = env::var("SP1_PRIVATE_KEY") .unwrap_or_else(|_| panic!("SP1_PRIVATE_KEY must be set for remote proving")); @@ -48,7 +44,7 @@ impl NetworkProver { // Execute the runtime before creating the proof request. // TODO: Maybe we don't want to always do this locally, with large programs. Or we may want // to disable events at least. - let public_values = SP1Prover::execute(elf, &stdin); + let _public_values = SP1Prover::execute(elf, &stdin); println!("Simulation complete"); let proof_id = client.create_proof(elf, &stdin, mode).await?; @@ -144,16 +140,24 @@ impl NetworkProver { } impl Prover for NetworkProver { + fn id(&self) -> String { + "remote".to_string() + } + fn setup(&self, elf: &[u8]) -> (SP1ProvingKey, SP1VerifyingKey) { self.local_prover.setup(elf) } - fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + fn sp1_prover(&self) -> &SP1Prover { + self.local_prover.sp1_prover() + } + + fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { let rt = tokio::runtime::Runtime::new()?; rt.block_on(async { self.prove_async(&pk.elf, stdin, ProofMode::Core).await }) } - fn prove_reduced(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + fn prove_compressed(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { let rt = tokio::runtime::Runtime::new()?; rt.block_on(async { self.prove_async(&pk.elf, stdin, ProofMode::Compressed) @@ -161,29 +165,19 @@ impl Prover for NetworkProver { }) } - fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - let rt = tokio::runtime::Runtime::new()?; - rt.block_on(async { self.prove_async(&pk.elf, stdin, ProofMode::Plonk).await }) - } - fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { let rt = tokio::runtime::Runtime::new()?; rt.block_on(async { self.prove_async(&pk.elf, stdin, ProofMode::Groth16).await }) } - fn verify(&self, proof: &SP1CoreProof, vkey: &SP1VerifyingKey) -> Result<()> { - self.local_prover.verify(proof, vkey) - } - - fn verify_reduced(&self, proof: &SP1ReducedProof, vkey: &SP1VerifyingKey) -> Result<()> { - self.local_prover.verify_reduced(proof, vkey) - } - - fn verify_plonk(&self, proof: &SP1PlonkProof, vkey: &SP1VerifyingKey) -> Result<()> { - self.local_prover.verify_plonk(proof, vkey) + fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { + let rt = tokio::runtime::Runtime::new()?; + rt.block_on(async { self.prove_async(&pk.elf, stdin, ProofMode::Plonk).await }) } +} - fn verify_groth16(&self, proof: &SP1Groth16Proof, vkey: &SP1VerifyingKey) -> Result<()> { - self.local_prover.verify_groth16(proof, vkey) +impl Default for NetworkProver { + fn default() -> Self { + Self::new() } } diff --git a/sdk/src/provers/utils.rs b/sdk/src/provers/utils.rs new file mode 100644 index 0000000000..446d4bb63e --- /dev/null +++ b/sdk/src/provers/utils.rs @@ -0,0 +1,22 @@ +use std::path::PathBuf; + +/// Returns whether the `SP1_GROTH16_DEV_MODE` environment variable is enabled or disabled. +/// +/// This variable controls whether a smaller version of the circuit will be used for generating the +/// Groth16 proofs. This is useful for development and testing purposes. +/// +/// By default, the variable is enabled. It should be disabled for production use. +pub fn groth16_dev_mode() -> bool { + let value = std::env::var("SP1_GROTH16_DEV_MODE").unwrap_or_else(|_| "true".to_string()); + value == "1" || value.to_lowercase() == "true" +} + +/// Returns the path to the directory where the groth16 artifacts are stored. +/// +/// This variable is useful for when you want to test the production circuit and have a local build +/// available for development purposes. +pub fn groth16_artifacts_dir() -> Option { + std::env::var("SP1_GROTH16_ARTIFACTS_DIR") + .map(PathBuf::from) + .ok() +} diff --git a/sdk/src/utils.rs b/sdk/src/utils.rs deleted file mode 100644 index 6f50996583..0000000000 --- a/sdk/src/utils.rs +++ /dev/null @@ -1,30 +0,0 @@ -pub use sp1_core::utils::{setup_logger, setup_tracer}; - -use std::env; - -/// A guard that sets an environment variable to a new value and restores the original value when -/// dropped. -pub(crate) struct EnvVarGuard { - name: String, - original_value: Option, -} - -impl EnvVarGuard { - pub(crate) fn new(name: &str, value: &str) -> Self { - let original_value = env::var(name).ok(); - env::set_var(name, value); - EnvVarGuard { - name: name.to_string(), - original_value, - } - } -} - -impl Drop for EnvVarGuard { - fn drop(&mut self) { - match &self.original_value { - Some(value) => env::set_var(&self.name, value), - None => env::remove_var(&self.name), - } - } -}