use miden_mast_package::{Package as Library, ProcedureExport};
use miden_processor::mast::{MastForest, MastNodeExt};
use crate::account::AccountProcedureRoot;
use crate::assembly::Path;
use crate::vm::AdviceMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountComponentCode(Library);
impl AccountComponentCode {
pub fn as_library(&self) -> &Library {
&self.0
}
pub fn mast_forest(&self) -> &MastForest {
self.0.mast_forest().as_ref()
}
pub fn into_library(self) -> Library {
self.0
}
pub fn procedure_roots(&self) -> impl Iterator<Item = AccountProcedureRoot> + '_ {
self.exports().map(|proc_export| {
let digest = if let Some(node) = proc_export.node {
self.0.mast_forest()[node].digest()
} else {
proc_export.digest
};
AccountProcedureRoot::from_raw(digest)
})
}
pub fn exports(&self) -> impl Iterator<Item = &ProcedureExport> + '_ {
self.0
.manifest
.exports()
.filter_map(|export| export.as_procedure())
.filter(|proc_export| {
proc_export.attributes.has(super::ACCOUNT_PROCEDURE_ATTRIBUTE)
|| proc_export.attributes.has(super::AUTH_SCRIPT_ATTRIBUTE)
})
}
pub fn get_procedure_root_by_path(
&self,
proc_name: impl AsRef<Path>,
) -> Option<AccountProcedureRoot> {
let proc_name = proc_name.as_ref();
let absolute_proc_name = proc_name.to_absolute().ok();
self.0
.manifest
.exports()
.filter_map(|export| export.as_procedure())
.find(|export| {
absolute_proc_name.as_ref().is_some_and(|absolute_proc_name| {
export.path.as_ref() == absolute_proc_name.as_ref()
})
})
.map(|export| AccountProcedureRoot::from_raw(export.digest))
}
pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
if advice_map.is_empty() {
return self;
}
Self(self.0.with_advice_map(advice_map))
}
}
impl AsRef<Library> for AccountComponentCode {
fn as_ref(&self) -> &Library {
self.as_library()
}
}
impl From<Library> for AccountComponentCode {
fn from(value: Library) -> Self {
Self(value)
}
}
impl From<AccountComponentCode> for Library {
fn from(value: AccountComponentCode) -> Self {
value.into_library()
}
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use miden_core::{Felt, Word};
use super::*;
use crate::testing::assembler::assemble_test_library;
#[test]
fn test_account_component_code_with_advice_map() {
let library = assemble_test_library(
"test-component-code-advice-map",
"test::component_code_advice_map",
"@account_procedure pub proc test nop end",
);
let component_code = AccountComponentCode::from(library);
assert!(component_code.mast_forest().advice_map().is_empty());
let cloned = component_code.clone();
let original_digest = cloned.as_library().digest();
let component_code = component_code.with_advice_map(AdviceMap::default());
assert_eq!(original_digest, component_code.as_library().digest());
let key = Word::from([10u32, 20, 30, 40]);
let value = vec![Felt::from(200_u8)];
let mut advice_map = AdviceMap::default();
advice_map.insert(key, value.clone());
let component_code = component_code.with_advice_map(advice_map);
let mast = component_code.mast_forest();
let stored = mast.advice_map().get(&key).expect("entry should be present");
assert_eq!(stored.as_ref(), value.as_slice());
}
#[test]
fn test_get_procedure_root_by_path() {
let library = assemble_test_library(
"test-component-code-procedure-root",
"test::component_code_procedure_root",
"@account_procedure pub proc test_proc nop end",
);
let component_code = AccountComponentCode::from(library);
assert_eq!(component_code.procedure_roots().count(), 1);
let expected = component_code.procedure_roots().next().expect("one procedure exported");
let library_namespace = component_code
.as_library()
.module_infos()
.next()
.expect("library should have one module")
.path()
.to_string();
let proc_path = alloc::format!("{library_namespace}::test_proc");
let root = component_code
.get_procedure_root_by_path(&*proc_path)
.expect("test_proc should be present");
assert_eq!(root, expected);
let relative_proc_path =
proc_path.strip_prefix("::").expect("test procedure path should be absolute");
let root = component_code
.get_procedure_root_by_path(relative_proc_path)
.expect("relative test_proc path should be present");
assert_eq!(root, expected);
assert!(component_code.get_procedure_root_by_path("bogus::missing").is_none());
}
}