Skip to content

Commit

Permalink
Feature: specify verifier when generating proof (#10)
Browse files Browse the repository at this point in the history
Problem: the proofs generated by the CLI are not compatible with the L1
verifier.

Solution: add a `--verifier` flag that lets the user decide for which
verifier the proof needs to be optimized.

Added the `--allow-missing-builtins` flag for testing with the
bootloader.
  • Loading branch information
odesenfans authored Feb 24, 2024
1 parent 172ee47 commit d2e5f8e
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 80 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ env_logger = { version = "0.11.2", features = ["color"] }
log = "0.4.20"
serde = { version = "1.0.196", features = ["derive"] }
serde_json = { version = "1.0.113" }
stone-prover-sdk = { git = "https://github.com/Moonsong-Labs/stone-prover-sdk", rev = "e38435cad3d906f1169979947204fff87aa328e1" }
stone-prover-sdk = { git = "https://github.com/Moonsong-Labs/stone-prover-sdk", rev = "4e86b70f245f02b8a76c7d7ad0ce93fe7f9a3400" }
thiserror = { version = "1.0.57" }

[dev-dependencies]
Expand Down
78 changes: 29 additions & 49 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use clap::error::ErrorKind;
use clap::{Args, CommandFactory, Parser, Subcommand};
use clap::{Args, CommandFactory, Parser};
use std::borrow::Cow;
use std::path::{Path, PathBuf};
use stone_prover_sdk::models::Layout;
use stone_prover_sdk::models::{Layout, Verifier};

#[derive(Parser, Debug)]
#[command(name = "stone")]
Expand All @@ -25,6 +25,12 @@ pub struct ProveArgs {
#[clap(long = "layout")]
pub layout: Option<Layout>,

#[clap(long = "verifier")]
pub verifier: Option<Verifier>,

#[clap(long = "allow-missing-builtins", action)]
pub allow_missing_builtins: bool,

#[clap(flatten)]
pub config: ConfigArgs,

Expand All @@ -51,62 +57,36 @@ impl ProveArgs {
.exit();
}

if self.with_bootloader {
let args = ProveWithBootloaderArgs {
programs: self.programs,
config: self.config,
layout: self.layout,
};
return ProveCommand::WithBootloader(args);
}
let args = ProveBareArgs {
program: self.programs.remove(0),
program_input: self.program_input,
layout: self.layout,
config: self.config,
let executable = match self.with_bootloader {
true => Executable::WithBootloader(self.programs),
false => Executable::BareMetal(self.programs.remove(0)),
};
ProveCommand::Bare(args)
}
}
let layout = self.layout.unwrap_or(Layout::StarknetWithKeccak);
let verifier = self.verifier.unwrap_or(Verifier::Stone);

#[derive(Subcommand, Debug)]
pub enum ProveCommand {
Bare(ProveBareArgs),
WithBootloader(ProveWithBootloaderArgs),
}

impl ProveCommand {
pub fn config(&self) -> &ConfigArgs {
match self {
ProveCommand::Bare(args) => &args.config,
ProveCommand::WithBootloader(args) => &args.config,
ProveCommand {
executable,
config: self.config,
layout,
verifier,
allow_missing_builtins: self.allow_missing_builtins,
}
}
}

#[derive(Args, Debug)]
pub struct ProveBareArgs {
pub program: PathBuf,

#[clap(long = "program-input")]
pub program_input: Option<PathBuf>,

#[clap(long = "layout")]
pub layout: Option<Layout>,

#[clap(flatten)]
#[derive(Debug, Clone)]
pub struct ProveCommand {
pub executable: Executable,
pub config: ConfigArgs,
pub layout: Layout,
pub verifier: Verifier,
pub allow_missing_builtins: bool,
}

#[derive(Args, Debug)]
pub struct ProveWithBootloaderArgs {
pub programs: Vec<PathBuf>,

#[clap(long = "layout")]
pub layout: Option<Layout>,

#[clap(flatten)]
pub config: ConfigArgs,
#[derive(Debug, Clone)]
pub enum Executable {
BareMetal(PathBuf),
WithBootloader(Vec<PathBuf>),
}

#[derive(Args, Clone, Debug)]
Expand Down
50 changes: 28 additions & 22 deletions src/commands/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use stone_prover_sdk::fri::generate_prover_parameters;
use stone_prover_sdk::models::{Layout, ProverConfig};
use stone_prover_sdk::prover::run_prover;

use crate::cli::{ProveBareArgs, ProveCommand, ProveWithBootloaderArgs};
use crate::cli::{Executable, ProveCommand};
use crate::toolkit::json::{read_json_from_file, ReadJsonError};

const BOOTLOADER_PROGRAM: &[u8] =
Expand Down Expand Up @@ -53,11 +53,13 @@ pub enum RunError {
Prover(#[from] ProverError),
}

pub fn run_program(args: ProveBareArgs) -> Result<ExecutionArtifacts, RunError> {
let layout = args.layout.unwrap_or(Layout::StarknetWithKeccak);
let allow_missing_builtins = false;

let program = std::fs::read(&args.program).map_err(|e| RunError::Io(args.program, e))?;
pub fn run_program(
program_path: PathBuf,
layout: Layout,
allow_missing_builtins: bool,
) -> Result<ExecutionArtifacts, RunError> {
let program =
std::fs::read(program_path.as_path()).map_err(|e| RunError::Io(program_path, e))?;
let (runner, vm) = run_in_proof_mode(&program, layout, Some(allow_missing_builtins))
.map_err(ExecutionError::RunFailed)?;
extract_execution_artifacts(runner, vm).map_err(|e| e.into())
Expand Down Expand Up @@ -91,18 +93,19 @@ fn task_from_file(file: &Path) -> Result<TaskSpec, TaskError> {
Ok(TaskSpec { task })
}

pub fn run_with_bootloader(args: ProveWithBootloaderArgs) -> Result<ExecutionArtifacts, RunError> {
let layout = args.layout.unwrap_or(Layout::StarknetWithKeccak);
let allow_missing_builtins = false;
pub fn run_with_bootloader(
executables: &[PathBuf],
layout: Layout,
allow_missing_builtins: bool,
) -> Result<ExecutionArtifacts, RunError> {
let bootloader = Program::from_bytes(BOOTLOADER_PROGRAM, Some("main"))
.map_err(RunError::FailedToLoadBootloader)?;
let tasks: Result<Vec<TaskSpec>, RunError> = args
.programs
.into_iter()
.map(|path_buf| {
task_from_file(path_buf.as_path()).map_err(|e| match e {
TaskError::Pie(e) => RunError::FailedToLoadPie(path_buf, e),
TaskError::Program(e) => RunError::FailedToLoadProgram(path_buf, e),
let tasks: Result<Vec<TaskSpec>, RunError> = executables
.iter()
.map(|path| {
task_from_file(path).map_err(|e| match e {
TaskError::Pie(e) => RunError::FailedToLoadPie(path.to_path_buf(), e),
TaskError::Program(e) => RunError::FailedToLoadProgram(path.to_path_buf(), e),
})
})
.collect();
Expand All @@ -120,7 +123,7 @@ pub fn prove(command: ProveCommand) -> Result<(), RunError> {
debug!("preparing config files...");

// Cloning here is the easiest solution to avoid borrow checks.
let config_args = command.config().clone();
let config_args = command.config.clone();

let user_prover_config = config_args
.prover_config_file
Expand All @@ -136,15 +139,18 @@ pub fn prove(command: ProveCommand) -> Result<(), RunError> {
.transpose()?;

info!("execution in progress...");
let execution_artifacts = match command {
ProveCommand::Bare(args) => run_program(args)?,
ProveCommand::WithBootloader(args) => run_with_bootloader(args)?,
let execution_artifacts = match command.executable {
Executable::BareMetal(program_path) => {
run_program(program_path, command.layout, command.allow_missing_builtins)?
}
Executable::WithBootloader(executables) => {
run_with_bootloader(&executables, command.layout, command.allow_missing_builtins)?
}
};

let last_layer_degree_bound = 64;
let prover_parameters = user_prover_parameters.unwrap_or(generate_prover_parameters(
execution_artifacts.public_input.n_steps,
last_layer_degree_bound,
command.verifier,
));

info!("proving in progress...");
Expand Down
60 changes: 53 additions & 7 deletions tests/test_prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ use std::path::Path;
use cairo_vm::air_private_input::{AirPrivateInput, AirPrivateInputSerializable};
use rstest::rstest;
use stone_prover_sdk::json::read_json_from_file;
use stone_prover_sdk::models::Proof;
use stone_prover_sdk::models::{Proof, Verifier};

use crate::common::cli_in_path;

mod common;

fn invoke_cli(
with_bootloader: bool,
programs: &[&Path],
program_input: Option<&Path>,
executables: &[&Path],
verifier: Option<Verifier>,
prover_config: Option<&Path>,
prover_parameters: Option<&Path>,
output_file: Option<&Path>,
Expand All @@ -24,12 +24,12 @@ fn invoke_cli(
if with_bootloader {
command.arg("--with-bootloader");
}
for program in programs {
command.arg(*program);
for executable in executables {
command.arg(*executable);
}

if let Some(input_file) = program_input {
command.arg("--program-input").arg(input_file);
if let Some(verifier) = verifier {
command.arg("--verifier").arg(verifier.to_string());
}
if let Some(config_file) = prover_config {
command.arg("--prover-config-file").arg(config_file);
Expand Down Expand Up @@ -123,6 +123,52 @@ fn execute_and_prove_program(
assert_proof_eq(proof, expected_proof);
}

#[rstest]
fn execute_and_prove_program_l1_verifier(#[from(cli_in_path)] _path: ()) {
let output_dir = tempfile::tempdir().unwrap();
let proof_file = output_dir.path().join("proof.json");

// Sanity check
assert!(!proof_file.exists());

let test_case_dir =
Path::new(env!("CARGO_MANIFEST_DIR")).join("dependencies/cairo-programs/cairo0/fibonacci");

let program = test_case_dir.join("fibonacci.json");

let result = invoke_cli(
false,
&vec![program.as_path()],
Some(Verifier::L1),
None,
None,
Some(proof_file.as_path()),
)
.expect("Command should succeed");

println!(
"stdout: {}\n\n\nstderr: {}",
String::from_utf8_lossy(&result.stdout),
String::from_utf8_lossy(&result.stderr)
);
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);

assert!(proof_file.exists());

let proof: Proof = read_json_from_file(proof_file).unwrap();
// TODO: test with L1 verifier
// Check that the FRI steps are compatible with the L1 verifier
assert_eq!(
proof.proof_parameters.stark.fri.fri_step_list,
vec![0, 2, 2, 2, 2, 2, 2, 2]
);
assert_eq!(proof.proof_parameters.stark.fri.last_layer_degree_bound, 32);
}

#[rstest]
fn execute_and_prove_program_with_bootloader(#[from(cli_in_path)] _path: ()) {
let output_dir = tempfile::tempdir().unwrap();
Expand Down

0 comments on commit d2e5f8e

Please sign in to comment.