miden_node_proto/domain/
account.rs

1use std::fmt::{Debug, Display, Formatter};
2
3use miden_node_utils::formatting::format_opt;
4use miden_objects::{
5    Digest,
6    account::{Account, AccountHeader, AccountId},
7    block::BlockNumber,
8    crypto::{hash::rpo::RpoDigest, merkle::MerklePath},
9    utils::{Deserializable, Serializable},
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_commitment: 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_commitment: Some(update.account_commitment.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 WITNESS RECORD
153// ================================================================================================
154
155#[derive(Clone, Debug)]
156pub struct AccountWitnessRecord {
157    pub account_id: AccountId,
158    pub initial_state_commitment: Digest,
159    pub proof: MerklePath,
160}
161
162impl From<AccountWitnessRecord> for proto::responses::AccountWitness {
163    fn from(from: AccountWitnessRecord) -> Self {
164        Self {
165            account_id: Some(from.account_id.into()),
166            initial_state_commitment: Some(from.initial_state_commitment.into()),
167            proof: Some(Into::into(&from.proof)),
168        }
169    }
170}
171
172impl TryFrom<proto::responses::AccountWitness> for AccountWitnessRecord {
173    type Error = ConversionError;
174
175    fn try_from(
176        account_witness_record: proto::responses::AccountWitness,
177    ) -> Result<Self, Self::Error> {
178        Ok(Self {
179            account_id: account_witness_record
180                .account_id
181                .ok_or(proto::responses::AccountWitness::missing_field(stringify!(account_id)))?
182                .try_into()?,
183            initial_state_commitment: account_witness_record
184                .initial_state_commitment
185                .ok_or(proto::responses::AccountWitness::missing_field(stringify!(
186                    account_commitment
187                )))?
188                .try_into()?,
189            proof: account_witness_record
190                .proof
191                .as_ref()
192                .ok_or(proto::responses::AccountWitness::missing_field(stringify!(proof)))?
193                .try_into()?,
194        })
195    }
196}
197
198// ACCOUNT STATE
199// ================================================================================================
200
201/// Information needed from the store to verify account in transaction.
202#[derive(Debug)]
203pub struct AccountState {
204    /// Account ID
205    pub account_id: AccountId,
206    /// The account commitment in the store corresponding to tx's account ID
207    pub account_commitment: Option<Digest>,
208}
209
210impl Display for AccountState {
211    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
212        f.write_fmt(format_args!(
213            "{{ account_id: {}, account_commitment: {} }}",
214            self.account_id,
215            format_opt(self.account_commitment.as_ref()),
216        ))
217    }
218}
219
220impl From<AccountState> for proto::responses::AccountTransactionInputRecord {
221    fn from(from: AccountState) -> Self {
222        Self {
223            account_id: Some(from.account_id.into()),
224            account_commitment: from.account_commitment.map(Into::into),
225        }
226    }
227}
228
229impl From<AccountHeader> for proto::account::AccountHeader {
230    fn from(from: AccountHeader) -> Self {
231        Self {
232            vault_root: Some(from.vault_root().into()),
233            storage_commitment: Some(from.storage_commitment().into()),
234            code_commitment: Some(from.code_commitment().into()),
235            nonce: from.nonce().into(),
236        }
237    }
238}
239
240impl TryFrom<proto::responses::AccountTransactionInputRecord> for AccountState {
241    type Error = ConversionError;
242
243    fn try_from(
244        from: proto::responses::AccountTransactionInputRecord,
245    ) -> Result<Self, Self::Error> {
246        let account_id = from
247            .account_id
248            .ok_or(proto::responses::AccountTransactionInputRecord::missing_field(stringify!(
249                account_id
250            )))?
251            .try_into()?;
252
253        let account_commitment = from
254            .account_commitment
255            .ok_or(proto::responses::AccountTransactionInputRecord::missing_field(stringify!(
256                account_commitment
257            )))?
258            .try_into()?;
259
260        // If the commitment is equal to `Digest::default()`, it signifies that this is a new
261        // account which is not yet present in the Store.
262        let account_commitment = if account_commitment == Digest::default() {
263            None
264        } else {
265            Some(account_commitment)
266        };
267
268        Ok(Self { account_id, account_commitment })
269    }
270}