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

wip: proposed sdk refactor #1872

Closed
wants to merge 4 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
217 changes: 94 additions & 123 deletions crates/sdk/src/action.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
use sp1_core_executor::{ExecutionReport, HookEnv, SP1ContextBuilder};
use crate::{provers::ProveOpts, Prover, SP1ProofKind, SP1ProofWithPublicValues};
use anyhow::{Ok, Result};
use sp1_core_executor::{ExecutionReport, SP1Context};
use sp1_core_machine::io::SP1Stdin;
use sp1_primitives::io::SP1PublicValues;
use sp1_prover::{components::DefaultProverComponents, SP1ProvingKey};

use anyhow::{Ok, Result};
use sp1_stark::{SP1CoreOpts, SP1ProverOpts};
use sp1_stark::SP1ProverOpts;
use std::time::Duration;

use crate::{provers::ProofOpts, Prover, SP1ProofKind, SP1ProofWithPublicValues};

/// Builder to prepare and configure execution of a program on an input.
/// May be run with [Self::run].
pub struct Execute<'a> {
prover: &'a dyn Prover<DefaultProverComponents>,
context_builder: SP1ContextBuilder<'a>,
elf: &'a [u8],
stdin: SP1Stdin,
local_opts: LocalProveOpts<'a>,
}

