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