miden_tx/host/
account_procedures.rs

1use miden_lib::transaction::memory::{ACCOUNT_STACK_TOP_PTR, ACCT_CODE_COMMITMENT_OFFSET};
2use miden_objects::account::AccountCode;
3
4use super::{BTreeMap, ProcessState, Word};
5use crate::errors::{TransactionHostError, TransactionKernelError};
6
7// ACCOUNT PROCEDURE INDEX MAP
8// ================================================================================================
9
10/// A map of maps { acct_code_commitment |-> { proc_root |-> proc_index } } for all known
11/// procedures of account interfaces for all accounts expected to be invoked during transaction
12/// execution.
13#[derive(Debug, Clone, Default)]
14pub struct AccountProcedureIndexMap(BTreeMap<Word, BTreeMap<Word, u8>>);
15
16impl AccountProcedureIndexMap {
17    /// Returns a new [`AccountProcedureIndexMap`] instantiated with account procedures from the
18    /// provided iterator of [`AccountCode`].
19    pub fn new<'code>(
20        account_codes: impl IntoIterator<Item = &'code AccountCode>,
21    ) -> Result<Self, TransactionHostError> {
22        let mut index_map = Self::default();
23
24        for account_code in account_codes {
25            // Insert each account procedures only once.
26            if !index_map.0.contains_key(&account_code.commitment()) {
27                index_map.insert_code(account_code)?;
28            }
29        }
30
31        Ok(index_map)
32    }
33
34    /// Inserts the procedures from the provided [`AccountCode`] into the advice inputs, using
35    /// [`AccountCode::commitment`] as the key.
36    ///
37    /// The resulting instance will map the account code commitment to a mapping of
38    /// `proc_root |-> proc_index` for any account that is expected to be involved in the
39    /// transaction, enabling fast procedure index lookups at runtime.
40    pub fn insert_code(&mut self, code: &AccountCode) -> Result<(), TransactionHostError> {
41        let mut procedure_map = BTreeMap::new();
42        for (proc_idx, proc_info) in code.procedures().iter().enumerate() {
43            let proc_idx = u8::try_from(proc_idx).map_err(|_| {
44                TransactionHostError::AccountProcedureIndexMapError(
45                    "procedure index out of bounds".into(),
46                )
47            })?;
48
49            procedure_map.insert(*proc_info.mast_root(), proc_idx);
50        }
51
52        self.0.insert(code.commitment(), procedure_map);
53
54        Ok(())
55    }
56
57    /// Returns index of the procedure whose root is currently at the top of the operand stack in
58    /// the provided process.
59    ///
60    /// # Errors
61    /// Returns an error if the procedure at the top of the operand stack is not present in this
62    /// map.
63    pub fn get_proc_index(&self, process: &ProcessState) -> Result<u8, TransactionKernelError> {
64        // get active account code commitment
65        let code_commitment = {
66            let account_stack_top_ptr = process
67                .get_mem_value(process.ctx(), ACCOUNT_STACK_TOP_PTR)
68                .expect("Account stack top pointer was not initialized")
69                .as_int();
70            let curr_data_ptr = process
71                .get_mem_value(
72                    process.ctx(),
73                    account_stack_top_ptr
74                        .try_into()
75                        .expect("account stack top pointer should be less than u32::MAX"),
76                )
77                .expect("active account pointer was not initialized")
78                .as_int();
79            process
80                .get_mem_word(process.ctx(), curr_data_ptr as u32 + ACCT_CODE_COMMITMENT_OFFSET)
81                .expect("failed to read a word from memory")
82                .expect("active account code commitment was not initialized")
83        };
84
85        let proc_root = process.get_stack_word_be(1);
86
87        self.0
88            .get(&code_commitment)
89            .ok_or(TransactionKernelError::UnknownCodeCommitment(code_commitment))?
90            .get(&proc_root)
91            .cloned()
92            .ok_or(TransactionKernelError::UnknownAccountProcedure(proc_root))
93    }
94}