#![no_std]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::vec::Vec;
use core::{
fmt::{self, Display, LowerHex},
ops::ControlFlow,
};
mod continuation_stack;
mod debug;
mod errors;
mod execution;
mod execution_options;
mod fast;
mod host;
mod processor;
mod tracer;
use crate::{
advice::{AdviceInputs, AdviceProvider},
continuation_stack::ContinuationStack,
errors::MapExecErr,
processor::{Processor, SystemInterface},
trace::{ExecutionTrace, RowIndex},
};
#[cfg(any(test, feature = "testing"))]
mod test_utils;
#[cfg(any(test, feature = "testing"))]
pub use test_utils::{ProcessorStateSnapshot, TestHost, TraceCollector};
#[cfg(test)]
mod tests;
pub use continuation_stack::Continuation;
pub use errors::{AceError, ExecutionError, HostError, MemoryError};
pub use execution_options::{ExecutionOptions, ExecutionOptionsError};
pub use fast::{BreakReason, ExecutionOutput, FastProcessor, ResumeContext};
pub use host::{
FutureMaybeSend, Host, MastForestStore, MemMastForestStore,
debug::DefaultDebugHandler,
default::{DefaultHost, HostLibrary},
handlers::{DebugError, DebugHandler, TraceError},
};
pub use miden_core::{
EMPTY_WORD, Felt, ONE, WORD_SIZE, Word, ZERO, crypto, field, mast, precompile,
program::{
InputError, Kernel, MIN_STACK_DEPTH, Program, ProgramInfo, StackInputs, StackOutputs,
},
serde, utils,
};
pub mod advice {
pub use miden_core::advice::{AdviceInputs, AdviceMap, AdviceStackBuilder};
pub use super::host::{
AdviceMutation,
advice::{AdviceError, AdviceProvider},
};
}
pub mod event {
pub use miden_core::events::*;
pub use crate::host::handlers::{
EventError, EventHandler, EventHandlerRegistry, NoopEventHandler,
};
}
pub mod operation {
pub use miden_core::operations::*;
pub use crate::errors::OperationError;
}
pub mod trace;
#[tracing::instrument("execute_program", skip_all)]
pub async fn execute(
program: &Program,
stack_inputs: StackInputs,
advice_inputs: AdviceInputs,
host: &mut impl Host,
options: ExecutionOptions,
) -> Result<ExecutionTrace, ExecutionError> {
let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, options);
let (execution_output, trace_generation_context) =
processor.execute_for_trace(program, host).await?;
let trace = trace::build_trace(execution_output, trace_generation_context, program.to_info())?;
assert_eq!(&program.hash(), trace.program_hash(), "inconsistent program hash");
Ok(trace)
}
#[cfg(not(target_family = "wasm"))]
#[tracing::instrument("execute_program_sync", skip_all)]
pub fn execute_sync(
program: &Program,
stack_inputs: StackInputs,
advice_inputs: AdviceInputs,
host: &mut impl Host,
options: ExecutionOptions,
) -> Result<ExecutionTrace, ExecutionError> {
match tokio::runtime::Handle::try_current() {
Ok(_handle) => {
panic!(
"Cannot call execute_sync from within a Tokio runtime. \
Use the async execute() method instead."
)
},
Err(_) => {
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
rt.block_on(execute(program, stack_inputs, advice_inputs, host, options))
},
}
}
#[derive(Debug)]
pub struct ProcessorState<'a> {
processor: &'a FastProcessor,
}
impl<'a> ProcessorState<'a> {
#[inline(always)]
pub fn advice_provider(&self) -> &AdviceProvider {
self.processor.advice_provider()
}
#[inline(always)]
pub fn execution_options(&self) -> &ExecutionOptions {
self.processor.execution_options()
}
#[inline(always)]
pub fn clock(&self) -> RowIndex {
self.processor.clock()
}
#[inline(always)]
pub fn ctx(&self) -> ContextId {
self.processor.ctx()
}
#[inline(always)]
pub fn get_stack_item(&self, pos: usize) -> Felt {
self.processor.stack_get_safe(pos)
}
#[inline(always)]
pub fn get_stack_word(&self, start_idx: usize) -> Word {
self.processor.stack_get_word_safe(start_idx)
}
#[inline(always)]
pub fn get_stack_state(&self) -> Vec<Felt> {
self.processor.stack().iter().rev().copied().collect()
}
#[inline(always)]
pub fn get_mem_value(&self, ctx: ContextId, addr: u32) -> Option<Felt> {
self.processor.memory().read_element_impl(ctx, addr)
}
#[inline(always)]
pub fn get_mem_word(&self, ctx: ContextId, addr: u32) -> Result<Option<Word>, MemoryError> {
self.processor.memory().read_word_impl(ctx, addr)
}
pub fn get_mem_addr_range(
&self,
start_idx: usize,
end_idx: usize,
) -> Result<core::ops::Range<u32>, MemoryError> {
let start_addr = self.get_stack_item(start_idx).as_canonical_u64();
let end_addr = self.get_stack_item(end_idx).as_canonical_u64();
if start_addr > u32::MAX as u64 {
return Err(MemoryError::AddressOutOfBounds { addr: start_addr });
}
if end_addr > u32::MAX as u64 {
return Err(MemoryError::AddressOutOfBounds { addr: end_addr });
}
if start_addr > end_addr {
return Err(MemoryError::InvalidMemoryRange { start_addr, end_addr });
}
Ok(start_addr as u32..end_addr as u32)
}
#[inline(always)]
pub fn get_mem_state(&self, ctx: ContextId) -> Vec<(MemoryAddress, Felt)> {
self.processor.memory().get_memory_state(ctx)
}
}
pub trait Stopper {
type Processor;
fn should_stop(
&self,
processor: &Self::Processor,
continuation_stack: &ContinuationStack,
continuation_after_stop: impl FnOnce() -> Option<continuation_stack::Continuation>,
) -> ControlFlow<BreakReason>;
}
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct ContextId(u32);
impl ContextId {
pub fn root() -> Self {
Self(0)
}
pub fn is_root(&self) -> bool {
self.0 == 0
}
}
impl From<RowIndex> for ContextId {
fn from(value: RowIndex) -> Self {
Self(value.as_u32())
}
}
impl From<u32> for ContextId {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<ContextId> for u32 {
fn from(context_id: ContextId) -> Self {
context_id.0
}
}
impl From<ContextId> for u64 {
fn from(context_id: ContextId) -> Self {
context_id.0.into()
}
}
impl From<ContextId> for Felt {
fn from(context_id: ContextId) -> Self {
Felt::from_u32(context_id.0)
}
}
impl Display for ContextId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct MemoryAddress(u32);
impl From<u32> for MemoryAddress {
fn from(addr: u32) -> Self {
MemoryAddress(addr)
}
}
impl From<MemoryAddress> for u32 {
fn from(value: MemoryAddress) -> Self {
value.0
}
}
impl Display for MemoryAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Display::fmt(&self.0, f)
}
}
impl LowerHex for MemoryAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
LowerHex::fmt(&self.0, f)
}
}
impl core::ops::Add<MemoryAddress> for MemoryAddress {
type Output = Self;
fn add(self, rhs: MemoryAddress) -> Self::Output {
MemoryAddress(self.0 + rhs.0)
}
}
impl core::ops::Add<u32> for MemoryAddress {
type Output = Self;
fn add(self, rhs: u32) -> Self::Output {
MemoryAddress(self.0 + rhs)
}
}