impl<'a> Execute<'a> {
Expand All @@ -28,51 +26,95 @@ impl<'a> Execute<'a> {
elf: &'a [u8],
stdin: SP1Stdin,
) -> Self {
Self { prover, elf, stdin, context_builder: Default::default() }
Self { prover, elf, stdin, local_opts: Default::default() }
}

/// Execute the program on the input, consuming the built action `self`.
pub fn run(self) -> Result<(SP1PublicValues, ExecutionReport)> {
let Self { prover, elf, stdin, mut context_builder } = self;
let context = context_builder.build();
Ok(prover.sp1_prover().execute(elf, &stdin, context)?)
let Self { prover, elf, stdin, local_opts } = self;
Ok(prover.sp1_prover().execute(elf, &stdin, local_opts.context)?)
}

/// Add a runtime [Hook](super::Hook) into the context.
///
/// Hooks may be invoked from within SP1 by writing to the specified file descriptor `fd`
/// with [`sp1_zkvm::io::write`], returning a list of arbitrary data that may be read
/// with successive calls to [`sp1_zkvm::io::read`].
pub fn with_hook(
mut self,
fd: u32,
f: impl FnMut(HookEnv, &[u8]) -> Vec<Vec<u8>> + Send + Sync + 'a,
) -> Self {
self.context_builder.hook(fd, f);
/// Set the [LocalProveOpts] for this execution.
pub fn with_local_opts(mut self, local_opts: LocalProveOpts<'a>) -> Self {
self.local_opts = local_opts;
self
}
}

/// Avoid registering the default hooks in the runtime.
///
/// It is not necessary to call this to override hooks --- instead, simply
/// register a hook with the same value of `fd` by calling [`Self::with_hook`].
pub fn without_default_hooks(mut self) -> Self {
self.context_builder.without_default_hooks();
/// Options to configure execution and proving for the CPU and mock provers.
#[derive(Default, Clone)]
pub struct LocalProveOpts<'a> {
pub(crate) prover_opts: SP1ProverOpts,
pub(crate) context: SP1Context<'a>,
}

impl<'a> LocalProveOpts<'a> {
/// Create a new `LocalProveOpts` with default values.
pub fn new() -> Self {
Self::default()
}

/// Set the SP1ProverOpts.
pub fn prover_opts(mut self, prover_opts: SP1ProverOpts) -> Self {
self.prover_opts = prover_opts;
self
}

/// Set the maximum number of cpu cycles to use for execution.
///
/// If the cycle limit is exceeded, execution will return
/// [`sp1_core_executor::ExecutionError::ExceededCycleLimit`].
pub fn max_cycles(mut self, max_cycles: u64) -> Self {
self.context_builder.max_cycles(max_cycles);
/// Set the SP1Context.
pub fn context(mut self, context: SP1Context<'a>) -> Self {
self.context = context;
self
}

/// Skip deferred proof verification.
pub fn set_skip_deferred_proof_verification(mut self, value: bool) -> Self {
self.context_builder.set_skip_deferred_proof_verification(value);
/// Warns if `opts` or `context` are not default values, since they are currently unsupported by
/// certain provers.
pub(crate) fn warn_if_not_default(&self, prover_type: &str) {
if self.prover_opts != SP1ProverOpts::default() {
tracing::warn!("non-default opts will be ignored: {:?}", self.prover_opts);
tracing::warn!(
"custom SP1ProverOpts are currently unsupported by the {} prover",
prover_type
);
}
// Exhaustive match is done to ensure we update the warnings if the types change.
let SP1Context { hook_registry, subproof_verifier, .. } = &self.context;
if hook_registry.is_some() {
tracing::warn!(
"non-default context.hook_registry will be ignored: {:?}",
hook_registry
);
tracing::warn!(
"custom runtime hooks are currently unsupported by the {} prover",
prover_type
);
tracing::warn!("proving may fail due to missing hooks");
}
if subproof_verifier.is_some() {
tracing::warn!("non-default context.subproof_verifier will be ignored");
tracing::warn!(
"custom subproof verifiers are currently unsupported by the {} prover",
prover_type
);
}
}
}

/// Options to configure the network prover.
#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub struct NetworkProveOpts {
pub(crate) timeout: Option<Duration>,
}

impl NetworkProveOpts {
/// Create a new `NetworkProveOpts` with default values.
pub fn new() -> Self {
Self::default()
}

/// Set the timeout.
pub fn timeout(&mut self, timeout: Duration) -> &mut Self {
self.timeout = Some(timeout);
self
}
}
Expand All @@ -82,12 +124,10 @@ impl<'a> Execute<'a> {
pub struct Prove<'a> {
prover: &'a dyn Prover<DefaultProverComponents>,
kind: SP1ProofKind,
context_builder: SP1ContextBuilder<'a>,
pk: &'a SP1ProvingKey,
stdin: SP1Stdin,
core_opts: SP1CoreOpts,
recursion_opts: SP1CoreOpts,
timeout: Option<Duration>,
local_opts: Option<LocalProveOpts<'a>>,
network_opts: Option<NetworkProveOpts>,
}

impl<'a> Prove<'a> {
Expand All @@ -100,33 +140,16 @@ impl<'a> Prove<'a> {
pk: &'a SP1ProvingKey,
stdin: SP1Stdin,
) -> Self {
Self {
prover,
kind: Default::default(),
pk,
stdin,
context_builder: Default::default(),
core_opts: SP1CoreOpts::default(),
recursion_opts: SP1CoreOpts::recursion(),
timeout: None,
}
Self { prover, kind: Default::default(), pk, stdin, local_opts: None, network_opts: None }
}

/// Prove the execution of the program on the input, consuming the built action `self`.
pub fn run(self) -> Result<SP1ProofWithPublicValues> {
let Self {
prover,
kind,
pk,
stdin,
mut context_builder,
core_opts,
recursion_opts,
timeout,
} = self;
let opts = SP1ProverOpts { core_opts, recursion_opts };
let proof_opts = ProofOpts { sp1_prover_opts: opts, timeout };
let context = context_builder.build();
let Self { prover, kind, pk, stdin, local_opts, network_opts } = self;
let opts = ProveOpts {
local_opts: local_opts.unwrap_or_default(),
network_opts: network_opts.unwrap_or_default(),
};

// Dump the program and stdin to files for debugging if `SP1_DUMP` is set.
if std::env::var("SP1_DUMP")
Expand All @@ -139,7 +162,7 @@ impl<'a> Prove<'a> {
std::fs::write("stdin.bin", stdin.clone()).unwrap();
}

prover.prove(pk, stdin, proof_opts, context, kind)
prover.prove(pk, stdin, opts, kind)
}

/// Set the proof kind to the core mode. This is the default.
Expand All @@ -166,67 +189,15 @@ impl<'a> Prove<'a> {
self
}

/// Add a runtime [Hook](super::Hook) into the context.
///
/// Hooks may be invoked from within SP1 by writing to the specified file descriptor `fd`
/// with [`sp1_zkvm::io::write`], returning a list of arbitrary data that may be read
/// with successive calls to [`sp1_zkvm::io::read`].
pub fn with_hook(
mut self,
fd: u32,
f: impl FnMut(HookEnv, &[u8]) -> Vec<Vec<u8>> + Send + Sync + 'a,
) -> Self {
self.context_builder.hook(fd, f);
self
}

/// Avoid registering the default hooks in the runtime.
///
/// It is not necessary to call this to override hooks --- instead, simply
/// register a hook with the same value of `fd` by calling [`Self::with_hook`].
pub fn without_default_hooks(mut self) -> Self {
self.context_builder.without_default_hooks();
self
}

/// Set the shard size for proving.
pub fn shard_size(mut self, value: usize) -> Self {
self.core_opts.shard_size = value;
self
}

/// Set the shard batch size for proving.
pub fn shard_batch_size(mut self, value: usize) -> Self {
self.core_opts.shard_batch_size = value;
self
}

/// Set whether we should reconstruct commitments while proving.
pub fn reconstruct_commitments(mut self, value: bool) -> Self {
self.core_opts.reconstruct_commitments = value;
self
}

/// Set the maximum number of cpu cycles to use for execution.
///
/// If the cycle limit is exceeded, execution will return
/// [`sp1_core_executor::ExecutionError::ExceededCycleLimit`].
pub fn cycle_limit(mut self, cycle_limit: u64) -> Self {
self.context_builder.max_cycles(cycle_limit);
self
}

/// Set the timeout for the proof's generation.
///
/// This parameter is only used when the prover is run in network mode.
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
/// Set the local prover options, which are only used by the local and mock provers.
pub fn local_opts(mut self, local_opts: LocalProveOpts<'a>) -> Self {
self.local_opts = Some(local_opts);
self
}

/// Set the skip deferred proof verification flag.
pub fn set_skip_deferred_proof_verification(mut self, value: bool) -> Self {
self.context_builder.set_skip_deferred_proof_verification(value);
/// Set the network prover options, which are only used by the network prover.
pub fn network_opts(mut self, network_opts: NetworkProveOpts) -> Self {
self.network_opts = Some(network_opts);
self
}
}
27 changes: 23 additions & 4 deletions crates/sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,28 @@
/// std::env::set_var("SP1_PROVER", "local");
/// let client = ProverClient::new();
/// ```
#[deprecated(since = "4.0.0", note = "Use ProverClient::from_env() instead")]
pub fn new() -> Self {
Self::from_env()
}

/// Creates a new [ProverClient].
///
/// Setting the `SP1_PROVER` environment variable can change the prover used under the hood.
/// - `local` (default): Uses [CpuProver] or [CudaProver] if the `cuda` feature is enabled.
/// Recommended for proving end-to-end locally.
/// - `mock`: Uses [MockProver]. Recommended for testing and development.
/// - `network`: Uses [NetworkProver]. Recommended for outsourcing proof generation to an RPC.
///
/// ### Examples
///
/// ```no_run
/// use sp1_sdk::ProverClient;
///
/// std::env::set_var("SP1_PROVER", "local");
/// let client = ProverClient::from_env();
/// ```
pub fn from_env() -> Self {
#[allow(unreachable_code)]
match env::var("SP1_PROVER").unwrap_or("local".to_string()).to_lowercase().as_str() {
"mock" => Self { prover: Box::new(MockProver::new()) },
Expand Down Expand Up @@ -192,12 +213,10 @@
Self {
prover: Box::new(NetworkProverV2::new(&private_key, rpc_url, skip_simulation)),
}
} else if #[cfg(feature = "network")] {
} else {
Self {
prover: Box::new(NetworkProverV1::new(&private_key, rpc_url, skip_simulation)),
}
} else {
panic!("network feature is not enabled")
}
}
}
Expand Down Expand Up @@ -316,7 +335,7 @@

impl Default for ProverClient {
fn default() -> Self {
Self::new()
Self::from_env()
}
}

Expand Down Expand Up @@ -488,7 +507,7 @@
let elf = test_artifacts::PANIC_ELF;
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
client.execute(elf, stdin).max_cycles(1).run().unwrap();

Check failure on line 510 in crates/sdk/src/lib.rs

View workflow job for this annotation

GitHub Actions / Cargo Check

no method named `max_cycles` found for struct `Execute` in the current scope

Check failure on line 510 in crates/sdk/src/lib.rs

View workflow job for this annotation

GitHub Actions / Formatting & Clippy

no method named `max_cycles` found for struct `action::Execute` in the current scope

Check failure on line 510 in crates/sdk/src/lib.rs

View workflow job for this annotation

GitHub Actions / Test (ARM)

no method named `max_cycles` found for struct `Execute` in the current scope

Check failure on line 510 in crates/sdk/src/lib.rs

View workflow job for this annotation

GitHub Actions / Test (x86-64)

no method named `max_cycles` found for struct `Execute` in the current scope
}

