From 8d3fd68d98b8e8313994748eb52a5ea294160a28 Mon Sep 17 00:00:00 2001 From: Adrian Hamelink Date: Wed, 29 Jul 2026 14:25:03 +0200 Subject: [PATCH 1/3] refactor: move event handling interface to core --- Cargo.lock | 1 + core/Cargo.toml | 2 + core/src/advice/mod.rs | 27 +- .../src/host => core/src/events}/debug.rs | 5 +- core/src/events/handlers.rs | 233 ++++++++++++++ core/src/events/mod.rs | 7 + core/src/execution.rs | 131 ++++++++ core/src/lib.rs | 2 + crates/lib/core/Cargo.toml | 2 - crates/lib/core/src/handlers/aead_decrypt.rs | 14 +- crates/lib/core/src/handlers/debug.rs | 43 +-- crates/lib/core/src/handlers/falcon_div.rs | 9 +- crates/lib/core/src/handlers/mod.rs | 13 +- .../src/handlers/precompiles/keccak256.rs | 14 +- .../handlers/precompiles/uint_field_inv.rs | 10 +- crates/lib/core/src/handlers/readonly.rs | 7 +- crates/lib/core/src/handlers/smt_peek.rs | 18 +- crates/lib/core/src/handlers/sorted_array.rs | 14 +- crates/lib/core/src/handlers/u128_div.rs | 11 +- crates/lib/core/src/handlers/u256_div.rs | 11 +- crates/lib/core/src/handlers/u64_div.rs | 7 +- crates/lib/core/src/lib.rs | 8 +- crates/lib/core/tests/collections/mmr.rs | 2 +- .../core/tests/collections/sorted_array.rs | 15 +- crates/lib/core/tests/crypto/aead.rs | 13 +- crates/lib/core/tests/crypto/falcon.rs | 23 +- crates/lib/core/tests/debug.rs | 13 +- .../lib/core/tests/stark/batch_query_gen.rs | 3 +- crates/lib/core/tests/stark/mod.rs | 2 +- crates/mast-package/src/host_library.rs | 65 ++++ crates/mast-package/src/lib.rs | 2 + crates/test-utils/src/lib.rs | 19 +- processor/src/errors.rs | 37 +-- processor/src/execution/mod.rs | 8 +- processor/src/fast/mod.rs | 100 +++++- processor/src/fast/tests/mod.rs | 6 +- processor/src/host/advice/mod.rs | 10 +- processor/src/host/default.rs | 84 +---- processor/src/host/handlers.rs | 77 +---- processor/src/host/mod.rs | 43 +-- processor/src/lib.rs | 294 ++---------------- processor/src/test_utils/test_host.rs | 24 +- processor/src/tests/mod.rs | 12 +- .../src/trace/chiplets/memory/segment.rs | 16 +- processor/src/trace/chiplets/memory/tests.rs | 16 +- processor/tests/async_compat.rs | 6 +- 46 files changed, 780 insertions(+), 699 deletions(-) rename {processor/src/host => core/src/events}/debug.rs (99%) create mode 100644 core/src/events/handlers.rs create mode 100644 core/src/execution.rs create mode 100644 crates/mast-package/src/host_library.rs diff --git a/Cargo.lock b/Cargo.lock index 13b679bf0e..3e7f88f408 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2163,6 +2163,7 @@ dependencies = [ "miden-test-serde-macros", "miden-test-utils", "miden-utils-core-derive", + "miden-utils-diagnostics", "miden-utils-indexing", "miden-utils-sync", "proptest", diff --git a/core/Cargo.toml b/core/Cargo.toml index 831545c20f..8850d84bfe 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -34,6 +34,7 @@ std = [ "miden-crypto/std", "miden-debug-types/std", "miden-formatting/std", + "miden-utils-diagnostics/std", "miden-utils-indexing/std", "miden-utils-sync/std", "thiserror/std", @@ -54,6 +55,7 @@ miden-crypto.workspace = true miden-debug-types.workspace = true miden-formatting.workspace = true miden-utils-core-derive.workspace = true +miden-utils-diagnostics.workspace = true miden-utils-indexing.workspace = true miden-utils-sync.workspace = true diff --git a/core/src/advice/mod.rs b/core/src/advice/mod.rs index f26d2e5d37..fab8b570af 100644 --- a/core/src/advice/mod.rs +++ b/core/src/advice/mod.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use crate::{ Felt, Word, - crypto::merkle::MerkleStore, + crypto::merkle::{InnerNodeInfo, MerkleStore}, serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, }; @@ -12,6 +12,31 @@ pub use map::AdviceMap; mod stack; pub use stack::AdviceStack; +/// Maximum number of elements allowed on the advice stack. Set to 2^17. +pub const MAX_ADVICE_STACK_SIZE: usize = 1 << 17; + +/// A declarative change to advice data requested by an event handler. +#[derive(Debug, PartialEq, Eq)] +pub enum AdviceMutation { + ExtendStack { stack: AdviceStack }, + ExtendMap { other: AdviceMap }, + ExtendMerkleStore { infos: Vec }, +} + +impl AdviceMutation { + pub fn extend_advice_stack(stack: AdviceStack) -> Self { + Self::ExtendStack { stack } + } + + pub fn extend_map(other: AdviceMap) -> Self { + Self::ExtendMap { other } + } + + pub fn extend_merkle_store(infos: impl IntoIterator) -> Self { + Self::ExtendMerkleStore { infos: Vec::from_iter(infos) } + } +} + // ADVICE INPUTS // ================================================================================================ diff --git a/processor/src/host/debug.rs b/core/src/events/debug.rs similarity index 99% rename from processor/src/host/debug.rs rename to core/src/events/debug.rs index d044c8685f..ebbdc4ad28 100644 --- a/processor/src/host/debug.rs +++ b/core/src/events/debug.rs @@ -4,7 +4,7 @@ use alloc::{ }; use core::fmt; -use miden_core::Felt; +use crate::Felt; // WRITER IMPLEMENTATIONS // ================================================================================================ @@ -137,9 +137,8 @@ pub fn format_value(value: Option) -> String { mod tests { use alloc::{string::String, vec}; - use miden_core::Felt; - use super::{format_value, write_interval, write_stack}; + use crate::Felt; #[test] fn write_stack_full_uses_tree_style() { diff --git a/core/src/events/handlers.rs b/core/src/events/handlers.rs new file mode 100644 index 0000000000..cc9516bf08 --- /dev/null +++ b/core/src/events/handlers.rs @@ -0,0 +1,233 @@ +use alloc::{boxed::Box, vec::Vec}; +use core::{error::Error, fmt}; + +use crate::{ + Felt, Word, + advice::{AdviceMap, AdviceMutation}, + deferred::{Digest, Node, PrecompileError}, + execution::{ContextId, MemoryAddress, MemoryError}, +}; + +/// A generic error returned by an [`EventHandler`]. +pub type EventError = Box; + +/// Read-only capabilities an execution engine provides to an [`EventContext`]. +/// +/// This interface is intended for execution-engine adapters. Event handlers should use +/// [`EventContext`] instead of depending on a concrete adapter. +pub trait EventContextProvider { + fn get_stack_item(&self, position: usize) -> Felt; + + fn get_stack_word(&self, start: usize) -> Word; + + fn get_stack_state(&self) -> Vec; + + fn clock(&self) -> u32; + + fn context_id(&self) -> ContextId; + + fn get_mem_value(&self, context: ContextId, address: u32) -> Option; + + fn get_mem_word(&self, context: ContextId, address: u32) -> Result, MemoryError>; + + fn get_mem_state(&self, context: ContextId) -> Vec<(MemoryAddress, Felt)>; + + fn advice_stack(&self) -> Vec; + + fn advice_map(&self) -> &AdviceMap; + + fn get_advice_map_entry(&self, key: &Word) -> Option<&[Felt]>; + + fn get_advice_tree_node( + &self, + root: Word, + depth: Felt, + index: Felt, + ) -> Result; + + fn max_hash_len_bytes(&self) -> usize; + + fn require_canonical_deferred_node( + &self, + digest: Digest, + ) -> Result<(Digest, &Node), PrecompileError>; +} + +/// A read-only view of execution state exposed to an [`EventHandler`]. +/// +/// The context exposes capabilities required by handlers without revealing the execution engine's +/// concrete processor state. +pub struct EventContext<'a> { + provider: &'a dyn EventContextProvider, +} + +impl fmt::Debug for EventContext<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("EventContext").finish_non_exhaustive() + } +} + +impl<'a> EventContext<'a> { + pub fn new(provider: &'a dyn EventContextProvider) -> Self { + Self { provider } + } + + pub fn get_stack_item(&self, position: usize) -> Felt { + self.provider.get_stack_item(position) + } + + pub fn get_stack_word(&self, start: usize) -> Word { + self.provider.get_stack_word(start) + } + + pub fn get_stack_state(&self) -> Vec { + self.provider.get_stack_state() + } + + pub fn clock(&self) -> u32 { + self.provider.clock() + } + + pub fn ctx(&self) -> ContextId { + self.provider.context_id() + } + + pub fn get_mem_value(&self, context: ContextId, address: u32) -> Option { + self.provider.get_mem_value(context, address) + } + + pub fn get_mem_word( + &self, + context: ContextId, + address: u32, + ) -> Result, MemoryError> { + self.provider.get_mem_word(context, address) + } + + pub fn get_mem_state(&self, context: ContextId) -> Vec<(MemoryAddress, Felt)> { + self.provider.get_mem_state(context) + } + + pub fn get_mem_addr_range( + &self, + start_position: usize, + end_position: usize, + ) -> Result, MemoryError> { + let start_addr = self.get_stack_item(start_position).as_canonical_u64(); + let end_addr = self.get_stack_item(end_position).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) + } + + pub fn advice_stack(&self) -> Vec { + self.provider.advice_stack() + } + + pub fn advice_map(&self) -> &AdviceMap { + self.provider.advice_map() + } + + pub fn get_advice_map_entry(&self, key: &Word) -> Option<&[Felt]> { + self.provider.get_advice_map_entry(key) + } + + pub fn get_advice_tree_node( + &self, + root: Word, + depth: Felt, + index: Felt, + ) -> Result { + self.provider.get_advice_tree_node(root, depth, index) + } + + pub fn max_hash_len_bytes(&self) -> usize { + self.provider.max_hash_len_bytes() + } + + pub fn require_canonical_deferred_node( + &self, + digest: Digest, + ) -> Result<(Digest, &Node), PrecompileError> { + self.provider.require_canonical_deferred_node(digest) + } + + /// Returns a compatibility view of the advice provider. + pub fn advice_provider(&self) -> AdviceProviderView<'a> { + AdviceProviderView { provider: self.provider } + } + + /// Returns a compatibility view of execution options used by event handlers. + pub fn execution_options(&self) -> ExecutionOptionsView<'a> { + ExecutionOptionsView { provider: self.provider } + } +} + +/// Temporary read-only compatibility view for handlers that access the advice provider directly. +pub struct AdviceProviderView<'a> { + provider: &'a dyn EventContextProvider, +} + +impl<'a> AdviceProviderView<'a> { + pub fn stack(&self) -> Vec { + self.provider.advice_stack() + } + + pub fn map(&self) -> &'a AdviceMap { + self.provider.advice_map() + } + + pub fn get_mapped_values(&self, key: &Word) -> Option<&'a [Felt]> { + self.provider.get_advice_map_entry(key) + } + + pub fn get_tree_node(&self, root: Word, depth: Felt, index: Felt) -> Result { + self.provider.get_advice_tree_node(root, depth, index) + } +} + +/// Temporary compatibility view for execution limits used by event handlers. +pub struct ExecutionOptionsView<'a> { + provider: &'a dyn EventContextProvider, +} + +impl ExecutionOptionsView<'_> { + pub fn max_hash_len_bytes(&self) -> usize { + self.provider.max_hash_len_bytes() + } +} + +/// Handles an event emitted by the VM. +pub trait EventHandler: Send + Sync + 'static { + fn on_event(&self, context: &EventContext<'_>) -> Result, EventError>; +} + +impl EventHandler for F +where + F: for<'a> Fn(&EventContext<'a>) -> Result, EventError> + + Send + + Sync + + 'static, +{ + fn on_event(&self, context: &EventContext<'_>) -> Result, EventError> { + self(context) + } +} + +/// An event handler that leaves advice unchanged. +pub struct NoopEventHandler; + +impl EventHandler for NoopEventHandler { + fn on_event(&self, _context: &EventContext<'_>) -> Result, EventError> { + Ok(Vec::new()) + } +} diff --git a/core/src/events/mod.rs b/core/src/events/mod.rs index 2ca90d454c..cb7bca6ee2 100644 --- a/core/src/events/mod.rs +++ b/core/src/events/mod.rs @@ -10,7 +10,14 @@ use serde::{Deserialize, Serialize}; use crate::{Felt, utils::hash_string_to_word}; +pub mod debug; +mod handlers; mod sys_events; + +pub use handlers::{ + AdviceProviderView, EventContext, EventContextProvider, EventError, EventHandler, + ExecutionOptionsView, NoopEventHandler, +}; pub use sys_events::SystemEvent; // EVENT ID diff --git a/core/src/execution.rs b/core/src/execution.rs new file mode 100644 index 0000000000..774b38b4cb --- /dev/null +++ b/core/src/execution.rs @@ -0,0 +1,131 @@ +use alloc::string::String; +use core::fmt::{self, Display, LowerHex}; + +use miden_utils_diagnostics::{Diagnostic, miette}; + +use crate::Felt; + +/// Identifies an execution context. +#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +pub struct ContextId(u32); + +impl ContextId { + pub const fn root() -> Self { + Self(0) + } + + pub const fn is_root(&self) -> bool { + self.0 == 0 + } +} + +impl From for ContextId { + fn from(value: u32) -> Self { + Self(value) + } +} + +impl From for u32 { + fn from(context_id: ContextId) -> Self { + context_id.0 + } +} + +impl From for u64 { + fn from(context_id: ContextId) -> Self { + context_id.0.into() + } +} + +impl From 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) + } +} + +/// Identifies a memory address. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct MemoryAddress(u32); + +impl MemoryAddress { + pub const fn new(address: u32) -> Self { + Self(address) + } +} + +impl From for MemoryAddress { + fn from(address: u32) -> Self { + Self(address) + } +} + +impl From for u32 { + fn from(address: MemoryAddress) -> Self { + address.0 + } +} + +impl Display for MemoryAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl LowerHex for MemoryAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + LowerHex::fmt(&self.0, f) + } +} + +impl core::ops::Add for MemoryAddress { + type Output = Self; + + fn add(self, rhs: MemoryAddress) -> Self::Output { + Self(self.0 + rhs.0) + } +} + +impl core::ops::Add for MemoryAddress { + type Output = Self; + + fn add(self, rhs: u32) -> Self::Output { + Self(self.0 + rhs) + } +} + +/// Lightweight error type for memory operations. +/// +/// This enum captures error conditions without expensive source context. Execution engines can add +/// source context when converting it into their top-level execution error. +#[derive(Debug, thiserror::Error, Diagnostic)] +pub enum MemoryError { + #[error("memory address cannot exceed 2^32 but was {addr}")] + AddressOutOfBounds { addr: u64 }, + #[error( + "memory address {addr} in context {ctx} was read and written, or written twice, in the same clock cycle {clk}" + )] + IllegalMemoryAccess { ctx: ContextId, addr: u32, clk: Felt }, + #[error( + "memory range start address cannot exceed end address, but was ({start_addr}, {end_addr})" + )] + InvalidMemoryRange { start_addr: u64, end_addr: u64 }, + #[error( + "word access at memory address {addr} in context {ctx} is unaligned: word accesses require addresses that are multiples of 4" + )] + UnalignedWordAccess { addr: u32, ctx: ContextId }, + #[error("failed to read from memory: {0}")] + MemoryReadFailed(String), + #[error( + "writing to memory address {addr} in context {ctx} would exceed the maximum number of memory elements {max}" + )] + #[diagnostic(help( + "increase the limit via `ExecutionOptions::with_max_memory_elements`, or reduce the number of distinct memory addresses the program writes to" + ))] + MemoryElementLimitExceeded { ctx: ContextId, addr: u32, max: usize }, +} diff --git a/core/src/lib.rs b/core/src/lib.rs index ee6723d6b7..07c3c48c8d 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -9,6 +9,7 @@ extern crate std; // EXPORTS // ================================================================================================ +pub use execution::{ContextId, MemoryAddress, MemoryError}; pub use miden_crypto::{EMPTY_WORD, Felt, ONE, Word, ZERO}; /// The number of field elements in a Miden word. @@ -18,6 +19,7 @@ pub mod advice; pub mod chiplets; pub mod deferred; pub mod events; +pub mod execution; pub mod mast; pub mod operations; pub mod program; diff --git a/crates/lib/core/Cargo.toml b/crates/lib/core/Cargo.toml index 0524ec76f6..d82cde07f3 100644 --- a/crates/lib/core/Cargo.toml +++ b/crates/lib/core/Cargo.toml @@ -31,7 +31,6 @@ arbitrary = ["std", "miden-assembly/testing", "miden-utils-testing/arbitrary"] std = [ "miden-assembly/std", "miden-precompiles/std", - "miden-processor/std", "miden-utils-sync/std", ] testing = ["arbitrary"] @@ -43,7 +42,6 @@ miden-mast-package.workspace = true miden-core.workspace = true miden-crypto.workspace = true miden-precompiles.workspace = true -miden-processor.workspace = true miden-utils-sync.workspace = true miden-air = { workspace = true, optional = true } miden-ace-codegen = { workspace = true, optional = true } diff --git a/crates/lib/core/src/handlers/aead_decrypt.rs b/crates/lib/core/src/handlers/aead_decrypt.rs index aa0aa8dac2..cc71deab7a 100644 --- a/crates/lib/core/src/handlers/aead_decrypt.rs +++ b/crates/lib/core/src/handlers/aead_decrypt.rs @@ -7,16 +7,14 @@ use alloc::{vec, vec::Vec}; -use miden_core::events::EventName; +use miden_core::{ + advice::{AdviceMutation, AdviceStack, MAX_ADVICE_STACK_SIZE}, + events::{EventContext, EventError, EventName}, +}; use miden_crypto::aead::{ DataType, EncryptionError, aead_poseidon2::{AuthTag, EncryptedData, Nonce, SecretKey}, }; -use miden_processor::{ - ProcessorState, - advice::{AdviceMutation, AdviceStack, MAX_ADVICE_STACK_SIZE}, - event::EventError, -}; use crate::handlers::read_memory_region; @@ -54,7 +52,7 @@ pub const AEAD_DECRYPT_EVENT_NAME: EventName = EventName::new("miden::core::cryp /// 1. The MASM procedure re-verifies the tag when decrypting /// 2. The deterministic encryption creates a bijection between plaintext and ciphertext /// 3. A malicious prover cannot provide incorrect plaintext without causing tag mismatch -pub fn handle_aead_decrypt(process: &ProcessorState) -> Result, EventError> { +pub fn handle_aead_decrypt(process: &EventContext<'_>) -> Result, EventError> { // Stack: [event_id, key:Word(4), nonce:Word(4), src_ptr, dst_ptr, num_blocks, ...] // where: // src_ptr = ciphertext + encrypted_padding + tag location (input) @@ -164,7 +162,7 @@ enum AeadDecryptError { #[cfg(test)] mod tests { - use miden_processor::advice::MAX_ADVICE_STACK_SIZE; + use miden_core::advice::MAX_ADVICE_STACK_SIZE; use crate::handlers::aead_decrypt::{AEAD_DECRYPT_EVENT_NAME, AeadDecryptError, compute_sizes}; diff --git a/crates/lib/core/src/handlers/debug.rs b/crates/lib/core/src/handlers/debug.rs index 7b0b63f397..e9e6c07689 100644 --- a/crates/lib/core/src/handlers/debug.rs +++ b/crates/lib/core/src/handlers/debug.rs @@ -3,11 +3,11 @@ //! Each `miden::core::debug::print_*` procedure emits a well-known event. This module registers a //! single [`DebugPrinter`] handler for all of those events; when one fires, the handler reads the //! requested piece of VM state (operand stack, memory, advice stack, or advice map) and prints it -//! using the VM's tree-style debug formatting via [`miden_processor::write_stack`] / -//! [`miden_processor::write_interval`]. A range-based procedure may share an event with its -//! full-state variant when the full-state behavior can be represented as an unbounded range (e.g. -//! the advice stack); memory uses a dedicated full-state event because `print_mem` enumerates its -//! (capped) range while `print_mem_all` lists only initialized cells. +//! using the VM's tree-style debug formatting via [`miden_core::events::debug::write_stack`] / +//! [`miden_core::events::debug::write_interval`]. A range-based procedure may share an event with +//! its full-state variant when the full-state behavior can be represented as an unbounded range +//! (e.g. the advice stack); memory uses a dedicated full-state event because `print_mem` enumerates +//! its (capped) range while `print_mem_all` lists only initialized cells. //! //! These are ordinary `emit` events: they carry no MAST/decorator cost and print whenever the //! procedure is executed. @@ -21,12 +21,13 @@ use alloc::{ }; use core::fmt; -use miden_core::{Felt, Word}; -use miden_processor::{ - MemoryError, ProcessorState, StdoutWriter, +use miden_core::{ + Felt, MemoryError, Word, advice::AdviceMutation, - event::{EventError, EventHandler, EventId, EventName}, - write_interval, write_stack, + events::{ + EventContext, EventError, EventHandler, EventId, EventName, + debug::{StdoutWriter, write_interval, write_stack}, + }, }; use miden_utils_sync::RwLock; @@ -140,7 +141,7 @@ impl DebugPrinter { } impl EventHandler for DebugPrinter { - fn on_event(&self, process: &ProcessorState) -> Result, EventError> { + fn on_event(&self, process: &EventContext<'_>) -> Result, EventError> { // The event id sits at the top of the stack (position 0); the procedure's arguments, if // any, are immediately below it. let id = EventId::from_felt(process.get_stack_item(0)); @@ -171,7 +172,7 @@ impl EventHandler for DebugPrinter { } else if id == PRINT_ADV_STACK_EVENT_NAME.to_event_id() { let start = stack_item_as_usize(process, 1); let end = stack_item_as_usize(process, 2); - let adv_stack = process.advice_provider().stack(); + let adv_stack = process.advice_stack(); let slice = slice_range(&adv_stack, start, end); write_stack(w, slice, None, "Advice stack", process.clock())?; } else if id == PRINT_ADV_MAP_EVENT_NAME.to_event_id() { @@ -188,7 +189,7 @@ impl EventHandler for DebugPrinter { struct NoopDebugHandler; impl EventHandler for NoopDebugHandler { - fn on_event(&self, _process: &ProcessorState) -> Result, EventError> { + fn on_event(&self, _process: &EventContext<'_>) -> Result, EventError> { Ok(Vec::new()) } } @@ -197,7 +198,7 @@ impl EventHandler for NoopDebugHandler { // ================================================================================================ /// Reads the element at `pos` on the operand stack as a `usize` (saturating). -fn stack_item_as_usize(process: &ProcessorState, pos: usize) -> usize { +fn stack_item_as_usize(process: &EventContext<'_>, pos: usize) -> usize { usize::try_from(process.get_stack_item(pos).as_canonical_u64()).unwrap_or(usize::MAX) } @@ -216,7 +217,7 @@ fn slice_range(slice: &[Felt], start: usize, end: usize) -> &[Felt] { /// `2^32` (one past the last address) so the cell at `u32::MAX` stays reachable; it folds into an /// inclusive end of `u32::MAX`. fn read_mem_print_range( - process: &ProcessorState, + process: &EventContext<'_>, start_idx: usize, end_idx: usize, ) -> Result, MemoryError> { @@ -250,7 +251,7 @@ fn read_mem_print_range( /// `u32`. The caller is responsible for capping the range length (see [`MAX_PRINT_MEM_RANGE`]). fn write_mem_range( w: &mut W, - process: &ProcessorState, + process: &EventContext<'_>, bounds: Option<(u32, u32)>, ) -> fmt::Result { let (ctx, clk) = (process.ctx(), process.clock()); @@ -271,7 +272,7 @@ fn write_mem_range( } /// Prints all initialized memory cells of the current context. -fn write_mem_all(w: &mut W, process: &ProcessorState) -> fmt::Result { +fn write_mem_all(w: &mut W, process: &EventContext<'_>) -> fmt::Result { let (ctx, clk) = (process.ctx(), process.clock()); writeln!(w, "Memory state before step {clk} for context {ctx}:")?; let items: Vec<_> = process @@ -283,9 +284,9 @@ fn write_mem_all(w: &mut W, process: &ProcessorState) -> fmt::Res } /// Prints the full advice map. -fn write_adv_map(w: &mut W, process: &ProcessorState) -> fmt::Result { +fn write_adv_map(w: &mut W, process: &EventContext<'_>) -> fmt::Result { let clk = process.clock(); - let map = process.advice_provider().map(); + let map = process.advice_map(); if map.is_empty() { return writeln!(w, "Advice map before step {clk}: empty."); } @@ -299,11 +300,11 @@ fn write_adv_map(w: &mut W, process: &ProcessorState) -> fmt::Res } /// Looks up the WORD key (at stack positions 1..5) in the advice map and prints its values. -fn write_adv_map_entry(w: &mut W, process: &ProcessorState) -> fmt::Result { +fn write_adv_map_entry(w: &mut W, process: &EventContext<'_>) -> fmt::Result { let key = process.get_stack_word(1); let key_str = format_word(&key); let clk = process.clock(); - match process.advice_provider().get_mapped_values(&key) { + match process.get_advice_map_entry(&key) { Some(values) => { writeln!(w, "Advice map entry for key {key_str} before step {clk}:")?; let items: Vec<_> = values diff --git a/crates/lib/core/src/handlers/falcon_div.rs b/crates/lib/core/src/handlers/falcon_div.rs index 25a0e711b0..938cf0b98d 100644 --- a/crates/lib/core/src/handlers/falcon_div.rs +++ b/crates/lib/core/src/handlers/falcon_div.rs @@ -5,11 +5,10 @@ use alloc::{vec, vec::Vec}; -use miden_core::{ZERO, events::EventName}; -use miden_processor::{ - ProcessorState, +use miden_core::{ + ZERO, advice::{AdviceMutation, AdviceStack}, - event::EventError, + events::{EventContext, EventError, EventName}, }; use crate::handlers::u64_to_u32_elements; @@ -40,7 +39,7 @@ pub const FALCON_DIV_EVENT_NAME: EventName = /// # Errors /// - Returns an error if the divisor is ZERO. /// - Returns an error if either a0 or a1 is not a u32. -pub fn handle_falcon_div(process: &ProcessorState) -> Result, EventError> { +pub fn handle_falcon_div(process: &EventContext<'_>) -> Result, EventError> { let dividend_hi = process.get_stack_item(1).as_canonical_u64(); let dividend_lo = process.get_stack_item(2).as_canonical_u64(); diff --git a/crates/lib/core/src/handlers/mod.rs b/crates/lib/core/src/handlers/mod.rs index 0e420ec31f..e7bfd7489b 100644 --- a/crates/lib/core/src/handlers/mod.rs +++ b/crates/lib/core/src/handlers/mod.rs @@ -1,5 +1,4 @@ -use miden_core::Felt; -use miden_processor::ProcessorState; +use miden_core::{Felt, events::EventContext}; pub mod aead_decrypt; use alloc::vec::Vec; @@ -34,7 +33,7 @@ fn u64_to_u32_elements(value: u64) -> (Felt, Felt) { /// - Returns `None` if any validation fails or if any memory location is uninitialized /// /// # Arguments -/// * `process` - Process state to read memory from +/// * `context` - Event context to read memory from /// * `start_ptr` - Starting address (u64 from stack), must be word-aligned /// * `len` - Number of elements to read (u64) /// @@ -43,11 +42,11 @@ fn u64_to_u32_elements(value: u64) -> (Felt, Felt) { /// /// # Example /// ```ignore -/// let elements = read_memory_region(process, src_ptr, num_elements) +/// let elements = read_memory_region(context, src_ptr, num_elements) /// .ok_or(MyError::MemoryReadFailed)?; /// ``` pub(crate) fn read_memory_region( - process: &ProcessorState, + context: &EventContext<'_>, start_ptr: u64, len: u64, ) -> Option> { @@ -64,6 +63,6 @@ pub(crate) fn read_memory_region( let end_addr = start_addr.checked_add(len_u32)?; // Read all elements in the range from the current execution context - let ctx = process.ctx(); - (start_addr..end_addr).map(|addr| process.get_mem_value(ctx, addr)).collect() + let ctx = context.ctx(); + (start_addr..end_addr).map(|addr| context.get_mem_value(ctx, addr)).collect() } diff --git a/crates/lib/core/src/handlers/precompiles/keccak256.rs b/crates/lib/core/src/handlers/precompiles/keccak256.rs index 83bf68739d..46a229f1e0 100644 --- a/crates/lib/core/src/handlers/precompiles/keccak256.rs +++ b/crates/lib/core/src/handlers/precompiles/keccak256.rs @@ -5,15 +5,11 @@ use core::mem::size_of; use miden_core::{ WORD_SIZE, - events::EventName, + advice::{AdviceMutation, AdviceStack}, + events::{EventContext, EventError, EventName}, utils::{bytes_to_packed_u32_elements, packed_u32_elements_to_bytes}, }; use miden_crypto::hash::keccak::Keccak256; -use miden_processor::{ - ProcessorState, - advice::{AdviceMutation, AdviceStack}, - event::EventError, -}; use crate::handlers::read_memory_region; @@ -28,12 +24,12 @@ const KECCAK256_DIGEST_FELTS: usize = 8; /// Reads the requested u32-packed memory preimage, computes Keccak-256, and pushes the digest limbs /// onto the advice stack for the MASM wrapper to bind with deferred assertions. pub fn handle_keccak256_digest( - process: &ProcessorState<'_>, + process: &EventContext<'_>, ) -> Result, EventError> { let ptr = process.get_stack_item(1).as_canonical_u64(); let len_bytes = process.get_stack_item(2).as_canonical_u64(); - let max = process.execution_options().max_hash_len_bytes(); + let max = process.max_hash_len_bytes(); if len_bytes > max as u64 { return Err(Keccak256DigestEventError::InputTooLong { len_bytes, max }.into()); } @@ -58,7 +54,7 @@ pub fn handle_keccak256_digest( } fn read_memory_packed_u32( - process: &ProcessorState<'_>, + process: &EventContext<'_>, start: u64, len_bytes: usize, ) -> Result, Keccak256DigestEventError> { diff --git a/crates/lib/core/src/handlers/precompiles/uint_field_inv.rs b/crates/lib/core/src/handlers/precompiles/uint_field_inv.rs index cacf072a44..bed936ba65 100644 --- a/crates/lib/core/src/handlers/precompiles/uint_field_inv.rs +++ b/crates/lib/core/src/handlers/precompiles/uint_field_inv.rs @@ -4,15 +4,11 @@ use alloc::{vec, vec::Vec}; use miden_core::{ Felt, Word, ZERO, + advice::{AdviceMutation, AdviceStack}, deferred::{DeferredError, Node}, - events::EventName, + events::{EventContext, EventError, EventName}, }; use miden_precompiles::{Limbs, UintDomain, UintPrecompile}; -use miden_processor::{ - ProcessorState, - advice::{AdviceMutation, AdviceStack}, - event::EventError, -}; /// Event used by generated field uint wrappers to request an inverse witness from the host. pub const UINT_FIELD_INV_EVENT_NAME: EventName = @@ -21,7 +17,7 @@ pub const UINT_FIELD_INV_EVENT_NAME: EventName = /// Resolves the input uint value digest from deferred state, computes its inverse in the encoded /// prime-field domain, and pushes the inverse limbs onto the advice stack for MASM validation. pub fn handle_uint_field_inv( - process: &ProcessorState<'_>, + process: &EventContext<'_>, ) -> Result, EventError> { let input_digest = process.get_stack_word(1); let (_, canonical_node) = process.require_canonical_deferred_node(input_digest)?; diff --git a/crates/lib/core/src/handlers/readonly.rs b/crates/lib/core/src/handlers/readonly.rs index e30a00a05f..8d04325320 100644 --- a/crates/lib/core/src/handlers/readonly.rs +++ b/crates/lib/core/src/handlers/readonly.rs @@ -7,10 +7,9 @@ use alloc::{sync::Arc, vec, vec::Vec}; -use miden_processor::{ - ProcessorState, +use miden_core::{ advice::AdviceMutation, - event::{EventError, EventHandler, EventName}, + events::{EventContext, EventError, EventHandler, EventName}, }; // EVENT NAMES @@ -37,7 +36,7 @@ pub const READONLY_MIDEN_DEBUG_PRINTLN: EventName = struct ReadonlyNoopHandler; impl EventHandler for ReadonlyNoopHandler { - fn on_event(&self, _process: &ProcessorState) -> Result, EventError> { + fn on_event(&self, _process: &EventContext<'_>) -> Result, EventError> { Ok(vec![]) } } diff --git a/crates/lib/core/src/handlers/smt_peek.rs b/crates/lib/core/src/handlers/smt_peek.rs index ef136b23f6..964a482a9b 100644 --- a/crates/lib/core/src/handlers/smt_peek.rs +++ b/crates/lib/core/src/handlers/smt_peek.rs @@ -8,13 +8,9 @@ use alloc::{format, string::String, vec, vec::Vec}; use miden_core::{ Felt, WORD_SIZE, Word, - crypto::merkle::{EmptySubtreeRoots, SMT_DEPTH, Smt}, - events::EventName, -}; -use miden_processor::{ - ProcessorState, advice::{AdviceMutation, AdviceStack}, - event::EventError, + crypto::merkle::{EmptySubtreeRoots, SMT_DEPTH, Smt}, + events::{EventContext, EventError, EventName}, }; /// Event name for the smt_peek operation. @@ -50,7 +46,7 @@ pub const SMT_PEEK_EVENT_NAME: EventName = /// /// # Panics /// Will panic as unimplemented if the target depth is `64`. -pub fn handle_smt_peek(process: &ProcessorState) -> Result, EventError> { +pub fn handle_smt_peek(process: &EventContext<'_>) -> Result, EventError> { let empty_leaf = EmptySubtreeRoots::entry(SMT_DEPTH, SMT_DEPTH); // fetch the arguments from the operand stack // Stack at emit: [event_id, KEY, ROOT, ...] where KEY and ROOT are structural words. @@ -61,8 +57,7 @@ pub fn handle_smt_peek(process: &ProcessorState) -> Result, // or a root of an empty subtree at the returned depth // K[3] is used as the leaf index (most significant in BE ordering) let node = process - .advice_provider() - .get_tree_node(root, Felt::new_unchecked(SMT_DEPTH as u64), key[3]) + .get_advice_tree_node(root, Felt::new_unchecked(SMT_DEPTH as u64), key[3]) .map_err(|err| SmtPeekError::AdviceProviderError { message: format!("Failed to get tree node: {err}"), })?; @@ -95,12 +90,11 @@ pub fn handle_smt_peek(process: &ProcessorState) -> Result, /// Retrieves the preimage of an SMT leaf node from the advice provider. fn get_smt_leaf_preimage( - process: &ProcessorState, + process: &EventContext<'_>, node: Word, ) -> Result, SmtPeekError> { let kv_pairs = process - .advice_provider() - .get_mapped_values(&node) + .get_advice_map_entry(&node) .ok_or(SmtPeekError::SmtNodeNotFound { node })?; if kv_pairs.len() % (WORD_SIZE * 2) != 0 { diff --git a/crates/lib/core/src/handlers/sorted_array.rs b/crates/lib/core/src/handlers/sorted_array.rs index 1d3c58c44a..3f3f7a7c78 100644 --- a/crates/lib/core/src/handlers/sorted_array.rs +++ b/crates/lib/core/src/handlers/sorted_array.rs @@ -1,10 +1,10 @@ use alloc::{vec, vec::Vec}; -use miden_core::{Felt, Word, events::EventName, field::PrimeCharacteristicRing}; -use miden_processor::{ - MemoryError, ProcessorState, +use miden_core::{ + Felt, MemoryError, Word, advice::{AdviceMutation, AdviceStack}, - event::EventError, + events::{EventContext, EventError, EventName}, + field::PrimeCharacteristicRing, }; /// Event name for the lowerbound_array operation. @@ -36,7 +36,7 @@ enum KeySize { /// # Errors /// Returns an error if the provided word array is not sorted in non-decreasing order. pub fn handle_lowerbound_array( - process: &ProcessorState, + process: &EventContext<'_>, ) -> Result, EventError> { push_lowerbound_result(process, 4, KeySize::Full) } @@ -59,7 +59,7 @@ pub fn handle_lowerbound_array( /// # Errors /// Returns an error if the keys are not sorted in non-decreasing order. pub fn handle_lowerbound_key_value( - process: &ProcessorState, + process: &EventContext<'_>, ) -> Result, EventError> { let use_full_key = process.get_stack_item(7); @@ -82,7 +82,7 @@ const START_ADDR_OFFSET: usize = 5; const END_ADDR_OFFSET: usize = 6; fn push_lowerbound_result( - process: &ProcessorState, + process: &EventContext<'_>, stride: u32, key_size: KeySize, ) -> Result, EventError> { diff --git a/crates/lib/core/src/handlers/u128_div.rs b/crates/lib/core/src/handlers/u128_div.rs index 5e50bb492d..9b633ef3b9 100644 --- a/crates/lib/core/src/handlers/u128_div.rs +++ b/crates/lib/core/src/handlers/u128_div.rs @@ -5,11 +5,10 @@ use alloc::{vec, vec::Vec}; -use miden_core::{Felt, Word}; -use miden_processor::{ - ProcessorState, +use miden_core::{ + Felt, Word, advice::{AdviceMutation, AdviceStack}, - event::{EventError, EventName}, + events::{EventContext, EventError, EventName}, }; /// Event name for the u128_div operation. @@ -36,7 +35,7 @@ pub const U128_DIV_EVENT_NAME: EventName = EventName::new("miden::core::math::u1 /// /// # Errors /// Returns an error if the divisor is ZERO or any limb is not a valid u32. -pub fn handle_u128_div(process: &ProcessorState) -> Result, EventError> { +pub fn handle_u128_div(process: &EventContext<'_>) -> Result, EventError> { let divisor = read_u128_from_stack(process, 1, "divisor")?; if divisor == 0 { @@ -62,7 +61,7 @@ pub fn handle_u128_div(process: &ProcessorState) -> Result, /// Reads a u128 value from 4 consecutive stack positions starting at `start`. fn read_u128_from_stack( - process: &ProcessorState, + process: &EventContext<'_>, start: usize, name: &'static str, ) -> Result { diff --git a/crates/lib/core/src/handlers/u256_div.rs b/crates/lib/core/src/handlers/u256_div.rs index 9030c0c4ac..37a40dcea1 100644 --- a/crates/lib/core/src/handlers/u256_div.rs +++ b/crates/lib/core/src/handlers/u256_div.rs @@ -5,11 +5,10 @@ use alloc::{vec, vec::Vec}; -use miden_core::{Felt, Word}; -use miden_processor::{ - ProcessorState, +use miden_core::{ + Felt, Word, advice::{AdviceMutation, AdviceStack}, - event::{EventError, EventName}, + events::{EventContext, EventError, EventName}, }; /// Event name for the u256_div operation. @@ -38,7 +37,7 @@ pub const U256_DIV_EVENT_NAME: EventName = EventName::new("miden::core::math::u2 /// /// # Errors /// Returns an error if the divisor is ZERO or any limb is not a valid u32. -pub fn handle_u256_div(process: &ProcessorState) -> Result, EventError> { +pub fn handle_u256_div(process: &EventContext<'_>) -> Result, EventError> { let divisor = read_u256_from_stack(process, 1, "divisor")?; if divisor == (0, 0) { @@ -68,7 +67,7 @@ pub fn handle_u256_div(process: &ProcessorState) -> Result, /// /// Returned as a `(lo, hi)` pair of u128s. fn read_u256_from_stack( - process: &ProcessorState, + process: &EventContext<'_>, start: usize, name: &'static str, ) -> Result<(u128, u128), EventError> { diff --git a/crates/lib/core/src/handlers/u64_div.rs b/crates/lib/core/src/handlers/u64_div.rs index c419c54650..4bc6a9ba88 100644 --- a/crates/lib/core/src/handlers/u64_div.rs +++ b/crates/lib/core/src/handlers/u64_div.rs @@ -5,10 +5,9 @@ use alloc::{vec, vec::Vec}; -use miden_processor::{ - ProcessorState, +use miden_core::{ advice::{AdviceMutation, AdviceStack}, - event::{EventError, EventName}, + events::{EventContext, EventError, EventName}, }; use crate::handlers::u64_to_u32_elements; @@ -35,7 +34,7 @@ pub const U64_DIV_EVENT_NAME: EventName = EventName::new("miden::core::math::u64 /// /// # Errors /// Returns an error if the divisor is ZERO. -pub fn handle_u64_div(process: &ProcessorState) -> Result, EventError> { +pub fn handle_u64_div(process: &EventContext<'_>) -> Result, EventError> { // Read divisor from positions 1 (lo) and 2 (hi) - b is on top of stack let divisor = { let divisor_lo = process.get_stack_item(1).as_canonical_u64(); diff --git a/crates/lib/core/src/lib.rs b/crates/lib/core/src/lib.rs index 237037c586..81b2ad4325 100644 --- a/crates/lib/core/src/lib.rs +++ b/crates/lib/core/src/lib.rs @@ -12,9 +12,11 @@ extern crate alloc; use alloc::{sync::Arc, vec, vec::Vec}; -use miden_core::{events::EventName, mast::MastForest}; -use miden_mast_package::Package; -use miden_processor::{HostLibrary, event::EventHandler}; +use miden_core::{ + events::{EventHandler, EventName}, + mast::MastForest, +}; +use miden_mast_package::{HostLibrary, Package}; use miden_utils_sync::LazyLock; use crate::handlers::{ diff --git a/crates/lib/core/tests/collections/mmr.rs b/crates/lib/core/tests/collections/mmr.rs index 1b211fad01..034228d459 100644 --- a/crates/lib/core/tests/collections/mmr.rs +++ b/crates/lib/core/tests/collections/mmr.rs @@ -833,7 +833,7 @@ fn debug_mmr_peaks_vs_vm_memory() { rust_mem.extend(digests_to_ints(rust_peaks)); // Read back the same region from VM memory: first num_leaves word + one word per peak. - use miden_processor::ContextId; + use miden_core::ContextId; let mut vm_mem = Vec::new(); let words_to_read = 1 + rust_peaks.len(); for word_idx in 0..words_to_read { diff --git a/crates/lib/core/tests/collections/sorted_array.rs b/crates/lib/core/tests/collections/sorted_array.rs index 619f7717f9..c161d5bd00 100644 --- a/crates/lib/core/tests/collections/sorted_array.rs +++ b/crates/lib/core/tests/collections/sorted_array.rs @@ -1,12 +1,11 @@ +use miden_core::{ + advice::{AdviceMutation, AdviceStack}, + events::{EventContext, EventError}, +}; use miden_core_lib::{ CoreLibrary, handlers::sorted_array::{LOWERBOUND_ARRAY_EVENT_NAME, LOWERBOUND_KEY_VALUE_EVENT_NAME}, }; -use miden_processor::{ - ProcessorState, - advice::{AdviceMutation, AdviceStack}, - event::EventError, -}; use super::*; @@ -975,7 +974,7 @@ fn build_lib_test(source: &str, op_stack: &[u64]) -> miden_utils_testing::Test { /// Returns `(was_found = false, maybe_value_ptr = 204)` regardless of the actual array. 204 is /// past the array's `end_ptr = 112`, so the bounds check must fire. fn malicious_lowerbound_oob_above( - _process: &ProcessorState, + _process: &EventContext<'_>, ) -> Result, EventError> { Ok(vec![advice_stack_mutation([Felt::new_unchecked(204), Felt::ZERO])]) } @@ -983,7 +982,7 @@ fn malicious_lowerbound_oob_above( #[allow(clippy::unnecessary_wraps)] /// Returns `(was_found = false, maybe_value_ptr = 40)` which is below `start_ptr = 100`. fn malicious_lowerbound_oob_below( - _process: &ProcessorState, + _process: &EventContext<'_>, ) -> Result, EventError> { Ok(vec![advice_stack_mutation([Felt::new_unchecked(40), Felt::ZERO])]) } @@ -991,7 +990,7 @@ fn malicious_lowerbound_oob_below( #[allow(clippy::unnecessary_wraps)] /// Returns `(was_found = false, maybe_ptr = start_ptr)` regardless of the actual range. fn malicious_lowerbound_start_ptr( - _process: &ProcessorState, + _process: &EventContext<'_>, ) -> Result, EventError> { Ok(vec![advice_stack_mutation([Felt::new_unchecked(100), Felt::ZERO])]) } diff --git a/crates/lib/core/tests/crypto/aead.rs b/crates/lib/core/tests/crypto/aead.rs index f73ac9e105..dea4ff19c6 100644 --- a/crates/lib/core/tests/crypto/aead.rs +++ b/crates/lib/core/tests/crypto/aead.rs @@ -1,16 +1,15 @@ use std::sync::Arc; use miden_air::Felt; +use miden_core::{ + advice::{AdviceMutation, AdviceStack}, + events::{EventContext, EventError, EventHandler}, +}; use miden_core_lib::handlers::aead_decrypt::AEAD_DECRYPT_EVENT_NAME; use miden_crypto::aead::{ DataType, aead_poseidon2::{AuthTag, EncryptedData, Nonce, SecretKey}, }; -use miden_processor::{ - ProcessorState, - advice::{AdviceMutation, AdviceStack}, - event::{EventError, EventHandler}, -}; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; @@ -183,7 +182,7 @@ fn test_decrypt_rejects_tampered_final_tag() { let mut test = build_test!(source.as_str(), &[]); let valid_plaintext = plaintext; let malicious_handler: Arc = - Arc::new(move |_process: &ProcessorState| -> Result, EventError> { + Arc::new(move |_process: &EventContext<'_>| -> Result, EventError> { Ok(vec![advice_stack_mutation(valid_plaintext.clone())]) }); @@ -442,7 +441,7 @@ fn test_decrypt_rejects_adversarial_plaintext_for_unrelated_ciphertext() { let mut test = build_test!(source.as_str(), &[]); let adversarial_plaintext = plaintext; let malicious_handler: Arc = - Arc::new(move |_process: &ProcessorState| -> Result, EventError> { + Arc::new(move |_process: &EventContext<'_>| -> Result, EventError> { Ok(vec![advice_stack_mutation(adversarial_plaintext.clone())]) }); diff --git a/crates/lib/core/tests/crypto/falcon.rs b/crates/lib/core/tests/crypto/falcon.rs index c55fa10c60..4f9447a1f6 100644 --- a/crates/lib/core/tests/crypto/falcon.rs +++ b/crates/lib/core/tests/crypto/falcon.rs @@ -4,17 +4,15 @@ use miden_air::Felt; use miden_assembly::{Assembler, Linkage}; use miden_core::{ ZERO, - events::EventName, + advice::{AdviceInputs, AdviceMutation, AdviceStack}, + events::{EventContext, EventError, EventName}, field::PrimeField64, mast::error_code_from_msg, serde::{Deserializable, Serializable}, }; use miden_core_lib::{CoreLibrary, dsa::falcon512_poseidon2}; use miden_processor::{ - DefaultHost, ExecutionError, FastProcessor, ProcessorState, Program, - advice::{AdviceInputs, AdviceMutation, AdviceStack}, - crypto::random::RandomCoin, - event::EventError, + DefaultHost, ExecutionError, FastProcessor, Program, crypto::random::RandomCoin, operation::OperationError, }; #[cfg(feature = "arbitrary")] @@ -72,13 +70,14 @@ const EVENT_FALCON_SIG_TO_STACK: EventName = EventName::new("test::falcon::sig_t /// - SIGNATURE is the signature being verified. /// /// The advice provider is expected to contain the private key associated to the public key PK. -pub fn push_falcon_signature(process: &ProcessorState) -> Result, EventError> { +pub fn push_falcon_signature( + process: &EventContext<'_>, +) -> Result, EventError> { let pub_key = process.get_stack_word(1); let msg = process.get_stack_word(5); let pk_sk_felts = process - .advice_provider() - .get_mapped_values(&pub_key) + .get_advice_map_entry(&pub_key) .ok_or(FalconError::NoSecretKey { key: pub_key })?; // Convert felts back to bytes (each felt was a single byte stored as u64) @@ -359,7 +358,7 @@ fn test_mod_12289_rejects_forged_remainder_zero(#[case] a_hi: u64, #[case] a_lo: // Malicious event handler that always returns remainder = 0. // Signature matches the event-handler callback contract. #[allow(clippy::unnecessary_wraps)] - fn malicious_falcon_div(process: &ProcessorState) -> Result, EventError> { + fn malicious_falcon_div(process: &EventContext<'_>) -> Result, EventError> { let a_hi = process.get_stack_item(1).as_canonical_u64(); let a_lo = process.get_stack_item(2).as_canonical_u64(); let a = (a_hi << 32) | a_lo; @@ -417,7 +416,9 @@ fn test_mod_12289_rejects_forged_addition_overflow() { // Malicious event handler that forges q/r to trigger the addition-overflow assertion. #[allow(clippy::unnecessary_wraps)] - fn malicious_falcon_div(_process: &ProcessorState) -> Result, EventError> { + fn malicious_falcon_div( + _process: &EventContext<'_>, + ) -> Result, EventError> { let q_hi = Felt::new_unchecked(FORGED_Q >> 32); let q_lo = Felt::new_unchecked(FORGED_Q & 0xffff_ffff); @@ -459,7 +460,7 @@ fn test_mod_12289_rejects_non_u32_remainder_advice() { EventName::new("miden::core::crypto::dsa::falcon512_poseidon2::falcon_div"); #[allow(clippy::unnecessary_wraps)] - fn malicious_falcon_div(process: &ProcessorState) -> Result, EventError> { + fn malicious_falcon_div(process: &EventContext<'_>) -> Result, EventError> { let a_hi = process.get_stack_item(1).as_canonical_u64(); let a_lo = process.get_stack_item(2).as_canonical_u64(); let dividend = (a_hi << 32) | a_lo; diff --git a/crates/lib/core/tests/debug.rs b/crates/lib/core/tests/debug.rs index b8d7584cef..c2c1f6b2ab 100644 --- a/crates/lib/core/tests/debug.rs +++ b/crates/lib/core/tests/debug.rs @@ -10,7 +10,11 @@ use std::{ }; use miden_assembly::{Assembler, Linkage}; -use miden_core::{Felt, Word}; +use miden_core::{ + Felt, MemoryError, Word, + advice::{AdviceInputs, AdviceStack}, + events::{EventHandler, EventName}, +}; use miden_core_lib::{ CoreLibrary, handlers::debug::{ @@ -19,12 +23,9 @@ use miden_core_lib::{ PRINT_STACK_EVENT_NAME, advice_debug_handlers, debug_handlers, noop_debug_handlers, }, }; +use miden_mast_package::HostLibrary; use miden_processor::{ - DefaultHost, ExecutionError, ExecutionOptions, ExecutionOutput, HostLibrary, MemoryError, - StackInputs, - advice::{AdviceInputs, AdviceStack}, - event::{EventHandler, EventName}, - execute_sync, + DefaultHost, ExecutionError, ExecutionOptions, ExecutionOutput, StackInputs, execute_sync, }; // HARNESS diff --git a/crates/lib/core/tests/stark/batch_query_gen.rs b/crates/lib/core/tests/stark/batch_query_gen.rs index d5f22055cf..ea88d7aef8 100644 --- a/crates/lib/core/tests/stark/batch_query_gen.rs +++ b/crates/lib/core/tests/stark/batch_query_gen.rs @@ -4,8 +4,7 @@ //! implementation that calls `sample_bits` in a loop. Both programs start from the same //! sponge state and parameters, and we compare the resulting query words stored in memory. -use miden_core::Felt; -use miden_processor::ContextId; +use miden_core::{ContextId, Felt}; use rand::{RngExt, SeedableRng}; use rand_chacha::ChaCha20Rng; use rstest::rstest; diff --git a/crates/lib/core/tests/stark/mod.rs b/crates/lib/core/tests/stark/mod.rs index 257da9d644..f5a0eb9415 100644 --- a/crates/lib/core/tests/stark/mod.rs +++ b/crates/lib/core/tests/stark/mod.rs @@ -510,7 +510,7 @@ fn reduced_inputs_and_outer_logup_boundary(#[case] num_kernel_proc_digests: usiz let test = build_test!(source, &initial_stack, &advice_stack); let (output, _host) = test.execute_for_output().expect("execution failed"); - use miden_processor::ContextId; + use miden_core::ContextId; let ctx = ContextId::root(); let read_elem = |addr: u32| -> u64 { output diff --git a/crates/mast-package/src/host_library.rs b/crates/mast-package/src/host_library.rs new file mode 100644 index 0000000000..3fab3b50cd --- /dev/null +++ b/crates/mast-package/src/host_library.rs @@ -0,0 +1,65 @@ +use alloc::{sync::Arc, vec::Vec}; + +use miden_core::{ + events::{EventHandler, EventName}, + mast::MastForest, +}; + +use crate::{Package, PackageDebugInfoError, debug_info::PackageDebugInfo}; + +/// A rich library representing a [`MastForest`] which also exports a list of handlers for events it +/// may call. +pub struct HostLibrary { + /// A [`MastForest`] with procedures exposed by this library. + pub mast_forest: Arc, + /// Package-owned debug info that belongs to `mast_forest`. + pub package_debug_info: Result, PackageDebugInfoError>, + /// List of handlers along with their event names to call them with `emit`. + pub handlers: Vec<(EventName, Arc)>, +} + +impl Default for HostLibrary { + fn default() -> Self { + Self { + mast_forest: Arc::new(MastForest::new()), + package_debug_info: Ok(None), + handlers: Vec::new(), + } + } +} + +impl From> for HostLibrary { + fn from(package: Arc) -> Self { + let package_debug_info = match package.debug_info() { + Ok(debug_info) => Ok(debug_info), + Err(PackageDebugInfoError::UntrustedSections) => Ok(None), + Err(err) => Err(err), + }; + + Self { + mast_forest: package.mast_forest().clone(), + package_debug_info, + handlers: Vec::new(), + } + } +} + +impl From> for HostLibrary { + fn from(mast_forest: Arc) -> Self { + Self { + mast_forest, + package_debug_info: Ok(None), + handlers: Vec::new(), + } + } +} + +impl From<&Arc> for HostLibrary { + fn from(mast_forest: &Arc) -> Self { + Self { + mast_forest: mast_forest.clone(), + package_debug_info: Ok(None), + handlers: Vec::new(), + } + } +} diff --git a/crates/mast-package/src/lib.rs b/crates/mast-package/src/lib.rs index 1d354b60c5..d26dd48a17 100644 --- a/crates/mast-package/src/lib.rs +++ b/crates/mast-package/src/lib.rs @@ -10,6 +10,7 @@ extern crate std; pub mod debug_info; mod dependency; +mod host_library; mod package; pub use miden_assembly_syntax::{ @@ -22,6 +23,7 @@ pub use miden_core::{Word, mast::MastForest, program::Program}; pub use self::package::arbitrary; pub use self::{ dependency::Dependency, + host_library::HostLibrary, package::{ ConstantExport, InvalidSectionIdError, InvalidTargetTypeError, ManifestValidationError, Package, PackageDebugInfoError, PackageExport, PackageId, PackageManifest, PackageModule, diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 63696808bd..ba6da0a000 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -31,19 +31,18 @@ pub use miden_core::{ }; use miden_core::{ chiplets::hasher::apply_permutation, - events::{EventName, SystemEvent}, + events::{EventHandler, EventName, SystemEvent}, }; use miden_mast_package::{Package, debug_info::PackageDebugInfo}; #[cfg(not(target_family = "wasm"))] use miden_processor::trace::build_trace; pub use miden_processor::{ - ContextId, ExecutionError, ProcessorState, + ContextId, ExecutionError, advice::{AdviceInputs, AdviceProvider, AdviceStack}, trace::ExecutionTrace, }; use miden_processor::{ DefaultHost, ExecutionOptions, ExecutionOutput, FastProcessor, Program, TraceBuildInputs, - event::EventHandler, }; #[cfg(not(target_family = "wasm"))] pub use miden_prover::prove_sync; @@ -838,7 +837,10 @@ mod tests { }, }; - use miden_processor::{advice::AdviceMutation, event::EventError}; + use miden_core::{ + advice::AdviceMutation, + events::{EventContext, EventError}, + }; use super::*; @@ -890,10 +892,11 @@ mod tests { let invocations = Arc::new(AtomicUsize::new(0)); let handler_invocations = invocations.clone(); - let handler = move |_process: &ProcessorState| -> Result, EventError> { - handler_invocations.fetch_add(1, Ordering::SeqCst); - Ok(Vec::new()) - }; + let handler = + move |_context: &EventContext<'_>| -> Result, EventError> { + handler_invocations.fetch_add(1, Ordering::SeqCst); + Ok(Vec::new()) + }; let source = alloc::format!( r#" diff --git a/processor/src/errors.rs b/processor/src/errors.rs index a48ae5fd32..1564dd0adf 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -12,7 +12,7 @@ use miden_mast_package::{ use miden_utils_diagnostics::{Diagnostic, miette}; use crate::{ - BaseHost, ContextId, Felt, Word, + BaseHost, Felt, MemoryError, Word, advice::AdviceError, event::{EventError, EventId, EventName}, fast::SystemEventError, @@ -210,41 +210,6 @@ impl From for IoError { } } -// MEMORY ERROR -// ================================================================================================ - -/// Lightweight error type for memory operations. -/// -/// This enum captures error conditions without expensive context information (no source location, -/// no file references). When a `MemoryError` propagates up to become an `ExecutionError`, the -/// context is resolved lazily via `MapExecErr::map_exec_err`. -#[derive(Debug, thiserror::Error, Diagnostic)] -pub enum MemoryError { - #[error("memory address cannot exceed 2^32 but was {addr}")] - AddressOutOfBounds { addr: u64 }, - #[error( - "memory address {addr} in context {ctx} was read and written, or written twice, in the same clock cycle {clk}" - )] - IllegalMemoryAccess { ctx: ContextId, addr: u32, clk: Felt }, - #[error( - "memory range start address cannot exceed end address, but was ({start_addr}, {end_addr})" - )] - InvalidMemoryRange { start_addr: u64, end_addr: u64 }, - #[error( - "word access at memory address {addr} in context {ctx} is unaligned: word accesses require addresses that are multiples of 4" - )] - UnalignedWordAccess { addr: u32, ctx: ContextId }, - #[error("failed to read from memory: {0}")] - MemoryReadFailed(String), - #[error( - "writing to memory address {addr} in context {ctx} would exceed the maximum number of memory elements {max}" - )] - #[diagnostic(help( - "increase the limit via `ExecutionOptions::with_max_memory_elements`, or reduce the number of distinct memory addresses the program writes to" - ))] - MemoryElementLimitExceeded { ctx: ContextId, addr: u32, max: usize }, -} - // CRYPTO ERROR // ================================================================================================ diff --git a/processor/src/execution/mod.rs b/processor/src/execution/mod.rs index b2a256b233..cd24c2d984 100644 --- a/processor/src/execution/mod.rs +++ b/processor/src/execution/mod.rs @@ -151,7 +151,7 @@ impl<'a, P, H: BaseHost, S, T, F> ExecutionState<'a, P, H, S, T, F> { /// InternalBreakReason::Emit { op_idx, continuation, source_node_id } => { /// // Handle Emit operation (e.g., call `SyncHost::on_event`) /// self.op_emit(...); -/// +/// /// // As per `InternalBreakReason::Emit` documentation, we call `finish_emit_op_execution` /// // to complete the execution of the Emit operation. /// finish_emit_op_execution(...); @@ -159,7 +159,7 @@ impl<'a, P, H: BaseHost, S, T, F> ExecutionState<'a, P, H, S, T, F> { /// InternalBreakReason::LoadMastForestFromDyn { callee_hash } => { /// // load MAST forest containing the callee procedure /// let (procedure_id, new_forest) = self.load_mast_forest(...); -/// +/// /// // As per `InternalBreakReason::LoadMastForestFromDyn` documentation, we call /// // `finish_load_mast_forest_from_dyn_start` to complete the execution of the operation. /// finish_load_mast_forest_from_dyn_start(...); @@ -167,7 +167,7 @@ impl<'a, P, H: BaseHost, S, T, F> ExecutionState<'a, P, H, S, T, F> { /// InternalBreakReason::LoadMastForestFromExternal { external_node_id, procedure_hash } => { /// // load MAST forest containing the callee procedure /// let (procedure_id, new_forest) = self.load_mast_forest(...); -/// +/// /// // As per `InternalBreakReason::LoadMastForestFromExternal` documentation, we call /// // `finish_load_mast_forest_from_external_start` to complete the execution of the operation. /// finish_load_mast_forest_from_external_start(...); @@ -718,5 +718,5 @@ where /// specifically the `SYSCALL` operation doesn't apply as it always goes back to the root /// context. fn get_next_ctx_id(processor: &impl Processor) -> ContextId { - (processor.system().clock() + 1).into() + (processor.system().clock() + 1).as_u32().into() } diff --git a/processor/src/fast/mod.rs b/processor/src/fast/mod.rs index 01be2bf6ce..f514232d59 100644 --- a/processor/src/fast/mod.rs +++ b/processor/src/fast/mod.rs @@ -3,8 +3,10 @@ use core::{cmp::min, ops::ControlFlow}; use miden_air::{Felt, trace::RowIndex}; use miden_core::{ - EMPTY_WORD, WORD_SIZE, Word, ZERO, - deferred::DeferredState, + ContextId, EMPTY_WORD, MemoryError, WORD_SIZE, Word, ZERO, + advice::AdviceMap, + deferred::{DeferredState, Digest, Node, PrecompileError}, + events::{EventContext, EventContextProvider, EventError}, mast::{ExecutableMastForest, MastForest}, program::{MIN_STACK_DEPTH, Program, StackInputs, StackOutputs}, utils::range, @@ -12,8 +14,8 @@ use miden_core::{ use miden_mast_package::Package; use crate::{ - AdviceInputs, AdviceProvider, ContextId, ExecutionError, ExecutionOptions, ProcessorState, - advice::AdviceError, + ExecutionError, ExecutionOptions, MemoryAddress, + advice::{AdviceError, AdviceInputs, AdviceProvider}, continuation_stack::{Continuation, ContinuationStack}, errors::MapExecErrNoCtx, tracer::{OperationHelperRegisters, Tracer}, @@ -487,10 +489,10 @@ impl FastProcessor { &self.options } - /// Returns a narrowed interface for reading and updating the processor state. + /// Returns the event context exposed to host callbacks. #[inline(always)] - pub fn state(&self) -> ProcessorState<'_> { - ProcessorState { processor: self } + pub fn state(&self) -> EventContext<'_> { + EventContext::new(self) } // MUTATORS @@ -661,8 +663,90 @@ impl FastProcessor { } } +impl EventContextProvider for FastProcessor { + #[inline(always)] + fn get_stack_item(&self, position: usize) -> Felt { + self.stack_get_safe(position) + } + + #[inline(always)] + fn get_stack_word(&self, start: usize) -> Word { + self.stack_get_word_safe(start) + } + + #[inline(always)] + fn get_stack_state(&self) -> Vec { + self.stack().iter().rev().copied().collect() + } + + #[inline(always)] + fn clock(&self) -> u32 { + self.clk.as_u32() + } + + #[inline(always)] + fn context_id(&self) -> ContextId { + self.ctx + } + + #[inline(always)] + fn get_mem_value(&self, context: ContextId, address: u32) -> Option { + self.memory.read_element_impl(context, address) + } + + #[inline(always)] + fn get_mem_word(&self, context: ContextId, address: u32) -> Result, MemoryError> { + self.memory.read_word_impl(context, address) + } + + #[inline(always)] + fn get_mem_state(&self, context: ContextId) -> Vec<(MemoryAddress, Felt)> { + self.memory.get_memory_state(context) + } + + #[inline(always)] + fn advice_stack(&self) -> Vec { + self.advice.stack() + } + + #[inline(always)] + fn advice_map(&self) -> &AdviceMap { + self.advice.map() + } + + #[inline(always)] + fn get_advice_map_entry(&self, key: &Word) -> Option<&[Felt]> { + self.advice.get_mapped_values(key) + } + + #[inline(always)] + fn get_advice_tree_node( + &self, + root: Word, + depth: Felt, + index: Felt, + ) -> Result { + self.advice + .get_tree_node(root, depth, index) + .map_err(|err| Box::new(err) as EventError) + } + + #[inline(always)] + fn max_hash_len_bytes(&self) -> usize { + self.options.max_hash_len_bytes() + } + + #[inline(always)] + fn require_canonical_deferred_node( + &self, + digest: Digest, + ) -> Result<(Digest, &Node), PrecompileError> { + self.deferred_state.require_canonical_node(digest) + } +} + // EXECUTION OUTPUT -// =============================================================================================== +// ================================================================================================ /// The output of a program execution, containing the state of the stack, advice provider, memory, /// and final deferred state at the end of execution. diff --git a/processor/src/fast/tests/mod.rs b/processor/src/fast/tests/mod.rs index 0afa8c51e3..2bace0f542 100644 --- a/processor/src/fast/tests/mod.rs +++ b/processor/src/fast/tests/mod.rs @@ -32,9 +32,9 @@ use rstest::rstest; use super::*; use crate::{ - AdviceInputs, BaseHost, DefaultHost, LoadedMastForest, ProcessorState, SyncHost, + AdviceInputs, BaseHost, DefaultHost, LoadedMastForest, SyncHost, advice::AdviceMutation, - event::EventError, + event::{EventContext, EventError}, operation::OperationError, processor::{StackInterface, SystemInterface}, }; @@ -1202,7 +1202,7 @@ impl SyncHost for MalformedExternalHost { Some(self.loaded_mast_forest.clone()) } - fn on_event(&mut self, _process: &ProcessorState) -> Result, EventError> { + fn on_event(&mut self, _context: &EventContext<'_>) -> Result, EventError> { Ok(Vec::new()) } } diff --git a/processor/src/host/advice/mod.rs b/processor/src/host/advice/mod.rs index 3436496702..8e6a5992e7 100644 --- a/processor/src/host/advice/mod.rs +++ b/processor/src/host/advice/mod.rs @@ -2,7 +2,7 @@ use alloc::{collections::BTreeSet, vec::Vec}; use miden_core::{ Felt, WORD_SIZE, Word, - advice::{AdviceInputs, AdviceMap, AdviceStack}, + advice::{AdviceInputs, AdviceMap, AdviceMutation, AdviceStack, MAX_ADVICE_STACK_SIZE}, crypto::{ hash::Poseidon2, merkle::{InnerNodeInfo, MerkleError, MerklePath, MerkleStore, NodeIndex}, @@ -14,13 +14,7 @@ use miden_core::{crypto::hash::Blake3_256, serde::Serializable}; mod errors; pub use errors::AdviceError; -use crate::{ExecutionOptions, host::AdviceMutation, processor::AdviceProviderInterface}; - -// CONSTANTS -// ================================================================================================ - -/// Maximum number of elements allowed on the advice stack. Set to 2^17. -pub const MAX_ADVICE_STACK_SIZE: usize = 1 << 17; +use crate::{ExecutionOptions, processor::AdviceProviderInterface}; trait MerkleStoreBudget { fn contains_internal_node(&self, root: Word) -> bool; diff --git a/processor/src/host/default.rs b/processor/src/host/default.rs index 54be65629b..1b50de3661 100644 --- a/processor/src/host/default.rs +++ b/processor/src/host/default.rs @@ -2,16 +2,15 @@ use alloc::{sync::Arc, vec::Vec}; use miden_core::{ Word, - events::{EventId, EventName}, - mast::MastForest, + events::{EventContext, EventId, EventName}, }; use miden_debug_types::{DefaultSourceManager, Location, SourceFile, SourceManager, SourceSpan}; -use miden_mast_package::{PackageDebugInfoError, debug_info::PackageDebugInfo}; +pub use miden_mast_package::HostLibrary; use super::handlers::{EventError, EventHandler, EventHandlerRegistry}; use crate::{ - BaseHost, ExecutionError, LoadedMastForest, MastForestStore, MemMastForestStore, - ProcessorState, SyncHost, advice::AdviceMutation, + BaseHost, ExecutionError, LoadedMastForest, MastForestStore, MemMastForestStore, SyncHost, + advice::AdviceMutation, }; // DEFAULT HOST IMPLEMENTATION @@ -76,7 +75,7 @@ where /// Registers a single [`EventHandler`] into this host. /// /// The handler can be either a closure or a free function with signature - /// `fn(&mut ProcessorState) -> Result<(), EventHandler>` + /// `fn(&EventContext) -> Result, EventError>` pub fn register_handler( &mut self, event: EventName, @@ -127,12 +126,9 @@ where self.store.get(node_digest) } - fn on_event( - &mut self, - process: &ProcessorState<'_>, - ) -> Result, EventError> { - let event_id = EventId::from_felt(process.get_stack_item(0)); - match self.event_handlers.handle_event(event_id, process) { + fn on_event(&mut self, context: &EventContext<'_>) -> Result, EventError> { + let event_id = EventId::from_felt(context.get_stack_item(0)); + match self.event_handlers.handle_event(event_id, context) { Ok(Some(mutations)) => Ok(mutations), Ok(None) => { #[derive(Debug, thiserror::Error)] @@ -169,69 +165,7 @@ impl SyncHost for NoopHost { } #[inline(always)] - fn on_event( - &mut self, - _process: &ProcessorState<'_>, - ) -> Result, EventError> { + fn on_event(&mut self, _context: &EventContext<'_>) -> Result, EventError> { Ok(Vec::new()) } } - -// HOST LIBRARY -// ================================================================================================ - -/// A rich library representing a [`MastForest`] which also exports -/// a list of handlers for events it may call. -pub struct HostLibrary { - /// A `MastForest` with procedures exposed by this library. - pub mast_forest: Arc, - /// Package-owned debug info that belongs to `mast_forest`. - pub package_debug_info: Result, PackageDebugInfoError>, - /// List of handlers along with their event names to call them with `emit`. - pub handlers: Vec<(EventName, Arc)>, -} - -impl Default for HostLibrary { - fn default() -> Self { - Self { - mast_forest: Arc::new(MastForest::new()), - package_debug_info: Ok(None), - handlers: Vec::new(), - } - } -} - -impl From> for HostLibrary { - fn from(package: Arc) -> Self { - let package_debug_info = match package.debug_info() { - Ok(debug_info) => Ok(debug_info), - Err(PackageDebugInfoError::UntrustedSections) => Ok(None), - Err(err) => Err(err), - }; - Self { - mast_forest: package.mast_forest().clone(), - package_debug_info, - handlers: vec![], - } - } -} - -impl From> for HostLibrary { - fn from(mast_forest: Arc) -> Self { - Self { - mast_forest, - package_debug_info: Ok(None), - handlers: vec![], - } - } -} - -impl From<&Arc> for HostLibrary { - fn from(mast_forest: &Arc) -> Self { - Self { - mast_forest: mast_forest.clone(), - package_debug_info: Ok(None), - handlers: vec![], - } - } -} diff --git a/processor/src/host/handlers.rs b/processor/src/host/handlers.rs index 89d0a99a75..24d00403a8 100644 --- a/processor/src/host/handlers.rs +++ b/processor/src/host/handlers.rs @@ -1,74 +1,17 @@ use alloc::{ - boxed::Box, collections::{BTreeMap, btree_map::Entry}, sync::Arc, vec::Vec, }; -use core::{error::Error, fmt, fmt::Debug}; +use core::{fmt, fmt::Debug}; -use miden_core::events::{EventId, EventName, SystemEvent}; - -use crate::{ExecutionError, ProcessorState, advice::AdviceMutation}; - -// EVENT HANDLER TRAIT -// ================================================================================================ - -/// An [`EventHandler`] defines a function that that can be called from the processor which can -/// read the VM state and modify the state of the advice provider. -/// -/// A struct implementing this trait can access its own state, but any output it produces must -/// be stored in the process's advice provider. -pub trait EventHandler: Send + Sync + 'static { - /// Handles the event when triggered. - fn on_event(&self, process: &ProcessorState) -> Result, EventError>; -} - -/// Default implementation for both free functions and closures with signature -/// `fn(&ProcessorState) -> Result<(), HandlerError>` -impl EventHandler for F -where - F: for<'a> Fn(&'a ProcessorState) -> Result, EventError> - + Send - + Sync - + 'static, -{ - fn on_event(&self, process: &ProcessorState) -> Result, EventError> { - self(process) - } -} - -/// A handler which ignores the process state and leaves the `AdviceProvider` unchanged. -pub struct NoopEventHandler; - -impl EventHandler for NoopEventHandler { - fn on_event(&self, _process: &ProcessorState) -> Result, EventError> { - Ok(Vec::new()) - } -} - -// EVENT ERROR -// ================================================================================================ +pub use miden_core::events::{EventError, EventHandler}; +use miden_core::{ + advice::AdviceMutation, + events::{EventContext, EventId, EventName, SystemEvent}, +}; -/// A generic [`Error`] wrapper allowing handlers to return errors to the Host caller. -/// -/// Error handlers can define their own [`Error`] type which can be seamlessly converted -/// into this type since it is a [`Box`]. -/// -/// # Example -/// -/// ```rust, ignore -/// pub struct MyError{ /* ... */ }; -/// -/// fn try_something() -> Result<(), MyError> { /* ... */ } -/// -/// fn my_handler(process: &mut ProcessorState) -> Result<(), HandlerError> { -/// // ... -/// try_something()?; -/// // ... -/// Ok(()) -/// } -/// ``` -pub type EventError = Box; +use crate::ExecutionError; // EVENT HANDLER REGISTRY // ================================================================================================ @@ -81,7 +24,7 @@ pub type EventError = Box; /// impl Host for MyHost { /// fn on_event( /// &mut self, -/// process: &mut ProcessorState, +/// context: &EventContext<'_>, /// event_id: u32, /// ) -> Result<(), EventError> { /// if self @@ -157,10 +100,10 @@ impl EventHandlerRegistry { pub fn handle_event( &self, id: EventId, - process: &ProcessorState, + context: &EventContext<'_>, ) -> Result>, EventError> { if let Some((_event_name, handler)) = self.handlers.get(&id) { - let mutations = handler.on_event(process)?; + let mutations = handler.on_event(context)?; return Ok(Some(mutations)); } diff --git a/processor/src/host/mod.rs b/processor/src/host/mod.rs index 5272f8e985..d8e5b97db5 100644 --- a/processor/src/host/mod.rs +++ b/processor/src/host/mod.rs @@ -3,50 +3,20 @@ use core::future::Future; use miden_core::{ Word, - advice::{AdviceMap, AdviceStack}, - crypto::merkle::InnerNodeInfo, - events::{EventId, EventName}, + advice::AdviceMutation, + events::{EventContext, EventError, EventId, EventName}, }; use miden_debug_types::{Location, SourceFile, SourceSpan}; -use crate::ProcessorState; - pub(super) mod advice; -pub mod debug; - pub mod default; pub mod handlers; -use handlers::EventError; mod mast_forest_store; pub use mast_forest_store::{LoadedMastForest, MastForestStore, MemMastForestStore}; -// ADVICE MAP MUTATIONS -// ================================================================================================ - -/// Any possible way an event can modify the advice provider. -#[derive(Debug, PartialEq, Eq)] -pub enum AdviceMutation { - ExtendStack { stack: AdviceStack }, - ExtendMap { other: AdviceMap }, - ExtendMerkleStore { infos: Vec }, -} - -impl AdviceMutation { - pub fn extend_advice_stack(stack: AdviceStack) -> Self { - Self::ExtendStack { stack } - } - - pub fn extend_map(other: AdviceMap) -> Self { - Self::ExtendMap { other } - } - - pub fn extend_merkle_store(infos: impl IntoIterator) -> Self { - Self::ExtendMerkleStore { infos: Vec::from_iter(infos) } - } -} // HOST TRAIT // ================================================================================================ @@ -108,8 +78,7 @@ pub trait SyncHost: BaseHost { /// - Return errors without event names or IDs - the caller will enrich them via /// [`BaseHost::resolve_event()`] /// - System events (IDs 0-255) are handled by the VM before calling this method - fn on_event(&mut self, process: &ProcessorState<'_>) - -> Result, EventError>; + fn on_event(&mut self, context: &EventContext<'_>) -> Result, EventError>; } /// Defines an async interface by which the VM can interact with the host during execution. @@ -139,7 +108,7 @@ pub trait Host: BaseHost { /// - System events (IDs 0-255) are handled by the VM before calling this method fn on_event( &mut self, - process: &ProcessorState<'_>, + context: &EventContext<'_>, ) -> impl FutureMaybeSend, EventError>>; } @@ -157,9 +126,9 @@ where fn on_event( &mut self, - process: &ProcessorState<'_>, + context: &EventContext<'_>, ) -> impl FutureMaybeSend, EventError>> { - let result = SyncHost::on_event(self, process); + let result = SyncHost::on_event(self, context); async move { result } } } diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 0684d9326f..e3180e1305 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -9,11 +9,7 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; -use alloc::vec::Vec; -use core::{ - fmt::{self, Display, LowerHex}, - ops::ControlFlow, -}; +use core::ops::ControlFlow; use miden_mast_package::debug_info::DebugSourceNodeId; @@ -26,16 +22,12 @@ mod host; mod processor; mod tracer; -use miden_core::{ - deferred::{Digest, Node, PrecompileError}, - mast::ExecutableMastForest, -}; +use miden_core::mast::ExecutableMastForest; use crate::{ advice::{AdviceInputs, AdviceProvider}, continuation_stack::ContinuationStack, errors::{MapExecErr, MapExecErrNoCtx}, - processor::{Processor, SystemInterface}, trace::RowIndex, }; @@ -52,7 +44,7 @@ mod tests; pub use continuation_stack::Continuation; pub use errors::{ - AceError, ExecutionError, HostError, MemoryError, PackageSourceDebugContext, + AceError, ExecutionError, HostError, PackageSourceDebugContext, advice_error_with_package_source_context, event_error_with_package_source_context, procedure_not_found_with_package_source_context, }; @@ -61,11 +53,12 @@ pub use fast::{BreakReason, ExecutionOutput, FastProcessor, ResumeContext}; pub use host::{ BaseHost, FutureMaybeSend, Host, LoadedMastForest, MastForestStore, MemMastForestStore, SyncHost, - debug::{StdoutWriter, format_value, write_interval, write_stack}, default::{DefaultHost, HostLibrary}, }; pub use miden_core::{ - EMPTY_WORD, Felt, ONE, WORD_SIZE, Word, ZERO, crypto, field, mast, + ContextId, EMPTY_WORD, Felt, MemoryAddress, MemoryError, ONE, WORD_SIZE, Word, ZERO, crypto, + events::debug::{StdoutWriter, format_value, write_interval, write_stack}, + field, mast, program::{ InputError, KernelDescriptor, MIN_STACK_DEPTH, Program, ProgramInfo, StackInputs, StackOutputs, @@ -75,22 +68,25 @@ pub use miden_core::{ pub use trace::{TraceBuildInputs, TraceGenerationContext}; pub mod advice { - pub use miden_core::advice::{AdviceInputs, AdviceMap, AdviceStack}; - - pub use super::host::{ - AdviceMutation, - advice::{AdviceError, AdviceProvider, MAX_ADVICE_STACK_SIZE}, + pub use miden_core::advice::{ + AdviceInputs, AdviceMap, AdviceMutation, AdviceStack, MAX_ADVICE_STACK_SIZE, }; + + pub use super::host::advice::{AdviceError, AdviceProvider}; } pub mod event { - pub use miden_core::events::*; - - pub use crate::host::handlers::{ - EventError, EventHandler, EventHandlerRegistry, NoopEventHandler, + pub use miden_core::events::{ + AdviceProviderView, EventContext, EventContextProvider, EventError, EventHandler, EventId, + EventName, ExecutionOptionsView, NoopEventHandler, SystemEvent, debug, }; + + pub use crate::host::handlers::EventHandlerRegistry; } +/// Compatibility alias for the event context exposed to host callbacks. +pub type ProcessorState<'a> = miden_core::events::EventContext<'a>; + pub mod operation { pub use miden_core::operations::*; @@ -145,159 +141,6 @@ pub fn execute_sync( processor.execute_sync(program, host) } -// PROCESSOR STATE -// =============================================================================================== - -/// A view into the current state of the processor. -/// -/// This struct provides read access to the processor's state, including the stack, memory, -/// advice provider, and execution context information. -#[derive(Debug)] -pub struct ProcessorState<'a> { - processor: &'a FastProcessor, -} - -impl<'a> ProcessorState<'a> { - /// Returns a reference to the advice provider. - #[inline(always)] - pub fn advice_provider(&self) -> &AdviceProvider { - self.processor.advice_provider() - } - - /// Returns the execution options. - #[inline(always)] - pub fn execution_options(&self) -> &ExecutionOptions { - self.processor.execution_options() - } - - /// Returns the current clock cycle of a process. - #[inline(always)] - pub fn clock(&self) -> RowIndex { - self.processor.clock() - } - - /// Returns the current execution context ID. - #[inline(always)] - pub fn ctx(&self) -> ContextId { - self.processor.ctx() - } - - /// Returns the value located at the specified position on the stack at the current clock cycle. - /// - /// This method can access elements beyond the top 16 positions by using the overflow table. - #[inline(always)] - pub fn get_stack_item(&self, pos: usize) -> Felt { - self.processor.stack_get_safe(pos) - } - - /// Returns a word starting at the specified element index on the stack. - /// - /// The word is formed by taking 4 consecutive elements starting from the specified index. - /// For example, start_idx=0 creates a word from stack elements 0-3, start_idx=1 creates - /// a word from elements 1-4, etc. - /// - /// Stack element N will be at position 0 of the word, N+1 at position 1, N+2 at position 2, - /// and N+3 at position 3. `word[0]` corresponds to the top of the stack. - /// - /// This method can access elements beyond the top 16 positions by using the overflow table. - /// Creating a word does not change the state of the stack. - #[inline(always)] - pub fn get_stack_word(&self, start_idx: usize) -> Word { - self.processor.stack_get_word_safe(start_idx) - } - - /// Returns stack state at the current clock cycle. This includes the top 16 items of the - /// stack + overflow entries. - #[inline(always)] - pub fn get_stack_state(&self) -> Vec { - self.processor.stack().iter().rev().copied().collect() - } - - /// Returns the element located at the specified context/address, or None if the address hasn't - /// been accessed previously. - #[inline(always)] - pub fn get_mem_value(&self, ctx: ContextId, addr: u32) -> Option { - self.processor.memory().read_element_impl(ctx, addr) - } - - /// Returns the batch of elements starting at the specified context/address. - /// - /// # Errors - /// - If the address is not word aligned. - #[inline(always)] - pub fn get_mem_word(&self, ctx: ContextId, addr: u32) -> Result, MemoryError> { - self.processor.memory().read_word_impl(ctx, addr) - } - - /// Reads (start_addr, end_addr) tuple from the specified elements of the operand stack ( - /// without modifying the state of the stack), and verifies that memory range is valid. - /// - /// The range is half-open `[start, end)`; both `start` and `end` must be `<= u32::MAX`. - pub fn get_mem_addr_range( - &self, - start_idx: usize, - end_idx: usize, - ) -> Result, 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) - } - - /// Returns the entire memory state for the specified execution context at the current clock - /// cycle. - /// - /// The state is returned as a vector of (address, value) tuples, and includes addresses which - /// have been accessed at least once. - #[inline(always)] - pub fn get_mem_state(&self, ctx: ContextId) -> Vec<(MemoryAddress, Felt)> { - self.processor.memory().get_memory_state(ctx) - } - - /// Returns the already-memoized canonical deferred digest for `digest`, if present. - /// - /// This is a read-only lookup: it does not evaluate `digest`, register helper nodes, or mutate - /// deferred state. - #[inline(always)] - pub fn get_canonical_deferred_digest(&self, digest: Digest) -> Option { - self.processor.deferred_state().get_canonical_digest(digest) - } - - /// Returns the already-memoized canonical deferred node for `digest`, if present. - /// - /// This is a read-only lookup and returns only canonical results that were already memoized in - /// deferred state; it never evaluates or mutates deferred state. - #[inline(always)] - pub fn get_canonical_deferred_node(&self, digest: Digest) -> Option<(Digest, &Node)> { - self.processor.deferred_state().get_canonical_node(digest) - } - - /// Returns the already-memoized canonical deferred node for `digest`. - /// - /// This is a read-only lookup and never evaluates or mutates deferred state. - /// - /// # Errors - /// Returns [`PrecompileError::MissingNode`] if no memoized canonical node is available. - #[inline(always)] - pub fn require_canonical_deferred_node( - &self, - digest: Digest, - ) -> Result<(Digest, &Node), PrecompileError> { - self.processor.deferred_state().require_canonical_node(digest) - } -} - // STOPPER // =============================================================================================== @@ -340,107 +183,6 @@ pub trait Stopper { ) -> ControlFlow>; } -// EXECUTION CONTEXT -// ================================================================================================ - -/// Represents the ID of an execution context -#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] -pub struct ContextId(u32); - -impl ContextId { - /// Returns the root context ID - pub fn root() -> Self { - Self(0) - } - - /// Returns true if the context ID represents the root context - pub fn is_root(&self) -> bool { - self.0 == 0 - } -} - -impl From for ContextId { - fn from(value: RowIndex) -> Self { - Self(value.as_u32()) - } -} - -impl From for ContextId { - fn from(value: u32) -> Self { - Self(value) - } -} - -impl From for u32 { - fn from(context_id: ContextId) -> Self { - context_id.0 - } -} - -impl From for u64 { - fn from(context_id: ContextId) -> Self { - context_id.0.into() - } -} - -impl From 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) - } -} - -// MEMORY ADDRESS -// ================================================================================================ - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub struct MemoryAddress(u32); - -impl From for MemoryAddress { - fn from(addr: u32) -> Self { - MemoryAddress(addr) - } -} - -impl From for u32 { - fn from(value: MemoryAddress) -> Self { - value.0 - } -} - -impl Display for MemoryAddress { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.0, f) - } -} - -impl LowerHex for MemoryAddress { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - LowerHex::fmt(&self.0, f) - } -} - -impl core::ops::Add for MemoryAddress { - type Output = Self; - - fn add(self, rhs: MemoryAddress) -> Self::Output { - MemoryAddress(self.0 + rhs.0) - } -} - -impl core::ops::Add for MemoryAddress { - type Output = Self; - - fn add(self, rhs: u32) -> Self::Output { - MemoryAddress(self.0 + rhs) - } -} - // HELPERS // =============================================================================================== diff --git a/processor/src/test_utils/test_host.rs b/processor/src/test_utils/test_host.rs index fae16ab071..b3c327828f 100644 --- a/processor/src/test_utils/test_host.rs +++ b/processor/src/test_utils/test_host.rs @@ -6,8 +6,10 @@ use miden_debug_types::{ }; use crate::{ - BaseHost, LoadedMastForest, MastForestStore, MemMastForestStore, ProcessorState, SyncHost, - Word, advice::AdviceMutation, event::EventError, mast::MastForest, + BaseHost, LoadedMastForest, MastForestStore, MemMastForestStore, MemoryAddress, SyncHost, Word, + advice::AdviceMutation, + event::{EventContext, EventError}, + mast::MastForest, }; /// A snapshot of the processor state for consistency checking between processors. @@ -17,13 +19,13 @@ pub struct ProcessorStateSnapshot { ctx: u32, stack_state: Vec, stack_words: [Word; 4], - mem_state: Vec<(crate::MemoryAddress, Felt)>, + mem_state: Vec<(MemoryAddress, Felt)>, } -impl From<&ProcessorState<'_>> for ProcessorStateSnapshot { - fn from(state: &ProcessorState) -> Self { +impl From<&EventContext<'_>> for ProcessorStateSnapshot { + fn from(state: &EventContext<'_>) -> Self { ProcessorStateSnapshot { - clk: state.clock().into(), + clk: state.clock(), ctx: state.ctx().into(), stack_state: state.get_stack_state(), stack_words: [ @@ -44,14 +46,14 @@ impl ProcessorStateSnapshot { /// event ID at the top of the stack. The checkpoint snapshot skips that synthetic stack item to /// match the state after the trailing `drop`, and to preserve the old trace-decorator test /// shape. - fn from_emit_checkpoint(state: &ProcessorState) -> Self { + fn from_emit_checkpoint(state: &EventContext<'_>) -> Self { let mut stack_state = state.get_stack_state(); if !stack_state.is_empty() { stack_state.remove(0); } ProcessorStateSnapshot { - clk: state.clock().into(), + clk: state.clock(), ctx: state.ctx().into(), stack_state, stack_words: [ @@ -139,13 +141,13 @@ where self.store.get(node_digest) } - fn on_event(&mut self, process: &ProcessorState) -> Result, EventError> { - let event_id: u32 = process.get_stack_item(0).as_canonical_u64().try_into().unwrap(); + fn on_event(&mut self, context: &EventContext<'_>) -> Result, EventError> { + let event_id: u32 = context.get_stack_item(0).as_canonical_u64().try_into().unwrap(); self.event_handler.push(event_id); self.snapshots .entry(event_id) .or_default() - .push(ProcessorStateSnapshot::from_emit_checkpoint(process)); + .push(ProcessorStateSnapshot::from_emit_checkpoint(context)); Ok(Vec::new()) } } diff --git a/processor/src/tests/mod.rs b/processor/src/tests/mod.rs index 43fde3c6d3..58549e0c20 100644 --- a/processor/src/tests/mod.rs +++ b/processor/src/tests/mod.rs @@ -14,10 +14,10 @@ use miden_utils_testing::crypto::{init_merkle_leaves, init_merkle_store}; /// Tests in this file make sure that diagnostics presented to the user are as expected. use crate::{ - BaseHost, DefaultHost, FastProcessor, KernelDescriptor, LoadedMastForest, ONE, ProcessorState, - Program, StackInputs, SyncHost, Word, ZERO, + BaseHost, DefaultHost, FastProcessor, KernelDescriptor, LoadedMastForest, ONE, Program, + StackInputs, SyncHost, Word, ZERO, advice::{AdviceInputs, AdviceMap, AdviceMutation}, - event::{EventError, EventHandler, EventName}, + event::{EventContext, EventError, EventHandler, EventName}, operation::Operation, }; @@ -41,7 +41,7 @@ struct DummyHostEventError; struct AlwaysFailEventHandler; impl EventHandler for AlwaysFailEventHandler { - fn on_event(&self, _process: &ProcessorState) -> Result, EventError> { + fn on_event(&self, _context: &EventContext<'_>) -> Result, EventError> { Err(DummyHostEventError.into()) } } @@ -49,7 +49,7 @@ impl EventHandler for AlwaysFailEventHandler { struct DuplicateMapMutationHandler; impl EventHandler for DuplicateMapMutationHandler { - fn on_event(&self, _process: &ProcessorState) -> Result, EventError> { + fn on_event(&self, _context: &EventContext<'_>) -> Result, EventError> { Ok(vec![AdviceMutation::extend_map(AdviceMap::from_iter([( Word::default(), vec![ONE], @@ -112,7 +112,7 @@ impl SyncHost for MalformedMastForestHost { Some(LoadedMastForest::new(self.mast_forest.clone())) } - fn on_event(&mut self, _process: &ProcessorState) -> Result, EventError> { + fn on_event(&mut self, _context: &EventContext<'_>) -> Result, EventError> { Ok(Vec::new()) } } diff --git a/processor/src/trace/chiplets/memory/segment.rs b/processor/src/trace/chiplets/memory/segment.rs index 84c7d63b7b..d692972161 100644 --- a/processor/src/trace/chiplets/memory/segment.rs +++ b/processor/src/trace/chiplets/memory/segment.rs @@ -79,10 +79,10 @@ impl MemorySegmentTrace { Ok(i) => { let word_addr = addr_trace[i].word(); result.extend([ - (MemoryAddress(addr), word_addr[0]), - (MemoryAddress(addr + 1), word_addr[1]), - (MemoryAddress(addr + 2), word_addr[2]), - (MemoryAddress(addr + 3), word_addr[3]), + (MemoryAddress::new(addr), word_addr[0]), + (MemoryAddress::new(addr + 1), word_addr[1]), + (MemoryAddress::new(addr + 2), word_addr[2]), + (MemoryAddress::new(addr + 3), word_addr[3]), ]); }, Err(i) => { @@ -92,10 +92,10 @@ impl MemorySegmentTrace { if i > 0 { let word_addr = addr_trace[i - 1].word(); result.extend([ - (MemoryAddress(addr), word_addr[0]), - (MemoryAddress(addr + 1), word_addr[1]), - (MemoryAddress(addr + 2), word_addr[2]), - (MemoryAddress(addr + 3), word_addr[3]), + (MemoryAddress::new(addr), word_addr[0]), + (MemoryAddress::new(addr + 1), word_addr[1]), + (MemoryAddress::new(addr + 2), word_addr[2]), + (MemoryAddress::new(addr + 3), word_addr[3]), ]); } }, diff --git a/processor/src/trace/chiplets/memory/tests.rs b/processor/src/trace/chiplets/memory/tests.rs index ad1444e61f..1c80ec77bb 100644 --- a/processor/src/trace/chiplets/memory/tests.rs +++ b/processor/src/trace/chiplets/memory/tests.rs @@ -454,10 +454,10 @@ fn mem_get_state_at() { assert_eq!( mem.get_state_at(ContextId::root(), clk), vec![ - (MemoryAddress(addr_start), word1234[0]), - (MemoryAddress(addr_start + 1), word1234[1]), - (MemoryAddress(addr_start + 2), word1234[2]), - (MemoryAddress(addr_start + 3), word1234[3]) + (MemoryAddress::new(addr_start), word1234[0]), + (MemoryAddress::new(addr_start + 1), word1234[1]), + (MemoryAddress::new(addr_start + 2), word1234[2]), + (MemoryAddress::new(addr_start + 3), word1234[3]) ] ); assert_eq!(mem.get_state_at(3.into(), clk), vec![]); @@ -467,10 +467,10 @@ fn mem_get_state_at() { assert_eq!( mem.get_state_at(ContextId::root(), clk), vec![ - (MemoryAddress(addr_start), word4567[0]), - (MemoryAddress(addr_start + 1), word4567[1]), - (MemoryAddress(addr_start + 2), word4567[2]), - (MemoryAddress(addr_start + 3), word4567[3]) + (MemoryAddress::new(addr_start), word4567[0]), + (MemoryAddress::new(addr_start + 1), word4567[1]), + (MemoryAddress::new(addr_start + 2), word4567[2]), + (MemoryAddress::new(addr_start + 3), word4567[3]) ] ); assert_eq!(mem.get_state_at(3.into(), clk), vec![]); diff --git a/processor/tests/async_compat.rs b/processor/tests/async_compat.rs index b40f3b6d67..9f00d11c6d 100644 --- a/processor/tests/async_compat.rs +++ b/processor/tests/async_compat.rs @@ -4,9 +4,9 @@ use miden_assembly::Assembler; use miden_debug_types::{Location, SourceFile, SourceSpan}; use miden_processor::{ BaseHost, DefaultHost, ExecutionOptions, FastProcessor, Felt, FutureMaybeSend, Host, - LoadedMastForest, ProcessorState, StackInputs, Word, + LoadedMastForest, StackInputs, Word, advice::{AdviceInputs, AdviceMutation}, - event::{EventError, EventName}, + event::{EventContext, EventError, EventName}, }; struct YieldingAsyncHost { @@ -38,7 +38,7 @@ impl Host for YieldingAsyncHost { fn on_event( &mut self, - _process: &ProcessorState<'_>, + _context: &EventContext<'_>, ) -> impl FutureMaybeSend, EventError>> { self.event_calls += 1; async { From 05f4fd8dbe3e0f2c2446bed2b391a6463ae1e973 Mon Sep 17 00:00:00 2001 From: Adrian Hamelink Date: Wed, 29 Jul 2026 14:40:32 +0200 Subject: [PATCH 2/3] deprecate processor event handler paths --- core/src/events/handlers.rs | 2 + .../tests/integration/operations/sys_ops.rs | 8 +--- processor/src/errors.rs | 12 +++--- processor/src/fast/basic_block/mod.rs | 3 +- processor/src/fast/tests/mod.rs | 3 +- processor/src/host/default.rs | 4 +- processor/src/host/handlers.rs | 3 +- processor/src/lib.rs | 12 ++++-- processor/src/test_utils/test_host.rs | 9 ++-- processor/src/tests/mod.rs | 2 +- .../parallel/core_trace_fragment/tests.rs | 3 +- processor/tests/async_compat.rs | 2 +- processor/tests/event_handler_compat.rs | 41 +++++++++++++++++++ prover/tests/async_compat.rs | 7 ++-- 14 files changed, 76 insertions(+), 35 deletions(-) create mode 100644 processor/tests/event_handler_compat.rs diff --git a/core/src/events/handlers.rs b/core/src/events/handlers.rs index cc9516bf08..0e767e964d 100644 --- a/core/src/events/handlers.rs +++ b/core/src/events/handlers.rs @@ -162,11 +162,13 @@ impl<'a> EventContext<'a> { } /// Returns a compatibility view of the advice provider. + #[deprecated(note = "use EventContext advice accessors directly")] pub fn advice_provider(&self) -> AdviceProviderView<'a> { AdviceProviderView { provider: self.provider } } /// Returns a compatibility view of execution options used by event handlers. + #[deprecated(note = "use EventContext::max_hash_len_bytes")] pub fn execution_options(&self) -> ExecutionOptionsView<'a> { ExecutionOptionsView { provider: self.provider } } diff --git a/miden-vm/tests/integration/operations/sys_ops.rs b/miden-vm/tests/integration/operations/sys_ops.rs index 36ec6f31b8..729761f60b 100644 --- a/miden-vm/tests/integration/operations/sys_ops.rs +++ b/miden-vm/tests/integration/operations/sys_ops.rs @@ -1,9 +1,5 @@ -use miden_processor::{ - ExecutionError, ZERO, - event::{EventName, NoopEventHandler}, - mast, - operation::OperationError, -}; +use miden_core::events::{EventName, NoopEventHandler}; +use miden_processor::{ExecutionError, ZERO, mast, operation::OperationError}; use miden_utils_testing::{build_op_test, expect_exec_error_matches}; // SYSTEM OPS ASSERTIONS - MANUAL TESTS diff --git a/processor/src/errors.rs b/processor/src/errors.rs index 1564dd0adf..1bc7a835e3 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -3,7 +3,11 @@ use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec}; -use miden_core::{deferred::PrecompileError, program::MIN_STACK_DEPTH}; +use miden_core::{ + deferred::PrecompileError, + events::{EventError, EventId, EventName}, + program::MIN_STACK_DEPTH, +}; use miden_debug_types::{Location, SourceFile, SourceSpan}; use miden_mast_package::{ PackageDebugInfoError, @@ -12,11 +16,7 @@ use miden_mast_package::{ use miden_utils_diagnostics::{Diagnostic, miette}; use crate::{ - BaseHost, Felt, MemoryError, Word, - advice::AdviceError, - event::{EventError, EventId, EventName}, - fast::SystemEventError, - utils::to_hex, + BaseHost, Felt, MemoryError, Word, advice::AdviceError, fast::SystemEventError, utils::to_hex, }; // EXECUTION ERROR diff --git a/processor/src/fast/basic_block/mod.rs b/processor/src/fast/basic_block/mod.rs index c6a19eae69..522fb59362 100644 --- a/processor/src/fast/basic_block/mod.rs +++ b/processor/src/fast/basic_block/mod.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use core::ops::ControlFlow; -use miden_core::events::{EventId, SystemEvent}; +use miden_core::events::{EventError, EventId, SystemEvent}; use miden_mast_package::debug_info::{DebugSourceNodeId, PackageDebugInfo}; use crate::{ @@ -12,7 +12,6 @@ use crate::{ advice_error_with_package_source_context, event_error_with_context, event_error_with_package_source_context, }, - event::EventError, fast::{BreakReason, FastProcessor}, }; diff --git a/processor/src/fast/tests/mod.rs b/processor/src/fast/tests/mod.rs index 2bace0f542..0e033da617 100644 --- a/processor/src/fast/tests/mod.rs +++ b/processor/src/fast/tests/mod.rs @@ -8,7 +8,7 @@ use miden_assembly::{ }; use miden_core::{ ONE, Word, - events::SystemEvent, + events::{EventContext, EventError, SystemEvent}, mast::{ BasicBlockNodeBuilder, CallNodeBuilder, ExternalNodeBuilder, JoinNodeBuilder, MastNodeExt, MastNodeId, SplitNodeBuilder, @@ -34,7 +34,6 @@ use super::*; use crate::{ AdviceInputs, BaseHost, DefaultHost, LoadedMastForest, SyncHost, advice::AdviceMutation, - event::{EventContext, EventError}, operation::OperationError, processor::{StackInterface, SystemInterface}, }; diff --git a/processor/src/host/default.rs b/processor/src/host/default.rs index 1b50de3661..fda4d9177c 100644 --- a/processor/src/host/default.rs +++ b/processor/src/host/default.rs @@ -2,12 +2,12 @@ use alloc::{sync::Arc, vec::Vec}; use miden_core::{ Word, - events::{EventContext, EventId, EventName}, + events::{EventContext, EventError, EventHandler, EventId, EventName}, }; use miden_debug_types::{DefaultSourceManager, Location, SourceFile, SourceManager, SourceSpan}; pub use miden_mast_package::HostLibrary; -use super::handlers::{EventError, EventHandler, EventHandlerRegistry}; +use super::handlers::EventHandlerRegistry; use crate::{ BaseHost, ExecutionError, LoadedMastForest, MastForestStore, MemMastForestStore, SyncHost, advice::AdviceMutation, diff --git a/processor/src/host/handlers.rs b/processor/src/host/handlers.rs index 24d00403a8..2445fbc5c7 100644 --- a/processor/src/host/handlers.rs +++ b/processor/src/host/handlers.rs @@ -5,10 +5,9 @@ use alloc::{ }; use core::{fmt, fmt::Debug}; -pub use miden_core::events::{EventError, EventHandler}; use miden_core::{ advice::AdviceMutation, - events::{EventContext, EventId, EventName, SystemEvent}, + events::{EventContext, EventError, EventHandler, EventId, EventName, SystemEvent}, }; use crate::ExecutionError; diff --git a/processor/src/lib.rs b/processor/src/lib.rs index e3180e1305..e49742ec59 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -55,9 +55,10 @@ pub use host::{ SyncHost, default::{DefaultHost, HostLibrary}, }; +#[deprecated(note = "use miden_core::events::debug")] +pub use miden_core::events::debug::{StdoutWriter, format_value, write_interval, write_stack}; pub use miden_core::{ ContextId, EMPTY_WORD, Felt, MemoryAddress, MemoryError, ONE, WORD_SIZE, Word, ZERO, crypto, - events::debug::{StdoutWriter, format_value, write_interval, write_stack}, field, mast, program::{ InputError, KernelDescriptor, MIN_STACK_DEPTH, Program, ProgramInfo, StackInputs, @@ -77,14 +78,19 @@ pub mod advice { pub mod event { pub use miden_core::events::{ - AdviceProviderView, EventContext, EventContextProvider, EventError, EventHandler, EventId, - EventName, ExecutionOptionsView, NoopEventHandler, SystemEvent, debug, + AdviceProviderView, EventContext, EventContextProvider, EventId, EventName, + ExecutionOptionsView, SystemEvent, debug, }; + #[deprecated( + note = "import EventError, EventHandler, and NoopEventHandler from miden_core::events" + )] + pub use miden_core::events::{EventError, EventHandler, NoopEventHandler}; pub use crate::host::handlers::EventHandlerRegistry; } /// Compatibility alias for the event context exposed to host callbacks. +#[deprecated(note = "use miden_core::events::EventContext")] pub type ProcessorState<'a> = miden_core::events::EventContext<'a>; pub mod operation { diff --git a/processor/src/test_utils/test_host.rs b/processor/src/test_utils/test_host.rs index b3c327828f..ddec6924f1 100644 --- a/processor/src/test_utils/test_host.rs +++ b/processor/src/test_utils/test_host.rs @@ -1,15 +1,16 @@ use alloc::{collections::BTreeMap, sync::Arc, vec::Vec}; -use miden_core::Felt; +use miden_core::{ + Felt, + events::{EventContext, EventError}, +}; use miden_debug_types::{ DefaultSourceManager, Location, SourceFile, SourceManager, SourceManagerSync, SourceSpan, }; use crate::{ BaseHost, LoadedMastForest, MastForestStore, MemMastForestStore, MemoryAddress, SyncHost, Word, - advice::AdviceMutation, - event::{EventContext, EventError}, - mast::MastForest, + advice::AdviceMutation, mast::MastForest, }; /// A snapshot of the processor state for consistency checking between processors. diff --git a/processor/src/tests/mod.rs b/processor/src/tests/mod.rs index 58549e0c20..3bc975176f 100644 --- a/processor/src/tests/mod.rs +++ b/processor/src/tests/mod.rs @@ -7,6 +7,7 @@ use miden_assembly::{ }; use miden_core::{ crypto::merkle::{MerkleStore, MerkleTree}, + events::{EventContext, EventError, EventHandler, EventName}, mast::{BasicBlockNodeBuilder, MastForest, error_code_from_msg}, }; use miden_debug_types::{Location, SourceFile, SourceManager, SourceSpan}; @@ -17,7 +18,6 @@ use crate::{ BaseHost, DefaultHost, FastProcessor, KernelDescriptor, LoadedMastForest, ONE, Program, StackInputs, SyncHost, Word, ZERO, advice::{AdviceInputs, AdviceMap, AdviceMutation}, - event::{EventContext, EventError, EventHandler, EventName}, operation::Operation, }; diff --git a/processor/src/trace/parallel/core_trace_fragment/tests.rs b/processor/src/trace/parallel/core_trace_fragment/tests.rs index 633edd2158..c2c36e7a01 100644 --- a/processor/src/trace/parallel/core_trace_fragment/tests.rs +++ b/processor/src/trace/parallel/core_trace_fragment/tests.rs @@ -24,7 +24,7 @@ const OP_BATCH_FLAGS_RANGE: core::ops::Range = 19..19 + NUM_OP_BATCH_FLAG const OP_BITS_EXTRA_COLS_RANGE: core::ops::Range = 22..24; use miden_core::{ EMPTY_WORD, Felt, ONE, WORD_SIZE, Word, ZERO, - events::EventName, + events::{EventName, NoopEventHandler}, mast::{ BasicBlockNodeBuilder, CallNodeBuilder, DynNodeBuilder, JoinNodeBuilder, LoopNodeBuilder, MastForest, MastNodeExt, OP_BATCH_SIZE, SplitNodeBuilder, @@ -36,7 +36,6 @@ use miden_utils_testing::rand::rand_value; use crate::{ AdviceInputs, DefaultHost, ExecutionOptions, FastProcessor, - event::NoopEventHandler, trace::{ExecutionTrace, build_trace}, }; diff --git a/processor/tests/async_compat.rs b/processor/tests/async_compat.rs index 9f00d11c6d..03bc284fb4 100644 --- a/processor/tests/async_compat.rs +++ b/processor/tests/async_compat.rs @@ -1,12 +1,12 @@ use std::sync::Arc; use miden_assembly::Assembler; +use miden_core::events::{EventContext, EventError, EventName}; use miden_debug_types::{Location, SourceFile, SourceSpan}; use miden_processor::{ BaseHost, DefaultHost, ExecutionOptions, FastProcessor, Felt, FutureMaybeSend, Host, LoadedMastForest, StackInputs, Word, advice::{AdviceInputs, AdviceMutation}, - event::{EventContext, EventError, EventName}, }; struct YieldingAsyncHost { diff --git a/processor/tests/event_handler_compat.rs b/processor/tests/event_handler_compat.rs new file mode 100644 index 0000000000..72906e3f33 --- /dev/null +++ b/processor/tests/event_handler_compat.rs @@ -0,0 +1,41 @@ +#![allow(deprecated, clippy::unnecessary_wraps)] + +use std::sync::Arc; + +use miden_processor::{ + ProcessorState, + advice::AdviceMutation, + event::{EventError, EventHandler}, +}; + +struct ExistingHandler; + +impl EventHandler for ExistingHandler { + fn on_event(&self, process: &ProcessorState<'_>) -> Result, EventError> { + existing_handler_body(process) + } +} + +fn existing_handler(process: &ProcessorState<'_>) -> Result, EventError> { + existing_handler_body(process) +} + +fn existing_handler_body(process: &ProcessorState<'_>) -> Result, EventError> { + let key = process.get_stack_word(0); + let context = process.ctx(); + let _stack = process.get_stack_state(); + let _memory = process.get_mem_value(context, 0); + let _advice_stack = process.advice_provider().stack(); + let _mapped_values = process.advice_provider().get_mapped_values(&key); + let _max_hash_len = process.execution_options().max_hash_len_bytes(); + + Ok(Vec::new()) +} + +#[test] +fn deprecated_processor_event_interface_accepts_existing_handlers() { + let custom: Arc = Arc::new(ExistingHandler); + let function: Arc = Arc::new(existing_handler); + + drop((custom, function)); +} diff --git a/prover/tests/async_compat.rs b/prover/tests/async_compat.rs index 3246026491..1266a08ad7 100644 --- a/prover/tests/async_compat.rs +++ b/prover/tests/async_compat.rs @@ -1,12 +1,11 @@ use std::sync::Arc; use miden_assembly::Assembler; +use miden_core::events::{EventContext, EventError, EventName}; use miden_debug_types::{Location, SourceFile, SourceSpan}; use miden_processor::{ - BaseHost, DefaultHost, ExecutionOptions, Felt, FutureMaybeSend, Host, LoadedMastForest, - ProcessorState, Word, + BaseHost, DefaultHost, ExecutionOptions, Felt, FutureMaybeSend, Host, LoadedMastForest, Word, advice::AdviceMutation, - event::{EventError, EventName}, }; use miden_prover::{AdviceInputs, ProvingOptions, StackInputs, prove, prove_sync}; @@ -39,7 +38,7 @@ impl Host for YieldingAsyncHost { fn on_event( &mut self, - _process: &ProcessorState<'_>, + _context: &EventContext<'_>, ) -> impl FutureMaybeSend, EventError>> { self.event_calls += 1; async { From a0425d389335d6fd019ea238e6bcc9c54ddd1a60 Mon Sep 17 00:00:00 2001 From: Adrian Hamelink Date: Wed, 29 Jul 2026 18:53:17 +0200 Subject: [PATCH 3/3] refactor: refine event handler context interface --- air/src/trace/mod.rs | 5 +- core/src/events/compatibility.rs | 171 ++++++++++++++++++ core/src/events/handlers.rs | 170 +++++++---------- core/src/events/mod.rs | 4 +- core/src/execution.rs | 6 + .../src/execution/options.rs | 4 +- core/src/lib.rs | 4 +- crates/lib/core/src/handlers/aead_decrypt.rs | 17 +- crates/lib/core/src/handlers/debug.rs | 100 +++++----- crates/lib/core/src/handlers/falcon_div.rs | 6 +- crates/lib/core/src/handlers/mod.rs | 3 +- .../src/handlers/precompiles/keccak256.rs | 14 +- .../handlers/precompiles/uint_field_inv.rs | 6 +- crates/lib/core/src/handlers/readonly.rs | 2 +- crates/lib/core/src/handlers/smt_peek.rs | 18 +- crates/lib/core/src/handlers/sorted_array.rs | 43 ++--- crates/lib/core/src/handlers/u128_div.rs | 10 +- crates/lib/core/src/handlers/u256_div.rs | 10 +- crates/lib/core/src/handlers/u64_div.rs | 10 +- .../core/tests/collections/sorted_array.rs | 6 +- crates/lib/core/tests/crypto/aead.rs | 4 +- crates/lib/core/tests/crypto/falcon.rs | 24 +-- crates/lib/core/tests/debug.rs | 9 +- processor/src/fast/basic_block/mod.rs | 8 +- processor/src/fast/mod.rs | 40 ++-- ...s__advice_provider__event_checkpoints.snap | 29 --- processor/src/host/default.rs | 2 +- processor/src/host/mod.rs | 4 +- processor/src/lib.rs | 10 +- processor/src/test_utils/test_host.rs | 29 ++- processor/tests/async_compat.rs | 5 +- processor/tests/event_handler_compat.rs | 1 - 32 files changed, 431 insertions(+), 343 deletions(-) create mode 100644 core/src/events/compatibility.rs rename processor/src/execution_options.rs => core/src/execution/options.rs (99%) diff --git a/air/src/trace/mod.rs b/air/src/trace/mod.rs index 7fb4448eef..7ea04b4f3d 100644 --- a/air/src/trace/mod.rs +++ b/air/src/trace/mod.rs @@ -6,12 +6,9 @@ pub use rows::{RowIndex, RowIndexError}; mod main_trace; pub use main_trace::{MainTrace, MainTraceRow}; - // CONSTANTS // ================================================================================================ - -/// The minimum length of the execution trace. This is the minimum required to support range checks. -pub const MIN_TRACE_LEN: usize = 64; +pub use miden_core::execution::MIN_TRACE_LEN; // MAIN TRACE LAYOUT // ------------------------------------------------------------------------------------------------ diff --git a/core/src/events/compatibility.rs b/core/src/events/compatibility.rs new file mode 100644 index 0000000000..64ed5a1f36 --- /dev/null +++ b/core/src/events/compatibility.rs @@ -0,0 +1,171 @@ +//! Migration guide and temporary compatibility interface for event handlers. +//! +//! Event-handler interfaces moved from `miden-processor` to `miden-core`. The deprecated methods +//! in this module preserve common existing handler implementations during the transition; they do +//! not reproduce the complete former `ProcessorState` interface. +//! +//! # Imports +//! +//! Existing handlers can migrate from: +//! +//! ```rust,ignore +//! use miden_processor::{ +//! ProcessorState, +//! advice::AdviceMutation, +//! event::{EventError, EventHandler}, +//! }; +//! ``` +//! +//! to: +//! +//! ```rust,ignore +//! use miden_core::{ +//! advice::AdviceMutation, +//! events::{EventContext, EventError, EventHandler}, +//! }; +//! ``` +//! +//! # Accessor changes +//! +//! | Previous accessor | Replacement | +//! | --- | --- | +//! | `get_stack_item(position)` | `stack_item(position)` | +//! | `get_stack_word(start)` | `stack_word(start)` | +//! | `get_stack_state()` | `stack_snapshot()` | +//! | `get_mem_value(context, address)` | `memory_value(address)` | +//! | `get_mem_word(context, address)` | `memory_word(address)` | +//! | `get_mem_state(context)` | `memory_snapshot()` | +//! | `get_mem_addr_range(start, end)` | `memory_range_from_stack(start, end)` | +//! | `advice_provider().stack()` | `advice_stack_snapshot()` | +//! | `advice_provider().map()` | `advice_map()` | +//! | `advice_provider().get_mapped_values(key)` | `advice_map_entry(key)` | +//! | `advice_provider().get_tree_node(...)` | `advice_tree_node(...)` | +//! | `get_stack_item(0)` for the event identity | `event_id()` | +//! +//! New memory accessors always read the active execution context, so handlers no longer obtain or +//! pass a `ContextId`. The compatibility memory methods retain their old parameters but ignore the +//! supplied context and read active-context memory. +//! +//! `stack_snapshot()`, `memory_snapshot()`, and `advice_stack_snapshot()` allocate owned snapshots. +//! +//! # Intentional differences +//! +//! - `clock()` returns `u32` rather than `miden_air::trace::RowIndex`. +//! - The complete concrete `AdviceProvider` interface is not exposed. +//! - Optional deferred-state lookup methods are not preserved. +//! - Execution-options access is reserved for Miden's built-in precompile handlers and hidden from +//! the public handler documentation. +//! +//! # Example +//! +//! Before: +//! +//! ```rust,ignore +//! fn handle(process: &ProcessorState<'_>) -> Result, EventError> { +//! let event = EventId::from_felt(process.get_stack_item(0)); +//! let context = process.ctx(); +//! let value = process.get_mem_value(context, 0); +//! let advice = process.advice_provider().get_mapped_values(&process.get_stack_word(1)); +//! // ... +//! } +//! ``` +//! +//! After: +//! +//! ```rust,ignore +//! fn handle(context: &EventContext<'_>) -> Result, EventError> { +//! let event = context.event_id(); +//! let value = context.memory_value(0); +//! let advice = context.advice_map_entry(&context.stack_word(1)); +//! // ... +//! } +//! ``` + +use alloc::vec::Vec; + +use super::{EventContext, EventContextProvider, EventError}; +use crate::{ContextId, Felt, MemoryAddress, MemoryError, Word, advice::AdviceMap}; + +impl<'a> EventContext<'a> { + #[deprecated(note = "use EventContext::stack_item")] + pub fn get_stack_item(&self, position: usize) -> Felt { + self.stack_item(position) + } + + #[deprecated(note = "use EventContext::stack_word")] + pub fn get_stack_word(&self, start: usize) -> Word { + self.stack_word(start) + } + + #[deprecated(note = "use EventContext::stack_snapshot; it returns an allocated snapshot")] + pub fn get_stack_state(&self) -> Vec { + self.stack_snapshot() + } + + #[deprecated(note = "new handlers do not need an execution context identifier")] + pub fn ctx(&self) -> ContextId { + self.compatibility_context_id + } + + #[deprecated(note = "use EventContext::memory_value; it reads active-context memory")] + pub fn get_mem_value(&self, _context: ContextId, address: u32) -> Option { + self.memory_value(address) + } + + #[deprecated(note = "use EventContext::memory_word; it reads active-context memory")] + pub fn get_mem_word( + &self, + _context: ContextId, + address: u32, + ) -> Result, MemoryError> { + self.memory_word(address) + } + + #[deprecated( + note = "use EventContext::memory_snapshot; it returns an allocated active-context snapshot" + )] + pub fn get_mem_state(&self, _context: ContextId) -> Vec<(MemoryAddress, Felt)> { + self.memory_snapshot() + } + + #[deprecated(note = "use EventContext::memory_range_from_stack")] + pub fn get_mem_addr_range( + &self, + start_position: usize, + end_position: usize, + ) -> Result, MemoryError> { + self.memory_range_from_stack(start_position, end_position) + } + + #[allow(deprecated)] + #[deprecated(note = "use EventContext advice accessors directly")] + pub fn advice_provider(&self) -> AdviceProviderView<'a> { + AdviceProviderView { provider: self.provider } + } +} + +/// Temporary read-only compatibility view for handlers that accessed the advice provider directly. +#[deprecated(note = "use EventContext advice accessors directly")] +pub struct AdviceProviderView<'a> { + provider: &'a dyn EventContextProvider, +} + +#[allow(deprecated)] +impl<'a> AdviceProviderView<'a> { + /// Returns an allocated snapshot of the advice stack. + pub fn stack(&self) -> Vec { + self.provider.advice_stack_snapshot() + } + + pub fn map(&self) -> &'a AdviceMap { + self.provider.advice_map() + } + + pub fn get_mapped_values(&self, key: &Word) -> Option<&'a [Felt]> { + self.provider.advice_map_entry(key) + } + + pub fn get_tree_node(&self, root: Word, depth: Felt, index: Felt) -> Result { + self.provider.advice_tree_node(root, depth, index) + } +} diff --git a/core/src/events/handlers.rs b/core/src/events/handlers.rs index 0e767e964d..8cc0f36d94 100644 --- a/core/src/events/handlers.rs +++ b/core/src/events/handlers.rs @@ -1,11 +1,11 @@ use alloc::{boxed::Box, vec::Vec}; use core::{error::Error, fmt}; +use super::EventId; use crate::{ - Felt, Word, + ContextId, ExecutionOptions, Felt, MemoryAddress, MemoryError, Word, advice::{AdviceMap, AdviceMutation}, deferred::{Digest, Node, PrecompileError}, - execution::{ContextId, MemoryAddress, MemoryError}, }; /// A generic error returned by an [`EventHandler`]. @@ -15,37 +15,32 @@ pub type EventError = Box; /// /// This interface is intended for execution-engine adapters. Event handlers should use /// [`EventContext`] instead of depending on a concrete adapter. -pub trait EventContextProvider { - fn get_stack_item(&self, position: usize) -> Felt; +pub trait EventContextProvider: Sync { + fn stack_item(&self, position: usize) -> Felt; - fn get_stack_word(&self, start: usize) -> Word; + fn stack_word(&self, start: usize) -> Word; - fn get_stack_state(&self) -> Vec; + fn stack_snapshot(&self) -> Vec; fn clock(&self) -> u32; - fn context_id(&self) -> ContextId; + fn memory_value(&self, address: u32) -> Option; - fn get_mem_value(&self, context: ContextId, address: u32) -> Option; + fn memory_word(&self, address: u32) -> Result, MemoryError>; - fn get_mem_word(&self, context: ContextId, address: u32) -> Result, MemoryError>; + fn memory_snapshot(&self) -> Vec<(MemoryAddress, Felt)>; - fn get_mem_state(&self, context: ContextId) -> Vec<(MemoryAddress, Felt)>; - - fn advice_stack(&self) -> Vec; + fn advice_stack_snapshot(&self) -> Vec; fn advice_map(&self) -> &AdviceMap; - fn get_advice_map_entry(&self, key: &Word) -> Option<&[Felt]>; + fn advice_map_entry(&self, key: &Word) -> Option<&[Felt]>; - fn get_advice_tree_node( - &self, - root: Word, - depth: Felt, - index: Felt, - ) -> Result; + fn advice_tree_node(&self, root: Word, depth: Felt, index: Felt) -> Result; - fn max_hash_len_bytes(&self) -> usize; + /// Returns the processor execution options used by built-in precompile handlers. + #[doc(hidden)] + fn execution_options(&self) -> &ExecutionOptions; fn require_canonical_deferred_node( &self, @@ -56,9 +51,13 @@ pub trait EventContextProvider { /// A read-only view of execution state exposed to an [`EventHandler`]. /// /// The context exposes capabilities required by handlers without revealing the execution engine's -/// concrete processor state. +/// concrete processor state. Event handlers are trusted host code: they can inspect operand-stack, +/// memory, advice, and deferred-state data, including private witness values. pub struct EventContext<'a> { - provider: &'a dyn EventContextProvider, + pub(super) provider: &'a dyn EventContextProvider, + // Retained only for the deprecated ProcessorState compatibility methods. New handlers always + // access memory in the active execution context and do not need its identifier. + pub(super) compatibility_context_id: ContextId, } impl fmt::Debug for EventContext<'_> { @@ -68,53 +67,62 @@ impl fmt::Debug for EventContext<'_> { } impl<'a> EventContext<'a> { - pub fn new(provider: &'a dyn EventContextProvider) -> Self { - Self { provider } + /// Creates an event context backed by an execution-engine adapter. + pub fn new(provider: &'a dyn EventContextProvider, context_id: ContextId) -> Self { + Self { + provider, + compatibility_context_id: context_id, + } } - pub fn get_stack_item(&self, position: usize) -> Felt { - self.provider.get_stack_item(position) + /// Returns the identifier of the emitted event. + pub fn event_id(&self) -> EventId { + EventId::from_felt(self.stack_item(0)) } - pub fn get_stack_word(&self, start: usize) -> Word { - self.provider.get_stack_word(start) + /// Returns the value at `position` on the operand stack. + pub fn stack_item(&self, position: usize) -> Felt { + self.provider.stack_item(position) } - pub fn get_stack_state(&self) -> Vec { - self.provider.get_stack_state() + /// Returns the word starting at `start` on the operand stack. + pub fn stack_word(&self, start: usize) -> Word { + self.provider.stack_word(start) } - pub fn clock(&self) -> u32 { - self.provider.clock() + /// Returns an allocated snapshot of the complete operand stack. + pub fn stack_snapshot(&self) -> Vec { + self.provider.stack_snapshot() } - pub fn ctx(&self) -> ContextId { - self.provider.context_id() + /// Returns the current clock cycle. + pub fn clock(&self) -> u32 { + self.provider.clock() } - pub fn get_mem_value(&self, context: ContextId, address: u32) -> Option { - self.provider.get_mem_value(context, address) + /// Returns the value at `address` in the active execution context. + pub fn memory_value(&self, address: u32) -> Option { + self.provider.memory_value(address) } - pub fn get_mem_word( - &self, - context: ContextId, - address: u32, - ) -> Result, MemoryError> { - self.provider.get_mem_word(context, address) + /// Returns the word at `address` in the active execution context. + pub fn memory_word(&self, address: u32) -> Result, MemoryError> { + self.provider.memory_word(address) } - pub fn get_mem_state(&self, context: ContextId) -> Vec<(MemoryAddress, Felt)> { - self.provider.get_mem_state(context) + /// Returns an allocated snapshot of memory in the active execution context. + pub fn memory_snapshot(&self) -> Vec<(MemoryAddress, Felt)> { + self.provider.memory_snapshot() } - pub fn get_mem_addr_range( + /// Reads a half-open memory range from two operand-stack positions. + pub fn memory_range_from_stack( &self, start_position: usize, end_position: usize, ) -> Result, MemoryError> { - let start_addr = self.get_stack_item(start_position).as_canonical_u64(); - let end_addr = self.get_stack_item(end_position).as_canonical_u64(); + let start_addr = self.stack_item(start_position).as_canonical_u64(); + let end_addr = self.stack_item(end_position).as_canonical_u64(); if start_addr > u32::MAX as u64 { return Err(MemoryError::AddressOutOfBounds { addr: start_addr }); @@ -129,29 +137,35 @@ impl<'a> EventContext<'a> { Ok(start_addr as u32..end_addr as u32) } - pub fn advice_stack(&self) -> Vec { - self.provider.advice_stack() + /// Returns an allocated snapshot of the complete advice stack. + pub fn advice_stack_snapshot(&self) -> Vec { + self.provider.advice_stack_snapshot() } + /// Returns the advice map. pub fn advice_map(&self) -> &AdviceMap { self.provider.advice_map() } - pub fn get_advice_map_entry(&self, key: &Word) -> Option<&[Felt]> { - self.provider.get_advice_map_entry(key) + /// Returns the advice-map entry for `key`. + pub fn advice_map_entry(&self, key: &Word) -> Option<&[Felt]> { + self.provider.advice_map_entry(key) } - pub fn get_advice_tree_node( + /// Returns an advice Merkle-tree node. + pub fn advice_tree_node( &self, root: Word, depth: Felt, index: Felt, ) -> Result { - self.provider.get_advice_tree_node(root, depth, index) + self.provider.advice_tree_node(root, depth, index) } - pub fn max_hash_len_bytes(&self) -> usize { - self.provider.max_hash_len_bytes() + /// Returns the processor execution options used by built-in precompile handlers. + #[doc(hidden)] + pub fn execution_options(&self) -> &ExecutionOptions { + self.provider.execution_options() } pub fn require_canonical_deferred_node( @@ -160,52 +174,6 @@ impl<'a> EventContext<'a> { ) -> Result<(Digest, &Node), PrecompileError> { self.provider.require_canonical_deferred_node(digest) } - - /// Returns a compatibility view of the advice provider. - #[deprecated(note = "use EventContext advice accessors directly")] - pub fn advice_provider(&self) -> AdviceProviderView<'a> { - AdviceProviderView { provider: self.provider } - } - - /// Returns a compatibility view of execution options used by event handlers. - #[deprecated(note = "use EventContext::max_hash_len_bytes")] - pub fn execution_options(&self) -> ExecutionOptionsView<'a> { - ExecutionOptionsView { provider: self.provider } - } -} - -/// Temporary read-only compatibility view for handlers that access the advice provider directly. -pub struct AdviceProviderView<'a> { - provider: &'a dyn EventContextProvider, -} - -impl<'a> AdviceProviderView<'a> { - pub fn stack(&self) -> Vec { - self.provider.advice_stack() - } - - pub fn map(&self) -> &'a AdviceMap { - self.provider.advice_map() - } - - pub fn get_mapped_values(&self, key: &Word) -> Option<&'a [Felt]> { - self.provider.get_advice_map_entry(key) - } - - pub fn get_tree_node(&self, root: Word, depth: Felt, index: Felt) -> Result { - self.provider.get_advice_tree_node(root, depth, index) - } -} - -/// Temporary compatibility view for execution limits used by event handlers. -pub struct ExecutionOptionsView<'a> { - provider: &'a dyn EventContextProvider, -} - -impl ExecutionOptionsView<'_> { - pub fn max_hash_len_bytes(&self) -> usize { - self.provider.max_hash_len_bytes() - } } /// Handles an event emitted by the VM. diff --git a/core/src/events/mod.rs b/core/src/events/mod.rs index cb7bca6ee2..a3fc49f031 100644 --- a/core/src/events/mod.rs +++ b/core/src/events/mod.rs @@ -10,13 +10,13 @@ use serde::{Deserialize, Serialize}; use crate::{Felt, utils::hash_string_to_word}; +pub mod compatibility; pub mod debug; mod handlers; mod sys_events; pub use handlers::{ - AdviceProviderView, EventContext, EventContextProvider, EventError, EventHandler, - ExecutionOptionsView, NoopEventHandler, + EventContext, EventContextProvider, EventError, EventHandler, NoopEventHandler, }; pub use sys_events::SystemEvent; diff --git a/core/src/execution.rs b/core/src/execution.rs index 774b38b4cb..ce9a7af913 100644 --- a/core/src/execution.rs +++ b/core/src/execution.rs @@ -5,6 +5,12 @@ use miden_utils_diagnostics::{Diagnostic, miette}; use crate::Felt; +mod options; +pub use options::{ExecutionOptions, ExecutionOptionsError}; + +/// The minimum length of an execution trace required to support range checks. +pub const MIN_TRACE_LEN: usize = 64; + /// Identifies an execution context. #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] pub struct ContextId(u32); diff --git a/processor/src/execution_options.rs b/core/src/execution/options.rs similarity index 99% rename from processor/src/execution_options.rs rename to core/src/execution/options.rs index 32c08ae3cf..829b153054 100644 --- a/processor/src/execution_options.rs +++ b/core/src/execution/options.rs @@ -1,5 +1,5 @@ -use miden_air::trace::MIN_TRACE_LEN; -use miden_core::{ +use super::MIN_TRACE_LEN; +use crate::{ deferred::DEFAULT_MAX_DEFERRED_ELEMENTS as DEFAULT_DEFERRED_STATE_ELEMENTS, program::MIN_STACK_DEPTH, }; diff --git a/core/src/lib.rs b/core/src/lib.rs index 07c3c48c8d..c67161c4e8 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -9,7 +9,9 @@ extern crate std; // EXPORTS // ================================================================================================ -pub use execution::{ContextId, MemoryAddress, MemoryError}; +pub use execution::{ + ContextId, ExecutionOptions, ExecutionOptionsError, MemoryAddress, MemoryError, +}; pub use miden_crypto::{EMPTY_WORD, Felt, ONE, Word, ZERO}; /// The number of field elements in a Miden word. diff --git a/crates/lib/core/src/handlers/aead_decrypt.rs b/crates/lib/core/src/handlers/aead_decrypt.rs index cc71deab7a..849083ba2e 100644 --- a/crates/lib/core/src/handlers/aead_decrypt.rs +++ b/crates/lib/core/src/handlers/aead_decrypt.rs @@ -52,7 +52,7 @@ pub const AEAD_DECRYPT_EVENT_NAME: EventName = EventName::new("miden::core::cryp /// 1. The MASM procedure re-verifies the tag when decrypting /// 2. The deterministic encryption creates a bijection between plaintext and ciphertext /// 3. A malicious prover cannot provide incorrect plaintext without causing tag mismatch -pub fn handle_aead_decrypt(process: &EventContext<'_>) -> Result, EventError> { +pub fn handle_aead_decrypt(context: &EventContext<'_>) -> Result, EventError> { // Stack: [event_id, key:Word(4), nonce:Word(4), src_ptr, dst_ptr, num_blocks, ...] // where: // src_ptr = ciphertext + encrypted_padding + tag location (input) @@ -64,16 +64,16 @@ pub fn handle_aead_decrypt(process: &EventContext<'_>) -> Result) -> Result DebugPrinter { } impl EventHandler for DebugPrinter { - fn on_event(&self, process: &EventContext<'_>) -> Result, EventError> { + fn on_event(&self, context: &EventContext<'_>) -> Result, EventError> { // The event id sits at the top of the stack (position 0); the procedure's arguments, if // any, are immediately below it. - let id = EventId::from_felt(process.get_stack_item(0)); + let id = context.event_id(); let mut writer = self.writer.write(); let w: &mut W = &mut writer; if id == PRINT_STACK_EVENT_NAME.to_event_id() { // Skip position 0 (the event id) so only the user's operand stack is shown. Print the // entire stack (no cap). - let stack = process.get_stack_state(); + let stack = context.stack_snapshot(); let operand_stack = stack.get(1..).unwrap_or(&[]); - write_stack(w, operand_stack, None, "Stack", process.clock())?; + write_stack(w, operand_stack, None, "Stack", context.clock())?; } else if id == PRINT_MEM_EVENT_NAME.to_event_id() { - let bounds = read_mem_print_range(process, 1, 2)?; + let bounds = read_mem_print_range(context, 1, 2)?; // Guard against an accidentally huge explicit range. if let Some((first, last)) = bounds { let len = u64::from(last - first) + 1; @@ -166,19 +166,19 @@ impl EventHandler for DebugPrinter { .into()); } } - write_mem_range(w, process, bounds)?; + write_mem_range(w, context, bounds)?; } else if id == PRINT_MEM_ALL_EVENT_NAME.to_event_id() { - write_mem_all(w, process)?; + write_mem_all(w, context)?; } else if id == PRINT_ADV_STACK_EVENT_NAME.to_event_id() { - let start = stack_item_as_usize(process, 1); - let end = stack_item_as_usize(process, 2); - let adv_stack = process.advice_stack(); + let start = stack_item_as_usize(context, 1); + let end = stack_item_as_usize(context, 2); + let adv_stack = context.advice_stack_snapshot(); let slice = slice_range(&adv_stack, start, end); - write_stack(w, slice, None, "Advice stack", process.clock())?; + write_stack(w, slice, None, "Advice stack", context.clock())?; } else if id == PRINT_ADV_MAP_EVENT_NAME.to_event_id() { - write_adv_map(w, process)?; + write_adv_map(w, context)?; } else if id == PRINT_ADV_MAP_ITEM_EVENT_NAME.to_event_id() { - write_adv_map_entry(w, process)?; + write_adv_map_entry(w, context)?; } // Unknown ids are ignored: the handler is only registered for the events above. @@ -189,7 +189,7 @@ impl EventHandler for DebugPrinter { struct NoopDebugHandler; impl EventHandler for NoopDebugHandler { - fn on_event(&self, _process: &EventContext<'_>) -> Result, EventError> { + fn on_event(&self, _context: &EventContext<'_>) -> Result, EventError> { Ok(Vec::new()) } } @@ -198,8 +198,8 @@ impl EventHandler for NoopDebugHandler { // ================================================================================================ /// Reads the element at `pos` on the operand stack as a `usize` (saturating). -fn stack_item_as_usize(process: &EventContext<'_>, pos: usize) -> usize { - usize::try_from(process.get_stack_item(pos).as_canonical_u64()).unwrap_or(usize::MAX) +fn stack_item_as_usize(context: &EventContext<'_>, pos: usize) -> usize { + usize::try_from(context.stack_item(pos).as_canonical_u64()).unwrap_or(usize::MAX) } /// Returns `slice[start..end]`, clamped to the bounds of `slice` and to `start <= end`. @@ -217,30 +217,27 @@ fn slice_range(slice: &[Felt], start: usize, end: usize) -> &[Felt] { /// `2^32` (one past the last address) so the cell at `u32::MAX` stays reachable; it folds into an /// inclusive end of `u32::MAX`. fn read_mem_print_range( - process: &EventContext<'_>, + context: &EventContext<'_>, start_idx: usize, end_idx: usize, ) -> Result, MemoryError> { - let start_addr = process.get_stack_item(start_idx).as_canonical_u64(); - let end_addr = process.get_stack_item(end_idx).as_canonical_u64(); + let end_addr = context.stack_item(end_idx).as_canonical_u64(); - if start_addr > u32::MAX as u64 { - return Err(MemoryError::AddressOutOfBounds { addr: start_addr }); - } - // The exclusive end may be one past the last valid address (`2^32`). - if end_addr > u32::MAX as u64 + 1 { - return Err(MemoryError::AddressOutOfBounds { addr: end_addr }); - } - if start_addr > end_addr { - return Err(MemoryError::InvalidMemoryRange { start_addr, end_addr }); + // The generic range accessor accepts only `u32` bounds, but the debug procedure also supports + // the exclusive end `2^32` so that callers can print the cell at `u32::MAX`. + if end_addr == u32::MAX as u64 + 1 { + let start_addr = context.stack_item(start_idx).as_canonical_u64(); + if start_addr > u32::MAX as u64 { + return Err(MemoryError::AddressOutOfBounds { addr: start_addr }); + } + return Ok(Some((start_addr as u32, u32::MAX))); } - if start_addr == end_addr { + let range = context.memory_range_from_stack(start_idx, end_idx)?; + if range.is_empty() { Ok(None) } else { - // Subtract in `u64` before the cast, since `end_addr` may be `2^32`; the result is in - // `[0, u32::MAX]`. - Ok(Some((start_addr as u32, (end_addr - 1) as u32))) + Ok(Some((range.start, range.end - 1))) } } @@ -251,20 +248,17 @@ fn read_mem_print_range( /// `u32`. The caller is responsible for capping the range length (see [`MAX_PRINT_MEM_RANGE`]). fn write_mem_range( w: &mut W, - process: &EventContext<'_>, + context: &EventContext<'_>, bounds: Option<(u32, u32)>, ) -> fmt::Result { - let (ctx, clk) = (process.ctx(), process.clock()); + let clk = context.clock(); let Some((start, end)) = bounds else { - return writeln!(w, "Memory state before step {clk} for context {ctx}: range is empty."); + return writeln!(w, "Memory state before step {clk}: range is empty."); }; - writeln!( - w, - "Memory state before step {clk} for context {ctx} in the range [{start}, {end}]:", - )?; + writeln!(w, "Memory state before step {clk} in the range [{start}, {end}]:")?; let items: Vec<_> = (start..=end) .map(|addr| { - let value = process.get_mem_value(ctx, addr).map(|v| v.to_string()); + let value = context.memory_value(addr).map(|v| v.to_string()); (format!("{addr:#010x}"), value) }) .collect(); @@ -272,11 +266,11 @@ fn write_mem_range( } /// Prints all initialized memory cells of the current context. -fn write_mem_all(w: &mut W, process: &EventContext<'_>) -> fmt::Result { - let (ctx, clk) = (process.ctx(), process.clock()); - writeln!(w, "Memory state before step {clk} for context {ctx}:")?; - let items: Vec<_> = process - .get_mem_state(ctx) +fn write_mem_all(w: &mut W, context: &EventContext<'_>) -> fmt::Result { + let clk = context.clock(); + writeln!(w, "Memory state before step {clk}:")?; + let items: Vec<_> = context + .memory_snapshot() .into_iter() .map(|(addr, value)| (format!("{addr:#010x}"), Some(value.to_string()))) .collect(); @@ -284,9 +278,9 @@ fn write_mem_all(w: &mut W, process: &EventContext<'_>) -> fmt::R } /// Prints the full advice map. -fn write_adv_map(w: &mut W, process: &EventContext<'_>) -> fmt::Result { - let clk = process.clock(); - let map = process.advice_map(); +fn write_adv_map(w: &mut W, context: &EventContext<'_>) -> fmt::Result { + let clk = context.clock(); + let map = context.advice_map(); if map.is_empty() { return writeln!(w, "Advice map before step {clk}: empty."); } @@ -300,11 +294,11 @@ fn write_adv_map(w: &mut W, process: &EventContext<'_>) -> fmt::R } /// Looks up the WORD key (at stack positions 1..5) in the advice map and prints its values. -fn write_adv_map_entry(w: &mut W, process: &EventContext<'_>) -> fmt::Result { - let key = process.get_stack_word(1); +fn write_adv_map_entry(w: &mut W, context: &EventContext<'_>) -> fmt::Result { + let key = context.stack_word(1); let key_str = format_word(&key); - let clk = process.clock(); - match process.get_advice_map_entry(&key) { + let clk = context.clock(); + match context.advice_map_entry(&key) { Some(values) => { writeln!(w, "Advice map entry for key {key_str} before step {clk}:")?; let items: Vec<_> = values diff --git a/crates/lib/core/src/handlers/falcon_div.rs b/crates/lib/core/src/handlers/falcon_div.rs index 938cf0b98d..41421cb812 100644 --- a/crates/lib/core/src/handlers/falcon_div.rs +++ b/crates/lib/core/src/handlers/falcon_div.rs @@ -39,9 +39,9 @@ pub const FALCON_DIV_EVENT_NAME: EventName = /// # Errors /// - Returns an error if the divisor is ZERO. /// - Returns an error if either a0 or a1 is not a u32. -pub fn handle_falcon_div(process: &EventContext<'_>) -> Result, EventError> { - let dividend_hi = process.get_stack_item(1).as_canonical_u64(); - let dividend_lo = process.get_stack_item(2).as_canonical_u64(); +pub fn handle_falcon_div(context: &EventContext<'_>) -> Result, EventError> { + let dividend_hi = context.stack_item(1).as_canonical_u64(); + let dividend_lo = context.stack_item(2).as_canonical_u64(); if dividend_lo > u32::MAX.into() { return Err(FalconDivError::InputNotU32 { diff --git a/crates/lib/core/src/handlers/mod.rs b/crates/lib/core/src/handlers/mod.rs index e7bfd7489b..a1a250a169 100644 --- a/crates/lib/core/src/handlers/mod.rs +++ b/crates/lib/core/src/handlers/mod.rs @@ -63,6 +63,5 @@ pub(crate) fn read_memory_region( let end_addr = start_addr.checked_add(len_u32)?; // Read all elements in the range from the current execution context - let ctx = context.ctx(); - (start_addr..end_addr).map(|addr| context.get_mem_value(ctx, addr)).collect() + (start_addr..end_addr).map(|addr| context.memory_value(addr)).collect() } diff --git a/crates/lib/core/src/handlers/precompiles/keccak256.rs b/crates/lib/core/src/handlers/precompiles/keccak256.rs index 46a229f1e0..617cb171a1 100644 --- a/crates/lib/core/src/handlers/precompiles/keccak256.rs +++ b/crates/lib/core/src/handlers/precompiles/keccak256.rs @@ -24,19 +24,19 @@ const KECCAK256_DIGEST_FELTS: usize = 8; /// Reads the requested u32-packed memory preimage, computes Keccak-256, and pushes the digest limbs /// onto the advice stack for the MASM wrapper to bind with deferred assertions. pub fn handle_keccak256_digest( - process: &EventContext<'_>, + context: &EventContext<'_>, ) -> Result, EventError> { - let ptr = process.get_stack_item(1).as_canonical_u64(); - let len_bytes = process.get_stack_item(2).as_canonical_u64(); + let ptr = context.stack_item(1).as_canonical_u64(); + let len_bytes = context.stack_item(2).as_canonical_u64(); - let max = process.max_hash_len_bytes(); + let max = context.execution_options().max_hash_len_bytes(); if len_bytes > max as u64 { return Err(Keccak256DigestEventError::InputTooLong { len_bytes, max }.into()); } let len_bytes = usize::try_from(len_bytes) .map_err(|_| Keccak256DigestEventError::InputLengthTooLarge { len_bytes })?; - let input = read_memory_packed_u32(process, ptr, len_bytes)?; + let input = read_memory_packed_u32(context, ptr, len_bytes)?; let digest = <[u8; 32]>::from(Keccak256::hash(&input)); let digest_felts = bytes_to_packed_u32_elements(&digest); if digest_felts.len() != KECCAK256_DIGEST_FELTS { @@ -54,7 +54,7 @@ pub fn handle_keccak256_digest( } fn read_memory_packed_u32( - process: &EventContext<'_>, + context: &EventContext<'_>, start: u64, len_bytes: usize, ) -> Result, Keccak256DigestEventError> { @@ -76,7 +76,7 @@ fn read_memory_packed_u32( .checked_next_multiple_of(BYTES_PER_U32) .ok_or(Keccak256DigestEventError::AddressOverflow { start, len_bytes })?; - let felts = read_memory_region(process, start, len_felts_u64) + let felts = read_memory_region(context, start, len_felts_u64) .ok_or(Keccak256DigestEventError::MemoryAccessFailed { address: start_u32 })?; for (offset, felt) in felts.iter().enumerate() { diff --git a/crates/lib/core/src/handlers/precompiles/uint_field_inv.rs b/crates/lib/core/src/handlers/precompiles/uint_field_inv.rs index bed936ba65..a002f4176c 100644 --- a/crates/lib/core/src/handlers/precompiles/uint_field_inv.rs +++ b/crates/lib/core/src/handlers/precompiles/uint_field_inv.rs @@ -17,10 +17,10 @@ pub const UINT_FIELD_INV_EVENT_NAME: EventName = /// Resolves the input uint value digest from deferred state, computes its inverse in the encoded /// prime-field domain, and pushes the inverse limbs onto the advice stack for MASM validation. pub fn handle_uint_field_inv( - process: &EventContext<'_>, + context: &EventContext<'_>, ) -> Result, EventError> { - let input_digest = process.get_stack_word(1); - let (_, canonical_node) = process.require_canonical_deferred_node(input_digest)?; + let input_digest = context.stack_word(1); + let (_, canonical_node) = context.require_canonical_deferred_node(input_digest)?; let tag = canonical_node.tag(); let [op_id, bound_ptr, reserved] = tag.args(); diff --git a/crates/lib/core/src/handlers/readonly.rs b/crates/lib/core/src/handlers/readonly.rs index 8d04325320..75a03b605f 100644 --- a/crates/lib/core/src/handlers/readonly.rs +++ b/crates/lib/core/src/handlers/readonly.rs @@ -36,7 +36,7 @@ pub const READONLY_MIDEN_DEBUG_PRINTLN: EventName = struct ReadonlyNoopHandler; impl EventHandler for ReadonlyNoopHandler { - fn on_event(&self, _process: &EventContext<'_>) -> Result, EventError> { + fn on_event(&self, _context: &EventContext<'_>) -> Result, EventError> { Ok(vec![]) } } diff --git a/crates/lib/core/src/handlers/smt_peek.rs b/crates/lib/core/src/handlers/smt_peek.rs index 964a482a9b..b9c4c8f00d 100644 --- a/crates/lib/core/src/handlers/smt_peek.rs +++ b/crates/lib/core/src/handlers/smt_peek.rs @@ -46,18 +46,18 @@ pub const SMT_PEEK_EVENT_NAME: EventName = /// /// # Panics /// Will panic as unimplemented if the target depth is `64`. -pub fn handle_smt_peek(process: &EventContext<'_>) -> Result, EventError> { +pub fn handle_smt_peek(context: &EventContext<'_>) -> Result, EventError> { let empty_leaf = EmptySubtreeRoots::entry(SMT_DEPTH, SMT_DEPTH); // fetch the arguments from the operand stack // Stack at emit: [event_id, KEY, ROOT, ...] where KEY and ROOT are structural words. - let key = process.get_stack_word(1); - let root = process.get_stack_word(5); + let key = context.stack_word(1); + let root = context.stack_word(5); // get the node from the SMT for the specified key; this node can be either a leaf node, // or a root of an empty subtree at the returned depth // K[3] is used as the leaf index (most significant in BE ordering) - let node = process - .get_advice_tree_node(root, Felt::new_unchecked(SMT_DEPTH as u64), key[3]) + let node = context + .advice_tree_node(root, Felt::new_unchecked(SMT_DEPTH as u64), key[3]) .map_err(|err| SmtPeekError::AdviceProviderError { message: format!("Failed to get tree node: {err}"), })?; @@ -68,7 +68,7 @@ pub fn handle_smt_peek(process: &EventContext<'_>) -> Result let mutation = advice_stack_word_mutation(Smt::EMPTY_VALUE); Ok(vec![mutation]) } else { - let leaf_preimage = get_smt_leaf_preimage(process, node)?; + let leaf_preimage = get_smt_leaf_preimage(context, node)?; for (key_in_leaf, value_in_leaf) in leaf_preimage { if key == key_in_leaf { @@ -90,12 +90,10 @@ pub fn handle_smt_peek(process: &EventContext<'_>) -> Result /// Retrieves the preimage of an SMT leaf node from the advice provider. fn get_smt_leaf_preimage( - process: &EventContext<'_>, + context: &EventContext<'_>, node: Word, ) -> Result, SmtPeekError> { - let kv_pairs = process - .get_advice_map_entry(&node) - .ok_or(SmtPeekError::SmtNodeNotFound { node })?; + let kv_pairs = context.advice_map_entry(&node).ok_or(SmtPeekError::SmtNodeNotFound { node })?; if kv_pairs.len() % (WORD_SIZE * 2) != 0 { return Err(SmtPeekError::InvalidSmtNodePreimage { node, preimage_len: kv_pairs.len() }); diff --git a/crates/lib/core/src/handlers/sorted_array.rs b/crates/lib/core/src/handlers/sorted_array.rs index 3f3f7a7c78..73636c7312 100644 --- a/crates/lib/core/src/handlers/sorted_array.rs +++ b/crates/lib/core/src/handlers/sorted_array.rs @@ -1,7 +1,7 @@ use alloc::{vec, vec::Vec}; use miden_core::{ - Felt, MemoryError, Word, + Felt, Word, advice::{AdviceMutation, AdviceStack}, events::{EventContext, EventError, EventName}, field::PrimeCharacteristicRing, @@ -36,9 +36,9 @@ enum KeySize { /// # Errors /// Returns an error if the provided word array is not sorted in non-decreasing order. pub fn handle_lowerbound_array( - process: &EventContext<'_>, + context: &EventContext<'_>, ) -> Result, EventError> { - push_lowerbound_result(process, 4, KeySize::Full) + push_lowerbound_result(context, 4, KeySize::Full) } /// Pushes onto the advice stack the first pointer in [start_ptr, end_ptr) such that @@ -59,9 +59,9 @@ pub fn handle_lowerbound_array( /// # Errors /// Returns an error if the keys are not sorted in non-decreasing order. pub fn handle_lowerbound_key_value( - process: &EventContext<'_>, + context: &EventContext<'_>, ) -> Result, EventError> { - let use_full_key = process.get_stack_item(7); + let use_full_key = context.stack_item(7); let key_size = match use_full_key.as_canonical_u64() { 0 => KeySize::Half, @@ -73,7 +73,7 @@ pub fn handle_lowerbound_key_value( }, }; - push_lowerbound_result(process, 8, key_size) + push_lowerbound_result(context, 8, key_size) } /// Offsets for the push_lowerbound_result inputs from the top of the stack @@ -82,7 +82,7 @@ const START_ADDR_OFFSET: usize = 5; const END_ADDR_OFFSET: usize = 6; fn push_lowerbound_result( - process: &EventContext<'_>, + context: &EventContext<'_>, stride: u32, key_size: KeySize, ) -> Result, EventError> { @@ -90,17 +90,12 @@ fn push_lowerbound_result( assert!(stride == 4 || stride == 8); // Read inputs from the stack; keys are provided in structural / little-endian order. - let key = word_to_search_key(process.get_stack_word(KEY_OFFSET), key_size); - let addr_range = process.get_mem_addr_range(START_ADDR_OFFSET, END_ADDR_OFFSET)?; - - // Validate the start_addr is word-aligned (multiple of 4) - if addr_range.start % 4 != 0 { - return Err(MemoryError::UnalignedWordAccess { - addr: addr_range.start, - ctx: process.ctx(), - } - .into()); - } + let key = word_to_search_key(context.stack_word(KEY_OFFSET), key_size); + let addr_range = context.memory_range_from_stack(START_ADDR_OFFSET, END_ADDR_OFFSET)?; + + // Validate word alignment through the active-context memory API so it supplies the correct + // context in the resulting MemoryError. + let first_word = context.memory_word(addr_range.start)?; // Validate the end_addr is properly aligned (i.e. the entire array has size divisible by // stride) @@ -125,19 +120,17 @@ fn push_lowerbound_result( } // Helper function to get a word from memory and normalize it to the requested key size. - let get_word = { - |addr: u32| { - process - .get_mem_word(process.ctx(), addr) - .map(|word| word_to_search_key(word.unwrap_or_default(), key_size)) - } + let get_word = |addr: u32| { + context + .memory_word(addr) + .map(|word| word_to_search_key(word.unwrap_or_default(), key_size)) }; let mut was_key_found = false; let mut result = None; // Test the first element - let mut previous_word = get_word(addr_range.start)?; + let mut previous_word = word_to_search_key(first_word.unwrap_or_default(), key_size); if previous_word >= key { was_key_found = previous_word == key; result = Some(addr_range.start); diff --git a/crates/lib/core/src/handlers/u128_div.rs b/crates/lib/core/src/handlers/u128_div.rs index 9b633ef3b9..b330d9ca39 100644 --- a/crates/lib/core/src/handlers/u128_div.rs +++ b/crates/lib/core/src/handlers/u128_div.rs @@ -35,14 +35,14 @@ pub const U128_DIV_EVENT_NAME: EventName = EventName::new("miden::core::math::u1 /// /// # Errors /// Returns an error if the divisor is ZERO or any limb is not a valid u32. -pub fn handle_u128_div(process: &EventContext<'_>) -> Result, EventError> { - let divisor = read_u128_from_stack(process, 1, "divisor")?; +pub fn handle_u128_div(context: &EventContext<'_>) -> Result, EventError> { + let divisor = read_u128_from_stack(context, 1, "divisor")?; if divisor == 0 { return Err(U128DivError::DivideByZero.into()); } - let dividend = read_u128_from_stack(process, 5, "dividend")?; + let dividend = read_u128_from_stack(context, 5, "dividend")?; let quotient = dividend / divisor; let remainder = dividend - quotient * divisor; @@ -61,13 +61,13 @@ pub fn handle_u128_div(process: &EventContext<'_>) -> Result /// Reads a u128 value from 4 consecutive stack positions starting at `start`. fn read_u128_from_stack( - process: &EventContext<'_>, + context: &EventContext<'_>, start: usize, name: &'static str, ) -> Result { let mut value: u128 = 0; for i in (0..4).rev() { - let limb = process.get_stack_item(start + i).as_canonical_u64(); + let limb = context.stack_item(start + i).as_canonical_u64(); if limb > u32::MAX as u64 { return Err(U128DivError::NotU32Value { value: limb, diff --git a/crates/lib/core/src/handlers/u256_div.rs b/crates/lib/core/src/handlers/u256_div.rs index 37a40dcea1..9e711c7eb9 100644 --- a/crates/lib/core/src/handlers/u256_div.rs +++ b/crates/lib/core/src/handlers/u256_div.rs @@ -37,14 +37,14 @@ pub const U256_DIV_EVENT_NAME: EventName = EventName::new("miden::core::math::u2 /// /// # Errors /// Returns an error if the divisor is ZERO or any limb is not a valid u32. -pub fn handle_u256_div(process: &EventContext<'_>) -> Result, EventError> { - let divisor = read_u256_from_stack(process, 1, "divisor")?; +pub fn handle_u256_div(context: &EventContext<'_>) -> Result, EventError> { + let divisor = read_u256_from_stack(context, 1, "divisor")?; if divisor == (0, 0) { return Err(U256DivError::DivideByZero.into()); } - let dividend = read_u256_from_stack(process, 9, "dividend")?; + let dividend = read_u256_from_stack(context, 9, "dividend")?; let (quotient, remainder) = u256_divmod(dividend, divisor); @@ -67,14 +67,14 @@ pub fn handle_u256_div(process: &EventContext<'_>) -> Result /// /// Returned as a `(lo, hi)` pair of u128s. fn read_u256_from_stack( - process: &EventContext<'_>, + context: &EventContext<'_>, start: usize, name: &'static str, ) -> Result<(u128, u128), EventError> { let mut lo: u128 = 0; let mut hi: u128 = 0; for i in (0..8).rev() { - let limb = process.get_stack_item(start + i).as_canonical_u64(); + let limb = context.stack_item(start + i).as_canonical_u64(); if limb > u32::MAX as u64 { return Err(U256DivError::NotU32Value { value: limb, diff --git a/crates/lib/core/src/handlers/u64_div.rs b/crates/lib/core/src/handlers/u64_div.rs index 4bc6a9ba88..d5a62c423a 100644 --- a/crates/lib/core/src/handlers/u64_div.rs +++ b/crates/lib/core/src/handlers/u64_div.rs @@ -34,11 +34,11 @@ pub const U64_DIV_EVENT_NAME: EventName = EventName::new("miden::core::math::u64 /// /// # Errors /// Returns an error if the divisor is ZERO. -pub fn handle_u64_div(process: &EventContext<'_>) -> Result, EventError> { +pub fn handle_u64_div(context: &EventContext<'_>) -> Result, EventError> { // Read divisor from positions 1 (lo) and 2 (hi) - b is on top of stack let divisor = { - let divisor_lo = process.get_stack_item(1).as_canonical_u64(); - let divisor_hi = process.get_stack_item(2).as_canonical_u64(); + let divisor_lo = context.stack_item(1).as_canonical_u64(); + let divisor_hi = context.stack_item(2).as_canonical_u64(); // Ensure the divisor is a pair of u32 values if divisor_lo > u32::MAX.into() { @@ -67,8 +67,8 @@ pub fn handle_u64_div(process: &EventContext<'_>) -> Result, // Read dividend from positions 3 (lo) and 4 (hi) - a is below b let dividend = { - let dividend_lo = process.get_stack_item(3).as_canonical_u64(); - let dividend_hi = process.get_stack_item(4).as_canonical_u64(); + let dividend_lo = context.stack_item(3).as_canonical_u64(); + let dividend_hi = context.stack_item(4).as_canonical_u64(); // Ensure the dividend is a pair of u32 values if dividend_lo > u32::MAX.into() { diff --git a/crates/lib/core/tests/collections/sorted_array.rs b/crates/lib/core/tests/collections/sorted_array.rs index c161d5bd00..61b2b3f0b4 100644 --- a/crates/lib/core/tests/collections/sorted_array.rs +++ b/crates/lib/core/tests/collections/sorted_array.rs @@ -974,7 +974,7 @@ fn build_lib_test(source: &str, op_stack: &[u64]) -> miden_utils_testing::Test { /// Returns `(was_found = false, maybe_value_ptr = 204)` regardless of the actual array. 204 is /// past the array's `end_ptr = 112`, so the bounds check must fire. fn malicious_lowerbound_oob_above( - _process: &EventContext<'_>, + _context: &EventContext<'_>, ) -> Result, EventError> { Ok(vec![advice_stack_mutation([Felt::new_unchecked(204), Felt::ZERO])]) } @@ -982,7 +982,7 @@ fn malicious_lowerbound_oob_above( #[allow(clippy::unnecessary_wraps)] /// Returns `(was_found = false, maybe_value_ptr = 40)` which is below `start_ptr = 100`. fn malicious_lowerbound_oob_below( - _process: &EventContext<'_>, + _context: &EventContext<'_>, ) -> Result, EventError> { Ok(vec![advice_stack_mutation([Felt::new_unchecked(40), Felt::ZERO])]) } @@ -990,7 +990,7 @@ fn malicious_lowerbound_oob_below( #[allow(clippy::unnecessary_wraps)] /// Returns `(was_found = false, maybe_ptr = start_ptr)` regardless of the actual range. fn malicious_lowerbound_start_ptr( - _process: &EventContext<'_>, + _context: &EventContext<'_>, ) -> Result, EventError> { Ok(vec![advice_stack_mutation([Felt::new_unchecked(100), Felt::ZERO])]) } diff --git a/crates/lib/core/tests/crypto/aead.rs b/crates/lib/core/tests/crypto/aead.rs index dea4ff19c6..9889e42f0f 100644 --- a/crates/lib/core/tests/crypto/aead.rs +++ b/crates/lib/core/tests/crypto/aead.rs @@ -182,7 +182,7 @@ fn test_decrypt_rejects_tampered_final_tag() { let mut test = build_test!(source.as_str(), &[]); let valid_plaintext = plaintext; let malicious_handler: Arc = - Arc::new(move |_process: &EventContext<'_>| -> Result, EventError> { + Arc::new(move |_context: &EventContext<'_>| -> Result, EventError> { Ok(vec![advice_stack_mutation(valid_plaintext.clone())]) }); @@ -441,7 +441,7 @@ fn test_decrypt_rejects_adversarial_plaintext_for_unrelated_ciphertext() { let mut test = build_test!(source.as_str(), &[]); let adversarial_plaintext = plaintext; let malicious_handler: Arc = - Arc::new(move |_process: &EventContext<'_>| -> Result, EventError> { + Arc::new(move |_context: &EventContext<'_>| -> Result, EventError> { Ok(vec![advice_stack_mutation(adversarial_plaintext.clone())]) }); diff --git a/crates/lib/core/tests/crypto/falcon.rs b/crates/lib/core/tests/crypto/falcon.rs index 4f9447a1f6..c7088d0b61 100644 --- a/crates/lib/core/tests/crypto/falcon.rs +++ b/crates/lib/core/tests/crypto/falcon.rs @@ -71,13 +71,13 @@ const EVENT_FALCON_SIG_TO_STACK: EventName = EventName::new("test::falcon::sig_t /// /// The advice provider is expected to contain the private key associated to the public key PK. pub fn push_falcon_signature( - process: &EventContext<'_>, + context: &EventContext<'_>, ) -> Result, EventError> { - let pub_key = process.get_stack_word(1); - let msg = process.get_stack_word(5); + let pub_key = context.stack_word(1); + let msg = context.stack_word(5); - let pk_sk_felts = process - .get_advice_map_entry(&pub_key) + let pk_sk_felts = context + .advice_map_entry(&pub_key) .ok_or(FalconError::NoSecretKey { key: pub_key })?; // Convert felts back to bytes (each felt was a single byte stored as u64) @@ -358,9 +358,9 @@ fn test_mod_12289_rejects_forged_remainder_zero(#[case] a_hi: u64, #[case] a_lo: // Malicious event handler that always returns remainder = 0. // Signature matches the event-handler callback contract. #[allow(clippy::unnecessary_wraps)] - fn malicious_falcon_div(process: &EventContext<'_>) -> Result, EventError> { - let a_hi = process.get_stack_item(1).as_canonical_u64(); - let a_lo = process.get_stack_item(2).as_canonical_u64(); + fn malicious_falcon_div(context: &EventContext<'_>) -> Result, EventError> { + let a_hi = context.stack_item(1).as_canonical_u64(); + let a_lo = context.stack_item(2).as_canonical_u64(); let a = (a_hi << 32) | a_lo; let q = a.wrapping_mul(M_INV); @@ -417,7 +417,7 @@ fn test_mod_12289_rejects_forged_addition_overflow() { // Malicious event handler that forges q/r to trigger the addition-overflow assertion. #[allow(clippy::unnecessary_wraps)] fn malicious_falcon_div( - _process: &EventContext<'_>, + _context: &EventContext<'_>, ) -> Result, EventError> { let q_hi = Felt::new_unchecked(FORGED_Q >> 32); let q_lo = Felt::new_unchecked(FORGED_Q & 0xffff_ffff); @@ -460,9 +460,9 @@ fn test_mod_12289_rejects_non_u32_remainder_advice() { EventName::new("miden::core::crypto::dsa::falcon512_poseidon2::falcon_div"); #[allow(clippy::unnecessary_wraps)] - fn malicious_falcon_div(process: &EventContext<'_>) -> Result, EventError> { - let a_hi = process.get_stack_item(1).as_canonical_u64(); - let a_lo = process.get_stack_item(2).as_canonical_u64(); + fn malicious_falcon_div(context: &EventContext<'_>) -> Result, EventError> { + let a_hi = context.stack_item(1).as_canonical_u64(); + let a_lo = context.stack_item(2).as_canonical_u64(); let dividend = (a_hi << 32) | a_lo; let quotient = dividend / M; diff --git a/crates/lib/core/tests/debug.rs b/crates/lib/core/tests/debug.rs index c2c1f6b2ab..83d3235ce2 100644 --- a/crates/lib/core/tests/debug.rs +++ b/crates/lib/core/tests/debug.rs @@ -149,7 +149,11 @@ fn print_mem_outputs_range() { end "; let out = run_and_capture(source, AdviceInputs::default()); - assert!(out.contains("Memory state"), "missing header; got:\n{out}"); + assert!( + out.contains("Memory state before step ") && out.contains(" in the range [100, 101]:"), + "missing active-context memory header; got:\n{out}" + ); + assert!(!out.contains("for context"), "context id should not be printed; got:\n{out}"); assert!(out.contains("42") && out.contains("43"), "missing memory values; got:\n{out}"); } @@ -237,7 +241,8 @@ fn print_mem_all_outputs_memory() { end "; let out = run_and_capture(source, AdviceInputs::default()); - assert!(out.contains("Memory state"), "missing header; got:\n{out}"); + assert!(out.contains("Memory state before step "), "missing header; got:\n{out}"); + assert!(!out.contains("for context"), "context id should not be printed; got:\n{out}"); assert!(out.contains(": 7"), "missing stored value; got:\n{out}"); } diff --git a/processor/src/fast/basic_block/mod.rs b/processor/src/fast/basic_block/mod.rs index 522fb59362..a6b23e5d40 100644 --- a/processor/src/fast/basic_block/mod.rs +++ b/processor/src/fast/basic_block/mod.rs @@ -107,8 +107,8 @@ impl FastProcessor { ); } - let processor_state = self.state(); - let mutations = host.on_event(&processor_state); + let context = self.state(); + let mutations = host.on_event(&context); self.apply_host_event_mutations( host, op_idx, @@ -139,8 +139,8 @@ impl FastProcessor { ); } - let processor_state = self.state(); - let mutations = host.on_event(&processor_state).await; + let context = self.state(); + let mutations = host.on_event(&context).await; self.apply_host_event_mutations( host, op_idx, diff --git a/processor/src/fast/mod.rs b/processor/src/fast/mod.rs index f514232d59..667b797372 100644 --- a/processor/src/fast/mod.rs +++ b/processor/src/fast/mod.rs @@ -492,7 +492,7 @@ impl FastProcessor { /// Returns the event context exposed to host callbacks. #[inline(always)] pub fn state(&self) -> EventContext<'_> { - EventContext::new(self) + EventContext::new(self, self.ctx) } // MUTATORS @@ -665,17 +665,17 @@ impl FastProcessor { impl EventContextProvider for FastProcessor { #[inline(always)] - fn get_stack_item(&self, position: usize) -> Felt { + fn stack_item(&self, position: usize) -> Felt { self.stack_get_safe(position) } #[inline(always)] - fn get_stack_word(&self, start: usize) -> Word { + fn stack_word(&self, start: usize) -> Word { self.stack_get_word_safe(start) } #[inline(always)] - fn get_stack_state(&self) -> Vec { + fn stack_snapshot(&self) -> Vec { self.stack().iter().rev().copied().collect() } @@ -685,27 +685,22 @@ impl EventContextProvider for FastProcessor { } #[inline(always)] - fn context_id(&self) -> ContextId { - self.ctx + fn memory_value(&self, address: u32) -> Option { + self.memory.read_element_impl(self.ctx, address) } #[inline(always)] - fn get_mem_value(&self, context: ContextId, address: u32) -> Option { - self.memory.read_element_impl(context, address) + fn memory_word(&self, address: u32) -> Result, MemoryError> { + self.memory.read_word_impl(self.ctx, address) } #[inline(always)] - fn get_mem_word(&self, context: ContextId, address: u32) -> Result, MemoryError> { - self.memory.read_word_impl(context, address) + fn memory_snapshot(&self) -> Vec<(MemoryAddress, Felt)> { + self.memory.get_memory_state(self.ctx) } #[inline(always)] - fn get_mem_state(&self, context: ContextId) -> Vec<(MemoryAddress, Felt)> { - self.memory.get_memory_state(context) - } - - #[inline(always)] - fn advice_stack(&self) -> Vec { + fn advice_stack_snapshot(&self) -> Vec { self.advice.stack() } @@ -715,25 +710,20 @@ impl EventContextProvider for FastProcessor { } #[inline(always)] - fn get_advice_map_entry(&self, key: &Word) -> Option<&[Felt]> { + fn advice_map_entry(&self, key: &Word) -> Option<&[Felt]> { self.advice.get_mapped_values(key) } #[inline(always)] - fn get_advice_tree_node( - &self, - root: Word, - depth: Felt, - index: Felt, - ) -> Result { + fn advice_tree_node(&self, root: Word, depth: Felt, index: Felt) -> Result { self.advice .get_tree_node(root, depth, index) .map_err(|err| Box::new(err) as EventError) } #[inline(always)] - fn max_hash_len_bytes(&self) -> usize { - self.options.max_hash_len_bytes() + fn execution_options(&self) -> &ExecutionOptions { + &self.options } #[inline(always)] diff --git a/processor/src/fast/tests/snapshots/miden_processor__fast__tests__advice_provider__event_checkpoints.snap b/processor/src/fast/tests/snapshots/miden_processor__fast__tests__advice_provider__event_checkpoints.snap index f8bfcfd1ec..fa1a2f6cc9 100644 --- a/processor/src/fast/tests/snapshots/miden_processor__fast__tests__advice_provider__event_checkpoints.snap +++ b/processor/src/fast/tests/snapshots/miden_processor__fast__tests__advice_provider__event_checkpoints.snap @@ -6,7 +6,6 @@ expression: fast_host.snapshots() 0: [ ProcessorStateSnapshot { clk: 10, - ctx: 0, stack_state: [ 0, 0, @@ -90,7 +89,6 @@ expression: fast_host.snapshots() 1: [ ProcessorStateSnapshot { clk: 17, - ctx: 0, stack_state: [ 0, 0, @@ -172,7 +170,6 @@ expression: fast_host.snapshots() }, ProcessorStateSnapshot { clk: 33, - ctx: 0, stack_state: [ 0, 0, @@ -256,7 +253,6 @@ expression: fast_host.snapshots() 2: [ ProcessorStateSnapshot { clk: 49, - ctx: 0, stack_state: [ 0, 0, @@ -340,7 +336,6 @@ expression: fast_host.snapshots() 3: [ ProcessorStateSnapshot { clk: 69, - ctx: 0, stack_state: [ 0, 0, @@ -424,7 +419,6 @@ expression: fast_host.snapshots() 4: [ ProcessorStateSnapshot { clk: 92, - ctx: 0, stack_state: [ 0, 0, @@ -508,7 +502,6 @@ expression: fast_host.snapshots() 5: [ ProcessorStateSnapshot { clk: 122, - ctx: 0, stack_state: [ 7, 6, @@ -600,7 +593,6 @@ expression: fast_host.snapshots() 6: [ ProcessorStateSnapshot { clk: 143, - ctx: 0, stack_state: [ 6, 5, @@ -699,7 +691,6 @@ expression: fast_host.snapshots() 7: [ ProcessorStateSnapshot { clk: 156, - ctx: 0, stack_state: [ 5, 4, @@ -789,7 +780,6 @@ expression: fast_host.snapshots() 8: [ ProcessorStateSnapshot { clk: 169, - ctx: 0, stack_state: [ 5, 4, @@ -903,7 +893,6 @@ expression: fast_host.snapshots() 9: [ ProcessorStateSnapshot { clk: 176, - ctx: 0, stack_state: [ 5, 4, @@ -1017,7 +1006,6 @@ expression: fast_host.snapshots() 10: [ ProcessorStateSnapshot { clk: 179, - ctx: 0, stack_state: [ 5, 4, @@ -1131,7 +1119,6 @@ expression: fast_host.snapshots() 11: [ ProcessorStateSnapshot { clk: 191, - ctx: 0, stack_state: [ 5, 4, @@ -1261,7 +1248,6 @@ expression: fast_host.snapshots() }, ProcessorStateSnapshot { clk: 258, - ctx: 0, stack_state: [ 5, 4, @@ -1441,7 +1427,6 @@ expression: fast_host.snapshots() 12: [ ProcessorStateSnapshot { clk: 202, - ctx: 0, stack_state: [ 5, 4, @@ -1579,7 +1564,6 @@ expression: fast_host.snapshots() 13: [ ProcessorStateSnapshot { clk: 234, - ctx: 0, stack_state: [ 5, 4, @@ -1741,7 +1725,6 @@ expression: fast_host.snapshots() 14: [ ProcessorStateSnapshot { clk: 271, - ctx: 0, stack_state: [ 5, 4, @@ -1927,7 +1910,6 @@ expression: fast_host.snapshots() 15: [ ProcessorStateSnapshot { clk: 303, - ctx: 0, stack_state: [ 5, 4, @@ -2113,7 +2095,6 @@ expression: fast_host.snapshots() 16: [ ProcessorStateSnapshot { clk: 313, - ctx: 0, stack_state: [ 1, 5, @@ -2300,7 +2281,6 @@ expression: fast_host.snapshots() 17: [ ProcessorStateSnapshot { clk: 321, - ctx: 0, stack_state: [ 5, 4, @@ -2486,7 +2466,6 @@ expression: fast_host.snapshots() 18: [ ProcessorStateSnapshot { clk: 330, - ctx: 0, stack_state: [ 0, 5, @@ -2673,7 +2652,6 @@ expression: fast_host.snapshots() 19: [ ProcessorStateSnapshot { clk: 338, - ctx: 0, stack_state: [ 5, 4, @@ -2859,7 +2837,6 @@ expression: fast_host.snapshots() 20: [ ProcessorStateSnapshot { clk: 355, - ctx: 0, stack_state: [ 3, 5, @@ -3044,7 +3021,6 @@ expression: fast_host.snapshots() }, ProcessorStateSnapshot { clk: 367, - ctx: 0, stack_state: [ 2, 5, @@ -3229,7 +3205,6 @@ expression: fast_host.snapshots() }, ProcessorStateSnapshot { clk: 379, - ctx: 0, stack_state: [ 1, 5, @@ -3416,7 +3391,6 @@ expression: fast_host.snapshots() 21: [ ProcessorStateSnapshot { clk: 393, - ctx: 0, stack_state: [ 0, 5, @@ -3603,7 +3577,6 @@ expression: fast_host.snapshots() 22: [ ProcessorStateSnapshot { clk: 475, - ctx: 0, stack_state: [ 0, 5, @@ -3807,7 +3780,6 @@ expression: fast_host.snapshots() 100: [ ProcessorStateSnapshot { clk: 225, - ctx: 219, stack_state: [ 5, 4, @@ -3915,7 +3887,6 @@ expression: fast_host.snapshots() 101: [ ProcessorStateSnapshot { clk: 294, - ctx: 0, stack_state: [ 5, 4, diff --git a/processor/src/host/default.rs b/processor/src/host/default.rs index fda4d9177c..d8a89a060f 100644 --- a/processor/src/host/default.rs +++ b/processor/src/host/default.rs @@ -127,7 +127,7 @@ where } fn on_event(&mut self, context: &EventContext<'_>) -> Result, EventError> { - let event_id = EventId::from_felt(context.get_stack_item(0)); + let event_id = context.event_id(); match self.event_handlers.handle_event(event_id, context) { Ok(Some(mutations)) => Ok(mutations), Ok(None) => { diff --git a/processor/src/host/mod.rs b/processor/src/host/mod.rs index d8e5b97db5..b0c0f6eaaa 100644 --- a/processor/src/host/mod.rs +++ b/processor/src/host/mod.rs @@ -74,7 +74,7 @@ pub trait SyncHost: BaseHost { /// may have been pushed onto the stack prior to the emit operation. /// /// ## Implementation notes - /// - Extract the event ID via `EventId::from_felt(process.get_stack_item(0))` + /// - Extract the event ID via `context.event_id()` /// - Return errors without event names or IDs - the caller will enrich them via /// [`BaseHost::resolve_event()`] /// - System events (IDs 0-255) are handled by the VM before calling this method @@ -102,7 +102,7 @@ pub trait Host: BaseHost { /// may have been pushed onto the stack prior to the emit operation. /// /// ## Implementation notes - /// - Extract the event ID via `EventId::from_felt(process.get_stack_item(0))` + /// - Extract the event ID via `context.event_id()` /// - Return errors without event names or IDs - the caller will enrich them via /// [`BaseHost::resolve_event()`] /// - System events (IDs 0-255) are handled by the VM before calling this method diff --git a/processor/src/lib.rs b/processor/src/lib.rs index e49742ec59..48b3cc3f36 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -16,7 +16,7 @@ use miden_mast_package::debug_info::DebugSourceNodeId; mod continuation_stack; mod errors; mod execution; -mod execution_options; + mod fast; mod host; mod processor; @@ -48,7 +48,6 @@ pub use errors::{ advice_error_with_package_source_context, event_error_with_package_source_context, procedure_not_found_with_package_source_context, }; -pub use execution_options::{ExecutionOptions, ExecutionOptionsError}; pub use fast::{BreakReason, ExecutionOutput, FastProcessor, ResumeContext}; pub use host::{ BaseHost, FutureMaybeSend, Host, LoadedMastForest, MastForestStore, MemMastForestStore, @@ -58,8 +57,8 @@ pub use host::{ #[deprecated(note = "use miden_core::events::debug")] pub use miden_core::events::debug::{StdoutWriter, format_value, write_interval, write_stack}; pub use miden_core::{ - ContextId, EMPTY_WORD, Felt, MemoryAddress, MemoryError, ONE, WORD_SIZE, Word, ZERO, crypto, - field, mast, + ContextId, EMPTY_WORD, ExecutionOptions, ExecutionOptionsError, Felt, MemoryAddress, + MemoryError, ONE, WORD_SIZE, Word, ZERO, crypto, field, mast, program::{ InputError, KernelDescriptor, MIN_STACK_DEPTH, Program, ProgramInfo, StackInputs, StackOutputs, @@ -78,8 +77,7 @@ pub mod advice { pub mod event { pub use miden_core::events::{ - AdviceProviderView, EventContext, EventContextProvider, EventId, EventName, - ExecutionOptionsView, SystemEvent, debug, + EventContext, EventContextProvider, EventId, EventName, SystemEvent, debug, }; #[deprecated( note = "import EventError, EventHandler, and NoopEventHandler from miden_core::events" diff --git a/processor/src/test_utils/test_host.rs b/processor/src/test_utils/test_host.rs index ddec6924f1..40a3b8c629 100644 --- a/processor/src/test_utils/test_host.rs +++ b/processor/src/test_utils/test_host.rs @@ -17,7 +17,6 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProcessorStateSnapshot { clk: u32, - ctx: u32, stack_state: Vec, stack_words: [Word; 4], mem_state: Vec<(MemoryAddress, Felt)>, @@ -27,15 +26,14 @@ impl From<&EventContext<'_>> for ProcessorStateSnapshot { fn from(state: &EventContext<'_>) -> Self { ProcessorStateSnapshot { clk: state.clock(), - ctx: state.ctx().into(), - stack_state: state.get_stack_state(), + stack_state: state.stack_snapshot(), stack_words: [ - state.get_stack_word(0), - state.get_stack_word(4), - state.get_stack_word(8), - state.get_stack_word(12), + state.stack_word(0), + state.stack_word(4), + state.stack_word(8), + state.stack_word(12), ], - mem_state: state.get_mem_state(state.ctx()), + mem_state: state.memory_snapshot(), } } } @@ -48,22 +46,21 @@ impl ProcessorStateSnapshot { /// match the state after the trailing `drop`, and to preserve the old trace-decorator test /// shape. fn from_emit_checkpoint(state: &EventContext<'_>) -> Self { - let mut stack_state = state.get_stack_state(); + let mut stack_state = state.stack_snapshot(); if !stack_state.is_empty() { stack_state.remove(0); } ProcessorStateSnapshot { clk: state.clock(), - ctx: state.ctx().into(), stack_state, stack_words: [ - state.get_stack_word(1), - state.get_stack_word(5), - state.get_stack_word(9), - state.get_stack_word(13), + state.stack_word(1), + state.stack_word(5), + state.stack_word(9), + state.stack_word(13), ], - mem_state: state.get_mem_state(state.ctx()), + mem_state: state.memory_snapshot(), } } } @@ -143,7 +140,7 @@ where } fn on_event(&mut self, context: &EventContext<'_>) -> Result, EventError> { - let event_id: u32 = context.get_stack_item(0).as_canonical_u64().try_into().unwrap(); + let event_id: u32 = context.event_id().as_u64().try_into().unwrap(); self.event_handler.push(event_id); self.snapshots .entry(event_id) diff --git a/processor/tests/async_compat.rs b/processor/tests/async_compat.rs index 03bc284fb4..b6c8061e4c 100644 --- a/processor/tests/async_compat.rs +++ b/processor/tests/async_compat.rs @@ -38,11 +38,12 @@ impl Host for YieldingAsyncHost { fn on_event( &mut self, - _context: &EventContext<'_>, + context: &EventContext<'_>, ) -> impl FutureMaybeSend, EventError>> { self.event_calls += 1; - async { + async move { tokio::task::yield_now().await; + let _event_id = context.event_id(); Ok(Vec::new()) } } diff --git a/processor/tests/event_handler_compat.rs b/processor/tests/event_handler_compat.rs index 72906e3f33..f7d3dc4453 100644 --- a/processor/tests/event_handler_compat.rs +++ b/processor/tests/event_handler_compat.rs @@ -27,7 +27,6 @@ fn existing_handler_body(process: &ProcessorState<'_>) -> Result