1use std::{cmp::max, rc::Rc};
6
7use crate::ledger_info::get_key_durability;
8use crate::storage::EntryWithLiveUntil;
9#[cfg(any(test, feature = "recording_mode"))]
10use crate::{
11 auth::RecordedAuthPayload,
12 xdr::{ContractEvent, ReadXdr, ScVal, SorobanAddressCredentials, SorobanCredentials, WriteXdr},
13 DEFAULT_XDR_RW_LIMITS,
14};
15use crate::{
16 budget::{AsBudget, Budget},
17 crypto::sha256_hash_from_bytes,
18 events::Events,
19 fees::LedgerEntryRentChange,
20 host::{
21 metered_clone::{MeteredAlloc, MeteredClone, MeteredContainer, MeteredIterator},
22 metered_xdr::{metered_from_xdr_with_budget, metered_write_xdr},
23 TraceHook,
24 },
25 storage::{AccessType, Footprint, FootprintMap, SnapshotSource, Storage, StorageMap},
26 xdr::{
27 AccountId, ContractDataDurability, ContractEventType, DiagnosticEvent, HostFunction,
28 LedgerEntry, LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyAccount,
29 LedgerKeyContractCode, LedgerKeyContractData, LedgerKeyTrustLine, ScErrorCode, ScErrorType,
30 SorobanAuthorizationEntry, SorobanResources, TtlEntry,
31 },
32 DiagnosticLevel, Error, Host, HostError, LedgerInfo, MeteredOrdMap,
33};
34#[cfg(any(test, feature = "recording_mode"))]
35use sha2::{Digest, Sha256};
36
37pub type TtlEntryMap = MeteredOrdMap<Rc<LedgerKey>, Rc<TtlEntry>, Budget>;
38
39pub struct InvokeHostFunctionResult {
41 pub encoded_invoke_result: Result<Vec<u8>, HostError>,
43 pub ledger_changes: Vec<LedgerEntryChange>,
52 pub encoded_contract_events: Vec<Vec<u8>>,
57}
58
59#[cfg(any(test, feature = "recording_mode"))]
61pub struct InvokeHostFunctionRecordingModeResult {
62 pub invoke_result: Result<ScVal, HostError>,
64 pub resources: SorobanResources,
66 pub auth: Vec<SorobanAuthorizationEntry>,
69 pub ledger_changes: Vec<LedgerEntryChange>,
76 pub contract_events: Vec<ContractEvent>,
80 pub contract_events_and_return_value_size: u32,
83}
84
85#[derive(Default)]
89pub struct LedgerEntryChange {
90 pub read_only: bool,
92
93 pub encoded_key: Vec<u8>,
95 pub old_entry_size_bytes: u32,
98 pub encoded_new_value: Option<Vec<u8>>,
101 pub ttl_change: Option<LedgerEntryLiveUntilChange>,
104}
105
106#[derive(Debug, Eq, PartialEq, Clone)]
108pub struct LedgerEntryLiveUntilChange {
109 pub key_hash: Vec<u8>,
111 pub durability: ContractDataDurability,
113 pub old_live_until_ledger: u32,
115 pub new_live_until_ledger: u32,
118}
119
120pub fn get_ledger_changes(
124 budget: &Budget,
125 storage: &Storage,
126 init_storage_snapshot: &(impl SnapshotSource + ?Sized),
127 init_ttl_entries: TtlEntryMap,
128) -> Result<Vec<LedgerEntryChange>, HostError> {
129 let mut changes = Vec::with_capacity(storage.map.len());
132
133 let footprint_map = &storage.footprint.0;
134 let internal_error = || {
138 HostError::from(Error::from_type_and_code(
139 ScErrorType::Storage,
140 ScErrorCode::InternalError,
141 ))
142 };
143 for (key, entry_with_live_until_ledger) in storage.map.iter(budget)? {
144 let mut entry_change = LedgerEntryChange::default();
145 metered_write_xdr(budget, key.as_ref(), &mut entry_change.encoded_key)?;
146 let durability = get_key_durability(key);
147
148 if let Some(durability) = durability {
149 let key_hash = match init_ttl_entries.get::<Rc<LedgerKey>>(key, budget)? {
150 Some(ttl_entry) => ttl_entry.key_hash.0.to_vec(),
151 None => sha256_hash_from_bytes(entry_change.encoded_key.as_slice(), budget)?,
152 };
153
154 entry_change.ttl_change = Some(LedgerEntryLiveUntilChange {
155 key_hash,
156 durability,
157 old_live_until_ledger: 0,
158 new_live_until_ledger: 0,
159 });
160 }
161 let entry_with_live_until = init_storage_snapshot.get(key)?;
162 if let Some((old_entry, old_live_until_ledger)) = entry_with_live_until {
163 let mut buf = vec![];
164 metered_write_xdr(budget, old_entry.as_ref(), &mut buf)?;
165 entry_change.old_entry_size_bytes = buf.len() as u32;
166
167 if let Some(ref mut ttl_change) = &mut entry_change.ttl_change {
168 ttl_change.old_live_until_ledger =
169 old_live_until_ledger.ok_or_else(internal_error)?;
170 }
171 }
172 if let Some((_, new_live_until_ledger)) = entry_with_live_until_ledger {
173 if let Some(ref mut ttl_change) = &mut entry_change.ttl_change {
174 ttl_change.new_live_until_ledger = max(
176 new_live_until_ledger.ok_or_else(internal_error)?,
177 ttl_change.old_live_until_ledger,
178 );
179 }
180 }
181 let maybe_access_type: Option<AccessType> =
182 footprint_map.get::<Rc<LedgerKey>>(key, budget)?.copied();
183 match maybe_access_type {
184 Some(AccessType::ReadOnly) => {
185 entry_change.read_only = true;
186 }
187 Some(AccessType::ReadWrite) => {
188 if let Some((entry, _)) = entry_with_live_until_ledger {
189 let mut entry_buf = vec![];
190 metered_write_xdr(budget, entry.as_ref(), &mut entry_buf)?;
191 entry_change.encoded_new_value = Some(entry_buf);
192 }
193 }
194 None => {
195 return Err(internal_error());
196 }
197 }
198 changes.push(entry_change);
199 }
200 Ok(changes)
201}
202
203#[cfg(any(test, feature = "recording_mode"))]
213fn add_footprint_only_ledger_changes(
214 budget: &Budget,
215 storage: &Storage,
216 changes: &mut Vec<LedgerEntryChange>,
217) -> Result<(), HostError> {
218 for (key, access_type) in storage.footprint.0.iter(budget)? {
219 if storage.map.contains_key(key, budget)? {
222 continue;
223 }
224 let mut entry_change = LedgerEntryChange::default();
225 metered_write_xdr(budget, key.as_ref(), &mut entry_change.encoded_key)?;
226 entry_change.read_only = matches!(*access_type, AccessType::ReadOnly);
227 changes.push(entry_change);
228 }
229 Ok(())
230}
231
232pub fn extract_rent_changes(ledger_changes: &[LedgerEntryChange]) -> Vec<LedgerEntryRentChange> {
238 ledger_changes
239 .iter()
240 .filter_map(|entry_change| {
241 if let (Some(ttl_change), optional_encoded_new_value) =
244 (&entry_change.ttl_change, &entry_change.encoded_new_value)
245 {
246 let new_size_bytes = if let Some(encoded_new_value) = optional_encoded_new_value {
247 encoded_new_value.len() as u32
248 } else {
249 entry_change.old_entry_size_bytes
250 };
251
252 if ttl_change.old_live_until_ledger >= ttl_change.new_live_until_ledger
254 && entry_change.old_entry_size_bytes >= new_size_bytes
255 {
256 return None;
257 }
258 Some(LedgerEntryRentChange {
259 is_persistent: matches!(
260 ttl_change.durability,
261 ContractDataDurability::Persistent
262 ),
263 old_size_bytes: entry_change.old_entry_size_bytes,
264 new_size_bytes,
265 old_live_until_ledger: ttl_change.old_live_until_ledger,
266 new_live_until_ledger: ttl_change.new_live_until_ledger,
267 })
268 } else {
269 None
270 }
271 })
272 .collect()
273}
274
275#[allow(clippy::too_many_arguments)]
294pub fn invoke_host_function<T: AsRef<[u8]>, I: ExactSizeIterator<Item = T>>(
295 budget: &Budget,
296 enable_diagnostics: bool,
297 encoded_host_fn: T,
298 encoded_resources: T,
299 encoded_source_account: T,
300 encoded_auth_entries: I,
301 ledger_info: LedgerInfo,
302 encoded_ledger_entries: I,
303 encoded_ttl_entries: I,
304 base_prng_seed: T,
305 diagnostic_events: &mut Vec<DiagnosticEvent>,
306) -> Result<InvokeHostFunctionResult, HostError> {
307 invoke_host_function_with_trace_hook(
308 budget,
309 enable_diagnostics,
310 encoded_host_fn,
311 encoded_resources,
312 encoded_source_account,
313 encoded_auth_entries,
314 ledger_info,
315 encoded_ledger_entries,
316 encoded_ttl_entries,
317 base_prng_seed,
318 diagnostic_events,
319 None,
320 )
321}
322
323#[allow(clippy::too_many_arguments)]
326pub fn invoke_host_function_with_trace_hook<T: AsRef<[u8]>, I: ExactSizeIterator<Item = T>>(
327 budget: &Budget,
328 enable_diagnostics: bool,
329 encoded_host_fn: T,
330 encoded_resources: T,
331 encoded_source_account: T,
332 encoded_auth_entries: I,
333 ledger_info: LedgerInfo,
334 encoded_ledger_entries: I,
335 encoded_ttl_entries: I,
336 base_prng_seed: T,
337 diagnostic_events: &mut Vec<DiagnosticEvent>,
338 trace_hook: Option<TraceHook>,
339) -> Result<InvokeHostFunctionResult, HostError> {
340 let _span0 = tracy_span!("invoke_host_function");
341
342 let resources: SorobanResources =
343 metered_from_xdr_with_budget(encoded_resources.as_ref(), &budget)?;
344 let footprint = build_storage_footprint_from_xdr(&budget, resources.footprint)?;
345 let (storage_map, init_ttl_map) = build_storage_map_from_xdr_ledger_entries(
346 &budget,
347 &footprint,
348 encoded_ledger_entries,
349 encoded_ttl_entries,
350 ledger_info.sequence_number,
351 )?;
352
353 let init_storage_map = storage_map.metered_clone(budget)?;
354
355 let storage = Storage::with_enforcing_footprint_and_map(footprint, storage_map);
356 let host = Host::with_storage_and_budget(storage, budget.clone());
357 let have_trace_hook = trace_hook.is_some();
358 if let Some(th) = trace_hook {
359 host.set_trace_hook(Some(th))?;
360 }
361 let auth_entries = host.build_auth_entries_from_xdr(encoded_auth_entries)?;
362 let host_function: HostFunction = host.metered_from_xdr(encoded_host_fn.as_ref())?;
363 let source_account: AccountId = host.metered_from_xdr(encoded_source_account.as_ref())?;
364 host.set_source_account(source_account)?;
365 host.set_ledger_info(ledger_info)?;
366 host.set_authorization_entries(auth_entries)?;
367 let seed32: [u8; 32] = base_prng_seed.as_ref().try_into().map_err(|_| {
368 host.err(
369 ScErrorType::Context,
370 ScErrorCode::InternalError,
371 "base PRNG seed is not 32-bytes long",
372 &[],
373 )
374 })?;
375 host.set_base_prng_seed(seed32)?;
376 if enable_diagnostics {
377 host.set_diagnostic_level(DiagnosticLevel::Debug)?;
378 }
379 let result = {
380 let _span1 = tracy_span!("Host::invoke_function");
381 host.invoke_function(host_function)
382 };
383 if have_trace_hook {
384 host.set_trace_hook(None)?;
385 }
386 let (storage, events) = host.try_finish()?;
387 if enable_diagnostics {
388 extract_diagnostic_events(&events, diagnostic_events);
389 }
390 let encoded_invoke_result = result.and_then(|res| {
391 let mut encoded_result_sc_val = vec![];
392 metered_write_xdr(&budget, &res, &mut encoded_result_sc_val).map(|_| encoded_result_sc_val)
393 });
394 if encoded_invoke_result.is_ok() {
395 let init_storage_snapshot = StorageMapSnapshotSource {
396 budget: &budget,
397 map: &init_storage_map,
398 };
399 let ledger_changes =
400 get_ledger_changes(&budget, &storage, &init_storage_snapshot, init_ttl_map)?;
401 let encoded_contract_events = encode_contract_events(budget, &events)?;
402 Ok(InvokeHostFunctionResult {
403 encoded_invoke_result,
404 ledger_changes,
405 encoded_contract_events,
406 })
407 } else {
408 Ok(InvokeHostFunctionResult {
409 encoded_invoke_result,
410 ledger_changes: vec![],
411 encoded_contract_events: vec![],
412 })
413 }
414}
415
416#[cfg(any(test, feature = "recording_mode"))]
417impl Host {
418 fn to_xdr_non_metered(&self, v: &impl WriteXdr) -> Result<Vec<u8>, HostError> {
419 v.to_xdr(DEFAULT_XDR_RW_LIMITS).map_err(|_| {
420 self.err(
421 ScErrorType::Value,
422 ScErrorCode::InvalidInput,
423 "could not convert XDR struct to bytes - the input is too deep or too large",
424 &[],
425 )
426 })
427 }
428
429 fn xdr_roundtrip<T>(&self, v: &T) -> Result<T, HostError>
430 where
431 T: WriteXdr + ReadXdr,
432 {
433 self.metered_from_xdr(self.to_xdr_non_metered(v)?.as_slice())
434 }
435}
436
437#[cfg(any(test, feature = "recording_mode"))]
438fn storage_footprint_to_ledger_footprint(
439 footprint: &Footprint,
440) -> Result<LedgerFootprint, HostError> {
441 let mut read_only: Vec<LedgerKey> = Vec::with_capacity(footprint.0.len());
442 let mut read_write: Vec<LedgerKey> = Vec::with_capacity(footprint.0.len());
443 for (key, access_type) in &footprint.0 {
444 match access_type {
445 AccessType::ReadOnly => read_only.push((**key).clone()),
446 AccessType::ReadWrite => read_write.push((**key).clone()),
447 }
448 }
449 Ok(LedgerFootprint {
450 read_only: read_only
451 .try_into()
452 .map_err(|_| HostError::from((ScErrorType::Storage, ScErrorCode::InternalError)))?,
453 read_write: read_write
454 .try_into()
455 .map_err(|_| HostError::from((ScErrorType::Storage, ScErrorCode::InternalError)))?,
456 })
457}
458
459#[cfg(any(test, feature = "recording_mode"))]
460impl RecordedAuthPayload {
461 fn into_auth_entry_with_emulated_signature(
462 self,
463 ) -> Result<SorobanAuthorizationEntry, HostError> {
464 const EMULATED_SIGNATURE_SIZE: usize = 512;
465
466 match (self.address, self.nonce) {
467 (Some(address), Some(nonce)) => Ok(SorobanAuthorizationEntry {
468 credentials: SorobanCredentials::Address(SorobanAddressCredentials {
469 address,
470 nonce,
471 signature_expiration_ledger: 0,
472 signature: ScVal::Bytes(
473 vec![0_u8; EMULATED_SIGNATURE_SIZE].try_into().unwrap(),
474 ),
475 }),
476 root_invocation: self.invocation,
477 }),
478 (None, None) => Ok(SorobanAuthorizationEntry {
479 credentials: SorobanCredentials::SourceAccount,
480 root_invocation: self.invocation,
481 }),
482 (_, _) => Err((ScErrorType::Auth, ScErrorCode::InternalError).into()),
483 }
484 }
485}
486
487#[cfg(any(test, feature = "recording_mode"))]
488fn clear_signature(auth_entry: &mut SorobanAuthorizationEntry) {
489 match &mut auth_entry.credentials {
490 SorobanCredentials::Address(address_creds) => {
491 address_creds.signature = ScVal::Void;
492 }
493 SorobanCredentials::SourceAccount => {}
494 }
495}
496
497#[cfg(any(test, feature = "recording_mode"))]
498#[cfg(not(feature = "unstable-next-api"))]
499pub type RecordingInvocationAuthMode = Option<Vec<SorobanAuthorizationEntry>>;
504
505#[cfg(all(any(test, feature = "recording_mode"), feature = "unstable-next-api"))]
506pub enum RecordingInvocationAuthMode {
508 Enforcing(Vec<SorobanAuthorizationEntry>),
510 Recording(bool),
514}
515
516#[cfg(any(test, feature = "recording_mode"))]
549#[allow(clippy::too_many_arguments)]
550pub fn invoke_host_function_in_recording_mode(
551 budget: &Budget,
552 enable_diagnostics: bool,
553 host_fn: &HostFunction,
554 source_account: &AccountId,
555 auth_mode: RecordingInvocationAuthMode,
556 ledger_info: LedgerInfo,
557 ledger_snapshot: Rc<dyn SnapshotSource>,
558 base_prng_seed: [u8; 32],
559 diagnostic_events: &mut Vec<DiagnosticEvent>,
560) -> Result<InvokeHostFunctionRecordingModeResult, HostError> {
561 let storage = Storage::with_recording_footprint(ledger_snapshot.clone());
562 let host = Host::with_storage_and_budget(storage, budget.clone());
563 #[cfg(not(feature = "unstable-next-api"))]
564 let is_recording_auth = auth_mode.is_none();
565 #[cfg(feature = "unstable-next-api")]
566 let is_recording_auth = matches!(auth_mode, RecordingInvocationAuthMode::Recording(_));
567 let ledger_seq = ledger_info.sequence_number;
568 let host_function = host.xdr_roundtrip(host_fn)?;
569 let source_account: AccountId = host.xdr_roundtrip(source_account)?;
570 host.set_source_account(source_account)?;
571 host.set_ledger_info(ledger_info)?;
572 host.set_base_prng_seed(base_prng_seed)?;
573
574 #[cfg(not(feature = "unstable-next-api"))]
575 if let Some(auth_entries) = &auth_mode {
576 host.set_authorization_entries(auth_entries.clone())?;
577 } else {
578 host.switch_to_recording_auth(true)?;
579 }
580 #[cfg(feature = "unstable-next-api")]
581 match &auth_mode {
582 RecordingInvocationAuthMode::Enforcing(auth_entries) => {
583 host.set_authorization_entries(auth_entries.clone())?;
584 }
585 RecordingInvocationAuthMode::Recording(disable_non_root_auth) => {
586 host.switch_to_recording_auth(*disable_non_root_auth)?;
587 }
588 }
589
590 if enable_diagnostics {
591 host.set_diagnostic_level(DiagnosticLevel::Debug)?;
592 }
593 let invoke_result = host.invoke_function(host_function);
594 let mut contract_events_and_return_value_size = 0_u32;
595 if let Ok(res) = &invoke_result {
596 let mut encoded_result_sc_val = vec![];
597 metered_write_xdr(&budget, res, &mut encoded_result_sc_val)?;
598 contract_events_and_return_value_size = contract_events_and_return_value_size
599 .saturating_add(encoded_result_sc_val.len() as u32);
600 }
601
602 #[cfg(not(feature = "unstable-next-api"))]
603 let mut output_auth = if let Some(auth_entries) = auth_mode {
604 auth_entries
605 } else {
606 let recorded_auth = host.get_recorded_auth_payloads()?;
607 recorded_auth
608 .into_iter()
609 .map(|a| a.into_auth_entry_with_emulated_signature())
610 .collect::<Result<Vec<SorobanAuthorizationEntry>, HostError>>()?
611 };
612 #[cfg(feature = "unstable-next-api")]
613 let mut output_auth = if let RecordingInvocationAuthMode::Enforcing(auth_entries) = auth_mode {
614 auth_entries
615 } else {
616 let recorded_auth = host.get_recorded_auth_payloads()?;
617 recorded_auth
618 .into_iter()
619 .map(|a| a.into_auth_entry_with_emulated_signature())
620 .collect::<Result<Vec<SorobanAuthorizationEntry>, HostError>>()?
621 };
622
623 let encoded_auth_entries = output_auth
624 .iter()
625 .map(|e| host.to_xdr_non_metered(e))
626 .collect::<Result<Vec<Vec<u8>>, HostError>>()?;
627 let decoded_auth_entries = host.build_auth_entries_from_xdr(encoded_auth_entries.iter())?;
628 if is_recording_auth {
629 host.set_authorization_entries(decoded_auth_entries)?;
630 for auth_entry in &mut output_auth {
631 clear_signature(auth_entry);
632 }
633 }
634
635 let (footprint, read_bytes, init_ttl_map) = host.with_mut_storage(|storage| {
636 let footprint = storage_footprint_to_ledger_footprint(&storage.footprint)?;
637 let _footprint_from_xdr = build_storage_footprint_from_xdr(&budget, footprint.clone())?;
638
639 let mut encoded_ledger_entries = Vec::with_capacity(storage.footprint.0.len());
640 let mut encoded_ttl_entries = Vec::with_capacity(storage.footprint.0.len());
641 let mut read_bytes = 0_u32;
642 for (lk, _) in &storage.footprint.0 {
643 let entry_with_live_until = ledger_snapshot.get(lk)?;
644 if let Some((le, live_until)) = entry_with_live_until {
645 let encoded_le = host.to_xdr_non_metered(&*le)?;
646 read_bytes = read_bytes.saturating_add(encoded_le.len() as u32);
647 encoded_ledger_entries.push(encoded_le);
648 if let Some(live_until_ledger) = live_until {
649 let key_xdr = host.to_xdr_non_metered(lk.as_ref())?;
650 let key_hash: [u8; 32] = Sha256::digest(&key_xdr).into();
651 let ttl_entry = TtlEntry {
652 key_hash: key_hash.try_into().map_err(|_| {
653 HostError::from((ScErrorType::Context, ScErrorCode::InternalError))
654 })?,
655 live_until_ledger_seq: live_until_ledger,
656 };
657 encoded_ttl_entries.push(host.to_xdr_non_metered(&ttl_entry)?);
658 } else {
659 encoded_ttl_entries.push(vec![]);
660 }
661 }
662 }
663 let (init_storage, init_ttl_map) = build_storage_map_from_xdr_ledger_entries(
664 &budget,
665 &storage.footprint,
666 encoded_ledger_entries.iter(),
667 encoded_ttl_entries.iter(),
668 ledger_seq,
669 )?;
670 let _init_storage_clone = init_storage.metered_clone(budget)?;
671 Ok((footprint, read_bytes, init_ttl_map))
672 })?;
673 let mut resources = SorobanResources {
674 footprint,
675 instructions: 0,
676 read_bytes,
677 write_bytes: 0,
678 };
679 let _resources_roundtrip: SorobanResources =
680 host.metered_from_xdr(host.to_xdr_non_metered(&resources)?.as_slice())?;
681 let (storage, events) = host.try_finish()?;
682 if enable_diagnostics {
683 extract_diagnostic_events(&events, diagnostic_events);
684 }
685
686 let (ledger_changes, contract_events) = if invoke_result.is_ok() {
687 let mut ledger_changes =
688 get_ledger_changes(&budget, &storage, &*ledger_snapshot, init_ttl_map)?;
689 budget.with_shadow_mode(|| {
693 add_footprint_only_ledger_changes(budget, &storage, &mut ledger_changes)
694 });
695
696 let encoded_contract_events = encode_contract_events(budget, &events)?;
697 for e in &encoded_contract_events {
698 contract_events_and_return_value_size =
699 contract_events_and_return_value_size.saturating_add(e.len() as u32);
700 }
701 let contract_events: Vec<ContractEvent> = events
702 .0
703 .into_iter()
704 .filter(|e| !e.failed_call && e.event.type_ != ContractEventType::Diagnostic)
705 .map(|e| e.event)
706 .collect();
707
708 (ledger_changes, contract_events)
709 } else {
710 (vec![], vec![])
711 };
712 resources.instructions = budget.get_cpu_insns_consumed()? as u32;
713 for ledger_change in &ledger_changes {
714 if !ledger_change.read_only {
715 if let Some(new_entry) = &ledger_change.encoded_new_value {
716 resources.write_bytes =
717 resources.write_bytes.saturating_add(new_entry.len() as u32);
718 }
719 }
720 }
721
722 Ok(InvokeHostFunctionRecordingModeResult {
723 invoke_result,
724 resources,
725 auth: output_auth,
726 ledger_changes,
727 contract_events,
728 contract_events_and_return_value_size,
729 })
730}
731
732pub fn encode_contract_events(budget: &Budget, events: &Events) -> Result<Vec<Vec<u8>>, HostError> {
734 let ce = events
735 .0
736 .iter()
737 .filter(|e| !e.failed_call && e.event.type_ != ContractEventType::Diagnostic)
738 .map(|e| {
739 let mut buf = vec![];
740 metered_write_xdr(budget, &e.event, &mut buf)?;
741 Ok(buf)
742 })
743 .collect::<Result<Vec<Vec<u8>>, HostError>>()?;
744 Vec::<Vec<u8>>::charge_bulk_init_cpy(ce.len() as u64, budget)?;
747 Ok(ce)
748}
749
750fn extract_diagnostic_events(events: &Events, diagnostic_events: &mut Vec<DiagnosticEvent>) {
751 for event in &events.0 {
754 diagnostic_events.push(DiagnosticEvent {
755 in_successful_contract_call: !event.failed_call,
756 event: event.event.clone(),
757 });
758 }
759}
760
761pub(crate) fn ledger_entry_to_ledger_key(
762 le: &LedgerEntry,
763 budget: &Budget,
764) -> Result<LedgerKey, HostError> {
765 match &le.data {
766 LedgerEntryData::Account(a) => Ok(LedgerKey::Account(LedgerKeyAccount {
767 account_id: a.account_id.metered_clone(budget)?,
768 })),
769 LedgerEntryData::Trustline(tl) => Ok(LedgerKey::Trustline(LedgerKeyTrustLine {
770 account_id: tl.account_id.metered_clone(budget)?,
771 asset: tl.asset.metered_clone(budget)?,
772 })),
773 LedgerEntryData::ContractData(cd) => Ok(LedgerKey::ContractData(LedgerKeyContractData {
774 contract: cd.contract.metered_clone(budget)?,
775 key: cd.key.metered_clone(budget)?,
776 durability: cd.durability,
777 })),
778 LedgerEntryData::ContractCode(code) => Ok(LedgerKey::ContractCode(LedgerKeyContractCode {
779 hash: code.hash.metered_clone(budget)?,
780 })),
781 _ => {
782 return Err(Error::from_type_and_code(
783 ScErrorType::Storage,
784 ScErrorCode::InternalError,
785 )
786 .into());
787 }
788 }
789}
790
791fn build_storage_footprint_from_xdr(
792 budget: &Budget,
793 footprint: LedgerFootprint,
794) -> Result<Footprint, HostError> {
795 let mut footprint_map = FootprintMap::new();
796
797 for key in footprint.read_write.as_vec() {
798 Storage::check_supported_ledger_key_type(&key)?;
799 footprint_map = footprint_map.insert(
800 Rc::metered_new(key.metered_clone(budget)?, budget)?,
801 AccessType::ReadWrite,
802 budget,
803 )?;
804 }
805
806 for key in footprint.read_only.as_vec() {
807 Storage::check_supported_ledger_key_type(&key)?;
808 footprint_map = footprint_map.insert(
809 Rc::metered_new(key.metered_clone(budget)?, budget)?,
810 AccessType::ReadOnly,
811 budget,
812 )?;
813 }
814 Ok(Footprint(footprint_map))
815}
816
817fn build_storage_map_from_xdr_ledger_entries<T: AsRef<[u8]>, I: ExactSizeIterator<Item = T>>(
818 budget: &Budget,
819 footprint: &Footprint,
820 encoded_ledger_entries: I,
821 encoded_ttl_entries: I,
822 ledger_num: u32,
823) -> Result<(StorageMap, TtlEntryMap), HostError> {
824 let mut storage_map = StorageMap::new();
825 let mut ttl_map = TtlEntryMap::new();
826
827 if encoded_ledger_entries.len() != encoded_ttl_entries.len() {
828 return Err(
829 Error::from_type_and_code(ScErrorType::Storage, ScErrorCode::InternalError).into(),
830 );
831 }
832
833 for (entry_buf, ttl_buf) in encoded_ledger_entries.zip(encoded_ttl_entries) {
834 let mut live_until_ledger: Option<u32> = None;
835
836 let le = Rc::metered_new(
837 metered_from_xdr_with_budget::<LedgerEntry>(entry_buf.as_ref(), budget)?,
838 budget,
839 )?;
840 let key = Rc::metered_new(ledger_entry_to_ledger_key(&le, budget)?, budget)?;
841
842 if !ttl_buf.as_ref().is_empty() {
843 let ttl_entry = Rc::metered_new(
844 metered_from_xdr_with_budget::<TtlEntry>(ttl_buf.as_ref(), budget)?,
845 budget,
846 )?;
847
848 if ttl_entry.live_until_ledger_seq < ledger_num {
849 return Err(Error::from_type_and_code(
850 ScErrorType::Storage,
851 ScErrorCode::InternalError,
852 )
853 .into());
854 }
855
856 live_until_ledger = Some(ttl_entry.live_until_ledger_seq);
857
858 ttl_map = ttl_map.insert(key.clone(), ttl_entry, budget)?;
859 } else if matches!(le.as_ref().data, LedgerEntryData::ContractData(_))
860 || matches!(le.as_ref().data, LedgerEntryData::ContractCode(_))
861 {
862 return Err(Error::from_type_and_code(
863 ScErrorType::Storage,
864 ScErrorCode::InternalError,
865 )
866 .into());
867 }
868
869 if !footprint.0.contains_key::<LedgerKey>(&key, budget)? {
870 return Err(Error::from_type_and_code(
871 ScErrorType::Storage,
872 ScErrorCode::InternalError,
873 )
874 .into());
875 }
876 storage_map = storage_map.insert(key, Some((le, live_until_ledger)), budget)?;
877 }
878
879 for k in footprint.0.keys(budget)? {
881 if !storage_map.contains_key::<LedgerKey>(k, budget)? {
882 storage_map = storage_map.insert(Rc::clone(k), None, budget)?;
883 }
884 }
885 Ok((storage_map, ttl_map))
886}
887
888impl Host {
889 fn build_auth_entries_from_xdr<T: AsRef<[u8]>, I: ExactSizeIterator<Item = T>>(
890 &self,
891 encoded_contract_auth_entries: I,
892 ) -> Result<Vec<SorobanAuthorizationEntry>, HostError> {
893 encoded_contract_auth_entries
894 .map(|buf| self.metered_from_xdr::<SorobanAuthorizationEntry>(buf.as_ref()))
895 .metered_collect::<Result<Vec<SorobanAuthorizationEntry>, HostError>>(
896 self.as_budget(),
897 )?
898 }
899}
900
901struct StorageMapSnapshotSource<'a> {
902 budget: &'a Budget,
903 map: &'a StorageMap,
904}
905
906impl SnapshotSource for StorageMapSnapshotSource<'_> {
907 fn get(&self, key: &Rc<LedgerKey>) -> Result<Option<EntryWithLiveUntil>, HostError> {
908 if let Some(Some((entry, live_until_ledger))) =
909 self.map.get::<Rc<LedgerKey>>(key, self.budget)?
910 {
911 Ok(Some((Rc::clone(entry), *live_until_ledger)))
912 } else {
913 Ok(None)
914 }
915 }
916}