diff --git a/book/src/getting-started/quickstart.md b/book/src/getting-started/quickstart.md index ae2d4de132..f0d5ca1f4b 100644 --- a/book/src/getting-started/quickstart.md +++ b/book/src/getting-started/quickstart.md @@ -7,10 +7,10 @@ In this section we will build and run a fibonacci program. First, create a new Rust project. ```bash -cargo init fibonacci +cargo openvm init fibonacci ``` -In `Cargo.toml`, add the following dependency: +This will generate an OpenVM-specific starter package. Notice `Cargo.toml` has the following dependency: ```toml [dependencies] @@ -49,7 +49,7 @@ To build the program, run: cargo openvm build ``` -This will output an OpenVM executable file to `./openvm/app.vmexe`. +This will output an OpenVM executable file to `./target/openvm/release/fibonacci.vmexe`. ## Keygen @@ -59,7 +59,7 @@ Before generating any proofs, we will also need to generate the proving and veri cargo openvm keygen ``` -This will output a serialized proving key to `./openvm/app.pk` and a verification key to `./openvm/app.vk`. +This will output a serialized proving key to `./target/openvm/app.pk` and a verification key to `./target/openvm/app.vk`. ## Proof Generation @@ -72,7 +72,7 @@ OPENVM_FAST_TEST=1 cargo openvm prove app --input "0x010A00000000000000" The `--input` field is passed to the program which receives it via the `io::read` function. In our `main.rs` we called `read()` to get `n: u64`. The input here is `n = 10u64` _in little endian_. Note that this value must be padded to exactly 8 bytes (64 bits) and is prefixed with `0x01` to indicate that the input is composed of raw bytes. -The serialized proof will be output to `./openvm/app.proof`. +The serialized proof will be output to `./fibonacci.app.proof`. The `OPENVM_FAST_TEST` environment variable is used to enable fast proving for testing purposes. To run with proof with secure parameters, remove the environmental variable. diff --git a/book/src/writing-apps/overview.md b/book/src/writing-apps/overview.md index 6666813193..9603ddef75 100644 --- a/book/src/writing-apps/overview.md +++ b/book/src/writing-apps/overview.md @@ -22,7 +22,7 @@ cargo openvm run --input Note if your program doesn't require inputs, you can omit the `--input` flag. -For more information on both commands, see the [build](./build.md) docs. +For more information see the [build](./build.md) and [run](./run.md) docs. ### Inputs diff --git a/book/src/writing-apps/prove.md b/book/src/writing-apps/prove.md index 2464ff3c45..1a4b3cc1e6 100644 --- a/book/src/writing-apps/prove.md +++ b/book/src/writing-apps/prove.md @@ -40,8 +40,7 @@ If `--exe` is not provided, the command will call `build` before generating a pr If your program doesn't require inputs, you can (and should) omit the `--input` flag. -If `--proof` is not provided then the command will write the proof to `./[app | stark | evm].proof` by default. - +If `--proof` is not provided then the command will write the proof to `./${bin_name}.[app | stark | evm].proof` by default, where `bin_name` is the file stem of the executable run. The `app` subcommand generates an application-level proof, the `stark` command generates an aggregated root-level proof, while the `evm` command generates an end-to-end EVM proof. For more information on aggregation, see [this specification](https://github.com/openvm-org/openvm/blob/bf8df90b13f4e80bb76dbb71f255a12154c84838/docs/specs/continuations.md). diff --git a/book/src/writing-apps/verify.md b/book/src/writing-apps/verify.md index bf813a350b..8a45f31ebd 100644 --- a/book/src/writing-apps/verify.md +++ b/book/src/writing-apps/verify.md @@ -10,7 +10,9 @@ cargo openvm verify app --proof ``` -Options `--manifest-path`, `--target-dir` are also available to `verify`. If you omit `--app_vk` and/or `--proof`, the command will search for those files at `${target_dir}/openvm/app.vk` and `./app.proof` respectively. +Options `--manifest-path`, `--target-dir` are also available to `verify`. If you omit `--app_vk` the command will search for the verifying key at `${target_dir}/openvm/app.vk`. + +If you omit `--proof`, the command will search the working directory for files with the `.app.proof` extension. Note that for this default case a single proof is expected to be found, and `verify` will fail otherwise. ## EVM Level @@ -93,11 +95,11 @@ cargo openvm prove evm --input cargo openvm verify evm --proof ``` -If `proof` is omitted, the `verify` command will search for the proof at `./openvm/evm.proof`. +If `proof` is omitted, the `verify` command will search for a file with extension `.evm.proof` in the working directory. ### EVM Proof: JSON Format -The EVM proof is written to `evm.proof` as a JSON of the following format: +The EVM proof is written as a JSON of the following format: ```json { diff --git a/book/src/writing-apps/write-program.md b/book/src/writing-apps/write-program.md index b4005953af..24f00fe10a 100644 --- a/book/src/writing-apps/write-program.md +++ b/book/src/writing-apps/write-program.md @@ -2,16 +2,17 @@ ## Writing a guest program -See the example [fibonacci program](https://github.com/openvm-org/openvm-example-fibonacci). +To initialize an OpenVM guest program package, you can use the following CLI command: -The guest program should be a `no_std` Rust crate. As long as it is `no_std`, you can import any other -`no_std` crates and write Rust as you normally would. Import the `openvm` library crate to use `openvm` intrinsic functions (for example `openvm::io::*`). +```bash +cargo openvm init +``` -More examples of guest programs can be found in the [benchmarks/guest](https://github.com/openvm-org/openvm/tree/main/benchmarks/guest) directory. +For a guest program example, see this [fibonacci program](https://github.com/openvm-org/openvm-example-fibonacci). More examples can be found in the [benchmarks/guest](https://github.com/openvm-org/openvm/tree/main/benchmarks/guest) directory. ## Handling I/O -The program can take input from stdin, with some functions provided by `openvm::io`. +The program can take input from stdin, with some functions provided by `openvm::io`. Make sure to import the `openvm` library crate to use `openvm` intrinsic functions. `openvm::io::read` takes from stdin and deserializes it into a generic type `T`, so one should specify the type when calling it: diff --git a/crates/cli/src/commands/commit.rs b/crates/cli/src/commands/commit.rs index 9850a31b0c..22e3b16d4f 100644 --- a/crates/cli/src/commands/commit.rs +++ b/crates/cli/src/commands/commit.rs @@ -11,9 +11,7 @@ use openvm_sdk::{commit::AppExecutionCommit, fs::write_to_file_json, Sdk}; use super::{RunArgs, RunCargoArgs}; use crate::{ commands::{load_app_pk, load_or_build_and_commit_exe}, - util::{ - get_manifest_path_and_dir, get_single_target_name, get_target_dir, get_target_output_dir, - }, + util::{get_manifest_path_and_dir, get_target_dir, get_target_output_dir}, }; #[derive(Parser)] @@ -76,7 +74,7 @@ impl CommitCmd { init_file_name: self.init_file_name.clone(), input: None, }; - let committed_exe = + let (committed_exe, target_name) = load_or_build_and_commit_exe(&sdk, &run_args, &self.cargo_args, &app_pk)?; let commits = AppExecutionCommit::compute( @@ -91,7 +89,6 @@ impl CommitCmd { let (manifest_path, _) = get_manifest_path_and_dir(&self.cargo_args.manifest_path)?; let target_dir = get_target_dir(&self.cargo_args.target_dir, &manifest_path); let target_output_dir = get_target_output_dir(&target_dir, &self.cargo_args.profile); - let target_name = get_single_target_name(&self.cargo_args)?; let commit_name = format!("{}.commit.json", &target_name); let commit_path = target_output_dir.join(&commit_name); diff --git a/crates/cli/src/commands/init.rs b/crates/cli/src/commands/init.rs index 5279c4b945..4d81a50af9 100644 --- a/crates/cli/src/commands/init.rs +++ b/crates/cli/src/commands/init.rs @@ -113,10 +113,10 @@ impl InitCmd { .path .clone() .unwrap_or(PathBuf::from(".").canonicalize()?); + args.push(path.to_str().unwrap()); let mut cmd = Command::new("cargo"); cmd.args(&args); - cmd.current_dir(&path); let status = cmd.status()?; if !status.success() { diff --git a/crates/cli/src/commands/prove.rs b/crates/cli/src/commands/prove.rs index e9aad20120..1b77b4a268 100644 --- a/crates/cli/src/commands/prove.rs +++ b/crates/cli/src/commands/prove.rs @@ -16,14 +16,14 @@ use openvm_sdk::{ }; use super::{RunArgs, RunCargoArgs}; -#[cfg(feature = "evm-prove")] -use crate::util::read_default_agg_pk; use crate::{ commands::build, - default::*, + default::default_agg_stark_pk_path, input::read_to_stdin, util::{get_app_pk_path, get_manifest_path_and_dir, get_single_target_name, get_target_dir}, }; +#[cfg(feature = "evm-prove")] +use crate::{default::default_params_dir, util::read_default_agg_pk}; #[derive(Parser)] #[command(name = "prove", about = "Generate a program proof")] @@ -38,11 +38,10 @@ enum ProveSubCommand { #[arg( long, action, - default_value = DEFAULT_APP_PROOF_PATH, - help = "Path to app proof output", + help = "Path to app proof output, by default will be ./${bin_name}.app.proof", help_heading = "Output" )] - proof: PathBuf, + proof: Option, #[arg( long, @@ -62,11 +61,10 @@ enum ProveSubCommand { #[arg( long, action, - default_value = DEFAULT_STARK_PROOF_PATH, - help = "Path to STARK proof output", + help = "Path to STARK proof output, by default will be ./${bin_name}.stark.proof", help_heading = "Output" )] - proof: PathBuf, + proof: Option, #[arg( long, @@ -90,11 +88,10 @@ enum ProveSubCommand { #[arg( long, action, - default_value = DEFAULT_EVM_PROOF_PATH, - help = "Path to EVM proof output", + help = "Path to EVM proof output, by default will be ./${bin_name}.evm.proof", help_heading = "Output" )] - proof: PathBuf, + proof: Option, #[arg( long, @@ -126,12 +123,18 @@ impl ProveCmd { } => { let sdk = Sdk::new(); let app_pk = load_app_pk(app_pk, cargo_args)?; - let committed_exe = + let (committed_exe, target_name) = load_or_build_and_commit_exe(&sdk, run_args, cargo_args, &app_pk)?; let app_proof = sdk.generate_app_proof(app_pk, committed_exe, read_to_stdin(&run_args.input)?)?; - write_app_proof_to_file(app_proof, proof)?; + + let proof_path = if let Some(proof) = proof { + proof + } else { + &PathBuf::from(format!("{}.app.proof", target_name)) + }; + write_app_proof_to_file(app_proof, proof_path)?; } ProveSubCommand::Stark { app_pk, @@ -142,7 +145,7 @@ impl ProveCmd { } => { let sdk = Sdk::new().with_agg_tree_config(*agg_tree_config); let app_pk = load_app_pk(app_pk, cargo_args)?; - let committed_exe = + let (committed_exe, target_name) = load_or_build_and_commit_exe(&sdk, run_args, cargo_args, &app_pk)?; let commits = AppExecutionCommit::compute( @@ -163,7 +166,12 @@ impl ProveCmd { read_to_stdin(&run_args.input)?, )?; - encode_to_file(proof, stark_proof)?; + let proof_path = if let Some(proof) = proof { + proof + } else { + &PathBuf::from(format!("{}.stark.proof", target_name)) + }; + encode_to_file(proof_path, stark_proof)?; } #[cfg(feature = "evm-prove")] ProveSubCommand::Evm { @@ -177,7 +185,7 @@ impl ProveCmd { let sdk = Sdk::new().with_agg_tree_config(*agg_tree_config); let app_pk = load_app_pk(app_pk, cargo_args)?; - let committed_exe = + let (committed_exe, target_name) = load_or_build_and_commit_exe(&sdk, run_args, cargo_args, &app_pk)?; let commits = AppExecutionCommit::compute( @@ -201,7 +209,12 @@ impl ProveCmd { read_to_stdin(&run_args.input)?, )?; - write_evm_proof_to_file(evm_proof, proof)?; + let proof_path = if let Some(proof) = proof { + proof + } else { + &PathBuf::from(format!("{}.evm.proof", target_name)) + }; + write_evm_proof_to_file(evm_proof, proof_path)?; } } Ok(()) @@ -224,12 +237,13 @@ pub(crate) fn load_app_pk( Ok(Arc::new(read_app_pk_from_file(app_pk_path)?)) } +// Returns (committed_exe, target_name) where target_name has no extension pub(crate) fn load_or_build_and_commit_exe( sdk: &Sdk, run_args: &RunArgs, cargo_args: &RunCargoArgs, app_pk: &Arc>, -) -> Result> { +) -> Result<(Arc, String)> { let exe_path = if let Some(exe) = &run_args.exe { exe } else { @@ -243,5 +257,8 @@ pub(crate) fn load_or_build_and_commit_exe( let app_exe = read_exe_from_file(exe_path)?; let committed_exe = sdk.commit_app_exe(app_pk.app_fri_params(), app_exe)?; - Ok(committed_exe) + Ok(( + committed_exe, + exe_path.file_stem().unwrap().to_string_lossy().into_owned(), + )) } diff --git a/crates/cli/src/commands/verify.rs b/crates/cli/src/commands/verify.rs index 09662fb96c..517b570cdd 100644 --- a/crates/cli/src/commands/verify.rs +++ b/crates/cli/src/commands/verify.rs @@ -1,16 +1,21 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use clap::Parser; use eyre::Result; use openvm_sdk::{ - fs::{read_app_proof_from_file, read_app_vk_from_file}, + fs::{ + decode_from_file, read_agg_stark_pk_from_file, read_app_proof_from_file, + read_app_vk_from_file, + }, Sdk, }; use super::KeygenCargoArgs; +#[cfg(feature = "evm-verify")] +use crate::default::default_evm_halo2_verifier_path; use crate::{ - default::*, - util::{get_app_vk_path, get_manifest_path_and_dir, get_target_dir}, + default::default_agg_stark_pk_path, + util::{get_app_vk_path, get_files_with_ext, get_manifest_path_and_dir, get_target_dir}, }; #[derive(Parser)] @@ -34,25 +39,32 @@ enum VerifySubCommand { #[arg( long, action, - default_value = DEFAULT_APP_PROOF_PATH, - help = "Path to app proof", + help = "Path to app proof, by default will search the working directory for a file with extension .app.proof", help_heading = "OpenVM Options" )] - proof: PathBuf, + proof: Option, #[command(flatten)] cargo_args: KeygenCargoArgs, }, + Stark { + #[arg( + long, + action, + help = "Path to STARK proof, by default will search the working directory for a file with extension .stark.proof", + help_heading = "OpenVM Options" + )] + proof: Option, + }, #[cfg(feature = "evm-verify")] Evm { #[arg( long, action, - default_value = DEFAULT_EVM_PROOF_PATH, - help = "Path to EVM proof", + help = "Path to EVM proof, by default will search the working directory for a file with extension .evm.proof", help_heading = "OpenVM Options" )] - proof: PathBuf, + proof: Option, }, } @@ -72,11 +84,46 @@ impl VerifyCmd { let target_dir = get_target_dir(&cargo_args.target_dir, &manifest_path); get_app_vk_path(&target_dir) }; - let app_vk = read_app_vk_from_file(app_vk_path)?; - let app_proof = read_app_proof_from_file(proof)?; + + let proof_path = if let Some(proof) = proof { + proof.clone() + } else { + let files = get_files_with_ext(Path::new("."), "app.proof")?; + if files.len() > 1 { + return Err(eyre::eyre!("multiple .app.proof files found, please specify the path using option --proof")); + } else if files.is_empty() { + return Err(eyre::eyre!("no .app.proof file found, please specify the path using option --proof")); + } + files[0].clone() + }; + println!("Verifying application proof at {}", proof_path.display()); + let app_proof = read_app_proof_from_file(proof_path)?; sdk.verify_app_proof(&app_vk, &app_proof)?; } + VerifySubCommand::Stark { proof } => { + let agg_stark_pk = read_agg_stark_pk_from_file(default_agg_stark_pk_path()) + .map_err(|e| { + eyre::eyre!( + "Failed to read aggregation STARK proving key: {}\nPlease run 'cargo openvm setup' first", + e + ) + })?; + let proof_path = if let Some(proof) = proof { + proof.clone() + } else { + let files = get_files_with_ext(Path::new("."), "stark.proof")?; + if files.len() > 1 { + return Err(eyre::eyre!("multiple .stark.proof files found, please specify the path using option --proof")); + } else if files.is_empty() { + return Err(eyre::eyre!("no .stark.proof file found, please specify the path using option --proof")); + } + files[0].clone() + }; + println!("Verifying STARK proof at {}", proof_path.display()); + let stark_proof = decode_from_file(proof_path)?; + sdk.verify_e2e_stark_proof(&agg_stark_pk, &stark_proof)?; + } #[cfg(feature = "evm-verify")] VerifySubCommand::Evm { proof } => { use openvm_sdk::fs::{ @@ -91,7 +138,20 @@ impl VerifyCmd { e ) })?; - let evm_proof = read_evm_proof_from_file(proof)?; + + let proof_path = if let Some(proof) = proof { + proof.clone() + } else { + let files = get_files_with_ext(Path::new("."), "evm.proof")?; + if files.len() > 1 { + return Err(eyre::eyre!("multiple .evm.proof files found, please specify the path using option --proof")); + } else if files.is_empty() { + return Err(eyre::eyre!("no .evm.proof file found, please specify the path using option --proof")); + } + files[0].clone() + }; + println!("Verifying EVM proof at {}", proof_path.display()); + let evm_proof = read_evm_proof_from_file(proof_path)?; sdk.verify_evm_halo2_proof(&evm_verifier, evm_proof)?; } } diff --git a/crates/cli/src/default.rs b/crates/cli/src/default.rs index 269499a332..d1972551b0 100644 --- a/crates/cli/src/default.rs +++ b/crates/cli/src/default.rs @@ -8,10 +8,6 @@ pub const DEFAULT_MANIFEST_DIR: &str = "."; pub const DEFAULT_APP_PK_NAME: &str = "app.pk"; pub const DEFAULT_APP_VK_NAME: &str = "app.vk"; -pub const DEFAULT_APP_PROOF_PATH: &str = "./app.proof"; -pub const DEFAULT_STARK_PROOF_PATH: &str = "./stark.proof"; -pub const DEFAULT_EVM_PROOF_PATH: &str = "./evm.proof"; - pub fn default_agg_stark_pk_path() -> String { env::var("HOME").unwrap() + "/.openvm/agg_stark.pk" } diff --git a/crates/cli/src/util.rs b/crates/cli/src/util.rs index c1bbb4cc88..5882d437c6 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -1,5 +1,5 @@ use std::{ - fs::read_to_string, + fs::{read_dir, read_to_string}, path::{Path, PathBuf}, }; @@ -145,3 +145,19 @@ pub fn get_single_target_name(cargo_args: &RunCargoArgs) -> Result { }; Ok(single_target_name) } + +pub fn get_files_with_ext(dir: &Path, extension: &str) -> Result> { + let dir = dir.canonicalize()?; + let mut files = Vec::new(); + for entry in read_dir(dir)? { + let path = entry?.path(); + if path.is_file() + && path + .to_str() + .is_some_and(|path_str| path_str.ends_with(extension)) + { + files.push(path); + } + } + Ok(files) +} diff --git a/crates/cli/tests/app_e2e.rs b/crates/cli/tests/app_e2e.rs index 641a6627f0..0b526979d7 100644 --- a/crates/cli/tests/app_e2e.rs +++ b/crates/cli/tests/app_e2e.rs @@ -10,7 +10,7 @@ fn test_cli_app_e2e() -> Result<()> { let exe_path = "tests/programs/fibonacci/target/openvm/release/openvm-cli-example-test.vmexe"; let temp_pk = temp_dir.path().join("app.pk"); let temp_vk = temp_dir.path().join("app.vk"); - let temp_proof = temp_dir.path().join("fibonacci.apppf"); + let temp_proof = temp_dir.path().join("fibonacci.app.proof"); run_cmd( "cargo", @@ -79,59 +79,6 @@ fn test_cli_app_e2e() -> Result<()> { Ok(()) } -#[test] -fn test_cli_app_e2e_default_paths() -> Result<()> { - run_cmd("cargo", &["install", "--path", ".", "--force"])?; - run_cmd( - "cargo", - &[ - "openvm", - "build", - "--manifest-path", - "tests/programs/fibonacci/Cargo.toml", - ], - )?; - run_cmd( - "cargo", - &[ - "openvm", - "keygen", - "--manifest-path", - "tests/programs/fibonacci/Cargo.toml", - ], - )?; - run_cmd( - "cargo", - &[ - "openvm", - "run", - "--manifest-path", - "tests/programs/fibonacci/Cargo.toml", - ], - )?; - run_cmd( - "cargo", - &[ - "openvm", - "prove", - "app", - "--manifest-path", - "tests/programs/fibonacci/Cargo.toml", - ], - )?; - run_cmd( - "cargo", - &[ - "openvm", - "verify", - "app", - "--manifest-path", - "tests/programs/fibonacci/Cargo.toml", - ], - )?; - Ok(()) -} - #[test] fn test_cli_app_e2e_simplified() -> Result<()> { run_cmd("cargo", &["install", "--path", ".", "--force"])?; diff --git a/crates/sdk/src/commit.rs b/crates/sdk/src/commit.rs index 0091fda623..4400e97735 100644 --- a/crates/sdk/src/commit.rs +++ b/crates/sdk/src/commit.rs @@ -62,6 +62,13 @@ impl AppExecutionCommit { } } + pub fn from_field_commit(vm_commit: [F; DIGEST_SIZE], exe_commit: [F; DIGEST_SIZE]) -> Self { + Self { + vm_commit: vm_commit.map(|x| x.as_canonical_u32()), + exe_commit: exe_commit.map(|x| x.as_canonical_u32()), + } + } + pub fn to_bn254_commit(&self) -> AppExecutionBn254Commit { AppExecutionBn254Commit { vm_commit: self.vm_commit_to_bn254(), @@ -77,6 +84,7 @@ impl AppExecutionCommit { babybear_u32_digest_to_bn254(&self.exe_commit) } } + fn babybear_u32_digest_to_bn254(digest: &[u32; DIGEST_SIZE]) -> Bn254Fr { babybear_digest_to_bn254(&digest.map(F::from_canonical_u32)) } diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index 7148c8e44e..ef5bde5bcd 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -1,8 +1,8 @@ -use std::{fs::read, marker::PhantomData, path::Path, sync::Arc}; +use std::{borrow::Borrow, fs::read, marker::PhantomData, path::Path, sync::Arc}; #[cfg(feature = "evm-verify")] use alloy_sol_types::sol; -use commit::commit_app_exe; +use commit::{commit_app_exe, AppExecutionCommit}; use config::{AggregationTreeConfig, AppConfig}; use eyre::Result; use keygen::{AppProvingKey, AppVerifyingKey}; @@ -11,16 +11,19 @@ use openvm_build::{ }; use openvm_circuit::{ arch::{ - hasher::poseidon2::vm_poseidon2_hasher, instructions::exe::VmExe, verify_segments, - ContinuationVmProof, ExecutionError, InitFileGenerator, VerifiedExecutionPayload, VmConfig, - VmExecutor, VmVerificationError, + hasher::{poseidon2::vm_poseidon2_hasher, Hasher}, + instructions::exe::VmExe, + verify_segments, ContinuationVmProof, ExecutionError, InitFileGenerator, + VerifiedExecutionPayload, VmConfig, VmExecutor, CONNECTOR_AIR_ID, PROGRAM_AIR_ID, + PROGRAM_CACHED_TRACE_INDEX, PUBLIC_VALUES_AIR_ID, }, system::{ memory::{tree::public_values::extract_public_values, CHUNK}, - program::trace::VmCommittedExe, + program::trace::{compute_exe_commit, VmCommittedExe}, }, }; use openvm_continuations::verifier::{ + common::types::VmVerifierPvs, internal::types::VmStarkProof, root::{types::RootVmVerifierInput, RootVmVerifierConfig}, }; @@ -33,7 +36,7 @@ use openvm_stark_backend::proof::Proof; use openvm_stark_sdk::{ config::{baby_bear_poseidon2::BabyBearPoseidon2Engine, FriParameters}, engine::StarkFriEngine, - openvm_stark_backend::{verifier::VerificationError, Chip}, + openvm_stark_backend::Chip, }; use openvm_transpiler::{ elf::Elf, @@ -228,7 +231,7 @@ impl> GenericSdk { &self, app_vk: &AppVerifyingKey, proof: &ContinuationVmProof, - ) -> Result { + ) -> Result { let engine = E::new(app_vk.fri_params); let VerifiedExecutionPayload { exe_commit, @@ -250,9 +253,10 @@ impl> GenericSdk { &self, app_vk: &AppVerifyingKey, proof: &Proof, - ) -> Result<(), VerificationError> { + ) -> Result<()> { let e = E::new(app_vk.fri_params); - e.verify(&app_vk.app_vm_vk, proof) + e.verify(&app_vk.app_vm_vk, proof)?; + Ok(()) } pub fn agg_keygen( @@ -322,6 +326,71 @@ impl> GenericSdk { Ok(proof) } + pub fn verify_e2e_stark_proof( + &self, + agg_stark_pk: &AggStarkProvingKey, + proof: &VmStarkProof, + ) -> Result { + if proof.proof.per_air.len() < 3 { + return Err(eyre::eyre!( + "Invalid number of AIRs: expected at least 3, got {}", + proof.proof.per_air.len() + )); + } else if proof.proof.per_air[0].air_id != PROGRAM_AIR_ID { + return Err(eyre::eyre!("Missing program AIR")); + } else if proof.proof.per_air[1].air_id != CONNECTOR_AIR_ID { + return Err(eyre::eyre!("Missing connector AIR")); + } else if proof.proof.per_air[2].air_id != PUBLIC_VALUES_AIR_ID { + return Err(eyre::eyre!("Missing public values AIR")); + } + + let vm_commit = proof.proof.commitments.main_trace[PROGRAM_CACHED_TRACE_INDEX].as_ref(); + let internal_commit: &[_; CHUNK] = &agg_stark_pk + .internal_committed_exe + .get_program_commit() + .into(); + + let vm_pk = if vm_commit == internal_commit { + &agg_stark_pk.internal_vm_pk + } else { + &agg_stark_pk.leaf_vm_pk + }; + let e = E::new(vm_pk.fri_params); + e.verify(&vm_pk.vm_pk.get_vk(), &proof.proof)?; + + let public_values_air_proof_data = &proof.proof.per_air[2]; + let pvs: &VmVerifierPvs<_> = + public_values_air_proof_data.public_values[..VmVerifierPvs::::width()].borrow(); + + if let Some(exit_code) = pvs.connector.exit_code() { + if exit_code != 0 { + return Err(eyre::eyre!( + "Invalid exit code: expected 0, got {}", + exit_code + )); + } + } else { + return Err(eyre::eyre!("Program did not terminate")); + } + + let hasher = vm_poseidon2_hasher(); + let public_values_root = hasher.merkle_root(&proof.user_public_values); + if public_values_root != pvs.public_values_commit { + return Err(eyre::eyre!( + "Invalid public values root: expected {:?}, got {:?}", + pvs.public_values_commit, + public_values_root + )); + } + + let start_pc = pvs.connector.initial_pc; + let initial_memory_root = &pvs.memory.initial_root; + let exe_commit = compute_exe_commit(&hasher, vm_commit, initial_memory_root, start_pc); + Ok(AppExecutionCommit::from_field_commit( + *vm_commit, exe_commit, + )) + } + #[cfg(feature = "evm-prove")] pub fn generate_evm_proof>( &self, diff --git a/crates/vm/src/system/connector/mod.rs b/crates/vm/src/system/connector/mod.rs index dc9ff88ea2..88a03c484b 100644 --- a/crates/vm/src/system/connector/mod.rs +++ b/crates/vm/src/system/connector/mod.rs @@ -57,6 +57,20 @@ pub struct VmConnectorPvs { pub is_terminate: F, } +impl VmConnectorPvs { + pub fn is_terminate(&self) -> bool { + self.is_terminate == F::from_bool(true) + } + + pub fn exit_code(&self) -> Option { + if self.is_terminate() && self.exit_code == F::ZERO { + Some(self.exit_code.as_canonical_u32()) + } else { + None + } + } +} + impl BaseAirWithPublicValues for VmConnectorAir { fn num_public_values(&self) -> usize { VmConnectorPvs::::width()