miden_objects/account/
file.rs

1use alloc::vec::Vec;
2#[cfg(feature = "std")]
3use std::{
4    fs::{self, File},
5    io::{self, Read},
6    path::Path,
7};
8
9use miden_crypto::utils::SliceReader;
10
11use super::super::utils::serde::{
12    ByteReader,
13    ByteWriter,
14    Deserializable,
15    DeserializationError,
16    Serializable,
17};
18use super::Account;
19use super::auth::AuthSecretKey;
20
21const MAGIC: &str = "acct";
22
23// ACCOUNT FILE
24// ================================================================================================
25
26/// Account file contains a complete description of an account, including the [`Account`] struct as
27/// well as account seed and account authentication info.
28///
29/// The account authentication info consists of a list of [`AuthSecretKey`] that the account may
30/// use within its code.
31///
32/// The intent of this struct is to provide an easy way to serialize and deserialize all
33/// account-related data as a single unit (e.g., to/from files).
34#[derive(Debug, Clone)]
35pub struct AccountFile {
36    pub account: Account,
37    pub auth_secret_keys: Vec<AuthSecretKey>,
38}
39
40impl AccountFile {
41    pub fn new(account: Account, auth_keys: Vec<AuthSecretKey>) -> Self {
42        Self { account, auth_secret_keys: auth_keys }
43    }
44}
45
46#[cfg(feature = "std")]
47impl AccountFile {
48    /// Serializes and writes binary [AccountFile] to specified file
49    pub fn write(&self, filepath: impl AsRef<Path>) -> io::Result<()> {
50        fs::write(filepath, self.to_bytes())
51    }
52
53    /// Reads from file and tries to deserialize an [AccountFile]
54    pub fn read(filepath: impl AsRef<Path>) -> io::Result<Self> {
55        let mut file = File::open(filepath)?;
56        let mut buffer = Vec::new();
57
58        file.read_to_end(&mut buffer)?;
59        let mut reader = SliceReader::new(&buffer);
60
61        Ok(AccountFile::read_from(&mut reader).map_err(|_| io::ErrorKind::InvalidData)?)
62    }
63}
64
65// SERIALIZATION
66// ================================================================================================
67
68impl Serializable for AccountFile {
69    fn write_into<W: ByteWriter>(&self, target: &mut W) {
70        target.write_bytes(MAGIC.as_bytes());
71        let AccountFile { account, auth_secret_keys: auth } = self;
72
73        account.write_into(target);
74        auth.write_into(target);
75    }
76}
77
78impl Deserializable for AccountFile {
79    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
80        let magic_value = source.read_string(4)?;
81        if magic_value != MAGIC {
82            return Err(DeserializationError::InvalidValue(format!(
83                "invalid account file marker: {magic_value}"
84            )));
85        }
86        let account = Account::read_from(source)?;
87        let auth_secret_keys = <Vec<AuthSecretKey>>::read_from(source)?;
88
89        Ok(Self::new(account, auth_secret_keys))
90    }
91
92    fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
93        Self::read_from(&mut SliceReader::new(bytes))
94    }
95}
96
97// TESTS
98// ================================================================================================
99
100#[cfg(test)]
101mod tests {
102    use miden_crypto::utils::{Deserializable, Serializable};
103    use storage::AccountStorage;
104    #[cfg(feature = "std")]
105    use tempfile::tempdir;
106
107    use super::AccountFile;
108    use crate::account::auth::AuthSecretKey;
109    use crate::account::{Account, AccountCode, AccountId, Felt, storage};
110    use crate::asset::AssetVault;
111    use crate::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE;
112
113    fn build_account_file() -> AccountFile {
114        let id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap();
115        let code = AccountCode::mock();
116
117        // create account and auth
118        let vault = AssetVault::new(&[]).unwrap();
119        let storage = AccountStorage::new(vec![]).unwrap();
120        let nonce = Felt::new(1);
121        let account = Account::new_existing(id, vault, storage, code, nonce);
122        let auth_secret_key = AuthSecretKey::new_rpo_falcon512();
123        let auth_secret_key_2 = AuthSecretKey::new_rpo_falcon512();
124
125        AccountFile::new(account, vec![auth_secret_key, auth_secret_key_2])
126    }
127
128    #[test]
129    fn test_serde() {
130        let account_file = build_account_file();
131        let serialized = account_file.to_bytes();
132        let deserialized = AccountFile::read_from_bytes(&serialized).unwrap();
133        assert_eq!(deserialized.account, account_file.account);
134        assert_eq!(
135            deserialized.auth_secret_keys.to_bytes(),
136            account_file.auth_secret_keys.to_bytes()
137        );
138    }
139
140    #[cfg(feature = "std")]
141    #[test]
142    fn test_serde_file() {
143        let dir = tempdir().unwrap();
144        let filepath = dir.path().join("account_file.mac");
145
146        let account_file = build_account_file();
147        account_file.write(filepath.as_path()).unwrap();
148        let deserialized = AccountFile::read(filepath.as_path()).unwrap();
149
150        assert_eq!(deserialized.account, account_file.account);
151        assert_eq!(
152            deserialized.auth_secret_keys.to_bytes(),
153            account_file.auth_secret_keys.to_bytes()
154        );
155    }
156}