Skip to main content

miden_objects/
account_file.rs

1//! The account file format.
2
3use alloc::vec::Vec;
4#[cfg(feature = "std")]
5use std::path::Path;
6
7use miden_protocol::account::Account;
8use miden_protocol::account::auth::AuthSecretKey;
9
10use crate::{ConversionError, DecodeMessageExt, proto};
11
12#[cfg(test)]
13mod tests;
14
15// ACCOUNT FILE
16// ================================================================================================
17
18/// A complete description of an account together with the secret keys that authenticate it.
19///
20/// The file is a single unit that carries everything a client needs to act as the account, so it
21/// is the usual way to move an account between clients.
22///
23/// # Warning
24///
25/// The encoded file contains secret key material in the clear.
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct AccountFile {
28    account: Account,
29    auth_secret_keys: Vec<AuthSecretKey>,
30}
31
32impl AccountFile {
33    /// Returns a new [`AccountFile`] with the provided account and secret keys.
34    ///
35    /// A stored key does not have to belong to the account's authentication component, so there is
36    /// nothing to validate between the two.
37    pub fn new(account: Account, auth_secret_keys: Vec<AuthSecretKey>) -> Self {
38        Self { account, auth_secret_keys }
39    }
40
41    /// Returns the account of this file.
42    pub fn account(&self) -> &Account {
43        &self.account
44    }
45
46    /// Returns the secret keys of this file.
47    pub fn auth_secret_keys(&self) -> &[AuthSecretKey] {
48        &self.auth_secret_keys
49    }
50
51    /// Consumes this file and returns its account and secret keys.
52    pub fn into_parts(self) -> (Account, Vec<AuthSecretKey>) {
53        (self.account, self.auth_secret_keys)
54    }
55
56    // SERIALIZATION
57    // --------------------------------------------------------------------------------------------
58
59    /// Returns the encoded file as a Protobuf message.
60    pub fn to_bytes(&self) -> Vec<u8> {
61        prost::Message::encode_to_vec(&proto::account_file::AccountFile::from(self))
62    }
63
64    /// Decodes an [`AccountFile`] from the provided bytes.
65    ///
66    /// The encoded account carries every storage map entry and every vault asset, so the size of
67    /// the file is unbounded. A caller that decodes untrusted bytes must cap their length first.
68    ///
69    /// # Errors
70    ///
71    /// Returns an error if the bytes are not a valid account file.
72    pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, AccountFileError> {
73        <proto::account_file::AccountFile as prost::Message>::decode(bytes)
74            .map_err(|error| AccountFileError::Decode(ConversionError::new(error)))?
75            .decode_and_verify()
76            .map_err(AccountFileError::Decode)
77    }
78
79    /// Writes the encoded file to the provided path.
80    #[cfg(feature = "std")]
81    pub fn write(&self, path: impl AsRef<Path>) -> Result<(), AccountFileError> {
82        std::fs::write(path, self.to_bytes()).map_err(AccountFileError::Io)
83    }
84
85    /// Reads an [`AccountFile`] from the provided path.
86    ///
87    /// # Errors
88    ///
89    /// Returns an error if the file cannot be read, or if [`Self::try_from_bytes`] rejects its
90    /// contents.
91    #[cfg(feature = "std")]
92    pub fn read(path: impl AsRef<Path>) -> Result<Self, AccountFileError> {
93        let bytes = std::fs::read(path).map_err(AccountFileError::Io)?;
94        Self::try_from_bytes(&bytes)
95    }
96}
97
98// ACCOUNT FILE ERROR
99// ================================================================================================
100
101#[derive(Debug, thiserror::Error)]
102#[non_exhaustive]
103pub enum AccountFileError {
104    #[error("failed to decode the account file")]
105    Decode(#[source] ConversionError),
106    #[cfg(feature = "std")]
107    #[error("failed to read or write the account file")]
108    Io(#[source] std::io::Error),
109}