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}