miden_client/transaction/request/
foreign.rs1use alloc::{string::ToString, vec::Vec};
3use core::cmp::Ordering;
4
5use miden_objects::{
6 account::{Account, AccountCode, AccountHeader, AccountId, AccountStorageHeader, StorageSlot},
7 crypto::merkle::{MerklePath, SmtProof},
8};
9use miden_tx::utils::{Deserializable, DeserializationError, Serializable};
10
11use super::TransactionRequestError;
12use crate::rpc::domain::account::{AccountProof, AccountStorageRequirements, StateHeaders};
13
14#[derive(Clone, Debug, PartialEq, Eq)]
19#[allow(clippy::large_enum_variant)]
20pub enum ForeignAccount {
21 Public(AccountId, AccountStorageRequirements),
25 Private(ForeignAccountInputs),
28}
29
30impl ForeignAccount {
31 pub fn public(
35 account_id: AccountId,
36 storage_requirements: AccountStorageRequirements,
37 ) -> Result<Self, TransactionRequestError> {
38 if !account_id.is_public() {
39 return Err(TransactionRequestError::InvalidForeignAccountId(account_id));
40 }
41
42 Ok(Self::Public(account_id, storage_requirements))
43 }
44
45 pub fn private(
48 account: impl Into<ForeignAccountInputs>,
49 ) -> Result<Self, TransactionRequestError> {
50 let foreign_account: ForeignAccountInputs = account.into();
51 if foreign_account.account_header().id().is_public() {
52 return Err(TransactionRequestError::InvalidForeignAccountId(
53 foreign_account.account_header().id(),
54 ));
55 }
56
57 Ok(Self::Private(foreign_account))
58 }
59
60 pub fn storage_slot_requirements(&self) -> AccountStorageRequirements {
61 match self {
62 ForeignAccount::Public(_, account_storage_requirements) => {
63 account_storage_requirements.clone()
64 },
65 ForeignAccount::Private(_) => AccountStorageRequirements::default(),
66 }
67 }
68
69 pub fn account_id(&self) -> AccountId {
71 match self {
72 ForeignAccount::Public(account_id, _) => *account_id,
73 ForeignAccount::Private(foreign_account_inputs) => {
74 foreign_account_inputs.account_header.id()
75 },
76 }
77 }
78}
79
80impl Ord for ForeignAccount {
81 fn cmp(&self, other: &Self) -> Ordering {
82 self.account_id().cmp(&other.account_id())
83 }
84}
85
86impl PartialOrd for ForeignAccount {
87 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
88 Some(self.cmp(other))
89 }
90}
91
92impl Serializable for ForeignAccount {
93 fn write_into<W: miden_tx::utils::ByteWriter>(&self, target: &mut W) {
94 match self {
95 ForeignAccount::Public(account_id, storage_requirements) => {
96 target.write(0u8);
97 account_id.write_into(target);
98 storage_requirements.write_into(target);
99 },
100 ForeignAccount::Private(foreign_account_inputs) => {
101 target.write(1u8);
102 foreign_account_inputs.write_into(target);
103 },
104 }
105 }
106}
107
108impl Deserializable for ForeignAccount {
109 fn read_from<R: miden_tx::utils::ByteReader>(
110 source: &mut R,
111 ) -> Result<Self, miden_tx::utils::DeserializationError> {
112 let account_type: u8 = source.read_u8()?;
113 match account_type {
114 0 => {
115 let account_id = AccountId::read_from(source)?;
116 let storage_requirements = AccountStorageRequirements::read_from(source)?;
117 Ok(ForeignAccount::Public(account_id, storage_requirements))
118 },
119 1 => {
120 let foreign_inputs = ForeignAccountInputs::read_from(source)?;
121 Ok(ForeignAccount::Private(foreign_inputs))
122 },
123 _ => Err(DeserializationError::InvalidValue("Invalid account type".to_string())),
124 }
125 }
126}
127
128#[derive(Clone, Debug, PartialEq, Eq)]
133pub struct ForeignAccountInputs {
134 account_header: AccountHeader,
136 storage_header: AccountStorageHeader,
138 account_code: AccountCode,
140 storage_map_proofs: Vec<SmtProof>,
142}
143
144impl ForeignAccountInputs {
145 pub fn new(
147 account_header: AccountHeader,
148 storage_header: AccountStorageHeader,
149 account_code: AccountCode,
150 storage_map_proofs: Vec<SmtProof>,
151 ) -> ForeignAccountInputs {
152 ForeignAccountInputs {
153 account_header,
154 storage_header,
155 account_code,
156 storage_map_proofs,
157 }
158 }
159
160 pub fn from_account(
167 account: Account,
168 storage_requirements: &AccountStorageRequirements,
169 ) -> Result<ForeignAccountInputs, TransactionRequestError> {
170 let mut smt_proofs = vec![];
172 for (slot_index, keys) in storage_requirements.inner() {
173 for key in keys {
174 let slot = account.storage().slots().get(*slot_index as usize);
175 match slot {
176 Some(StorageSlot::Map(map)) => {
177 smt_proofs.push(map.open(key));
178 },
179 Some(StorageSlot::Value(_)) => {
180 return Err(
181 TransactionRequestError::ForeignAccountStorageSlotInvalidIndex(
182 *slot_index,
183 ),
184 );
185 },
186 None => {
187 return Err(TransactionRequestError::StorageSlotNotFound(
188 *slot_index,
189 account.id(),
190 ));
191 },
192 }
193 }
194 }
195
196 let account_code: AccountCode = account.code().clone();
197 let storage_header: AccountStorageHeader = account.storage().get_header();
198 let account_header: AccountHeader = account.into();
199
200 Ok(ForeignAccountInputs::new(
201 account_header,
202 storage_header,
203 account_code,
204 smt_proofs,
205 ))
206 }
207
208 pub fn account_header(&self) -> &AccountHeader {
210 &self.account_header
211 }
212
213 pub fn storage_header(&self) -> &AccountStorageHeader {
215 &self.storage_header
216 }
217
218 pub fn storage_map_proofs(&self) -> &[SmtProof] {
220 &self.storage_map_proofs
221 }
222
223 pub fn account_code(&self) -> &AccountCode {
225 &self.account_code
226 }
227
228 #[must_use]
230 pub fn with_storage_map_proofs(
231 mut self,
232 smt_proofs: impl IntoIterator<Item = SmtProof>,
233 ) -> Self {
234 self.storage_map_proofs.extend(smt_proofs);
235 self
236 }
237
238 pub fn into_parts(self) -> (AccountHeader, AccountStorageHeader, AccountCode, Vec<SmtProof>) {
240 (
241 self.account_header,
242 self.storage_header,
243 self.account_code,
244 self.storage_map_proofs,
245 )
246 }
247}
248
249impl Serializable for ForeignAccountInputs {
250 fn write_into<W: miden_tx::utils::ByteWriter>(&self, target: &mut W) {
251 self.account_header.write_into(target);
252 self.storage_header.write_into(target);
253 self.account_code.write_into(target);
254 self.storage_map_proofs.write_into(target);
255 }
256}
257
258impl Deserializable for ForeignAccountInputs {
259 fn read_from<R: miden_tx::utils::ByteReader>(
260 source: &mut R,
261 ) -> Result<Self, miden_tx::utils::DeserializationError> {
262 let account_header = AccountHeader::read_from(source)?;
263 let storage_header = AccountStorageHeader::read_from(source)?;
264 let account_code = AccountCode::read_from(source)?;
265 let storage_maps = Vec::<SmtProof>::read_from(source)?;
266 Ok(ForeignAccountInputs::new(
267 account_header,
268 storage_header,
269 account_code,
270 storage_maps,
271 ))
272 }
273}
274
275impl TryFrom<AccountProof> for (ForeignAccountInputs, MerklePath) {
276 type Error = TransactionRequestError;
277
278 fn try_from(value: AccountProof) -> Result<Self, Self::Error> {
279 let (_, merkle_proof, _, state_headers) = value.into_parts();
280 if let Some(StateHeaders {
281 account_header,
282 storage_header,
283 code,
284 storage_slots,
285 }) = state_headers
286 {
287 let slots = storage_slots.into_iter().flat_map(|(_, slots)| slots).collect();
289 let inputs = ForeignAccountInputs::new(account_header, storage_header, code, slots);
290 return Ok((inputs, merkle_proof));
291 }
292 Err(TransactionRequestError::ForeignAccountDataMissing)
293 }
294}