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: cycle limit #1027

Merged
merged 1 commit into from
Jul 3, 2024
Merged
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
19 changes: 18 additions & 1 deletion core/src/runtime/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,23 @@ use super::{hookify, BoxedHook, HookEnv, HookRegistry, SubproofVerifier};
#[derive(Clone, Default)]
pub struct SP1Context<'a> {
/// The registry of hooks invokable from inside SP1.
/// `None` denotes the default list of hooks.
///
/// Note: `None` denotes the default list of hooks.
pub hook_registry: Option<HookRegistry<'a>>,

/// The verifier for verifying subproofs.
pub subproof_verifier: Option<Arc<dyn SubproofVerifier + 'a>>,

/// The maximum number of cpu cycles to use for execution.
pub max_cycles: Option<u64>,
}

#[derive(Clone, Default)]
pub struct SP1ContextBuilder<'a> {
no_default_hooks: bool,
hook_registry_entries: Vec<(u32, BoxedHook<'a>)>,
subproof_verifier: Option<Arc<dyn SubproofVerifier + 'a>>,
max_cycles: Option<u64>,
}

impl<'a> SP1Context<'a> {
Expand Down Expand Up @@ -52,9 +59,11 @@ impl<'a> SP1ContextBuilder<'a> {
HookRegistry { table }
});
let subproof_verifier = take(&mut self.subproof_verifier);
let cycle_limit = take(&mut self.max_cycles);
SP1Context {
hook_registry,
subproof_verifier,
max_cycles: cycle_limit,
}
}

Expand Down Expand Up @@ -91,6 +100,12 @@ impl<'a> SP1ContextBuilder<'a> {
self.subproof_verifier = Some(subproof_verifier);
self
}

/// Set the maximum number of cpu cycles to use for execution.
pub fn max_cycles(&mut self, max_cycles: u64) -> &mut Self {
self.max_cycles = Some(max_cycles);
self
}
}

#[cfg(test)]
Expand All @@ -104,9 +119,11 @@ mod tests {
let SP1Context {
hook_registry,
subproof_verifier,
max_cycles: cycle_limit,
} = SP1Context::builder().build();
assert!(hook_registry.is_none());
assert!(subproof_verifier.is_none());
assert!(cycle_limit.is_none());
}

#[test]
Expand Down
17 changes: 16 additions & 1 deletion core/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ pub struct Runtime<'a> {

/// Registry of hooks, to be invoked by writing to certain file descriptors.
pub hook_registry: HookRegistry<'a>,

/// The maximum number of cpu cycles to use for execution.
pub max_cycles: Option<u64>,
}

#[derive(Error, Debug)]
Expand All @@ -115,6 +118,8 @@ pub enum ExecutionError {
UnsupportedSyscall(u32),
#[error("breakpoint encountered")]
Breakpoint(),
#[error("exceeded cycle limit of {0}")]
ExceededCycleLimit(u64),
#[error("got unimplemented as opcode")]
Unimplemented(),
}
Expand Down Expand Up @@ -176,6 +181,7 @@ impl<'a> Runtime<'a> {
print_report: false,
subproof_verifier,
hook_registry,
max_cycles: context.max_cycles,
}
}

Expand Down Expand Up @@ -992,6 +998,13 @@ impl<'a> Runtime<'a> {
self.state.channel = 0;
}

// If the cycle limit is exceeded, return an error.
if let Some(max_cycles) = self.max_cycles {
if self.state.global_clk >= max_cycles {
return Err(ExecutionError::ExceededCycleLimit(max_cycles));
}
}

Ok(self.state.pc.wrapping_sub(self.program.pc_base)
>= (self.program.instructions.len() * 4) as u32)
}
Expand Down Expand Up @@ -1105,7 +1118,9 @@ impl<'a> Runtime<'a> {

// Ensure that all proofs and input bytes were read, otherwise warn the user.
if self.state.proof_stream_ptr != self.state.proof_stream.len() {
panic!("Not all proofs were read. Proving will fail during recursion. Did you pass too many proofs in or forget to call verify_sp1_proof?");
panic!(
"Not all proofs were read. Proving will fail during recursion. Did you pass too many proofs in or forget to call verify_sp1_proof?"
);
}
if self.state.input_stream_ptr != self.state.input_stream.len() {
log::warn!("Not all input bytes were read.");
Expand Down
16 changes: 16 additions & 0 deletions sdk/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ impl<'a> Execute<'a> {
self.context_builder.without_default_hooks();
self
}

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

/// Builder to prepare and configure proving execution of a program on an input.
Expand Down Expand Up @@ -175,4 +183,12 @@ impl<'a> Prove<'a> {
self.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::runtime::ExecutionError::ExceededCycleLimit].
pub fn cycle_limit(mut self, cycle_limit: u64) -> Self {
self.context_builder.max_cycles(cycle_limit);
self
}
}
11 changes: 11 additions & 0 deletions sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,17 @@ mod tests {
client.execute(elf, stdin).run().unwrap();
}

#[should_panic]
#[test]
fn test_cycle_limit_fail() {
utils::setup_logger();
let client = ProverClient::local();
let elf = include_bytes!("../../tests/panic/elf/riscv32im-succinct-zkvm-elf");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
client.execute(elf, stdin).max_cycles(1).run().unwrap();
}

#[test]
fn test_e2e_prove_plonk() {
utils::setup_logger();
Expand Down
1 change: 1 addition & 0 deletions sdk/src/network/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ fn warn_if_not_default(opts: &SP1ProverOpts, context: &SP1Context) {
let SP1Context {
hook_registry,
subproof_verifier,
..
} = context;
if hook_registry.is_some() {
tracing::warn!(
Expand Down
Loading