miden_node_proto/domain/
account.rs

1use std::fmt::{Debug, Display, Formatter};
2
3use miden_node_utils::formatting::format_opt;
4use miden_objects::{
5    account::{Account, AccountHeader, AccountId},
6    block::BlockNumber,
7    crypto::{hash::rpo::RpoDigest, merkle::MerklePath},
8    utils::{Deserializable, Serializable},
9    Digest,
10};
11
12use super::try_convert;
13use crate::{
14    errors::{ConversionError, MissingFieldHelper},
15    generated as proto,
16};
17
18// ACCOUNT ID
19// ================================================================================================
20
21impl Display for proto::account::AccountId {
22    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
23        write!(f, "0x")?;
24        for byte in &self.id {
25            write!(f, "{byte:02x}")?;
26        }
27        Ok(())
28    }
29}
30
31impl Debug for proto::account::AccountId {
32    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
33        Display::fmt(self, f)
34    }
35}
36
37// INTO PROTO ACCOUNT ID
38// ------------------------------------------------------------------------------------------------
39
40impl From<&AccountId> for proto::account::AccountId {
41    fn from(account_id: &AccountId) -> Self {
42        (*account_id).into()
43    }
44}
45
46impl From<AccountId> for proto::account::AccountId {
47    fn from(account_id: AccountId) -> Self {
48        Self { id: account_id.to_bytes() }
49    }
50}
51
52// FROM PROTO ACCOUNT ID
53// ------------------------------------------------------------------------------------------------
54
55impl TryFrom<proto::account::AccountId> for AccountId {
56    type Error = ConversionError;
57
58    fn try_from(account_id: proto::account::AccountId) -> Result<Self, Self::Error> {
59        AccountId::read_from_bytes(&account_id.id).map_err(|_| ConversionError::NotAValidFelt)
60    }
61}
62
63// ACCOUNT UPDATE
64// ================================================================================================
65
66#[derive(Debug, PartialEq)]
67pub struct AccountSummary {
68    pub account_id: AccountId,
69    pub account_hash: RpoDigest,
70    pub block_num: BlockNumber,
71}
72
73impl From<&AccountSummary> for proto::account::AccountSummary {
74    fn from(update: &AccountSummary) -> Self {
75        Self {
76            account_id: Some(update.account_id.into()),
77            account_hash: Some(update.account_hash.into()),
78            block_num: update.block_num.as_u32(),
79        }
80    }
81}
82
83#[derive(Debug, PartialEq)]
84pub struct AccountInfo {
85    pub summary: AccountSummary,
86    pub details: Option<Account>,
87}
88
89impl From<&AccountInfo> for proto::account::AccountInfo {
90    fn from(AccountInfo { summary, details }: &AccountInfo) -> Self {
91        Self {
92            summary: Some(summary.into()),
93            details: details.as_ref().map(miden_objects::utils::Serializable::to_bytes),
94        }
95    }
96}
97
98// ACCOUNT STORAGE REQUEST
99// ================================================================================================
100
101/// Represents a request for an account proof alongside specific storage data.
102pub struct AccountProofRequest {
103    pub account_id: AccountId,
104    pub storage_requests: Vec<StorageMapKeysProof>,
105}
106
107impl TryInto<AccountProofRequest> for proto::requests::get_account_proofs_request::AccountRequest {
108    type Error = ConversionError;
109
110    fn try_into(self) -> Result<AccountProofRequest, Self::Error> {
111        let proto::requests::get_account_proofs_request::AccountRequest {
112            account_id,
113            storage_requests,
114        } = self;
115
116        Ok(AccountProofRequest {
117            account_id: account_id
118                .clone()
119                .ok_or(proto::requests::get_account_proofs_request::AccountRequest::missing_field(
120                    stringify!(account_id),
121                ))?
122                .try_into()?,
123            storage_requests: try_convert(storage_requests)?,
124        })
125    }
126}
127
128/// Represents a request for an account's storage map values and its proof of existence.
129pub struct StorageMapKeysProof {
130    /// Index of the storage map
131    pub storage_index: u8,
132    /// List of requested keys in the map
133    pub storage_keys: Vec<Digest>,
134}
135
136impl TryInto<StorageMapKeysProof> for proto::requests::get_account_proofs_request::StorageRequest {
137    type Error = ConversionError;
138
139    fn try_into(self) -> Result<StorageMapKeysProof, Self::Error> {
140        let proto::requests::get_account_proofs_request::StorageRequest {
141            storage_slot_index,
142            map_keys,
143        } = self;
144
145        Ok(StorageMapKeysProof {
146            storage_index: storage_slot_index.try_into()?,
147            storage_keys: try_convert(map_keys)?,
148        })
149    }
150}
151
152// ACCOUNT INPUT RECORD
153// ================================================================================================
154
155#[derive(Clone, Debug)]
156pub struct AccountInputRecord {
157    pub account_id: AccountId,
158    pub account_hash: Digest,
159    pub proof: MerklePath,
160}
161
162impl From<AccountInputRecord> for proto::responses::AccountBlockInputRecord {
163    fn from(from: AccountInputRecord) -> Self {
164        Self {
165            account_id: Some(from.account_id.into()),
166            account_hash: Some(from.account_hash.into()),
167            proof: Some(Into::into(&from.proof)),
168        }
169    }
170}
171
172impl TryFrom<proto::responses::AccountBlockInputRecord> for AccountInputRecord {
173    type Error = ConversionError;
174
175    fn try_from(
176        account_input_record: proto::responses::AccountBlockInputRecord,
177    ) -> Result<Self, Self::Error> {
178        Ok(Self {
179            account_id: account_input_record
180                .account_id
181                .ok_or(proto::responses::AccountBlockInputRecord::missing_field(stringify!(
182                    account_id
183                )))?
184                .try_into()?,
185            account_hash: account_input_record
186                .account_hash
187                .ok_or(proto::responses::AccountBlockInputRecord::missing_field(stringify!(
188                    account_hash
189                )))?
190                .try_into()?,
191            proof: account_input_record
192                .proof
193                .as_ref()
194                .ok_or(proto::responses::AccountBlockInputRecord::missing_field(stringify!(proof)))?
195                .try_into()?,
196        })
197    }
198}
199
200// ACCOUNT STATE
201// ================================================================================================
202
203/// Information needed from the store to verify account in transaction.
204#[derive(Debug)]
205pub struct AccountState {
206    /// Account ID
207    pub account_id: AccountId,
208    /// The account hash in the store corresponding to tx's account ID
209    pub account_hash: Option<Digest>,
210}
211
212impl Display for AccountState {
213    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
214        f.write_fmt(format_args!(
215            "{{ account_id: {}, account_hash: {} }}",
216            self.account_id,
217            format_opt(self.account_hash.as_ref()),
218        ))
219    }
220}
221
222impl From<AccountState> for proto::responses::AccountTransactionInputRecord {
223    fn from(from: AccountState) -> Self {
224        Self {
225            account_id: Some(from.account_id.into()),
226            account_hash: from.account_hash.map(Into::into),
227        }
228    }
229}
230
231impl From<AccountHeader> for proto::account::AccountHeader {
232    fn from(from: AccountHeader) -> Self {
233        Self {
234            vault_root: Some(from.vault_root().into()),
235            storage_commitment: Some(from.storage_commitment().into()),
236            code_commitment: Some(from.code_commitment().into()),
237            nonce: from.nonce().into(),
238        }
239    }
240}
241
242impl TryFrom<proto::responses::AccountTransactionInputRecord> for AccountState {
243    type Error = ConversionError;
244
245    fn try_from(
246        from: proto::responses::AccountTransactionInputRecord,
247    ) -> Result<Self, Self::Error> {
248        let account_id = from
249            .account_id
250            .ok_or(proto::responses::AccountTransactionInputRecord::missing_field(stringify!(
251                account_id
252            )))?
253            .try_into()?;
254
255        let account_hash = from
256            .account_hash
257            .ok_or(proto::responses::AccountTransactionInputRecord::missing_field(stringify!(
258                account_hash
259            )))?
260            .try_into()?;
261
262        // If the hash is equal to `Digest::default()`, it signifies that this is a new account
263        // which is not yet present in the Store.
264        let account_hash = if account_hash == Digest::default() {
265            None
266        } else {
267            Some(account_hash)
268        };
269
270        Ok(Self { account_id, account_hash })
271    }
272}