miden_client/transaction/request/
foreign.rs1use alloc::string::ToString;
3use alloc::vec::Vec;
4use core::cmp::Ordering;
5
6use miden_protocol::account::{
7 AccountId,
8 PartialAccount,
9 PartialStorage,
10 PartialStorageMap,
11 StorageMap,
12 StorageMapKey,
13 StorageMapWitness,
14};
15use miden_protocol::asset::{AssetVault, PartialVault};
16use miden_protocol::crypto::merkle::smt::SmtProof;
17use miden_protocol::transaction::AccountInputs;
18use miden_tx::utils::serde::{Deserializable, DeserializationError, Serializable};
19
20use super::TransactionRequestError;
21use crate::rpc::domain::account::{
22 AccountDetails,
23 AccountProof,
24 AccountStorageRequirements,
25 StorageMapEntries,
26};
27
28#[derive(Clone, Debug, PartialEq, Eq)]
33#[allow(clippy::large_enum_variant)]
34pub enum ForeignAccount {
35 Public(AccountId, AccountStorageRequirements),
42 Private(PartialAccount),
46}
47
48impl ForeignAccount {
49 pub fn public(
53 account_id: AccountId,
54 storage_requirements: AccountStorageRequirements,
55 ) -> Result<Self, TransactionRequestError> {
56 if !account_id.has_public_state() {
57 return Err(TransactionRequestError::InvalidForeignAccountId(account_id));
58 }
59
60 Ok(Self::Public(account_id, storage_requirements))
61 }
62
63 pub fn private(account: impl Into<PartialAccount>) -> Result<Self, TransactionRequestError> {
66 let partial_account: PartialAccount = account.into();
67 if partial_account.id().has_public_state() {
68 return Err(TransactionRequestError::InvalidForeignAccountId(partial_account.id()));
69 }
70
71 Ok(Self::Private(partial_account))
72 }
73
74 pub fn storage_slot_requirements(&self) -> AccountStorageRequirements {
75 match self {
76 ForeignAccount::Public(_, account_storage_requirements) => {
77 account_storage_requirements.clone()
78 },
79 ForeignAccount::Private(_) => AccountStorageRequirements::default(),
80 }
81 }
82
83 pub fn account_id(&self) -> AccountId {
85 match self {
86 ForeignAccount::Public(account_id, _) => *account_id,
87 ForeignAccount::Private(partial_account) => partial_account.id(),
88 }
89 }
90}
91
92impl Ord for ForeignAccount {
93 fn cmp(&self, other: &Self) -> Ordering {
94 self.account_id().cmp(&other.account_id())
95 }
96}
97
98impl PartialOrd for ForeignAccount {
99 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
100 Some(self.cmp(other))
101 }
102}
103
104impl Serializable for ForeignAccount {
105 fn write_into<W: miden_tx::utils::serde::ByteWriter>(&self, target: &mut W) {
106 match self {
107 ForeignAccount::Public(account_id, storage_requirements) => {
108 target.write(0u8);
109 account_id.write_into(target);
110 storage_requirements.write_into(target);
111 },
112 ForeignAccount::Private(partial_account) => {
113 target.write(1u8);
114 partial_account.write_into(target);
115 },
116 }
117 }
118}
119
120impl Deserializable for ForeignAccount {
121 fn read_from<R: miden_tx::utils::serde::ByteReader>(
122 source: &mut R,
123 ) -> Result<Self, miden_tx::utils::serde::DeserializationError> {
124 let account_type: u8 = source.read_u8()?;
125 match account_type {
126 0 => {
127 let account_id = AccountId::read_from(source)?;
128 let storage_requirements = AccountStorageRequirements::read_from(source)?;
129 Ok(ForeignAccount::Public(account_id, storage_requirements))
130 },
131 1 => {
132 let foreign_inputs = PartialAccount::read_from(source)?;
133 Ok(ForeignAccount::Private(foreign_inputs))
134 },
135 _ => Err(DeserializationError::InvalidValue("Invalid account type".to_string())),
136 }
137 }
138}
139
140pub(crate) fn account_proof_into_inputs(
145 account_proof: AccountProof,
146 storage_requirements: &AccountStorageRequirements,
147) -> Result<AccountInputs, TransactionRequestError> {
148 let (witness, account_details) = account_proof.into_parts();
149
150 if let Some(AccountDetails {
151 header: account_header,
152 code,
153 storage_details,
154 vault_details,
155 }) = account_details
156 {
157 let account_storage_map_details = storage_details.map_details;
159 let mut storage_map_proofs = Vec::with_capacity(account_storage_map_details.len());
160 for account_storage_detail in account_storage_map_details {
161 let partial_storage = match account_storage_detail.entries {
162 StorageMapEntries::AllEntries(entries) => {
163 let storage_entries_iter = entries.iter().map(|e| (e.key, e.value));
165 PartialStorageMap::new_full(
166 StorageMap::with_entries(storage_entries_iter)
167 .map_err(TransactionRequestError::StorageMapError)?,
168 )
169 },
170 StorageMapEntries::EntriesWithProofs(proofs) => {
171 let keys =
173 storage_requirements.keys_for_slot(&account_storage_detail.slot_name);
174 let witnesses = proofs_to_witnesses(proofs, keys)?;
175 PartialStorageMap::with_witnesses(witnesses)?
176 },
177 };
178 storage_map_proofs.push(partial_storage);
179 }
180
181 let vault = AssetVault::new(&vault_details.assets)?;
182 return Ok(AccountInputs::new(
183 PartialAccount::new(
184 account_header.id(),
185 account_header.nonce(),
186 code,
187 PartialStorage::new(storage_details.header, storage_map_proofs)?,
188 PartialVault::new_full(vault),
189 None,
190 )?,
191 witness,
192 ));
193 }
194 Err(TransactionRequestError::ForeignAccountDataMissing)
195}
196
197fn proofs_to_witnesses(
204 proofs: Vec<SmtProof>,
205 keys: &[StorageMapKey],
206) -> Result<Vec<StorageMapWitness>, TransactionRequestError> {
207 proofs
208 .into_iter()
209 .zip(keys)
210 .map(|(proof, key)| {
211 StorageMapWitness::new(proof, [*key]).map_err(TransactionRequestError::StorageMapError)
212 })
213 .collect()
214}