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