#[test]
Expand Down
22 changes: 1 addition & 21 deletions crates/sdk/src/network-v2/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
use serde::de::DeserializeOwned;
use sp1_core_machine::io::SP1Stdin;
use sp1_prover::{components::DefaultProverComponents, SP1Prover, SP1_CIRCUIT_VERSION};
use sp1_stark::SP1ProverOpts;

Check failure on line 17 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Formatting & Clippy

unused import: `sp1_stark::SP1ProverOpts`

Check warning on line 17 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Test (ARM)

unused import: `sp1_stark::SP1ProverOpts`

Check warning on line 17 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Test (x86-64)

unused import: `sp1_stark::SP1ProverOpts`
use tonic::Code;

use {crate::block_on, tokio::time::sleep};

use crate::provers::{CpuProver, ProofOpts, ProverType};

Check failure on line 22 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Formatting & Clippy

unresolved import `crate::provers::ProofOpts`

Check failure on line 22 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Test (ARM)

unresolved import `crate::provers::ProofOpts`

Check failure on line 22 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Test (x86-64)

unresolved import `crate::provers::ProofOpts`

/// The timeout for a proof request to be fulfilled.
const TIMEOUT_SECS: u64 = 14400;
Expand Down Expand Up @@ -208,38 +208,18 @@
}

