miden_tx/host/
account_procedures.rs1use alloc::string::ToString;
2
3use miden_lib::transaction::memory::{ACCOUNT_STACK_TOP_PTR, ACCT_CODE_COMMITMENT_OFFSET};
4use miden_lib::transaction::{TransactionAdviceInputs, TransactionKernelError};
5use miden_objects::account::{AccountCode, AccountProcedureInfo};
6use miden_objects::transaction::{TransactionArgs, TransactionInputs};
7use miden_processor::AdviceInputs;
8
9use super::{BTreeMap, Felt, ProcessState, Word};
10use crate::errors::TransactionHostError;
11
12pub struct AccountProcedureIndexMap(BTreeMap<Word, BTreeMap<Word, u8>>);
19
20impl AccountProcedureIndexMap {
21 pub fn new(
27 account_code_commitments: impl IntoIterator<Item = Word>,
28 adv_provider: &AdviceInputs,
29 ) -> Result<Self, TransactionHostError> {
30 let mut result = BTreeMap::new();
31
32 for code_commitment in account_code_commitments {
33 let account_procs_map = build_account_procedure_map(code_commitment, adv_provider)?;
34 result.insert(code_commitment, account_procs_map);
35 }
36
37 Ok(Self(result))
38 }
39
40 pub fn from_transaction_params(
46 tx_inputs: &TransactionInputs,
47 tx_args: &TransactionArgs,
48 tx_advice_inputs: &TransactionAdviceInputs,
49 ) -> Result<Self, TransactionHostError> {
50 let mut account_code_commitments = tx_args.to_foreign_account_code_commitments();
51 account_code_commitments.insert(tx_inputs.account().code().commitment());
52
53 Self::new(account_code_commitments, tx_advice_inputs.as_advice_inputs())
54 }
55
56 pub fn get_proc_index(&self, process: &ProcessState) -> Result<u8, TransactionKernelError> {
63 let code_commitment = {
65 let account_stack_top_ptr = process
66 .get_mem_value(process.ctx(), ACCOUNT_STACK_TOP_PTR)
67 .expect("Account stack top pointer was not initialized")
68 .as_int();
69 let curr_data_ptr = process
70 .get_mem_value(
71 process.ctx(),
72 account_stack_top_ptr
73 .try_into()
74 .expect("account stack top pointer should be less than u32::MAX"),
75 )
76 .expect("Current account pointer was not initialized")
77 .as_int();
78 process
79 .get_mem_word(process.ctx(), curr_data_ptr as u32 + ACCT_CODE_COMMITMENT_OFFSET)
80 .expect("failed to read a word from memory")
81 .expect("current account code commitment was not initialized")
82 };
83
84 let proc_root = process.get_stack_word(0);
85
86 self.0
87 .get(&code_commitment)
88 .ok_or(TransactionKernelError::UnknownCodeCommitment(code_commitment))?
89 .get(&proc_root)
90 .cloned()
91 .ok_or(TransactionKernelError::UnknownAccountProcedure(proc_root))
92 }
93}
94
95fn build_account_procedure_map(
99 code_commitment: Word,
100 advice_inputs: &AdviceInputs,
101) -> Result<BTreeMap<Word, u8>, TransactionHostError> {
102 let proc_data = advice_inputs.map.get(&code_commitment).ok_or_else(|| {
104 TransactionHostError::AccountProcedureIndexMapError(
105 "failed to read account procedure data from the advice provider".to_string(),
106 )
107 })?;
108
109 let mut account_procs_map = BTreeMap::new();
110
111 if proc_data.is_empty() {
115 return Err(TransactionHostError::AccountProcedureIndexMapError(
116 "account code does not contain any procedures.".to_string(),
117 ));
118 }
119
120 if proc_data.len() % AccountProcedureInfo::NUM_ELEMENTS_PER_PROC != 0 {
122 return Err(TransactionHostError::AccountProcedureIndexMapError(
123 "account procedure data has invalid length.".to_string(),
124 ));
125 }
126
127 let num_procs = proc_data.len() / AccountProcedureInfo::NUM_ELEMENTS_PER_PROC;
129
130 if num_procs > AccountCode::MAX_NUM_PROCEDURES {
132 return Err(TransactionHostError::AccountProcedureIndexMapError(
133 "account code contains too many procedures.".to_string(),
134 ));
135 }
136
137 for (proc_idx, proc_info) in
138 proc_data.chunks_exact(AccountProcedureInfo::NUM_ELEMENTS_PER_PROC).enumerate()
139 {
140 let proc_info_array: [Felt; AccountProcedureInfo::NUM_ELEMENTS_PER_PROC] =
141 proc_info.try_into().expect("Failed conversion into procedure info array.");
142
143 let procedure = AccountProcedureInfo::try_from(proc_info_array)
144 .map_err(TransactionHostError::AccountProcedureInfoCreationFailed)?;
145
146 let proc_idx = u8::try_from(proc_idx).expect("Invalid procedure index.");
147
148 account_procs_map.insert(*procedure.mast_root(), proc_idx);
149 }
150
151 Ok(account_procs_map)
152}