Skip to content

Commit

Permalink
Merge pull request #998 from 0xPolygonMiden/andrew-impl-exec-limit
Browse files Browse the repository at this point in the history
Implement execution options for processor and prover
  • Loading branch information
Fumuran authored Jul 21, 2023
2 parents 06721cb + 4460662 commit 0d01aa9
Show file tree
Hide file tree
Showing 18 changed files with 181 additions and 62 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#### VM Internals
- Simplified range checker and removed 1 main and 1 auxiliary trace column (#949).
- Added `get_mapped_values()` and `get_store_subset()` methods to the `AdviceProvider` trait (#987).
- [BREAKING] Added options to specify maximum number of cycles and expected number of cycles for a program (#998).
- Improved handling of invalid/incomplete parameters in `StackOutputs` constructors (#1010).
- Allowed the assembler to produce programs with "phantom" calls (#1019).

Expand Down
5 changes: 5 additions & 0 deletions air/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::String;
use crate::trace::MIN_TRACE_LEN;
use core::fmt::{Display, Formatter};

// EXECUTION ERROR
Expand All @@ -7,6 +8,7 @@ use core::fmt::{Display, Formatter};
#[derive(Debug)]
pub enum ExecutionOptionsError {
ExpectedCyclesTooBig(u32, u32),
MaxCycleNumTooSmall(u32),
OtherErrors(String),
}

Expand All @@ -18,6 +20,9 @@ impl Display for ExecutionOptionsError {
ExpectedCyclesTooBig(max, expected) => {
write!(f, "The expected number of cycles must be smaller than the maximum number of cycles: maximum is {max}, but expectd is {expected}")
}
MaxCycleNumTooSmall(max) => {
write!(f, "The maximum number of cycles must be greater than the minimum number of cycles: minimum is {MIN_TRACE_LEN}, but maximum is {max}")
}
OtherErrors(error) => write!(f, "{error}"),
}
}
Expand Down
35 changes: 27 additions & 8 deletions air/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{ExecutionOptionsError, HashFunction};
use crate::trace::MIN_TRACE_LEN;
use winter_air::{FieldExtension, ProofOptions as WinterProofOptions};

// PROVING OPTIONS
Expand Down Expand Up @@ -133,15 +134,15 @@ impl From<ProvingOptions> for WinterProofOptions {
/// - `expected_cycles` specifies the number of cycles a program is expected to execute.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExecutionOptions {
max_cycles: Option<u32>,
max_cycles: u32,
expected_cycles: u32,
}

impl Default for ExecutionOptions {
fn default() -> Self {
ExecutionOptions {
max_cycles: None,
expected_cycles: 64,
max_cycles: u32::MAX,
expected_cycles: MIN_TRACE_LEN as u32,
}
}
}
Expand All @@ -151,19 +152,37 @@ impl ExecutionOptions {
// --------------------------------------------------------------------------------------------

/// Creates a new instance of [ExecutionOptions] from the specified parameters.
///
/// If the `max_cycles` is `None` the maximum number of cycles will be set to `u32::MAX`
pub fn new(
max_cycles: Option<u32>,
expected_cycles: u32,
) -> Result<Self, ExecutionOptionsError> {
if max_cycles.is_some_and(|max_cycles| max_cycles < expected_cycles) {
return Err(ExecutionOptionsError::ExpectedCyclesTooBig(
max_cycles.unwrap(),
expected_cycles,
));
let max_cycles = max_cycles.unwrap_or(u32::MAX);
if max_cycles < MIN_TRACE_LEN as u32 {
return Err(ExecutionOptionsError::MaxCycleNumTooSmall(expected_cycles));
}
if max_cycles < expected_cycles {
return Err(ExecutionOptionsError::ExpectedCyclesTooBig(max_cycles, expected_cycles));
}

// Round up the expected number of cycles to the next power of two. If it is smaller than
// MIN_TRACE_LEN -- pad expected number to it.
let expected_cycles = expected_cycles.next_power_of_two().max(MIN_TRACE_LEN as u32);

Ok(ExecutionOptions {
max_cycles,
expected_cycles,
})
}

/// Returns maximum number of cycles
pub fn max_cycles(&self) -> u32 {
self.max_cycles
}

/// Returns number of the expected cycles
pub fn expected_cycles(&self) -> u32 {
self.expected_cycles
}
}
15 changes: 7 additions & 8 deletions miden/src/cli/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ pub struct ProveCmd {
library_paths: Vec<PathBuf>,

/// Maximum number of cycles a program is allowed to consume
#[structopt(short = "m", long = "max-cycles")]
max_cycles: Option<u32>,
#[structopt(short = "m", long = "max-cycles", default_value = "4294967295")]
max_cycles: u32,

/// Number of outputs
#[structopt(short = "n", long = "num-outputs", default_value = "16")]
Expand All @@ -51,14 +51,13 @@ pub struct ProveCmd {

impl ProveCmd {
pub fn get_proof_options(&self) -> Result<ProvingOptions, ExecutionOptionsError> {
let exec_options = ExecutionOptions::new(self.max_cycles, self.expected_cycles)?;
match self.security.as_str() {
"96bits" => Ok(ProvingOptions::with_96_bit_security(self.recursive)
.with_execution_options(exec_options)),
"128bits" => Ok(ProvingOptions::with_128_bit_security(self.recursive)
.with_execution_options(exec_options)),
let exec_options = ExecutionOptions::new(Some(self.max_cycles), self.expected_cycles)?;
Ok(match self.security.as_str() {
"96bits" => ProvingOptions::with_96_bit_security(self.recursive),
"128bits" => ProvingOptions::with_128_bit_security(self.recursive),
other => panic!("{} is not a valid security setting", other),
}
.with_execution_options(exec_options))
}

pub fn execute(&self) -> Result<(), String> {
Expand Down
8 changes: 4 additions & 4 deletions miden/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ pub struct RunCmd {
library_paths: Vec<PathBuf>,

/// Maximum number of cycles a program is allowed to consume
#[structopt(short = "m", long = "max-cycles")]
max_cycles: Option<u32>,
#[structopt(short = "m", long = "max-cycles", default_value = "4294967295")]
max_cycles: u32,

/// Number of ouptuts
#[structopt(short = "n", long = "num-outputs", default_value = "16")]
Expand All @@ -51,7 +51,7 @@ impl RunCmd {
let input_data = InputFile::read(&self.input_file, &self.assembly_file)?;

// get execution options
let execution_options = ExecutionOptions::new(self.max_cycles, self.expected_cycles)
let execution_options = ExecutionOptions::new(Some(self.max_cycles), self.expected_cycles)
.map_err(|err| format!("{err}"))?;

// fetch the stack and program inputs from the arguments
Expand All @@ -64,7 +64,7 @@ impl RunCmd {

// execute program and generate outputs
let trace = processor::execute(&program, stack_inputs, advice_provider, execution_options)
.map_err(|err| format!("Failed to generate exection trace = {:?}", err))?;
.map_err(|err| format!("Failed to generate execution trace = {:?}", err))?;

println!("done ({} steps in {} ms)", trace.get_trace_len(), now.elapsed().as_millis());

Expand Down
23 changes: 17 additions & 6 deletions miden/src/examples/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use miden::{AdviceProvider, ExecutionProof, Program, ProgramInfo, ProvingOptions, StackInputs};
use processor::{ExecutionOptions, ExecutionOptionsError};
use std::io::Write;
use std::time::Instant;
use structopt::StructOpt;
Expand Down Expand Up @@ -28,13 +29,21 @@ pub struct ExampleOptions {
#[structopt(subcommand)]
pub example: ExampleType,

/// Security level for execution proofs generated by the VM
#[structopt(short = "s", long = "security", default_value = "96bits")]
security: String,
/// Number of cycles the program is expected to consume
#[structopt(short = "e", long = "exp-cycles", default_value = "64")]
expected_cycles: u32,

/// Maximum number of cycles a program is allowed to consume
#[structopt(short = "m", long = "max-cycles", default_value = "4294967295")]
max_cycles: u32,

/// Enable generation of proofs suitable for recursive verification
#[structopt(short = "r", long = "recursive")]
recursive: bool,

/// Security level for execution proofs generated by the VM
#[structopt(short = "s", long = "security", default_value = "96bits")]
security: String,
}

#[derive(StructOpt, Debug)]
Expand All @@ -49,12 +58,14 @@ pub enum ExampleType {
}

impl ExampleOptions {
pub fn get_proof_options(&self) -> ProvingOptions {
match self.security.as_str() {
pub fn get_proof_options(&self) -> Result<ProvingOptions, ExecutionOptionsError> {
let exec_options = ExecutionOptions::new(Some(self.max_cycles), self.expected_cycles)?;
Ok(match self.security.as_str() {
"96bits" => ProvingOptions::with_96_bit_security(self.recursive),
"128bits" => ProvingOptions::with_128_bit_security(self.recursive),
other => panic!("{} is not a valid security level", other),
}
.with_execution_options(exec_options))
}

pub fn execute(&self) -> Result<(), String> {
Expand All @@ -66,7 +77,7 @@ impl ExampleOptions {
.filter_level(log::LevelFilter::Debug)
.init();

let proof_options = self.get_proof_options();
let proof_options = self.get_proof_options().map_err(|err| format!("{err}"))?;

// instantiate and prepare the example
let example = match self.example {
Expand Down
10 changes: 9 additions & 1 deletion miden/tests/integration/cli/cli_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ fn cli_run() -> Result<(), Box<dyn std::error::Error>> {

let mut cmd = bin_under_test.command();

cmd.arg("run").arg("-a").arg("examples/fib/fib.masm").arg("-n").arg("1");
cmd.arg("run")
.arg("-a")
.arg("examples/fib/fib.masm")
.arg("-n")
.arg("1")
.arg("-m")
.arg("4096")
.arg("-e")
.arg("4096");

let output = cmd.unwrap();

Expand Down
7 changes: 4 additions & 3 deletions processor/src/chiplets/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
utils::get_trace_len, CodeBlock, ExecutionTrace, Kernel, MemAdviceProvider, Operation, Process,
StackInputs, Vec,
utils::get_trace_len, CodeBlock, ExecutionOptions, ExecutionTrace, Kernel, MemAdviceProvider,
Operation, Process, StackInputs, Vec,
};
use miden_air::trace::{
chiplets::{
Expand Down Expand Up @@ -112,7 +112,8 @@ fn build_trace(
) -> (ChipletsTrace, usize) {
let stack_inputs = StackInputs::try_from_values(stack_inputs.iter().copied()).unwrap();
let advice_provider = MemAdviceProvider::default();
let mut process = Process::new(kernel, stack_inputs, advice_provider);
let mut process =
Process::new(kernel, stack_inputs, advice_provider, ExecutionOptions::default());
let program = CodeBlock::new_span(operations);
process.execute_code_block(&program, &CodeBlockTable::default()).unwrap();

Expand Down
10 changes: 6 additions & 4 deletions processor/src/decoder/tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{
super::{
utils::get_trace_len, ExecutionTrace, Felt, Kernel, MemAdviceProvider, Operation, Process,
StackInputs, Word,
utils::get_trace_len, ExecutionOptions, ExecutionTrace, Felt, Kernel, MemAdviceProvider,
Operation, Process, StackInputs, Word,
},
build_op_group, AuxTraceHints, BlockHashTableRow, BlockStackTableRow, BlockTableUpdate,
ExecutionContextInfo, OpGroupTableRow, OpGroupTableUpdate,
Expand Down Expand Up @@ -1500,7 +1500,8 @@ fn set_user_op_helpers_many() {
fn build_trace(stack_inputs: &[u64], program: &CodeBlock) -> (DecoderTrace, AuxTraceHints, usize) {
let stack_inputs = StackInputs::try_from_values(stack_inputs.iter().copied()).unwrap();
let advice_provider = MemAdviceProvider::default();
let mut process = Process::new(Kernel::default(), stack_inputs, advice_provider);
let mut process =
Process::new(Kernel::default(), stack_inputs, advice_provider, ExecutionOptions::default());
process.execute_code_block(program, &CodeBlockTable::default()).unwrap();

let (trace, aux_hints) = ExecutionTrace::test_finalize_trace(process);
Expand All @@ -1527,7 +1528,8 @@ fn build_call_trace(
};
let advice_provider = MemAdviceProvider::default();
let stack_inputs = crate::StackInputs::default();
let mut process = Process::new(kernel, stack_inputs, advice_provider);
let mut process =
Process::new(kernel, stack_inputs, advice_provider, ExecutionOptions::default());

// build code block table
let mut cb_table = CodeBlockTable::default();
Expand Down
8 changes: 5 additions & 3 deletions processor/src/decorators/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{
super::{AdviceInputs, Felt, FieldElement, Kernel, Operation, StarkField},
super::{AdviceInputs, ExecutionOptions, Felt, FieldElement, Kernel, Operation, StarkField},
Process,
};
use crate::{MemAdviceProvider, StackInputs, Word};
Expand Down Expand Up @@ -30,7 +30,8 @@ fn push_merkle_node() {
let stack_inputs = StackInputs::try_from_values(stack_inputs).unwrap();
let advice_inputs = AdviceInputs::default().with_merkle_store(store);
let advice_provider = MemAdviceProvider::from(advice_inputs);
let mut process = Process::new(Kernel::default(), stack_inputs, advice_provider);
let mut process =
Process::new(Kernel::default(), stack_inputs, advice_provider, ExecutionOptions::default());
process.execute_op(Operation::Noop).unwrap();

// push the node onto the advice stack
Expand Down Expand Up @@ -185,7 +186,8 @@ fn assert_case_smtget(
.with_merkle_store(store)
.with_map([(node.into_bytes(), mapped)]);
let advice_provider = MemAdviceProvider::from(advice_inputs);
let mut process = Process::new(Kernel::default(), stack_inputs, advice_provider);
let mut process =
Process::new(Kernel::default(), stack_inputs, advice_provider, ExecutionOptions::default());

// call the injector and clear the stack
process.execute_op(Operation::Noop).unwrap();
Expand Down
4 changes: 4 additions & 0 deletions processor/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub enum ExecutionError {
AdviceStackReadFailed(u32),
CallerNotInSyscall,
CodeBlockNotFound(Digest),
CycleLimitExceeded(u32),
DivideByZero(u32),
Ext2InttError(Ext2InttError),
FailedAssertion(u32),
Expand Down Expand Up @@ -61,6 +62,9 @@ impl Display for ExecutionError {
"Failed to execute code block with root {hex}; the block could not be found"
)
}
CycleLimitExceeded(max_cycles) => {
write!(f, "Exceeded the allowed number of cycles (max cycles = {max_cycles})")
}
DivideByZero(clk) => write!(f, "Division by zero at clock cycle {clk}"),
Ext2InttError(err) => write!(f, "Failed to execute Ext2Intt operation: {err}"),
FailedAssertion(clk) => write!(f, "Assertion failed at clock cycle {clk}"),
Expand Down
25 changes: 17 additions & 8 deletions processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,13 @@ pub fn execute<A>(
program: &Program,
stack_inputs: StackInputs,
advice_provider: A,
_options: ExecutionOptions,
options: ExecutionOptions,
) -> Result<ExecutionTrace, ExecutionError>
where
A: AdviceProvider,
{
let mut process = Process::new(program.kernel().clone(), stack_inputs, advice_provider);
// TODO: use ExecutionOptions to limit program execution
let mut process =
Process::new(program.kernel().clone(), stack_inputs, advice_provider, options);
let stack_outputs = process.execute(program)?;
let trace = ExecutionTrace::new(process, stack_outputs);
assert_eq!(&program.hash(), trace.program_hash(), "inconsistent program hash");
Expand Down Expand Up @@ -161,6 +161,7 @@ where
range: RangeChecker,
chiplets: Chiplets,
advice_provider: A,
max_cycles: u32,
}

impl<A> Process<A>
Expand All @@ -170,28 +171,35 @@ where
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Creates a new process with the provided inputs.
pub fn new(kernel: Kernel, stack_inputs: StackInputs, advice_provider: A) -> Self {
Self::initialize(kernel, stack_inputs, advice_provider, false)
pub fn new(
kernel: Kernel,
stack_inputs: StackInputs,
advice_provider: A,
execution_options: ExecutionOptions,
) -> Self {
Self::initialize(kernel, stack_inputs, advice_provider, false, execution_options)
}

/// Creates a new process with provided inputs and debug options enabled.
pub fn new_debug(kernel: Kernel, stack_inputs: StackInputs, advice_provider: A) -> Self {
Self::initialize(kernel, stack_inputs, advice_provider, true)
Self::initialize(kernel, stack_inputs, advice_provider, true, ExecutionOptions::default())
}

fn initialize(
kernel: Kernel,
stack: StackInputs,
advice_provider: A,
in_debug_mode: bool,
execution_options: ExecutionOptions,
) -> Self {
Self {
system: System::new(MIN_TRACE_LEN),
system: System::new(execution_options.expected_cycles() as usize),
decoder: Decoder::new(in_debug_mode),
stack: Stack::new(&stack, MIN_TRACE_LEN, in_debug_mode),
stack: Stack::new(&stack, execution_options.expected_cycles() as usize, in_debug_mode),
range: RangeChecker::new(),
chiplets: Chiplets::new(kernel),
advice_provider,
max_cycles: execution_options.max_cycles(),
}
}

Expand Down Expand Up @@ -473,4 +481,5 @@ where
pub range: RangeChecker,
pub chiplets: Chiplets,
pub advice_provider: A,
pub max_cycles: u32,
}
Loading

0 comments on commit 0d01aa9

Please sign in to comment.