use alloc::string::ToString;
use alloc::sync::Arc;
use miden_core::mast::MastForest;
use miden_core::prettier::PrettyPrint;
use miden_processor::{MastNode, MastNodeExt, MastNodeId};
use super::Felt;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use crate::{AccountError, FieldElement, Word};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct AccountProcedureInfo {
mast_root: Word,
storage_offset: u8,
storage_size: u8,
}
impl AccountProcedureInfo {
pub const NUM_ELEMENTS_PER_PROC: usize = 8;
pub fn new(
mast_root: Word,
storage_offset: u8,
storage_size: u8,
) -> Result<Self, AccountError> {
if storage_size == 0 && storage_offset != 0 {
return Err(AccountError::PureProcedureWithStorageOffset);
}
if storage_offset.checked_add(storage_size).is_none() {
return Err(AccountError::StorageOffsetPlusSizeOutOfBounds(
storage_offset as u16 + storage_size as u16,
));
}
Ok(Self { mast_root, storage_offset, storage_size })
}
pub fn mast_root(&self) -> &Word {
&self.mast_root
}
pub fn storage_offset(&self) -> u8 {
self.storage_offset
}
pub fn storage_size(&self) -> u8 {
self.storage_size
}
}
impl From<AccountProcedureInfo> for [Felt; 8] {
fn from(value: AccountProcedureInfo) -> Self {
let mut result = [Felt::ZERO; 8];
result[0..4].copy_from_slice(value.mast_root().as_elements());
result[4] = Felt::from(value.storage_offset);
result[5] = Felt::from(value.storage_size);
result
}
}
impl TryFrom<[Felt; 8]> for AccountProcedureInfo {
type Error = AccountError;
fn try_from(value: [Felt; 8]) -> Result<Self, Self::Error> {
let mast_root = Word::from(<[Felt; 4]>::try_from(&value[0..4]).unwrap());
let storage_offset: u8 = value[4].try_into().map_err(|_| {
AccountError::AccountCodeProcedureStorageOffsetTooLarge(mast_root, value[4])
})?;
let storage_size: u8 = value[5].try_into().map_err(|_| {
AccountError::AccountCodeProcedureStorageSizeTooLarge(mast_root, value[5])
})?;
if value[6] != Felt::ZERO || value[7] != Felt::ZERO {
return Err(AccountError::AccountCodeProcedureInvalidPadding(mast_root));
}
Ok(Self { mast_root, storage_offset, storage_size })
}
}
impl Serializable for AccountProcedureInfo {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write(self.mast_root);
target.write_u8(self.storage_offset);
target.write_u8(self.storage_size)
}
fn get_size_hint(&self) -> usize {
self.mast_root.get_size_hint()
+ self.storage_offset.get_size_hint()
+ self.storage_size.get_size_hint()
}
}
impl Deserializable for AccountProcedureInfo {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let mast_root: Word = source.read()?;
let storage_offset = source.read_u8()?;
let storage_size = source.read_u8()?;
Self::new(mast_root, storage_offset, storage_size)
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
#[derive(Debug, Clone)]
pub struct PrintableProcedure {
mast: Arc<MastForest>,
procedure_info: AccountProcedureInfo,
entrypoint: MastNodeId,
}
impl PrintableProcedure {
pub(crate) fn new(
mast: Arc<MastForest>,
procedure_info: AccountProcedureInfo,
entrypoint: MastNodeId,
) -> Self {
Self { mast, procedure_info, entrypoint }
}
fn entrypoint(&self) -> &MastNode {
&self.mast[self.entrypoint]
}
pub(crate) fn storage_offset(&self) -> u8 {
self.procedure_info.storage_offset()
}
pub(crate) fn storage_size(&self) -> u8 {
self.procedure_info.storage_size()
}
pub(crate) fn mast_root(&self) -> &Word {
self.procedure_info.mast_root()
}
}
impl PrettyPrint for PrintableProcedure {
fn render(&self) -> miden_core::prettier::Document {
use miden_core::prettier::*;
indent(
4,
const_text("begin") + nl() + self.entrypoint().to_pretty_print(&self.mast).render(),
) + nl()
+ const_text("end")
}
}
#[cfg(test)]
mod tests {
use miden_core::Felt;
use miden_crypto::utils::{Deserializable, Serializable};
use crate::account::{AccountCode, AccountProcedureInfo};
#[test]
fn test_from_to_account_procedure() {
let account_code = AccountCode::mock();
let procedure = account_code.procedures()[0];
let felts: [Felt; 8] = procedure.into();
let final_procedure: AccountProcedureInfo = felts.try_into().unwrap();
assert_eq!(procedure, final_procedure);
}
#[test]
fn test_serde_account_procedure() {
let account_code = AccountCode::mock();
let serialized = account_code.procedures()[0].to_bytes();
let deserialized = AccountProcedureInfo::read_from_bytes(&serialized).unwrap();
assert_eq!(account_code.procedures()[0], deserialized);
}
}