Skip to content

feat: CLI prove use bin name for output path #1675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 27, 2025
10 changes: 5 additions & 5 deletions book/src/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion book/src/writing-apps/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ cargo openvm run --input <path_to_input | hex_string>

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

Expand Down
3 changes: 1 addition & 2 deletions book/src/writing-apps/prove.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
8 changes: 5 additions & 3 deletions book/src/writing-apps/verify.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ cargo openvm verify app
--proof <path_to_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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think verify should always pass in --proof to be explicit

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily opposed to this, but our CLI toolchain generally has the semantic where we allow file inputs to be omitted if there's exactly one option. Given that we error out if there isn't exactly one option, I think it might be good to maintain this behavior in verify. Lmk what you think!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think when you do prove, because the input (exe) is a generated, and is never really being "used" directly outside of the openvm context, we can use some reasonable default.
But I think the "proof" is a bit different in that it's the final object that when will used elsewhere (onchain?) so would make sense to be explicit. Want to avoid the case that user think a proof is verified, but actually they verify something else (on the default path)

wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From earlier discussions with Jonathan, I think we should assume that all of the generated artifacts (i.e. .pk, .vk, .vmexe, etc.) can and likely will be used somewhere. Default naming is done using the project's structure, which the user should in theory know about and set.

I don't think it's a stretch to assume that users will be able to deduce that a verify call with no --proof specified will look in the working directory, especially since that's the output behavior of prove? In general I feel like if a user does prove --proof file.proof they should know to do verify --proof file.proof.

That being said, regardless of what we choose we should probably output a success message with the proof path. As of now there's no messaging, which does indeed make it difficult to confirm what's going on 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh i think printing the proof path is a good idea. ok i think this is probably fine


## EVM Level

Expand Down Expand Up @@ -93,11 +95,11 @@ cargo openvm prove evm --input <path_to_input>
cargo openvm verify evm --proof <path_to_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
{
Expand Down
11 changes: 6 additions & 5 deletions book/src/writing-apps/write-program.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
7 changes: 2 additions & 5 deletions crates/cli/src/commands/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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(
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
57 changes: 37 additions & 20 deletions crates/cli/src/commands/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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<PathBuf>,

#[arg(
long,
Expand All @@ -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<PathBuf>,

#[arg(
long,
Expand All @@ -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<PathBuf>,

#[arg(
long,
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand All @@ -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 {
Expand All @@ -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(
Expand All @@ -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(())
Expand All @@ -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<AppProvingKey<SdkVmConfig>>,
) -> Result<Arc<NonRootCommittedExe>> {
) -> Result<(Arc<NonRootCommittedExe>, String)> {
let exe_path = if let Some(exe) = &run_args.exe {
exe
} else {
Expand All @@ -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(),
))
}
Loading
Loading