Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
48 changes: 43 additions & 5 deletions crates/miden-tx/src/executor/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -40,6 +41,9 @@ pub use notes_checker::{
NoteConsumptionInfo,
};

mod program_executor;
pub use program_executor::ProgramExecutor;

// TRANSACTION EXECUTOR
// ================================================================================================

Expand All @@ -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<dyn SourceManagerSync>,
exec_options: ExecutionOptions,
_executor: PhantomData<EXEC>,
}

impl<'store, 'auth, STORE, AUTH> TransactionExecutor<'store, 'auth, STORE, AUTH>
Expand All @@ -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()),
Expand All @@ -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<EXEC2: ProgramExecutor>(
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,
}
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

let output = processor
.execute(&TransactionKernel::tx_script_main(), &mut host)
.await
Expand Down
14 changes: 8 additions & 6 deletions crates/miden-tx/src/executor/notes_checker.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR, but I wonder if the options should be configured somehow (i.e., we shouldn't be using just the default options). cc @PhilippGackstatter.

let result = processor
.execute(&TransactionKernel::main(), &mut host)
.await
Expand Down
52 changes: 52 additions & 0 deletions crates/miden-tx/src/executor/program_executor.rs
Original file line number Diff line number Diff line change
@@ -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<H: Host + Send>(
self,
program: &Program,
host: &mut H,
) -> impl FutureMaybeSend<Result<ExecutionOutput, ExecutionError>>;
}

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<H: Host + Send>(
self,
program: &Program,
host: &mut H,
) -> impl FutureMaybeSend<Result<ExecutionOutput, ExecutionError>> {
FastProcessor::execute(self, program, host)
}
}
1 change: 1 addition & 0 deletions crates/miden-tx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub use executor::{
MastForestStore,
NoteConsumptionChecker,
NoteConsumptionInfo,
ProgramExecutor,
TransactionExecutor,
TransactionExecutorHost,
};
Expand Down
Loading