miden_objects/conversion/
account.rs1use alloc::collections::BTreeSet;
2use alloc::format;
3use alloc::string::ToString;
4use alloc::vec::Vec;
5
6use miden_protocol::account::{
7 AccountHeader,
8 AccountId,
9 AccountStorageHeader,
10 PartialAccount,
11 PartialStorage,
12 PartialStorageMap,
13 StorageMapKey,
14 StorageSlotHeader,
15 StorageSlotId,
16 StorageSlotName,
17 StorageSlotType,
18};
19use miden_protocol::asset::{AssetId, PartialVault};
20use miden_protocol::block::account_tree::AccountWitness;
21use miden_protocol::{Felt, Word};
22
23use super::{MessageDecodeExt, required};
24use crate::{ConversionError, ConversionResultExt, proto};
25
26impl TryFrom<proto::account::AccountId> for AccountId {
27 type Error = ConversionError;
28
29 fn try_from(message: proto::account::AccountId) -> Result<Self, Self::Error> {
30 let bytes: [u8; AccountId::SERIALIZED_SIZE] =
31 message.id.as_slice().try_into().map_err(ConversionError::new)?;
32
33 AccountId::try_from(bytes).map_err(ConversionError::new)
34 }
35}
36
37impl From<&AccountId> for proto::account::AccountId {
38 fn from(account_id: &AccountId) -> Self {
39 let id: [u8; AccountId::SERIALIZED_SIZE] = (*account_id).into();
40 Self { id: id.into() }
41 }
42}
43
44impl From<AccountId> for proto::account::AccountId {
45 fn from(account_id: AccountId) -> Self {
46 (&account_id).into()
47 }
48}
49
50impl TryFrom<proto::account::StorageSlotId> for StorageSlotId {
54 type Error = ConversionError;
55
56 fn try_from(message: proto::account::StorageSlotId) -> Result<Self, Self::Error> {
57 let decoder = message.decoder();
58 let suffix = required!(decoder, message.suffix)?;
59 let prefix = required!(decoder, message.prefix)?;
60 Ok(Self::new(suffix, prefix))
61 }
62}
63
64impl From<StorageSlotId> for proto::account::StorageSlotId {
65 fn from(id: StorageSlotId) -> Self {
66 Self {
67 suffix: Some(id.suffix().into()),
68 prefix: Some(id.prefix().into()),
69 }
70 }
71}
72
73impl From<&StorageSlotId> for proto::account::StorageSlotId {
74 fn from(id: &StorageSlotId) -> Self {
75 (*id).into()
76 }
77}
78
79fn decode_storage_slot_type(slot_type: i32) -> Result<StorageSlotType, ConversionError> {
84 match proto::account::StorageSlotType::try_from(slot_type) {
85 Ok(proto::account::StorageSlotType::Value) => Ok(StorageSlotType::Value),
86 Ok(proto::account::StorageSlotType::Map) => Ok(StorageSlotType::Map),
87 Ok(proto::account::StorageSlotType::Unspecified) => {
88 Err(ConversionError::message("storage slot type is unspecified"))
89 },
90 Err(error) => Err(ConversionError::with_source(
91 format!("unknown storage slot type {slot_type}"),
92 error,
93 )),
94 }
95}
96
97fn encode_storage_slot_type(slot_type: StorageSlotType) -> i32 {
99 match slot_type {
100 StorageSlotType::Value => proto::account::StorageSlotType::Value as i32,
101 StorageSlotType::Map => proto::account::StorageSlotType::Map as i32,
102 }
103}
104
105impl TryFrom<proto::account::AccountStorageHeader> for AccountStorageHeader {
106 type Error = ConversionError;
107
108 fn try_from(message: proto::account::AccountStorageHeader) -> Result<Self, Self::Error> {
109 let slots = message
110 .slots
111 .into_iter()
112 .map(|slot| {
113 let decoder = slot.decoder();
114 let name = StorageSlotName::new(slot.slot_name)?;
115 let slot_type = decode_storage_slot_type(slot.slot_type).context("slot_type")?;
116 let commitment = required!(decoder, slot.commitment)?;
117 Ok(StorageSlotHeader::new(name, slot_type, commitment))
118 })
119 .collect::<Result<Vec<_>, ConversionError>>()
120 .context("slots")?;
121 AccountStorageHeader::new(slots).map_err(ConversionError::new)
122 }
123}
124
125impl From<&AccountStorageHeader> for proto::account::AccountStorageHeader {
126 fn from(account_storage_header: &AccountStorageHeader) -> Self {
127 Self {
128 slots: account_storage_header
129 .slots()
130 .map(|slot| proto::account::account_storage_header::StorageSlot {
131 slot_name: slot.name().to_string(),
132 slot_type: encode_storage_slot_type(slot.slot_type()),
133 commitment: Some(slot.value().into()),
134 })
135 .collect(),
136 }
137 }
138}
139
140impl From<AccountStorageHeader> for proto::account::AccountStorageHeader {
141 fn from(account_storage_header: AccountStorageHeader) -> Self {
142 (&account_storage_header).into()
143 }
144}
145
146fn decode_account_version(version: i32) -> Result<(), ConversionError> {
147 match proto::account::AccountVersion::try_from(version) {
148 Ok(proto::account::AccountVersion::V1) => Ok(()),
149 Ok(proto::account::AccountVersion::Unspecified) => {
150 Err(ConversionError::message("account header version is unspecified"))
151 },
152 Err(error) => Err(ConversionError::with_source(
153 format!("unknown account header version {version}"),
154 error,
155 )),
156 }
157}
158
159impl TryFrom<proto::account::PartialStorageMap> for PartialStorageMap {
163 type Error = ConversionError;
164
165 fn try_from(message: proto::account::PartialStorageMap) -> Result<Self, Self::Error> {
166 let decoder = message.decoder();
167 let smt = required!(decoder, message.smt)?;
168 let keys = message
169 .keys
170 .into_iter()
171 .enumerate()
172 .map(|(index, key)| {
173 Word::try_from(key)
174 .map(StorageMapKey::from_raw)
175 .context(format!("keys[{index}]"))
176 })
177 .collect::<Result<Vec<_>, _>>()?;
178
179 PartialStorageMap::try_from_parts(smt, keys).map_err(ConversionError::new)
180 }
181}
182
183impl From<&PartialStorageMap> for proto::account::PartialStorageMap {
184 fn from(map: &PartialStorageMap) -> Self {
185 Self {
186 smt: Some(map.partial_smt().clone().into()),
187 keys: map.entries().map(|(key, _)| Word::from(*key).into()).collect(),
188 }
189 }
190}
191
192impl From<PartialStorageMap> for proto::account::PartialStorageMap {
193 fn from(map: PartialStorageMap) -> Self {
194 (&map).into()
195 }
196}
197
198impl TryFrom<proto::account::PartialStorage> for PartialStorage {
202 type Error = ConversionError;
203
204 fn try_from(message: proto::account::PartialStorage) -> Result<Self, Self::Error> {
205 let decoder = message.decoder();
206 let header = required!(decoder, message.header)?;
207 let mut roots = BTreeSet::new();
208 let maps = message
209 .maps
210 .into_iter()
211 .enumerate()
212 .map(|(index, map)| {
213 let map_context = format!("maps[{index}]");
214 let map = PartialStorageMap::try_from(map).context(&map_context)?;
215 if !roots.insert(map.root()) {
216 return Err(ConversionError::message("duplicate partial storage map root")
217 .context(map_context));
218 }
219 Ok(map)
220 })
221 .collect::<Result<Vec<_>, _>>()?;
222
223 PartialStorage::new(header, maps).map_err(ConversionError::new)
224 }
225}
226
227impl From<&PartialStorage> for proto::account::PartialStorage {
228 fn from(storage: &PartialStorage) -> Self {
229 Self {
230 header: Some(storage.header().into()),
231 maps: storage.maps().map(Into::into).collect(),
232 }
233 }
234}
235
236impl From<PartialStorage> for proto::account::PartialStorage {
237 fn from(storage: PartialStorage) -> Self {
238 (&storage).into()
239 }
240}
241
242impl TryFrom<proto::account::PartialVault> for PartialVault {
246 type Error = ConversionError;
247
248 fn try_from(message: proto::account::PartialVault) -> Result<Self, Self::Error> {
249 let decoder = message.decoder();
250 let smt = required!(decoder, message.smt)?;
251 let asset_ids = message
252 .asset_ids
253 .into_iter()
254 .enumerate()
255 .map(|(index, id)| {
256 let asset_id_context = format!("asset_ids[{index}]");
257 Word::try_from(id)
258 .context(&asset_id_context)
259 .and_then(|id| AssetId::try_from(id).context(asset_id_context))
260 })
261 .collect::<Result<Vec<_>, _>>()?;
262
263 PartialVault::try_from_parts(smt, asset_ids).map_err(ConversionError::new)
264 }
265}
266
267impl From<&PartialVault> for proto::account::PartialVault {
268 fn from(vault: &PartialVault) -> Self {
269 Self {
270 smt: Some(vault.partial_smt().clone().into()),
271 asset_ids: vault.asset_ids().map(|id| Word::from(id).into()).collect(),
272 }
273 }
274}
275
276impl From<PartialVault> for proto::account::PartialVault {
277 fn from(vault: PartialVault) -> Self {
278 (&vault).into()
279 }
280}
281
282impl TryFrom<proto::account::PartialAccount> for PartialAccount {
286 type Error = ConversionError;
287
288 fn try_from(message: proto::account::PartialAccount) -> Result<Self, Self::Error> {
289 let decoder = message.decoder();
290 let account_id = required!(decoder, message.account_id)?;
291 let nonce = required!(decoder, message.nonce)?;
292 let code = required!(decoder, message.code)?;
293 let storage = required!(decoder, message.storage)?;
294 let vault = required!(decoder, message.vault)?;
295 let seed = message.seed.map(Word::try_from).transpose().context("seed")?;
296
297 PartialAccount::new(account_id, nonce, code, storage, vault, seed)
298 .map_err(ConversionError::new)
299 }
300}
301
302impl From<&PartialAccount> for proto::account::PartialAccount {
303 fn from(account: &PartialAccount) -> Self {
304 Self {
305 account_id: Some(account.id().into()),
306 nonce: Some(account.nonce().into()),
307 code: Some(account.code().into()),
308 storage: Some(account.storage().into()),
309 vault: Some(account.vault().into()),
310 seed: account.seed().map(Into::into),
311 }
312 }
313}
314
315impl From<PartialAccount> for proto::account::PartialAccount {
316 fn from(account: PartialAccount) -> Self {
317 (&account).into()
318 }
319}
320
321impl TryFrom<proto::account::AccountHeader> for AccountHeader {
322 type Error = ConversionError;
323
324 fn try_from(message: proto::account::AccountHeader) -> Result<Self, Self::Error> {
325 decode_account_version(message.version).context("version")?;
326
327 let decoder = message.decoder();
328 let account_id = required!(decoder, message.account_id)?;
329 let vault_root = required!(decoder, message.vault_root)?;
330 let storage_commitment = required!(decoder, message.storage_commitment)?;
331 let code_commitment = required!(decoder, message.code_commitment)?;
332 let nonce = Felt::try_from(message.nonce).map_err(ConversionError::new).context("nonce")?;
333 Ok(AccountHeader::new(
334 account_id,
335 nonce,
336 vault_root,
337 storage_commitment,
338 code_commitment,
339 ))
340 }
341}
342
343impl From<&AccountHeader> for proto::account::AccountHeader {
344 fn from(account_header: &AccountHeader) -> Self {
345 Self {
346 version: proto::account::AccountVersion::V1 as i32,
347 account_id: Some(account_header.id().into()),
348 vault_root: Some(account_header.vault_root().into()),
349 storage_commitment: Some(account_header.storage_commitment().into()),
350 code_commitment: Some(account_header.code_commitment().into()),
351 nonce: account_header.nonce().as_canonical_u64(),
352 }
353 }
354}
355
356impl From<AccountHeader> for proto::account::AccountHeader {
357 fn from(account_header: AccountHeader) -> Self {
358 (&account_header).into()
359 }
360}
361
362impl TryFrom<proto::account::AccountWitness> for AccountWitness {
363 type Error = ConversionError;
364
365 fn try_from(message: proto::account::AccountWitness) -> Result<Self, Self::Error> {
366 let decoder = message.decoder();
367 let witness_id = required!(decoder, message.witness_id)?;
368 let commitment = required!(decoder, message.commitment)?;
369 let path = required!(decoder, message.path)?;
370
371 AccountWitness::new(witness_id, commitment, path).map_err(ConversionError::new)
372 }
373}
374
375impl From<&AccountWitness> for proto::account::AccountWitness {
376 fn from(witness: &AccountWitness) -> Self {
377 Self {
378 witness_id: Some(witness.id().into()),
379 commitment: Some(witness.state_commitment().into()),
380 path: Some(witness.path().clone().into()),
381 }
382 }
383}
384
385impl From<AccountWitness> for proto::account::AccountWitness {
386 fn from(witness: AccountWitness) -> Self {
387 (&witness).into()
388 }
389}