miden_objects/account/
file.rs1use 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#[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 pub fn write(&self, filepath: impl AsRef<Path>) -> io::Result<()> {
50 fs::write(filepath, self.to_bytes())
51 }
52
53 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
65impl 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#[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 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}