miden_testing/
mock_host.rs

1use alloc::{boxed::Box, collections::BTreeSet, rc::Rc, sync::Arc};
2
3use miden_lib::transaction::{TransactionEvent, TransactionEventError};
4use miden_objects::{
5    Digest,
6    account::{AccountHeader, AccountVaultDelta},
7    assembly::mast::MastNodeExt,
8};
9use miden_tx::{TransactionMastStore, host::AccountProcedureIndexMap};
10use vm_processor::{
11    AdviceInputs, AdviceProvider, AdviceSource, ContextId, ErrorContext, ExecutionError, Host,
12    MastForest, MastForestStore, MemAdviceProvider, ProcessState,
13};
14
15// MOCK HOST
16// ================================================================================================
17
18/// This is very similar to the TransactionHost in miden-tx. The differences include:
19/// - We do not track account delta here.
20/// - There is special handling of EMPTY_DIGEST in account procedure index map.
21/// - This host uses `MemAdviceProvider` which is instantiated from the passed in advice inputs.
22pub struct MockHost {
23    adv_provider: MemAdviceProvider,
24    acct_procedure_index_map: AccountProcedureIndexMap,
25    mast_store: Rc<TransactionMastStore>,
26}
27
28impl MockHost {
29    /// Returns a new [MockHost] instance with the provided [AdviceInputs].
30    pub fn new(
31        account: AccountHeader,
32        advice_inputs: AdviceInputs,
33        mast_store: Rc<TransactionMastStore>,
34        mut foreign_code_commitments: BTreeSet<Digest>,
35    ) -> Self {
36        foreign_code_commitments.insert(account.code_commitment());
37        let adv_provider: MemAdviceProvider = advice_inputs.into();
38        let proc_index_map = AccountProcedureIndexMap::new(foreign_code_commitments, &adv_provider);
39
40        Self {
41            adv_provider,
42            acct_procedure_index_map: proc_index_map.unwrap(),
43            mast_store,
44        }
45    }
46
47    /// Consumes `self` and returns the advice provider and account vault delta.
48    pub fn into_parts(self) -> (MemAdviceProvider, AccountVaultDelta) {
49        (self.adv_provider, AccountVaultDelta::default())
50    }
51
52    // EVENT HANDLERS
53    // --------------------------------------------------------------------------------------------
54
55    fn on_push_account_procedure_index(
56        &mut self,
57        process: ProcessState,
58        err_ctx: &ErrorContext<'_, impl MastNodeExt>,
59    ) -> Result<(), ExecutionError> {
60        let proc_idx = self
61            .acct_procedure_index_map
62            .get_proc_index(&process)
63            .map_err(|err| ExecutionError::event_error(Box::new(err), err_ctx))?;
64        self.adv_provider.push_stack(AdviceSource::Value(proc_idx.into()), err_ctx)?;
65        Ok(())
66    }
67}
68
69impl Host for MockHost {
70    type AdviceProvider = MemAdviceProvider;
71
72    fn advice_provider(&self) -> &Self::AdviceProvider {
73        &self.adv_provider
74    }
75
76    fn advice_provider_mut(&mut self) -> &mut Self::AdviceProvider {
77        &mut self.adv_provider
78    }
79
80    fn get_mast_forest(&self, node_digest: &Digest) -> Option<Arc<MastForest>> {
81        self.mast_store.get(node_digest)
82    }
83
84    fn on_event(
85        &mut self,
86        process: ProcessState,
87        event_id: u32,
88        err_ctx: &ErrorContext<'_, impl MastNodeExt>,
89    ) -> Result<(), ExecutionError> {
90        let event = TransactionEvent::try_from(event_id)
91            .map_err(|err| ExecutionError::event_error(Box::new(err), err_ctx))?;
92
93        if process.ctx() != ContextId::root() {
94            return Err(ExecutionError::event_error(
95                Box::new(TransactionEventError::NotRootContext(event_id)),
96                err_ctx,
97            ));
98        }
99
100        match event {
101            TransactionEvent::AccountPushProcedureIndex => {
102                self.on_push_account_procedure_index(process, err_ctx)
103            },
104            _ => Ok(()),
105        }?;
106
107        Ok(())
108    }
109}