Skip to content
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

feat: add simulation function #832

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,32 @@ impl ProverClient {
pub fn verify_plonk(&self, proof: &SP1PlonkBn254Proof, vkey: &SP1VerifyingKey) -> Result<()> {
self.prover.verify_plonk(proof, vkey)
}

/// Simulates the execution of a program with the given input, and return the number of cycles.
///
/// ### Examples
/// ```no_run
/// use sp1_sdk::{ProverClient, SP1Stdin};
///
/// // Load the program.
/// let elf = include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf");
///
/// // Initialize the prover client.
/// let client = ProverClient::new();
///
/// // Setup the program.
/// let (pk, vk) = client.setup(elf);
///
/// // Setup the inputs.
/// let mut stdin = SP1Stdin::new();
/// stdin.write(&10usize);
///
/// // Simulate the execution of the program.
/// let cycles = client.simulate(elf, stdin).unwrap();
/// ```
pub fn simulate(&self, elf: &[u8], stdin: SP1Stdin) -> Result<u64> {
self.prover.simulate(elf, stdin)
}
}

impl Default for ProverClient {
Expand Down Expand Up @@ -450,4 +476,15 @@ mod tests {
let proof = client.prove_plonk(&pk, stdin).unwrap();
client.verify_plonk(&proof, &vk).unwrap();
}

#[test]
fn test_simulate() {
utils::setup_logger();
let client = ProverClient::local();
let elf =
include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let _ = client.simulate(elf, stdin).unwrap();
}
}
25 changes: 25 additions & 0 deletions sdk/src/provers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use anyhow::Result;
pub use local::LocalProver;
pub use mock::MockProver;
pub use network::NetworkProver;
use sp1_core::runtime::{Program, Runtime};
use sp1_core::stark::MachineVerificationError;
use sp1_core::utils::SP1CoreOpts;
use sp1_prover::CoreSC;
use sp1_prover::SP1CoreProofData;
use sp1_prover::SP1Prover;
Expand Down Expand Up @@ -72,4 +74,27 @@ pub trait Prover: Send + Sync {

Ok(())
}

// Simulates the execution of a program with the given input, and return the number of cycles.
fn simulate(&self, elf: &[u8], stdin: SP1Stdin) -> Result<u64> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we have this, don't we already have execute? I also don't like how we have copy-pasta here for this sort of code. We should have a function on runtime that basically handles the stdin or have run_untraced take in the stdin...otherwise we have to fix this code everywhere.

let program = Program::from(elf);
let mut runtime = Runtime::new(program, SP1CoreOpts::default());

runtime.write_vecs(&stdin.buffer);
for (proof, vkey) in stdin.proofs.iter() {
runtime.write_proof(proof.clone(), vkey.clone());
}

match runtime.run_untraced() {
Ok(_) => {
let cycles = runtime.state.global_clk;
log::info!("Simulation complete, cycles: {}", cycles);
Ok(cycles)
}
Err(e) => {
log::error!("Failed to simulate program: {}", e);
Err(e.into())
}
}
}
}
17 changes: 3 additions & 14 deletions sdk/src/provers/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ use crate::{
use crate::{SP1CompressedProof, SP1PlonkBn254Proof, SP1Proof, SP1ProvingKey, SP1VerifyingKey};
use anyhow::{Context, Result};
use serde::de::DeserializeOwned;
use sp1_core::runtime::{Program, Runtime};
use sp1_core::utils::SP1CoreOpts;
use sp1_prover::utils::block_on;
use sp1_prover::{SP1Prover, SP1Stdin};
use tokio::{runtime, time::sleep};
Expand Down Expand Up @@ -42,18 +40,6 @@ impl NetworkProver {
mode: ProofMode,
) -> Result<P> {
let client = &self.client;
// Execute the runtime before creating the proof request.
let program = Program::from(elf);
let opts = SP1CoreOpts::default();
let mut runtime = Runtime::new(program, opts);
runtime.write_vecs(&stdin.buffer);
for (proof, vkey) in stdin.proofs.iter() {
runtime.write_proof(proof.clone(), vkey.clone());
}
runtime
.run_untraced()
.context("Failed to execute program")?;
log::info!("Simulation complete, cycles: {}", runtime.state.global_clk);

let proof_id = client.create_proof(elf, &stdin, mode).await?;
log::info!("Created {}", proof_id);
Expand Down Expand Up @@ -164,14 +150,17 @@ impl Prover for NetworkProver {
}

fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result<SP1Proof> {
let _ = self.simulate(&pk.elf, stdin.clone());
block_on(self.prove_async(&pk.elf, stdin, ProofMode::Core))
}

fn prove_compressed(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result<SP1CompressedProof> {
let _ = self.simulate(&pk.elf, stdin.clone());
block_on(self.prove_async(&pk.elf, stdin, ProofMode::Compressed))
}

fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result<SP1PlonkBn254Proof> {
let _ = self.simulate(&pk.elf, stdin.clone());
block_on(self.prove_async(&pk.elf, stdin, ProofMode::Plonk))
}
}
Expand Down
Loading