diff --git a/CHANGELOG.md b/CHANGELOG.md index c75059727f..d627a6c4f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - Added `InputNoteCommitment::from_parts()` for construction of input note commitments from a nullifier and optional note header ([#2588](https://github.com/0xMiden/protocol/pull/2588)). - Added `bool` schema type to the type registry and updated ACL auth component to use it for boolean config fields ([#2591](https://github.com/0xMiden/protocol/pull/2591)). - Added `component_metadata()` to all account components to expose their metadata ([#2596](https://github.com/0xMiden/protocol/pull/2596)). +- Added `ProgramExecutor` hooks to support DAP and other custom transaction program executors ([#2574](https://github.com/0xMiden/protocol/pull/2574)). ### Changes diff --git a/crates/miden-tx/src/executor/mod.rs b/crates/miden-tx/src/executor/mod.rs index 7ebfcd02e0..88837b1ddb 100644 --- a/crates/miden-tx/src/executor/mod.rs +++ b/crates/miden-tx/src/executor/mod.rs @@ -1,5 +1,6 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; +use core::marker::PhantomData; use miden_processor::advice::AdviceInputs; use miden_processor::{ExecutionError, FastProcessor, StackInputs}; @@ -40,6 +41,9 @@ pub use notes_checker::{ NoteConsumptionInfo, }; +mod program_executor; +pub use program_executor::ProgramExecutor; + // TRANSACTION EXECUTOR // ================================================================================================ @@ -52,11 +56,18 @@ pub use notes_checker::{ /// The transaction executor uses dynamic dispatch with trait objects for the [DataStore] and /// [TransactionAuthenticator], allowing it to be used with different backend implementations. /// At the moment of execution, the [DataStore] is expected to provide all required MAST nodes. -pub struct TransactionExecutor<'store, 'auth, STORE: 'store, AUTH: 'auth> { +pub struct TransactionExecutor< + 'store, + 'auth, + STORE: 'store, + AUTH: 'auth, + EXEC: ProgramExecutor = FastProcessor, +> { data_store: &'store STORE, authenticator: Option<&'auth AUTH>, source_manager: Arc, exec_options: ExecutionOptions, + _executor: PhantomData, } impl<'store, 'auth, STORE, AUTH> TransactionExecutor<'store, 'auth, STORE, AUTH> @@ -71,9 +82,13 @@ where /// /// The created executor will not have the authenticator or source manager set, and tracing and /// debug mode will be turned off. + /// + /// By default, the executor uses [`FastProcessor`](miden_processor::FastProcessor) for program + /// execution. Use [`with_program_executor`](Self::with_program_executor) to plug in a + /// different execution engine. pub fn new(data_store: &'store STORE) -> Self { const _: () = assert!(MIN_TX_EXECUTION_CYCLES <= MAX_TX_EXECUTION_CYCLES); - TransactionExecutor { + Self { data_store, authenticator: None, source_manager: Arc::new(DefaultSourceManager::default()), @@ -85,6 +100,30 @@ where false, ) .expect("Must not fail while max cycles is more than min trace length"), + _executor: PhantomData, + } + } +} + +impl<'store, 'auth, STORE, AUTH, EXEC> TransactionExecutor<'store, 'auth, STORE, AUTH, EXEC> +where + STORE: DataStore + 'store + Sync, + AUTH: TransactionAuthenticator + 'auth + Sync, + EXEC: ProgramExecutor, +{ + /// Replaces the transaction program executor with a different implementation. + /// + /// This allows plugging in alternative execution engines while preserving the rest of the + /// transaction executor configuration. + pub fn with_program_executor( + self, + ) -> TransactionExecutor<'store, 'auth, STORE, AUTH, EXEC2> { + TransactionExecutor::<'store, 'auth, STORE, AUTH, EXEC2> { + data_store: self.data_store, + authenticator: self.authenticator, + source_manager: self.source_manager, + exec_options: self.exec_options, + _executor: PhantomData, } } @@ -186,8 +225,7 @@ where // instantiate the processor in debug mode only when debug mode is specified via execution // options; this is important because in debug mode execution is almost 100x slower - let processor = - FastProcessor::new_with_options(stack_inputs, advice_inputs, self.exec_options); + let processor = EXEC::new(stack_inputs, advice_inputs, self.exec_options); let output = processor .execute(&TransactionKernel::main(), &mut host) @@ -233,7 +271,7 @@ where let (mut host, stack_inputs, advice_inputs) = self.prepare_transaction(&tx_inputs).await?; - let processor = FastProcessor::new(stack_inputs).with_advice(advice_inputs); + let processor = EXEC::new(stack_inputs, advice_inputs, ExecutionOptions::default()); let output = processor .execute(&TransactionKernel::tx_script_main(), &mut host) .await diff --git a/crates/miden-tx/src/executor/notes_checker.rs b/crates/miden-tx/src/executor/notes_checker.rs index 0cbddbe041..33c5870b0d 100644 --- a/crates/miden-tx/src/executor/notes_checker.rs +++ b/crates/miden-tx/src/executor/notes_checker.rs @@ -1,7 +1,6 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; -use miden_processor::FastProcessor; use miden_processor::advice::AdviceInputs; use miden_protocol::account::AccountId; use miden_protocol::block::BlockNumber; @@ -15,7 +14,7 @@ use miden_protocol::transaction::{ }; use miden_standards::note::{NoteConsumptionStatus, StandardNote}; -use super::TransactionExecutor; +use super::{ExecutionOptions, ProgramExecutor, TransactionExecutor}; use crate::auth::TransactionAuthenticator; use crate::errors::TransactionCheckerError; use crate::executor::map_execution_error; @@ -73,15 +72,18 @@ impl NoteConsumptionInfo { /// The check is performed using the [NoteConsumptionChecker::check_notes_consumability] procedure. /// Essentially runs the transaction to make sure that provided input notes could be consumed by the /// account. -pub struct NoteConsumptionChecker<'a, STORE, AUTH>(&'a TransactionExecutor<'a, 'a, STORE, AUTH>); +pub struct NoteConsumptionChecker<'a, STORE, AUTH, EXEC: ProgramExecutor>( + &'a TransactionExecutor<'a, 'a, STORE, AUTH, EXEC>, +); -impl<'a, STORE, AUTH> NoteConsumptionChecker<'a, STORE, AUTH> +impl<'a, STORE, AUTH, EXEC> NoteConsumptionChecker<'a, STORE, AUTH, EXEC> where STORE: DataStore + Sync, AUTH: TransactionAuthenticator + Sync, + EXEC: ProgramExecutor, { /// Creates a new [`NoteConsumptionChecker`] instance with the given transaction executor. - pub fn new(tx_executor: &'a TransactionExecutor<'a, 'a, STORE, AUTH>) -> Self { + pub fn new(tx_executor: &'a TransactionExecutor<'a, 'a, STORE, AUTH, EXEC>) -> Self { NoteConsumptionChecker(tx_executor) } @@ -337,7 +339,7 @@ where .await .map_err(TransactionCheckerError::TransactionPreparation)?; - let processor = FastProcessor::new(stack_inputs).with_advice(advice_inputs); + let processor = EXEC::new(stack_inputs, advice_inputs, ExecutionOptions::default()); let result = processor .execute(&TransactionKernel::main(), &mut host) .await diff --git a/crates/miden-tx/src/executor/program_executor.rs b/crates/miden-tx/src/executor/program_executor.rs new file mode 100644 index 0000000000..f4dc8eaa1d --- /dev/null +++ b/crates/miden-tx/src/executor/program_executor.rs @@ -0,0 +1,52 @@ +use miden_processor::advice::AdviceInputs; +use miden_processor::{ + ExecutionError, + ExecutionOptions, + ExecutionOutput, + FastProcessor, + FutureMaybeSend, + Host, + Program, + StackInputs, +}; + +/// A transaction-scoped program executor used by +/// [`TransactionExecutor`](super::TransactionExecutor). +/// +/// TODO: Move this trait into `miden-vm` once the executor boundary is +/// consolidated there. +pub trait ProgramExecutor { + /// Create a new executor configured with the provided transaction inputs and options. + fn new( + stack_inputs: StackInputs, + advice_inputs: AdviceInputs, + options: ExecutionOptions, + ) -> Self + where + Self: Sized; + + /// Execute the provided program against the given host. + fn execute( + self, + program: &Program, + host: &mut H, + ) -> impl FutureMaybeSend>; +} + +impl ProgramExecutor for FastProcessor { + fn new( + stack_inputs: StackInputs, + advice_inputs: AdviceInputs, + options: ExecutionOptions, + ) -> Self { + FastProcessor::new_with_options(stack_inputs, advice_inputs, options) + } + + fn execute( + self, + program: &Program, + host: &mut H, + ) -> impl FutureMaybeSend> { + FastProcessor::execute(self, program, host) + } +} diff --git a/crates/miden-tx/src/lib.rs b/crates/miden-tx/src/lib.rs index a756df72b7..3dd06bdcc5 100644 --- a/crates/miden-tx/src/lib.rs +++ b/crates/miden-tx/src/lib.rs @@ -15,6 +15,7 @@ pub use executor::{ MastForestStore, NoteConsumptionChecker, NoteConsumptionInfo, + ProgramExecutor, TransactionExecutor, TransactionExecutorHost, };