diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 4602e9c05f..a258a14e44 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -49,9 +49,9 @@ jobs: working-directory: examples/ecc run: | cargo openvm build - mv openvm/app.vmexe app1.vmexe + mv target/openvm/release/ecc-example.vmexe app1.vmexe cargo openvm build - cmp app1.vmexe openvm/app.vmexe || (echo "Build is not deterministic!" && exit 1) + cmp app1.vmexe target/openvm/release/ecc-example.vmexe || (echo "Build is not deterministic!" && exit 1) - name: Build and run book examples working-directory: examples @@ -70,7 +70,7 @@ jobs: run: | export RUST_BACKTRACE=1 cargo build - cargo run --bin cargo-openvm -- openvm keygen --config ./example/app_config.toml --output app.pk --vk-output app.vk + cargo run --bin cargo-openvm -- openvm keygen --config ./example/app_config.toml --output-dir . - name: Run CLI tests working-directory: crates/cli diff --git a/Cargo.lock b/Cargo.lock index 7031868f8f..3959c0d0cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1287,10 +1287,10 @@ version = "1.1.2" dependencies = [ "aws-config", "aws-sdk-s3", - "bitcode", "clap", "eyre", "hex", + "itertools 0.14.0", "openvm-build", "openvm-circuit", "openvm-native-recursion", diff --git a/benchmarks/execute/src/main.rs b/benchmarks/execute/src/main.rs index 80db3ec5a4..a05baeea44 100644 --- a/benchmarks/execute/src/main.rs +++ b/benchmarks/execute/src/main.rs @@ -1,4 +1,4 @@ -use cargo_openvm::{default::DEFAULT_APP_CONFIG_PATH, util::read_config_toml_or_default}; +use cargo_openvm::util::read_config_toml_or_default; use clap::{Parser, ValueEnum}; use eyre::Result; use openvm_benchmarks_utils::{get_elf_path, get_programs_dir, read_elf_file}; @@ -106,7 +106,7 @@ fn main() -> Result<()> { let elf_path = get_elf_path(&program_dir); let elf = read_elf_file(&elf_path)?; - let config_path = program_dir.join(DEFAULT_APP_CONFIG_PATH); + let config_path = program_dir.join("openvm.toml"); let vm_config = read_config_toml_or_default(&config_path)?.app_vm_config; let exe = VmExe::from_elf(elf, vm_config.transpiler())?; diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 104ee4bd74..0bbe1daf92 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -12,6 +12,7 @@ - [Overview](./writing-apps/overview.md) - [Writing a Program](./writing-apps/write-program.md) - [Compiling](./writing-apps/build.md) +- [Running a Program](./writing-apps/run.md) - [Generating Proofs](./writing-apps/prove.md) - [Verifying Proofs](./writing-apps/verify.md) - [Solidity SDK](./writing-apps/solidity.md) diff --git a/book/src/writing-apps/build.md b/book/src/writing-apps/build.md index cb65db10c9..4f0d207598 100644 --- a/book/src/writing-apps/build.md +++ b/book/src/writing-apps/build.md @@ -9,135 +9,142 @@ The command `cargo openvm build` compiles the program on host to an executable f It first compiles the program normally on your _host_ platform with RISC-V and then transpiles it to a different target. See here for some explanation of [cross-compilation](https://rust-lang.github.io/rustup/cross-compilation.html). Right now we use `riscv32im-risc0-zkvm-elf` target which is available in the [Rust toolchain](https://doc.rust-lang.org/rustc/platform-support/riscv32im-risc0-zkvm-elf.html), but we will contribute an OpenVM target to Rust in the future. -## Build flags +## Build Flags -The following flags are available for the `cargo openvm build` command: +The following flags are available for the `cargo openvm build` command. You can run `cargo openvm build --help` for this list within the command line. -- `--manifest-dir ` +Generally, outputs will always be built to the **target directory**, which will either be determined by the manifest path or explicitly set using the `--target-dir` option. By default Cargo sets this to be `/target/`. - **Description**: Specifies the directory containing the `Cargo.toml` file for the guest code. +OpenVM-specific artifacts will be placed in `${target_dir}/openvm/`, but if `--output-dir` is specified they will be copied to `${output-dir}/` as well. - **Default**: The current directory (`.`). +### OpenVM Options - **Usage Example**: If your `Cargo.toml` is located in `my_project/`, you can run: +- `--no-transpile` - ```bash - cargo openvm build --manifest-dir my_project - ``` + **Description**: Skips transpilation into an OpenVM-compatible `.vmexe` executable when set. - This ensures the build command is executed in that directory. +- `--config ` -- `--target-dir ` + **Description**: Path to the OpenVM config `.toml` file that specifies the VM extensions. By default will search the manifest directory for `openvm.toml`. If no file is found, OpenVM will use a default configuration. Currently the CLI only supports known extensions listed in the [Using Existing Extensions](../custom-extensions/overview.md) section. To use other extensions, use the [SDK](../advanced-usage/sdk.md). - **Description**: Specifies the directory where the guest binary will be built. If not specified, the default target directory is used. +- `--output_dir ` - **Default**: The `target` directory in the package root directory. + **Description**: Output directory for OpenVM artifacts to be copied to. Keys will be placed in `${output-dir}/`, while all other artifacts will be in `${output-dir}/${profile}`. - **Usage Example**: To build the guest binary in the `my_target` directory: +- `--init-file-name ` - ```bash - cargo openvm build --target-dir my_target - ``` + **Description**: Name of the generated initialization file, which will be written into the manifest directory. -- `--features ` + **Default**: `openvm_init.rs` - **Description**: Passes a list of feature flags to the Cargo build process. These flags enable or disable conditional compilation features defined in your `Cargo.toml`. +### Package Selection - **Usage Example**: To enable the `my_feature` feature: +As with `cargo build`, default package selection depends on the working directory. If the working directory is a subdirectory of a specific package, then only that package will be built. Else, all packages in the workspace will be built by default. - ```bash - cargo openvm build --features my_feature - ``` +- `--package ` -- `--bin ` + **Description**: Builds only the specified packages. This flag may be specified multiple times or as a comma-separated list. - **Description**: Restricts the build to the binary target with the given name, similar to `cargo build --bin `. If your project has multiple target types (binaries, libraries, examples, etc.), using `--bin ` narrows down the build to the binary target with the given name. +- `--workspace` - **Usage Example**: + **Description**: Builds all members of the workspace (alias `--all`). - ```bash - cargo openvm build --bin my_bin - ``` +- `--exclude ` -- `--example ` + **Description**: Excludes the specified packages. Must be used in conjunction with `--workspace`. This flag may be specified multiple times or as a comma-separated list. - **Description**: Restricts the build to the example target with the given name, similar to `cargo build --example `. Projects often include code samples or demos under the examples directory, and this flag focuses on compiling a specific example. +### Target Selection - **Usage Example**: +By default all package libraries and binaries will be built. To build samples or demos under the `examples` directory, use either the `--example` or `--examples` option. - ```bash - cargo openvm build --example my_example - ``` +- `--lib` -- `--no-transpile` + **Description**: Builds the package's library. - **Description**: After building the guest code, doesn't transpile the target ELF into an OpenVM-compatible executable (by default it does). +- `--bin ` - **Usage Example**: + **Description**: Builds the specified binary. This flag may be specified multiple times or as a comma-separated list. - ```bash - cargo openvm build --no-transpile - ``` +- `--bins` -- `--config ` + **Description**: Builds all binary targets. + +- `--example ` + + **Description**: Builds the specified example. This flag may be specified multiple times or as a comma-separated list. + +- `--examples` + + **Description**: Builds all example targets. + +- `--all-targets` + + **Description**: Builds all package targets. Equivalent to specifying `--lib` `--bins` `--examples`. + +### Feature Selection + +The following options enable or disable conditional compilation features defined in your `Cargo.toml`. + +- `-F`, `--features ` + + **Description**: Space or comma separated list of features to activate. Features of workspace members may be enabled with `package-name/feature-name` syntax. This flag may also be specified multiple times. + +- `--all-features` + + **Description**: Activates all available features of all selected packages. + +- `--no-default-features` + + **Description**: Do not activate the `default` feature of the selected packages. + +### Compilation Options + +- `--profile ` - **Description**: Specifies the path to a .toml configuration file that defines which VM extensions to use. + **Description**: Builds with the given profile. Common profiles are `dev` (faster builds, less optimization) and `release` (slower builds, more optimization). For more information on profiles, see [Cargo's reference page](https://doc.rust-lang.org/cargo/reference/profiles.html). - **Default**: `./openvm.toml` if `--config` flag is not provided. + **Default**: `release` - **Usage Example**: +### Output Options - ```bash - cargo openvm build --config path/to/openvm.toml - ``` +- `--target_dir ` - This allows you to customize the extensions. Currently the CLI only supports known extensions listed in the [Using Existing Extensions](../custom-extensions/overview.md) section. To use other extensions, use the [SDK](../advanced-usage/sdk.md). + **Description**: Directory for all generated artifacts and intermediate files. Defaults to directory `target/` at the root of the workspace. -- `--exe-output ` +### Display Options - **Description**: Sets the output path for the transpiled program. +- `-v`, `--verbose` - **Default**: `./openvm/app.vmexe` if `--exe-output` flag is not provided. + **Description**: Use verbose output. - **Usage Example**: To specify a custom output filename: +- `-q`, `--quiet` - ```bash - cargo openvm build --exe-output ./output/custom_name.vmexe - ``` + **Description**: Do not print Cargo log messages. -- `--profile ` +- `--color ` - **Description**: Determines the build profile used by Cargo. Common profiles are dev (faster builds, less optimization) and release (slower builds, more optimization). + **Description**: Controls when colored output is used. - **Default**: release + **Default**: `always` - **Usage Example**: +### Manifest Options - ```bash - cargo openvm build --profile dev - ``` +- `--manifest-path ` -- `--help` + **Description**: Path to the guest code Cargo.toml file. By default, `build` searches for the file in the current or any parent directory. The `build` command will be executed in that directory. - **Description**: Prints a help message describing the available options and their usage. +- `--ignore-rust-version` - **Usage Example**: + **Description**: Ignores rust-version specification in packages. - ```bash - cargo openvm build --help - ``` +- `--locked` -## Running a Program + **Description**: Asserts the same dependencies and versions are used as when the existing Cargo.lock file was originally generated. -After building and transpiling a program, you can execute it using the `run` command. The `run` command has the following arguments: +- `--offline` -```bash -cargo openvm run - --exe - --config - --input -``` + **Description**: Prevents Cargo from accessing the network for any reason. -If `--exe` and/or `--config` are not provided, the command will search for these files in `./openvm/app.vmexe` and `./openvm.toml` respectively. If `./openvm.toml` is not present, a default configuration will be used. +- `--frozen` -If your program doesn't require inputs, you can (and should) omit the `--input` flag. + **Description**: Equivalent to specifying both `--locked` and `--offline`. diff --git a/book/src/writing-apps/prove.md b/book/src/writing-apps/prove.md index 88d7a40548..1cdfbc9cfa 100644 --- a/book/src/writing-apps/prove.md +++ b/book/src/writing-apps/prove.md @@ -4,41 +4,46 @@ Generating a proof using the CLI is simple - first generate a key, then generate ```bash cargo openvm keygen -cargo openvm prove [app | evm] +cargo openvm prove [app | stark | evm] ``` ## Key Generation -The `keygen` CLI command has the following optional arguments: +The `keygen` command generates both an application proving and verification key. ```bash cargo openvm keygen --config - --output - --vk_output ``` -If `--config` is not provided, the command will search for `./openvm.toml` and use that as the application configuration if present. If it is not present, a default configuration will be used. +Similarly to `build`, `run`, and `prove`, options `--manifest-path`, `--target-dir`, and `--output-dir` are provided. -If `--output` and/or `--vk_output` are not provided, the keys will be written to default locations `./openvm/app.pk` and/or `./openvm/app.vk` respectively. +If `--config` is not specified, the command will search for `openvm.toml` in the manifest directory. If the file isn't found, a default configuration will be used. + +The proving and verification key will be written to `${target_dir}/openvm/` (and `--output-dir` if specified). ## Proof Generation -The `prove` CLI command has the following optional arguments: +The `prove` CLI command, at its core, uses the options below. `prove` gets access to all of the options that `run` has (see [Running a Program](../writing-apps/run.md) for more information). ```bash -cargo openvm prove [app | evm] - --app_pk +cargo openvm prove [app | stark | evm] + --app-pk --exe --input - --output + --proof ``` +If `--app-pk` is not provided, the command will search for a proving key at `${target_dir}/openvm/app.pk`. + +If `--exe` is not provided, the command will call `build` before generating a proof. + If your program doesn't require inputs, you can (and should) omit the `--input` flag. -If `--app_pk` and/or `--exe` are not provided, the command will search for these files in `./openvm/app.pk` and `./openvm/app.vmexe` respectively. Similarly, if `--output` is not provided then the command will write the proof to `./openvm/[app | evm].proof` by default. +If `--proof` is not provided then the command will write the proof to `./[app | stark | evm].proof` by default. + -The `app` subcommand is used to generate an application-level proof, while the `evm` command generates an end-to-end EVM proof. +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). > ⚠️ **WARNING** > In order to run the `evm` subcommand, you must have previously called the costly `cargo openvm setup`, which requires very large amounts of computation and memory (~200 GB). diff --git a/book/src/writing-apps/run.md b/book/src/writing-apps/run.md new file mode 100644 index 0000000000..66a8dbf439 --- /dev/null +++ b/book/src/writing-apps/run.md @@ -0,0 +1,75 @@ +# Running a Program + +After building and transpiling a program, you can execute it using the `run` command. For example, you can call: + +```bash +cargo openvm run + --exe + --config + --input +``` + +If `--exe` is not provided, OpenVM will call `build` prior to attempting to run the executable. Note that only one executable may be run, so if your project contains multiple targets you will have to specify which one to run using the `--bin` or `--example` flag. + +If your program doesn't require inputs, you can (and should) omit the `--input` flag. + +## Run Flags + +Many of the options for `cargo openvm run` will be passed to `cargo openvm build` if `--exe` is not specified. For more information on `build` (or `run`'s **Feature Selection**, **Compilation**, **Output**, **Display**, and/or **Manifest** options) see [Compiling](./writing-apps/build.md). + +### OpenVM Options + +- `--exe ` + + **Description**: Path to the OpenVM executable, if specified `build` will be skipped. + +- `--config ` + + **Description**: Path to the OpenVM config `.toml` file that specifies the VM extensions. By default will search the manifest directory for `openvm.toml`. If no file is found, OpenVM will use a default configuration. Currently the CLI only supports known extensions listed in the [Using Existing Extensions](../custom-extensions/overview.md) section. To use other extensions, use the [SDK](../advanced-usage/sdk.md). + +- `--output_dir ` + + **Description**: Output directory for OpenVM artifacts to be copied to. Keys will be placed in `${output-dir}/`, while all other artifacts will be in `${output-dir}/${profile}`. + +- `--input ` + + **Description**: Input to the OpenVM program, or a hex string. + +- `--init-file-name ` + + **Description**: Name of the generated initialization file, which will be written into the manifest directory. + + **Default**: `openvm_init.rs` + +### Package Selection + +- `--package ` + + **Description**: The package to run, by default the package in the current workspace. + +### Target Selection + +Only one target may be built and run. + +- `--bin ` + + **Description**: Runs the specified binary. + +- `--example ` + + **Description**: Runs the specified example. + +## Examples + +### Running a Specific Binary + +```bash +cargo openvm run --bin bin_name +``` + +### Skipping Build Using `--exe` + +```bash +cargo openvm build --output-dir ./my_output_dir +cargo openvm run --exe ./my_output_dir/bin_name.vmexe +``` diff --git a/book/src/writing-apps/verify.md b/book/src/writing-apps/verify.md index 06e44b7389..bf813a350b 100644 --- a/book/src/writing-apps/verify.md +++ b/book/src/writing-apps/verify.md @@ -10,9 +10,7 @@ cargo openvm verify app --proof ``` -If you omit `--app_vk` and/or `--proof`, the command will search for those files at `./openvm/app.vk` and `./openvm/app.proof` respectively. - -Once again, if you omitted `--output` and `--vk_output` in the `keygen` and `prove` commands, you can omit `--app_vk` and `--proof` in the `verify` command. +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. ## EVM Level diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 9e916c9641..4f91df352e 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -37,7 +37,7 @@ hex = "0.4.3" target-lexicon = "0.12.15" tempfile = "3.10.1" toml = { workspace = true } -bitcode.workspace = true +itertools.workspace = true [features] default = ["parallel", "jemalloc", "evm-verify", "bench-metrics"] diff --git a/crates/cli/src/bin/cargo-openvm.rs b/crates/cli/src/bin/cargo-openvm.rs index 1a5e9f5c58..6e2fca5cc8 100644 --- a/crates/cli/src/bin/cargo-openvm.rs +++ b/crates/cli/src/bin/cargo-openvm.rs @@ -26,7 +26,7 @@ pub enum VmCliCommands { Prove(ProveCmd), Run(RunCmd), #[cfg(feature = "evm-verify")] - Setup(EvmProvingSetupCmd), + Setup(SetupCmd), Verify(VerifyCmd), } diff --git a/crates/cli/src/commands/build.rs b/crates/cli/src/commands/build.rs index 1d495b2bcf..496ca7e010 100644 --- a/crates/cli/src/commands/build.rs +++ b/crates/cli/src/commands/build.rs @@ -1,23 +1,21 @@ use std::{ env::var, - fs::{create_dir_all, read, write}, + fs::{copy, create_dir_all, read}, path::PathBuf, - sync::Arc, }; use clap::Parser; use eyre::Result; +use itertools::izip; use openvm_build::{ - build_generic, get_package, get_target_dir, get_workspace_packages, get_workspace_root, - GuestOptions, + build_generic, get_package, get_workspace_packages, get_workspace_root, GuestOptions, }; use openvm_circuit::arch::{InitFileGenerator, OPENVM_DEFAULT_INIT_FILE_NAME}; -use openvm_sdk::{commit::commit_app_exe, fs::write_exe_to_file, Sdk}; +use openvm_sdk::{fs::write_exe_to_file, Sdk}; use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE}; -use crate::{ - default::{DEFAULT_APP_CONFIG_PATH, DEFAULT_APP_EXE_PATH, DEFAULT_COMMITTED_APP_EXE_PATH}, - util::{find_manifest_dir, read_config_toml_or_default}, +use crate::util::{ + get_manifest_path_and_dir, get_target_dir, get_target_output_dir, read_config_toml_or_default, }; #[derive(Parser)] @@ -41,7 +39,6 @@ impl BuildCmd { pub struct BuildArgs { #[arg( long, - default_value = "false", help = "Skips transpilation into exe when set", help_heading = "OpenVM Options" )] @@ -49,27 +46,17 @@ pub struct BuildArgs { #[arg( long, - default_value = DEFAULT_APP_CONFIG_PATH, - help = "Path to the OpenVM config .toml file that specifies the VM extensions", + help = "Path to the OpenVM config .toml file that specifies the VM extensions, by default will search for the file at ${manifest_dir}/openvm.toml", help_heading = "OpenVM Options" )] - pub config: PathBuf, + pub config: Option, #[arg( long, - default_value = DEFAULT_APP_EXE_PATH, - help = "Output path for the transpiled program", + help = "Output directory that OpenVM proving artifacts will be copied to", help_heading = "OpenVM Options" )] - pub exe_output: PathBuf, - - #[arg( - long, - default_value = DEFAULT_COMMITTED_APP_EXE_PATH, - help = "Output path for the committed program", - help_heading = "OpenVM Options" - )] - pub committed_exe_output: PathBuf, + pub output_dir: Option, #[arg( long, @@ -80,6 +67,17 @@ pub struct BuildArgs { pub init_file_name: String, } +impl Default for BuildArgs { + fn default() -> Self { + Self { + no_transpile: false, + config: None, + output_dir: None, + init_file_name: OPENVM_DEFAULT_INIT_FILE_NAME.to_string(), + } + } +} + #[derive(Clone, Parser)] pub struct BuildCargoArgs { #[arg( @@ -254,30 +252,43 @@ pub struct BuildCargoArgs { pub frozen: bool, } -// Returns the paths to the ELF file for each built executable target. -pub fn build(build_args: &BuildArgs, cargo_args: &BuildCargoArgs) -> Result> { - println!("[openvm] Building the package..."); - - // Find manifest directory using either manifest_path or find_manifest_dir - let manifest_dir = if let Some(manifest_path) = &cargo_args.manifest_path { - if !manifest_path.ends_with("Cargo.toml") { - return Err(eyre::eyre!( - "manifest_path must be a path to a Cargo.toml file" - )); +impl Default for BuildCargoArgs { + fn default() -> Self { + Self { + package: vec![], + workspace: false, + exclude: vec![], + lib: false, + bin: vec![], + bins: false, + example: vec![], + examples: false, + all_targets: false, + features: vec![], + all_features: false, + no_default_features: false, + profile: "release".to_string(), + target_dir: None, + verbose: false, + quiet: false, + color: "always".to_string(), + manifest_path: None, + ignore_rust_version: false, + locked: false, + offline: false, + frozen: false, } - manifest_path.parent().unwrap().to_path_buf() - } else { - find_manifest_dir(PathBuf::from("."))? - }; - let manifest_path = manifest_dir.join("Cargo.toml"); - println!("[openvm] Manifest directory: {}", manifest_dir.display()); + } +} - // Get target path - let target_path = if let Some(target_path) = &cargo_args.target_dir { - target_path.to_path_buf() - } else { - get_target_dir(&manifest_path) - }; +// Returns either a) the default transpilation output directory or b) the ELF output +// directory if no_transpile is set to true. +pub fn build(build_args: &BuildArgs, cargo_args: &BuildCargoArgs) -> Result { + println!("[openvm] Building the package..."); + + // Find manifest_path, manifest_dir, and target_dir + let (manifest_path, manifest_dir) = get_manifest_path_and_dir(&cargo_args.manifest_path)?; + let target_dir = get_target_dir(&cargo_args.target_dir, &manifest_path); // Set guest options using build arguments; use found manifest directory for consistency let mut guest_options = GuestOptions::default() @@ -285,7 +296,7 @@ pub fn build(build_args: &BuildArgs, cargo_args: &BuildCargoArgs) -> Result Result target_dir, + let elf_target_dir = match build_generic(&guest_options) { + Ok(raw_target_dir) => raw_target_dir, Err(None) => { return Err(eyre::eyre!("Failed to build guest")); } @@ -344,9 +355,23 @@ pub fn build(build_args: &BuildArgs, cargo_args: &BuildCargoArgs) -> Result Result = packages + let elf_targets = packages + .iter() + .flat_map(|pkg| pkg.targets.iter()) + .filter(|target| { + // We only build bin and example targets (note they are mutually exclusive + // types). If no target selection flags are set, then all bin targets are + // built by default. + if target.is_example() { + all_examples || cargo_args.example.contains(&target.name) + } else if target.is_bin() { + all_bins + || cargo_args.bin.contains(&target.name) + || (!cargo_args.examples + && !cargo_args.lib + && cargo_args.bin.is_empty() + && cargo_args.example.is_empty()) + } else { + false + } + }) + .collect::>(); + let elf_paths = elf_targets .iter() - .flat_map(|pkg| { - pkg.targets - .iter() - .filter(move |target| { - // We only build bin and example targets (note they are mutually exclusive - // types). If no target selection flags are set, then all bin targets are - // built by default. - if target.is_example() { - return all_examples || cargo_args.example.contains(&target.name); - } else if target.is_bin() { - return all_bins - || cargo_args.bin.contains(&target.name) - || (!cargo_args.examples - && !cargo_args.lib - && cargo_args.bin.is_empty() - && cargo_args.example.is_empty()); - } - false - }) - .map(|target| { - if target.is_example() { - target_dir.join("examples") - } else { - target_dir.clone() - } - .join(&target.name) - }) - .collect::>() + .map(|target| { + if target.is_example() { + elf_target_dir.join("examples") + } else { + elf_target_dir.clone() + } + .join(&target.name) }) .collect::>(); - // Transpile and commit, storing in target_dir/openvm/${profile} by default - // TODO[stephen]: actually implement target_dir change - if !build_args.no_transpile { - for elf_path in &elf_paths { - println!("[openvm] Transpiling the package..."); - let output_path = &build_args.exe_output; - let transpiler = app_config.app_vm_config.transpiler(); - - let data = read(elf_path.clone())?; - let elf = Elf::decode(&data, MEM_SIZE as u32)?; - let exe = Sdk::new().transpile(elf, transpiler)?; - let committed_exe = commit_app_exe(app_config.app_fri_params.fri_params, exe.clone()); - write_exe_to_file(exe, output_path)?; - - if let Some(parent) = build_args.committed_exe_output.parent() { - create_dir_all(parent)?; - } - let committed_exe = match Arc::try_unwrap(committed_exe) { - Ok(exe) => exe, - Err(_) => return Err(eyre::eyre!("Failed to unwrap committed_exe Arc")), - }; - write( - &build_args.committed_exe_output, - bitcode::serialize(&committed_exe)?, - )?; - - println!( - "[openvm] Successfully transpiled to {}", - output_path.display() - ); + // Transpile, storing in ${target_dir}/openvm/${profile} by default + let target_output_dir = get_target_output_dir(&target_dir, &cargo_args.profile); + + println!("[openvm] Transpiling the package..."); + for (elf_path, target) in izip!(&elf_paths, &elf_targets) { + let transpiler = app_config.app_vm_config.transpiler(); + let data = read(elf_path.clone())?; + let elf = Elf::decode(&data, MEM_SIZE as u32)?; + let exe = Sdk::new().transpile(elf, transpiler)?; + + let target_name = if target.is_example() { + &format!("examples/{}", target.name) + } else { + &target.name + }; + let file_name = format!("{}.vmexe", target_name); + let file_path = target_output_dir.join(&file_name); + + write_exe_to_file(exe, &file_path)?; + if let Some(output_dir) = &build_args.output_dir { + create_dir_all(output_dir)?; + copy(file_path, output_dir.join(file_name))?; } } - // Return elf paths of all targets for all built packages - println!("[openvm] Successfully built the packages"); - Ok(elf_paths) + let final_output_dir = if let Some(output_dir) = &build_args.output_dir { + output_dir + } else { + &target_output_dir + }; + println!( + "[openvm] Successfully transpiled to {}", + final_output_dir.display() + ); + Ok(final_output_dir.clone()) } diff --git a/crates/cli/src/commands/keygen.rs b/crates/cli/src/commands/keygen.rs index b3070a10de..a021a183e1 100644 --- a/crates/cli/src/commands/keygen.rs +++ b/crates/cli/src/commands/keygen.rs @@ -1,4 +1,7 @@ -use std::path::{Path, PathBuf}; +use std::{ + fs::{copy, create_dir_all}, + path::{Path, PathBuf}, +}; use clap::Parser; use eyre::Result; @@ -8,48 +11,99 @@ use openvm_sdk::{ }; use crate::{ - default::{DEFAULT_APP_CONFIG_PATH, DEFAULT_APP_PK_PATH, DEFAULT_APP_VK_PATH}, - util::read_config_toml_or_default, + default::{DEFAULT_APP_PK_NAME, DEFAULT_APP_VK_NAME}, + util::{ + get_app_pk_path, get_app_vk_path, get_manifest_path_and_dir, get_target_dir, + read_config_toml_or_default, + }, }; #[derive(Parser)] #[command(name = "keygen", about = "Generate an application proving key")] pub struct KeygenCmd { - #[arg(long, action, help = "Path to app config TOML file", default_value = DEFAULT_APP_CONFIG_PATH)] - config: PathBuf, + #[arg( + long, + help = "Path to the OpenVM config .toml file that specifies the VM extensions, by default will search for the file at ${manifest_dir}/openvm.toml", + help_heading = "OpenVM Options" + )] + config: Option, #[arg( long, - action, - help = "Path to output app proving key file", - default_value = DEFAULT_APP_PK_PATH + help = "Output directory that OpenVM proving artifacts will be copied to", + help_heading = "OpenVM Options" )] - output: PathBuf, + output_dir: Option, + #[command(flatten)] + cargo_args: KeygenCargoArgs, +} + +#[derive(Parser)] +pub struct KeygenCargoArgs { #[arg( long, - action, - help = "Path to output app verifying key file", - default_value = DEFAULT_APP_VK_PATH + value_name = "DIR", + help = "Directory for all Cargo-generated artifacts and intermediate files", + help_heading = "Cargo Options" )] - vk_output: PathBuf, + pub(crate) target_dir: Option, + + #[arg( + long, + value_name = "PATH", + help = "Path to the Cargo.toml file, by default searches for the file in the current or any parent directory", + help_heading = "Cargo Options" + )] + pub(crate) manifest_path: Option, } impl KeygenCmd { pub fn run(&self) -> Result<()> { - keygen(&self.config, &self.output, &self.vk_output)?; + let (manifest_path, manifest_dir) = + get_manifest_path_and_dir(&self.cargo_args.manifest_path)?; + let target_dir = get_target_dir(&self.cargo_args.target_dir, &manifest_path); + let app_pk_path = get_app_pk_path(&target_dir); + let app_vk_path = get_app_vk_path(&target_dir); + + keygen( + self.config + .to_owned() + .unwrap_or_else(|| manifest_dir.join("openvm.toml")), + &app_pk_path, + &app_vk_path, + self.output_dir.as_ref(), + )?; + println!( + "Successfully generated app pk and vk in {}", + if let Some(output_dir) = self.output_dir.as_ref() { + output_dir.display() + } else { + app_pk_path.parent().unwrap().display() + } + ); Ok(()) } } pub(crate) fn keygen( config: impl AsRef, - output: impl AsRef, - vk_output: impl AsRef, + app_pk_path: impl AsRef, + app_vk_path: impl AsRef, + output_dir: Option>, ) -> Result<()> { let app_config = read_config_toml_or_default(config)?; let app_pk = Sdk::new().app_keygen(app_config)?; - write_app_vk_to_file(app_pk.get_app_vk(), vk_output.as_ref())?; - write_app_pk_to_file(app_pk, output.as_ref())?; + let app_vk = app_pk.get_app_vk(); + write_app_vk_to_file(app_vk, &app_vk_path)?; + write_app_pk_to_file(app_pk, &app_pk_path)?; + + if let Some(output_dir) = output_dir { + let output_dir = output_dir.as_ref(); + create_dir_all(output_dir)?; + copy(&app_pk_path, output_dir.join(DEFAULT_APP_PK_NAME))?; + copy(&app_vk_path, output_dir.join(DEFAULT_APP_VK_NAME))?; + } + Ok(()) } diff --git a/crates/cli/src/commands/prove.rs b/crates/cli/src/commands/prove.rs index 9c53e111f8..cc71011021 100644 --- a/crates/cli/src/commands/prove.rs +++ b/crates/cli/src/commands/prove.rs @@ -12,14 +12,17 @@ use openvm_sdk::{ write_app_proof_to_file, }, keygen::AppProvingKey, - NonRootCommittedExe, Sdk, StdIn, + NonRootCommittedExe, Sdk, }; +use super::{RunArgs, RunCargoArgs}; #[cfg(feature = "evm-prove")] use crate::util::read_default_agg_pk; use crate::{ + commands::build, default::*, - input::{read_to_stdin, Input}, + input::read_to_stdin, + util::{get_app_pk_path, get_manifest_path_and_dir, get_single_target_name, get_target_dir}, }; #[derive(Parser)] @@ -32,47 +35,80 @@ pub struct ProveCmd { #[derive(Parser)] enum ProveSubCommand { App { - #[arg(long, action, help = "Path to app proving key", default_value = DEFAULT_APP_PK_PATH)] - app_pk: PathBuf, + #[arg( + long, + action, + default_value = DEFAULT_APP_PROOF_PATH, + help = "Path to app proof output", + help_heading = "Output" + )] + proof: PathBuf, + + #[arg( + long, + action, + help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk", + help_heading = "OpenVM Options" + )] + app_pk: Option, - #[arg(long, action, help = "Path to OpenVM executable", default_value = DEFAULT_APP_EXE_PATH)] - exe: PathBuf, - - #[arg(long, value_parser, help = "Input to OpenVM program")] - input: Option, + #[command(flatten)] + run_args: RunArgs, - #[arg(long, action, help = "Path to output proof", default_value = DEFAULT_APP_PROOF_PATH)] - output: PathBuf, + #[command(flatten)] + cargo_args: RunCargoArgs, }, Stark { - #[arg(long, action, help = "Path to app proving key", default_value = DEFAULT_APP_PK_PATH)] - app_pk: PathBuf, + #[arg( + long, + action, + default_value = DEFAULT_STARK_PROOF_PATH, + help = "Path to STARK proof output", + help_heading = "Output" + )] + proof: PathBuf, + + #[arg( + long, + action, + help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk", + help_heading = "OpenVM Options" + )] + app_pk: Option, - #[arg(long, action, help = "Path to OpenVM executable", default_value = DEFAULT_APP_EXE_PATH)] - exe: PathBuf, - - #[arg(long, value_parser, help = "Input to OpenVM program")] - input: Option, + #[command(flatten)] + run_args: RunArgs, - #[arg(long, action, help = "Path to output proof", default_value = DEFAULT_APP_PROOF_PATH)] - output: PathBuf, + #[command(flatten)] + cargo_args: RunCargoArgs, #[command(flatten)] agg_tree_config: AggregationTreeConfig, }, #[cfg(feature = "evm-prove")] Evm { - #[arg(long, action, help = "Path to app proving key", default_value = DEFAULT_APP_PK_PATH)] - app_pk: PathBuf, - - #[arg(long, action, help = "Path to OpenVM executable", default_value = DEFAULT_APP_EXE_PATH)] - exe: PathBuf, + #[arg( + long, + action, + default_value = DEFAULT_EVM_PROOF_PATH, + help = "Path to EVM proof output", + help_heading = "Output" + )] + proof: PathBuf, + + #[arg( + long, + action, + help = "Path to app proving key, by default will be ${target_dir}/openvm/app.pk", + help_heading = "OpenVM Options" + )] + app_pk: Option, - #[arg(long, value_parser, help = "Input to OpenVM program")] - input: Option, + #[command(flatten)] + run_args: RunArgs, - #[arg(long, action, help = "Path to output proof", default_value = DEFAULT_EVM_PROOF_PATH)] - output: PathBuf, + #[command(flatten)] + cargo_args: RunCargoArgs, #[command(flatten)] agg_tree_config: AggregationTreeConfig, @@ -81,30 +117,34 @@ enum ProveSubCommand { impl ProveCmd { pub fn run(&self) -> Result<()> { - let sdk = Sdk::new(); match &self.command { ProveSubCommand::App { app_pk, - exe, - input, - output, + proof, + run_args, + cargo_args, } => { - let (app_pk, committed_exe, input) = - Self::prepare_execution(&sdk, app_pk, exe, input)?; - let app_proof = sdk.generate_app_proof(app_pk, committed_exe, input)?; - write_app_proof_to_file(app_proof, output)?; + let sdk = Sdk::new(); + let app_pk = Self::load_app_pk(app_pk, cargo_args)?; + let committed_exe = + Self::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)?; } ProveSubCommand::Stark { app_pk, - exe, - input, - output, + proof, + run_args, + cargo_args, agg_tree_config, } => { - let mut sdk = sdk; - sdk.set_agg_tree_config(*agg_tree_config); - let (app_pk, committed_exe, input) = - Self::prepare_execution(&sdk, app_pk, exe, input)?; + let sdk = Sdk::new().with_agg_tree_config(*agg_tree_config); + let app_pk = Self::load_app_pk(app_pk, cargo_args)?; + let committed_exe = + Self::load_or_build_and_commit_exe(&sdk, run_args, cargo_args, &app_pk)?; + let commits = AppExecutionCommit::compute( &app_pk.app_vm_pk.vm_config, &committed_exe, @@ -112,28 +152,34 @@ impl ProveCmd { ); println!("exe commit: {:?}", commits.exe_commit); println!("vm commit: {:?}", commits.vm_commit); + let agg_stark_pk = read_agg_stark_pk_from_file(default_agg_stark_pk_path()).map_err(|e| { eyre::eyre!("Failed to read aggregation proving key: {}\nPlease run 'cargo openvm setup' first", e) })?; - let stark_proof = - sdk.generate_e2e_stark_proof(app_pk, committed_exe, agg_stark_pk, input)?; - encode_to_file(output, stark_proof)?; + let stark_proof = sdk.generate_e2e_stark_proof( + app_pk, + committed_exe, + agg_stark_pk, + read_to_stdin(&run_args.input)?, + )?; + + encode_to_file(proof, stark_proof)?; } #[cfg(feature = "evm-prove")] ProveSubCommand::Evm { app_pk, - exe, - input, - output, + proof, + run_args, + cargo_args, agg_tree_config, } => { use openvm_native_recursion::halo2::utils::CacheHalo2ParamsReader; - let mut sdk = sdk; - sdk.set_agg_tree_config(*agg_tree_config); - let params_reader = CacheHalo2ParamsReader::new(default_params_dir()); - let (app_pk, committed_exe, input) = - Self::prepare_execution(&sdk, app_pk, exe, input)?; + let sdk = Sdk::new().with_agg_tree_config(*agg_tree_config); + let app_pk = Self::load_app_pk(app_pk, cargo_args)?; + let committed_exe = + Self::load_or_build_and_commit_exe(&sdk, run_args, cargo_args, &app_pk)?; + let commits = AppExecutionCommit::compute( &app_pk.app_vm_pk.vm_config, &committed_exe, @@ -146,29 +192,56 @@ impl ProveCmd { let agg_pk = read_default_agg_pk().map_err(|e| { eyre::eyre!("Failed to read aggregation proving key: {}\nPlease run 'cargo openvm setup' first", e) })?; - let evm_proof = - sdk.generate_evm_proof(¶ms_reader, app_pk, committed_exe, agg_pk, input)?; - write_evm_proof_to_file(evm_proof, output)?; + let params_reader = CacheHalo2ParamsReader::new(default_params_dir()); + let evm_proof = sdk.generate_evm_proof( + ¶ms_reader, + app_pk, + committed_exe, + agg_pk, + read_to_stdin(&run_args.input)?, + )?; + + write_evm_proof_to_file(evm_proof, proof)?; } } Ok(()) } - fn prepare_execution( + fn load_app_pk( + app_pk: &Option, + cargo_args: &RunCargoArgs, + ) -> Result>> { + let (manifest_path, _) = get_manifest_path_and_dir(&cargo_args.manifest_path)?; + let target_dir = get_target_dir(&cargo_args.target_dir, &manifest_path); + + let app_pk_path = if let Some(app_pk) = app_pk { + app_pk.to_path_buf() + } else { + get_app_pk_path(&target_dir) + }; + + Ok(Arc::new(read_app_pk_from_file(app_pk_path)?)) + } + + fn load_or_build_and_commit_exe( sdk: &Sdk, - app_pk: &PathBuf, - exe: &PathBuf, - input: &Option, - ) -> Result<( - Arc>, - Arc, - StdIn, - )> { - let app_pk: Arc> = Arc::new(read_app_pk_from_file(app_pk)?); - let app_exe = read_exe_from_file(exe)?; + run_args: &RunArgs, + cargo_args: &RunCargoArgs, + app_pk: &Arc>, + ) -> Result> { + let exe_path = if let Some(exe) = &run_args.exe { + exe + } else { + // Build and get the executable name + let target_name = get_single_target_name(cargo_args)?; + let build_args = run_args.clone().into(); + let cargo_args = cargo_args.clone().into(); + let output_dir = build(&build_args, &cargo_args)?; + &output_dir.join(format!("{}.vmexe", target_name)) + }; + + let app_exe = read_exe_from_file(exe_path)?; let committed_exe = sdk.commit_app_exe(app_pk.app_fri_params(), app_exe)?; - - let input = read_to_stdin(input)?; - Ok((app_pk, committed_exe, input)) + Ok(committed_exe) } } diff --git a/crates/cli/src/commands/run.rs b/crates/cli/src/commands/run.rs index 0bdcd438de..133e7eebf5 100644 --- a/crates/cli/src/commands/run.rs +++ b/crates/cli/src/commands/run.rs @@ -2,33 +2,259 @@ use std::path::PathBuf; use clap::Parser; use eyre::Result; +use openvm_circuit::arch::OPENVM_DEFAULT_INIT_FILE_NAME; use openvm_sdk::{fs::read_exe_from_file, Sdk}; +use super::{build, BuildArgs, BuildCargoArgs}; use crate::{ - default::{DEFAULT_APP_CONFIG_PATH, DEFAULT_APP_EXE_PATH}, input::{read_to_stdin, Input}, - util::read_config_toml_or_default, + util::{get_manifest_path_and_dir, get_single_target_name, read_config_toml_or_default}, }; #[derive(Parser)] #[command(name = "run", about = "Run an OpenVM program")] pub struct RunCmd { - #[arg(long, action, help = "Path to OpenVM executable", default_value = DEFAULT_APP_EXE_PATH)] - exe: PathBuf, + #[clap(flatten)] + run_args: RunArgs, - #[arg(long, action, help = "Path to app config TOML file", default_value = DEFAULT_APP_CONFIG_PATH)] - config: PathBuf, + #[clap(flatten)] + cargo_args: RunCargoArgs, +} + +#[derive(Clone, Parser)] +pub struct RunArgs { + #[arg( + long, + action, + help = "Path to OpenVM executable, if specified build will be skipped", + help_heading = "OpenVM Options" + )] + pub exe: Option, + + #[arg( + long, + help = "Path to the OpenVM config .toml file that specifies the VM extensions, by default will search for the file at ${manifest_dir}/openvm.toml", + help_heading = "OpenVM Options" + )] + pub config: Option, + + #[arg( + long, + help = "Output directory that OpenVM proving artifacts will be copied to", + help_heading = "OpenVM Options" + )] + pub output_dir: Option, + + #[arg( + long, + value_parser, + help = "Input to OpenVM program", + help_heading = "OpenVM Options" + )] + pub input: Option, + + #[arg( + long, + default_value = OPENVM_DEFAULT_INIT_FILE_NAME, + help = "Name of the init file", + help_heading = "OpenVM Options" + )] + pub init_file_name: String, +} + +impl From for BuildArgs { + fn from(args: RunArgs) -> Self { + BuildArgs { + config: args.config, + output_dir: args.output_dir, + init_file_name: args.init_file_name, + ..Default::default() + } + } +} + +#[derive(Clone, Parser)] +pub struct RunCargoArgs { + #[arg( + long, + short = 'p', + value_name = "PACKAGES", + help = "The package to run; by default is the package in the current workspace", + help_heading = "Package Selection" + )] + pub package: Option, + + #[arg( + long, + value_name = "BIN", + help = "Run the specified binary", + help_heading = "Target Selection" + )] + pub bin: Vec, + + #[arg( + long, + value_name = "EXAMPLE", + help = "Run the specified example", + help_heading = "Target Selection" + )] + pub example: Vec, + + #[arg( + long, + short = 'F', + value_name = "FEATURES", + value_delimiter = ',', + help = "Space/comma separated list of features to activate", + help_heading = "Feature Selection" + )] + pub features: Vec, + + #[arg( + long, + help = "Activate all available features of all selected packages", + help_heading = "Feature Selection" + )] + pub all_features: bool, - #[arg(long, value_parser, help = "Input to OpenVM program")] - input: Option, + #[arg( + long, + help = "Do not activate the `default` feature of the selected packages", + help_heading = "Feature Selection" + )] + pub no_default_features: bool, + + #[arg( + long, + value_name = "NAME", + default_value = "release", + help = "Run with the given profile", + help_heading = "Compilation Options" + )] + pub profile: String, + + #[arg( + long, + value_name = "DIR", + help = "Directory for all generated artifacts and intermediate files", + help_heading = "Output Options" + )] + pub target_dir: Option, + + #[arg( + long, + short = 'v', + help = "Use verbose output", + help_heading = "Display Options" + )] + pub verbose: bool, + + #[arg( + long, + short = 'q', + help = "Do not print cargo log messages", + help_heading = "Display Options" + )] + pub quiet: bool, + + #[arg( + long, + value_name = "WHEN", + default_value = "always", + help = "Control when colored output is used", + help_heading = "Display Options" + )] + pub color: String, + + #[arg( + long, + value_name = "PATH", + help = "Path to the Cargo.toml file, by default searches for the file in the current or any parent directory", + help_heading = "Manifest Options" + )] + pub manifest_path: Option, + + #[arg( + long, + help = "Ignore rust-version specification in packages", + help_heading = "Manifest Options" + )] + pub ignore_rust_version: bool, + + #[arg( + long, + help = "Asserts same dependencies and versions are used as when the existing Cargo.lock file was originally generated", + help_heading = "Manifest Options" + )] + pub locked: bool, + + #[arg( + long, + help = "Prevents Cargo from accessing the network for any reason", + help_heading = "Manifest Options" + )] + pub offline: bool, + + #[arg( + long, + help = "Equivalent to specifying both --locked and --offline", + help_heading = "Manifest Options" + )] + pub frozen: bool, +} + +impl From for BuildCargoArgs { + fn from(args: RunCargoArgs) -> Self { + BuildCargoArgs { + package: args.package.into_iter().collect(), + bin: args.bin, + example: args.example, + features: args.features, + all_features: args.all_features, + no_default_features: args.no_default_features, + profile: args.profile, + target_dir: args.target_dir, + verbose: args.verbose, + quiet: args.quiet, + color: args.color, + manifest_path: args.manifest_path, + ignore_rust_version: args.ignore_rust_version, + locked: args.locked, + offline: args.offline, + frozen: args.frozen, + ..Default::default() + } + } } impl RunCmd { pub fn run(&self) -> Result<()> { - let exe = read_exe_from_file(&self.exe)?; - let app_config = read_config_toml_or_default(&self.config)?; + let exe_path = if let Some(exe) = &self.run_args.exe { + exe + } else { + // Build and get the executable name + let target_name = get_single_target_name(&self.cargo_args)?; + let build_args = self.run_args.clone().into(); + let cargo_args = self.cargo_args.clone().into(); + let output_dir = build(&build_args, &cargo_args)?; + &output_dir.join(format!("{}.vmexe", target_name)) + }; + + let (_, manifest_dir) = get_manifest_path_and_dir(&self.cargo_args.manifest_path)?; + let app_config = read_config_toml_or_default( + self.run_args + .config + .to_owned() + .unwrap_or_else(|| manifest_dir.join("openvm.toml")), + )?; + let exe = read_exe_from_file(exe_path)?; + let sdk = Sdk::new(); - let output = sdk.execute(exe, app_config.app_vm_config, read_to_stdin(&self.input)?)?; + let output = sdk.execute( + exe, + app_config.app_vm_config, + read_to_stdin(&self.run_args.input)?, + )?; println!("Execution output: {:?}", output); Ok(()) } diff --git a/crates/cli/src/commands/setup.rs b/crates/cli/src/commands/setup.rs index d84255c7f4..770be24d4c 100644 --- a/crates/cli/src/commands/setup.rs +++ b/crates/cli/src/commands/setup.rs @@ -28,10 +28,10 @@ use crate::{ #[derive(Parser)] #[command( - name = "evm-proving-setup", + name = "setup", about = "Set up for generating EVM proofs. ATTENTION: this requires large amounts of computation and memory. " )] -pub struct EvmProvingSetupCmd { +pub struct SetupCmd { #[arg( long, default_value = "false", @@ -46,7 +46,7 @@ pub struct EvmProvingSetupCmd { pub force_agg_keygen: bool, } -impl EvmProvingSetupCmd { +impl SetupCmd { pub async fn run(&self) -> Result<()> { let default_agg_stark_pk_path = default_agg_stark_pk_path(); let default_params_dir = default_params_dir(); diff --git a/crates/cli/src/commands/verify.rs b/crates/cli/src/commands/verify.rs index 94c7f9e355..09662fb96c 100644 --- a/crates/cli/src/commands/verify.rs +++ b/crates/cli/src/commands/verify.rs @@ -7,7 +7,11 @@ use openvm_sdk::{ Sdk, }; -use crate::default::*; +use super::KeygenCargoArgs; +use crate::{ + default::*, + util::{get_app_vk_path, get_manifest_path_and_dir, get_target_dir}, +}; #[derive(Parser)] #[command(name = "verify", about = "Verify a proof")] @@ -19,15 +23,35 @@ pub struct VerifyCmd { #[derive(Parser)] enum VerifySubCommand { App { - #[arg(long, action, help = "Path to app verifying key", default_value = DEFAULT_APP_VK_PATH)] - app_vk: PathBuf, + #[arg( + long, + action, + help = "Path to app verifying key, by default will search for it in ${target_dir}/openvm/app.vk", + help_heading = "OpenVM Options" + )] + app_vk: Option, - #[arg(long, action, help = "Path to app proof", default_value = DEFAULT_APP_PROOF_PATH)] + #[arg( + long, + action, + default_value = DEFAULT_APP_PROOF_PATH, + help = "Path to app proof", + help_heading = "OpenVM Options" + )] proof: PathBuf, + + #[command(flatten)] + cargo_args: KeygenCargoArgs, }, #[cfg(feature = "evm-verify")] Evm { - #[arg(long, action, help = "Path to EVM proof", default_value = DEFAULT_EVM_PROOF_PATH)] + #[arg( + long, + action, + default_value = DEFAULT_EVM_PROOF_PATH, + help = "Path to EVM proof", + help_heading = "OpenVM Options" + )] proof: PathBuf, }, } @@ -36,8 +60,20 @@ impl VerifyCmd { pub fn run(&self) -> Result<()> { let sdk = Sdk::new(); match &self.command { - VerifySubCommand::App { app_vk, proof } => { - let app_vk = read_app_vk_from_file(app_vk)?; + VerifySubCommand::App { + app_vk, + proof, + cargo_args, + } => { + let app_vk_path = if let Some(app_vk) = app_vk { + app_vk.to_path_buf() + } else { + let (manifest_path, _) = get_manifest_path_and_dir(&cargo_args.manifest_path)?; + 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)?; sdk.verify_app_proof(&app_vk, &app_proof)?; } @@ -47,9 +83,14 @@ impl VerifyCmd { read_evm_halo2_verifier_from_folder, read_evm_proof_from_file, }; - let evm_verifier = read_evm_halo2_verifier_from_folder(default_evm_halo2_verifier_path()).map_err(|e| { - eyre::eyre!("Failed to read EVM verifier: {}\nPlease run 'cargo openvm evm-proving-setup' first", e) - })?; + let evm_verifier = + read_evm_halo2_verifier_from_folder(default_evm_halo2_verifier_path()) + .map_err(|e| { + eyre::eyre!( + "Failed to read EVM verifier: {}\nPlease run 'cargo openvm setup' first", + e + ) + })?; let evm_proof = read_evm_proof_from_file(proof)?; sdk.verify_evm_halo2_proof(&evm_verifier, evm_proof)?; } diff --git a/crates/cli/src/default.rs b/crates/cli/src/default.rs index 641c74b8ea..269499a332 100644 --- a/crates/cli/src/default.rs +++ b/crates/cli/src/default.rs @@ -5,13 +5,12 @@ use openvm_stark_sdk::config::FriParameters; pub const DEFAULT_MANIFEST_DIR: &str = "."; -pub const DEFAULT_APP_CONFIG_PATH: &str = "./openvm.toml"; -pub const DEFAULT_APP_EXE_PATH: &str = "./openvm/app.vmexe"; -pub const DEFAULT_COMMITTED_APP_EXE_PATH: &str = "./openvm/committed_app_exe.bc"; -pub const DEFAULT_APP_PK_PATH: &str = "./openvm/app.pk"; -pub const DEFAULT_APP_VK_PATH: &str = "./openvm/app.vk"; -pub const DEFAULT_APP_PROOF_PATH: &str = "./openvm/app.proof"; -pub const DEFAULT_EVM_PROOF_PATH: &str = "./openvm/evm.proof"; +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 aa4eaceba4..a78467b3b4 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -4,6 +4,7 @@ use std::{ }; use eyre::Result; +use openvm_build::get_workspace_packages; use openvm_sdk::{ config::{AppConfig, SdkVmConfig}, fs::{read_agg_halo2_pk_from_file, read_agg_stark_pk_from_file}, @@ -11,7 +12,13 @@ use openvm_sdk::{ }; use serde::de::DeserializeOwned; -use crate::default::{default_agg_halo2_pk_path, default_agg_stark_pk_path, default_app_config}; +use crate::{ + commands::RunCargoArgs, + default::{ + default_agg_halo2_pk_path, default_agg_stark_pk_path, default_app_config, + DEFAULT_APP_PK_NAME, DEFAULT_APP_VK_NAME, + }, +}; pub(crate) fn read_to_struct_toml(path: impl AsRef) -> Result { let toml = read_to_string(path)?; @@ -50,3 +57,87 @@ pub fn find_manifest_dir(mut current_dir: PathBuf) -> Result { } Ok(current_dir) } + +pub fn get_manifest_path_and_dir(manifest_path: &Option) -> Result<(PathBuf, PathBuf)> { + let manifest_dir = if let Some(manifest_path) = &manifest_path { + if !manifest_path.ends_with("Cargo.toml") { + return Err(eyre::eyre!( + "manifest_path must be a path to a Cargo.toml file" + )); + } + manifest_path.parent().unwrap().canonicalize()? + } else { + find_manifest_dir(PathBuf::from("."))? + }; + let manifest_path = manifest_dir.join("Cargo.toml"); + Ok((manifest_path.clone(), manifest_dir)) +} + +pub fn get_target_dir(target_dir: &Option, manifest_path: &PathBuf) -> PathBuf { + target_dir + .clone() + .unwrap_or_else(|| openvm_build::get_target_dir(manifest_path)) +} + +pub fn get_target_output_dir(target_dir: &Path, profile: &str) -> PathBuf { + target_dir.join("openvm").join(profile).to_path_buf() +} + +pub fn get_app_pk_path(target_dir: &Path) -> PathBuf { + target_dir.join("openvm").join(DEFAULT_APP_PK_NAME) +} + +pub fn get_app_vk_path(target_dir: &Path) -> PathBuf { + target_dir.join("openvm").join(DEFAULT_APP_VK_NAME) +} + +// Given the arguments to a run command, this function isolates the executable to +// run. If a specific binary or example is specified it will return that, else it +// will search the workspace/package for binary targets. If there is a single +// binary that will be returned, else an error will be raised. +pub fn get_single_target_name(cargo_args: &RunCargoArgs) -> Result { + let num_targets = cargo_args.bin.len() + cargo_args.example.len(); + let single_target_name = if num_targets > 1 { + return Err(eyre::eyre!( + "`cargo openvm run` can run at most one executable, but multiple were specified" + )); + } else if num_targets == 0 { + let (_, manifest_dir) = get_manifest_path_and_dir(&cargo_args.manifest_path)?; + + let packages = get_workspace_packages(&manifest_dir) + .into_iter() + .filter(|pkg| { + if let Some(package) = &cargo_args.package { + pkg.name == *package + } else { + true + } + }) + .collect::>(); + + let binaries = packages + .iter() + .flat_map(|pkg| pkg.targets.iter()) + .filter(|t| t.is_bin()) + .collect::>(); + + if binaries.len() > 1 { + return Err(eyre::eyre!( + "Could not determine which binary to run. Use the --bin flag to specify.\n\ + Available targets: {:?}", + binaries.iter().map(|t| t.name.clone()).collect::>() + )); + } else if binaries.is_empty() { + return Err(eyre::eyre!( + "No binaries found. If you would like to run an example, use the --example flag.", + )); + } else { + binaries[0].name.clone() + } + } else if cargo_args.bin.is_empty() { + format!("examples/{}", cargo_args.example[0]) + } else { + cargo_args.bin[0].clone() + }; + Ok(single_target_name) +} diff --git a/crates/cli/tests/app_e2e.rs b/crates/cli/tests/app_e2e.rs index 60bd5b2d68..3d146c52d2 100644 --- a/crates/cli/tests/app_e2e.rs +++ b/crates/cli/tests/app_e2e.rs @@ -7,9 +7,9 @@ use tempfile::tempdir; fn test_cli_app_e2e() -> Result<()> { let temp_dir = tempdir()?; run_cmd("cargo", &["install", "--path", ".", "--force"])?; - let temp_exe = temp_dir.path().join("fibonacci.vmexe"); - let temp_pk = temp_dir.path().join("fibonacci.pk"); - let temp_vk = temp_dir.path().join("fibonacci.vk"); + 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"); run_cmd( @@ -21,8 +21,6 @@ fn test_cli_app_e2e() -> Result<()> { "tests/programs/fibonacci/Cargo.toml", "--config", "tests/programs/fibonacci/openvm.toml", - "--exe-output", - temp_exe.to_str().unwrap(), ], )?; @@ -33,10 +31,8 @@ fn test_cli_app_e2e() -> Result<()> { "keygen", "--config", "tests/programs/fibonacci/openvm.toml", - "--output", - temp_pk.to_str().unwrap(), - "--vk-output", - temp_vk.to_str().unwrap(), + "--output-dir", + temp_dir.path().to_str().unwrap(), ], )?; @@ -46,7 +42,7 @@ fn test_cli_app_e2e() -> Result<()> { "openvm", "run", "--exe", - temp_exe.to_str().unwrap(), + exe_path, "--config", "tests/programs/fibonacci/openvm.toml", ], @@ -61,8 +57,8 @@ fn test_cli_app_e2e() -> Result<()> { "--app-pk", temp_pk.to_str().unwrap(), "--exe", - temp_exe.to_str().unwrap(), - "--output", + exe_path, + "--proof", temp_proof.to_str().unwrap(), ], )?; @@ -95,10 +91,81 @@ fn test_cli_app_e2e_default_paths() -> Result<()> { "tests/programs/fibonacci/Cargo.toml", ], )?; - run_cmd("cargo", &["openvm", "keygen"])?; - run_cmd("cargo", &["openvm", "run"])?; - run_cmd("cargo", &["openvm", "prove", "app"])?; - run_cmd("cargo", &["openvm", "verify", "app"])?; + 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"])?; + run_cmd( + "cargo", + &[ + "openvm", + "keygen", + "--manifest-path", + "tests/programs/multi/Cargo.toml", + ], + )?; + run_cmd( + "cargo", + &[ + "openvm", + "prove", + "app", + "--manifest-path", + "tests/programs/multi/Cargo.toml", + "--example", + "fibonacci", + ], + )?; + run_cmd( + "cargo", + &[ + "openvm", + "verify", + "app", + "--manifest-path", + "tests/programs/multi/Cargo.toml", + ], + )?; Ok(()) } diff --git a/crates/cli/tests/build.rs b/crates/cli/tests/build.rs index 42d3f09ee6..084db50367 100644 --- a/crates/cli/tests/build.rs +++ b/crates/cli/tests/build.rs @@ -1,46 +1,25 @@ use std::path::PathBuf; -use cargo_openvm::{ - commands::{build, BuildArgs, BuildCargoArgs}, - default::{DEFAULT_APP_EXE_PATH, DEFAULT_COMMITTED_APP_EXE_PATH}, -}; +use cargo_openvm::commands::{build, BuildArgs, BuildCargoArgs}; use eyre::Result; use openvm_build::RUSTC_TARGET; -use openvm_circuit::arch::OPENVM_DEFAULT_INIT_FILE_NAME; -fn default_build_args(example: &str) -> BuildArgs { +fn default_build_test_args(example: &str) -> BuildArgs { BuildArgs { no_transpile: true, - config: PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests") - .join("programs") - .join(example) - .join("openvm.toml"), - exe_output: PathBuf::from(DEFAULT_APP_EXE_PATH), - committed_exe_output: PathBuf::from(DEFAULT_COMMITTED_APP_EXE_PATH), - init_file_name: OPENVM_DEFAULT_INIT_FILE_NAME.to_string(), + config: Some( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests") + .join("programs") + .join(example) + .join("openvm.toml"), + ), + ..Default::default() } } -fn default_cargo_args(example: &str) -> BuildCargoArgs { +fn default_cargo_test_args(example: &str) -> BuildCargoArgs { BuildCargoArgs { - package: vec![], - workspace: false, - exclude: vec![], - lib: false, - bin: vec![], - bins: false, - example: vec![], - examples: false, - all_targets: false, - all_features: false, - no_default_features: false, - features: vec![], - profile: "release".to_string(), - target_dir: None, - verbose: false, - quiet: false, - color: "always".to_string(), manifest_path: Some( PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests") @@ -48,10 +27,7 @@ fn default_cargo_args(example: &str) -> BuildCargoArgs { .join(example) .join("Cargo.toml"), ), - ignore_rust_version: false, - locked: false, - offline: false, - frozen: false, + ..Default::default() } } @@ -60,8 +36,8 @@ fn test_build_with_profile() -> Result<()> { let temp_dir = tempfile::tempdir()?; let target_dir = temp_dir.path(); - let build_args = default_build_args("fibonacci"); - let mut cargo_args = default_cargo_args("fibonacci"); + let build_args = default_build_test_args("fibonacci"); + let mut cargo_args = default_cargo_test_args("fibonacci"); cargo_args.target_dir = Some(target_dir.to_path_buf()); cargo_args.profile = "dev".to_string(); @@ -79,63 +55,136 @@ fn test_multi_target_build() -> Result<()> { let temp_dir = tempfile::tempdir()?; let target_dir = temp_dir.path(); - let build_args = default_build_args("multi"); - let mut cargo_args = default_cargo_args("multi"); + let build_args = default_build_test_args("multi"); + let mut cargo_args = default_cargo_test_args("multi"); cargo_args.target_dir = Some(target_dir.to_path_buf()); // Build lib cargo_args.lib = true; - let build_result = build(&build_args, &cargo_args)?; - assert!(build_result.is_empty()); + let elf_dir = build(&build_args, &cargo_args)?; + assert!(!elf_dir.join("calculator").exists()); + assert!(!elf_dir.join("string-utils").exists()); + assert!(!elf_dir.join("examples/fibonacci").exists()); + assert!(!elf_dir.join("examples/palindrome").exists()); + std::fs::remove_dir_all(&elf_dir)?; + std::fs::create_dir_all(&elf_dir)?; // Build bins cargo_args.lib = false; - let build_result = build(&build_args, &cargo_args)?; - let binary_names: Vec = build_result - .iter() - .map(|path| path.file_stem().unwrap().to_string_lossy().to_string()) - .collect(); - assert!(binary_names.len() == 2); - assert!(binary_names.contains(&"calculator".to_string())); - assert!(binary_names.contains(&"string-utils".to_string())); + let elf_dir = build(&build_args, &cargo_args)?; + assert!(elf_dir.join("calculator").exists()); + assert!(elf_dir.join("string-utils").exists()); + assert!(!elf_dir.join("examples/fibonacci").exists()); + assert!(!elf_dir.join("examples/palindrome").exists()); + std::fs::remove_dir_all(&elf_dir)?; + std::fs::create_dir_all(&elf_dir)?; // Build examples cargo_args.examples = true; - let build_result = build(&build_args, &cargo_args)?; - let example_names: Vec = build_result - .iter() - .map(|path| path.file_stem().unwrap().to_string_lossy().to_string()) - .collect(); - assert!(example_names.len() == 2); - assert!(example_names.contains(&"fibonacci".to_string())); - assert!(example_names.contains(&"palindrome".to_string())); + let elf_dir = build(&build_args, &cargo_args)?; + assert!(!elf_dir.join("calculator").exists()); + assert!(!elf_dir.join("string-utils").exists()); + assert!(elf_dir.join("examples/fibonacci").exists()); + assert!(elf_dir.join("examples/palindrome").exists()); + std::fs::remove_dir_all(&elf_dir)?; + std::fs::create_dir_all(&elf_dir)?; // Build examples and a single bin cargo_args.bin = vec!["calculator".to_string()]; - let build_result = build(&build_args, &cargo_args)?; - let exe_names: Vec = build_result - .iter() - .map(|path| path.file_stem().unwrap().to_string_lossy().to_string()) - .collect(); - assert!(exe_names.len() == 3); - assert!(exe_names.contains(&"calculator".to_string())); - assert!(exe_names.contains(&"fibonacci".to_string())); - assert!(exe_names.contains(&"palindrome".to_string())); + let elf_dir = build(&build_args, &cargo_args)?; + assert!(elf_dir.join("calculator").exists()); + assert!(!elf_dir.join("string-utils").exists()); + assert!(elf_dir.join("examples/fibonacci").exists()); + assert!(elf_dir.join("examples/palindrome").exists()); + std::fs::remove_dir_all(&elf_dir)?; + std::fs::create_dir_all(&elf_dir)?; // Build all targets cargo_args.bin = vec![]; cargo_args.examples = false; cargo_args.all_targets = true; - let build_result = build(&build_args, &cargo_args)?; - let all_names: Vec = build_result - .iter() - .map(|path| path.file_stem().unwrap().to_string_lossy().to_string()) - .collect(); - assert!(all_names.len() == 4); - assert!(all_names.contains(&"calculator".to_string())); - assert!(all_names.contains(&"string-utils".to_string())); - assert!(all_names.contains(&"fibonacci".to_string())); - assert!(all_names.contains(&"palindrome".to_string())); + let elf_dir = build(&build_args, &cargo_args)?; + assert!(elf_dir.join("calculator").exists()); + assert!(elf_dir.join("string-utils").exists()); + assert!(elf_dir.join("examples/fibonacci").exists()); + assert!(elf_dir.join("examples/palindrome").exists()); + + temp_dir.close()?; + Ok(()) +} + +#[test] +fn test_multi_target_transpile_default() -> Result<()> { + let temp_dir = tempfile::tempdir()?; + let target_dir = temp_dir.path(); + + let mut build_args = default_build_test_args("multi"); + let mut cargo_args = default_cargo_test_args("multi"); + build_args.no_transpile = false; + cargo_args.target_dir = Some(target_dir.to_path_buf()); + cargo_args.all_targets = true; + + build(&build_args, &cargo_args)?; + + // Check for openvm directory + let openvm_dir = target_dir.join("openvm"); + assert!(openvm_dir.exists(),); + + // Check for release directory + let release_dir = openvm_dir.join("release"); + assert!(release_dir.exists()); + + // Check for expected vmexe files + let calculator_exe = release_dir.join("calculator.vmexe"); + let string_utils_exe = release_dir.join("string-utils.vmexe"); + assert!(calculator_exe.exists()); + assert!(string_utils_exe.exists()); + + // Check for example directory + let examples_dir = release_dir.join("examples"); + assert!(examples_dir.exists()); + + // Check for expected example files + let fibonacci_exe = examples_dir.join("fibonacci.vmexe"); + let palindrome_exe = examples_dir.join("palindrome.vmexe"); + assert!(fibonacci_exe.exists()); + assert!(palindrome_exe.exists()); + + Ok(()) +} + +#[test] +fn test_output_dir_copy() -> Result<()> { + let temp_dir = tempfile::tempdir()?; + let target_dir = temp_dir.path(); + + let temp_dir_2 = tempfile::tempdir()?; + let output_dir = temp_dir_2.path(); + + let mut build_args = default_build_test_args("fibonacci"); + let mut cargo_args = default_cargo_test_args("fibonacci"); + build_args.output_dir = Some(output_dir.to_path_buf()); + build_args.no_transpile = false; + cargo_args.target_dir = Some(target_dir.to_path_buf()); + + build(&build_args, &cargo_args)?; + + // Check for executable in target_dir + let default_target = target_dir + .join("openvm") + .join("release") + .join("openvm-cli-example-test.vmexe"); + assert!(default_target.exists()); + + // Check for executable in output_dir + let copied_target = output_dir.join("openvm-cli-example-test.vmexe"); + assert!(copied_target.exists()); + + // Check that the executable is the same + assert_eq!( + std::fs::read(default_target)?, + std::fs::read(copied_target)? + ); Ok(()) } diff --git a/crates/cli/tests/programs/multi/calculator/Cargo.toml b/crates/cli/tests/programs/multi/calculator/Cargo.toml index 1f8543e2ab..9362608c61 100644 --- a/crates/cli/tests/programs/multi/calculator/Cargo.toml +++ b/crates/cli/tests/programs/multi/calculator/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "openvm-calculator" +name = "openvm-cli-example-calculator" version = "0.0.0" edition = "2021" diff --git a/crates/cli/tests/programs/multi/string-utils/Cargo.toml b/crates/cli/tests/programs/multi/string-utils/Cargo.toml index 245b777912..f2f0561d70 100644 --- a/crates/cli/tests/programs/multi/string-utils/Cargo.toml +++ b/crates/cli/tests/programs/multi/string-utils/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "openvm-string-utils" +name = "openvm-cli-example-string-utils" version = "0.0.0" edition = "2021" diff --git a/crates/sdk/src/config/mod.rs b/crates/sdk/src/config/mod.rs index 95581c6084..3a231f180d 100644 --- a/crates/sdk/src/config/mod.rs +++ b/crates/sdk/src/config/mod.rs @@ -68,16 +68,31 @@ pub struct Halo2Config { #[derive(Clone, Copy, Debug, Serialize, Deserialize, Args)] pub struct AggregationTreeConfig { /// Each leaf verifier circuit will aggregate this many App VM proofs. - #[arg(long, default_value_t = DEFAULT_NUM_CHILDREN_LEAF)] + #[arg( + long, + default_value_t = DEFAULT_NUM_CHILDREN_LEAF, + help = "Number of children per leaf verifier circuit", + help_heading = "Aggregation Tree Options" + )] pub num_children_leaf: usize, /// Each internal verifier circuit will aggregate this many proofs, /// where each proof may be of either leaf or internal verifier (self) circuit. - #[arg(long, default_value_t = DEFAULT_NUM_CHILDREN_INTERNAL)] + #[arg( + long, + default_value_t = DEFAULT_NUM_CHILDREN_INTERNAL, + help = "Number of children per internal verifier circuit", + help_heading = "Aggregation Tree Options" + )] pub num_children_internal: usize, /// Safety threshold: how many times to do 1-to-1 aggregation of the "last" internal /// verifier proof before it is small enough for the root verifier circuit. /// Note: almost always no wrapping is needed. - #[arg(long, default_value_t = DEFAULT_MAX_INTERNAL_WRAPPER_LAYERS)] + #[arg( + long, + default_value_t = DEFAULT_MAX_INTERNAL_WRAPPER_LAYERS, + help = "Maximum number of internal wrapper layers", + help_heading = "Aggregation Tree Options" + )] pub max_internal_wrapper_layers: usize, // root currently always has 1 child for now } diff --git a/crates/sdk/src/fs.rs b/crates/sdk/src/fs.rs index f6a6d46edf..e849f1b49c 100644 --- a/crates/sdk/src/fs.rs +++ b/crates/sdk/src/fs.rs @@ -95,6 +95,9 @@ pub fn read_evm_proof_from_file>(path: P) -> Result { } pub fn write_evm_proof_to_file>(proof: EvmProof, path: P) -> Result<()> { + if let Some(parent) = path.as_ref().parent() { + create_dir_all(parent)?; + } serde_json::to_writer(File::create(path)?, &proof)?; Ok(()) } diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index 91b2db0cc0..7148c8e44e 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -119,12 +119,13 @@ impl> GenericSdk { Self::default() } - pub fn agg_tree_config(&self) -> &AggregationTreeConfig { - &self.agg_tree_config + pub fn with_agg_tree_config(mut self, agg_tree_config: AggregationTreeConfig) -> Self { + self.agg_tree_config = agg_tree_config; + self } - pub fn set_agg_tree_config(&mut self, agg_tree_config: AggregationTreeConfig) { - self.agg_tree_config = agg_tree_config; + pub fn agg_tree_config(&self) -> &AggregationTreeConfig { + &self.agg_tree_config } pub fn build>(