fn prove<'a>(
&'a self,

Check failure on line 211 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Formatting & Clippy

method `prove` has 6 parameters but the declaration in trait `provers::Prover::prove` has 5

Check failure on line 211 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Test (ARM)

method `prove` has 6 parameters but the declaration in trait `Prover::prove` has 5

Check failure on line 211 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Test (x86-64)

method `prove` has 6 parameters but the declaration in trait `Prover::prove` has 5
pk: &SP1ProvingKey,
stdin: SP1Stdin,
opts: ProofOpts,
context: SP1Context<'a>,
kind: SP1ProofKind,
) -> Result<SP1ProofWithPublicValues> {
warn_if_not_default(&opts.sp1_prover_opts, &context);
local_opts.warn_if_not_default("network");

Check failure on line 218 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Formatting & Clippy

cannot find value `local_opts` in this scope

Check failure on line 218 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Test (ARM)

cannot find value `local_opts` in this scope

Check failure on line 218 in crates/sdk/src/network-v2/prover.rs

View workflow job for this annotation

GitHub Actions / Test (x86-64)

cannot find value `local_opts` in this scope
block_on(self.prove(pk, stdin, kind.into(), opts.timeout))
}
}

/// Warns if `opts` or `context` are not default values, since they are currently unsupported.
fn warn_if_not_default(opts: &SP1ProverOpts, context: &SP1Context) {
let _guard = tracing::warn_span!("network_prover").entered();
if opts != &SP1ProverOpts::default() {
tracing::warn!("non-default opts will be ignored: {:?}", opts.core_opts);
tracing::warn!("custom SP1ProverOpts are currently unsupported by the network prover");
}
// Exhaustive match is done to ensure we update the warnings if the types change.
let SP1Context { hook_registry, subproof_verifier, .. } = context;
if hook_registry.is_some() {
tracing::warn!("non-default context.hook_registry will be ignored: {:?}", hook_registry);
tracing::warn!("custom runtime hooks are currently unsupported by the network prover");
tracing::warn!("proving may fail due to missing hooks");
}
if subproof_verifier.is_some() {
tracing::warn!("non-default context.subproof_verifier will be ignored");
tracing::warn!("custom subproof verifiers are currently unsupported by the network prover");
}
}

impl From<SP1ProofKind> for ProofMode {
fn from(value: SP1ProofKind) -> Self {
match value {
Expand Down
Loading
Loading