soroban_env_host_zephyr/
host.rs

1use core::{cell::RefCell, cmp::Ordering, fmt::Debug};
2use std::rc::Rc;
3
4use crate::{
5    auth::AuthorizationManager,
6    budget::{AsBudget, Budget},
7    events::{diagnostic::DiagnosticLevel, Events, InternalEventsBuffer},
8    host_object::{HostMap, HostObject, HostVec},
9    impl_bignum_host_fns, impl_bignum_host_fns_rhs_u32, impl_wrapping_obj_from_num,
10    impl_wrapping_obj_to_num,
11    num::*,
12    storage::Storage,
13    vm::{CustomContextVM, ModuleCache},
14    xdr::{
15        int128_helpers, AccountId, Asset, ContractCostType, ContractEventType, ContractExecutable,
16        ContractIdPreimage, ContractIdPreimageFromAddress, CreateContractArgs, Duration, Hash,
17        LedgerEntryData, PublicKey, ScAddress, ScBytes, ScErrorCode, ScErrorType, ScString,
18        ScSymbol, ScVal, TimePoint, Uint256,
19    },
20    AddressObject, Bool, BytesObject, Compare, ConversionError, EnvBase, Error, LedgerInfo,
21    MapObject, Object, StorageType, StringObject, Symbol, SymbolObject, TryFromVal, Val, VecObject,
22    VmCaller, VmCallerEnv, Void,
23};
24
25mod comparison;
26mod conversion;
27pub(crate) mod crypto;
28mod data_helper;
29mod declared_size;
30pub(crate) mod error;
31pub(crate) mod frame;
32pub(crate) mod ledger_info_helper;
33mod lifecycle;
34mod mem_helper;
35pub(crate) mod metered_clone;
36pub(crate) mod metered_hash;
37pub(crate) mod metered_map;
38pub(crate) mod metered_vector;
39pub(crate) mod metered_xdr;
40mod num;
41mod prng;
42pub(crate) mod trace;
43mod validity;
44
45pub use error::HostError;
46pub use prng::{Seed, SEED_BYTES};
47pub use trace::{TraceEvent, TraceHook, TraceRecord, TraceState};
48
49use self::{
50    frame::{Context, ContractReentryMode},
51    mem_helper::{MemFnArgs, MemFnArgsCustomVm, MemFnArgsCustomVmMut},
52    metered_clone::{MeteredClone, MeteredContainer},
53    metered_xdr::metered_write_xdr,
54    prng::Prng,
55};
56
57use crate::host::error::TryBorrowOrErr;
58#[cfg(any(test, feature = "testutils"))]
59pub use frame::ContractFunctionSet;
60pub(crate) use frame::Frame;
61#[cfg(any(test, feature = "recording_mode"))]
62use rand_chacha::ChaCha20Rng;
63use soroban_env_common::SymbolSmall;
64
65#[cfg(any(test, feature = "testutils"))]
66#[derive(Clone, Copy)]
67pub enum ContractInvocationEvent {
68    Start,
69    Finish,
70}
71
72#[cfg(any(test, feature = "testutils"))]
73pub type ContractInvocationHook = Rc<dyn for<'a> Fn(&'a Host, ContractInvocationEvent) -> ()>;
74
75#[cfg(any(test, feature = "testutils"))]
76#[derive(Clone, Default)]
77pub struct CoverageScoreboard {
78    pub vm_to_vm_calls: usize,
79}
80
81#[derive(Clone, Default)]
82struct HostImpl {
83    module_cache: RefCell<Option<ModuleCache>>,
84    shared_linker: RefCell<Option<wasmi::Linker<Host>>>,
85    source_account: RefCell<Option<AccountId>>,
86    ledger: RefCell<Option<LedgerInfo>>,
87    objects: RefCell<Vec<HostObject>>,
88    storage: RefCell<Storage>,
89    context_stack: RefCell<Vec<Context>>,
90    // Note: budget is refcounted and is _not_ deep-cloned when you call HostImpl::deep_clone,
91    // mainly because it's not really possible to achieve (the same budget is connected to many
92    // metered sub-objects) but also because it's plausible that the person calling deep_clone
93    // actually wants their clones to be metered by "the same" total budget
94    // FIXME: deep_clone is gone, maybe Budget should not be separately refcounted?
95    budget: Budget,
96    events: RefCell<InternalEventsBuffer>,
97    authorization_manager: RefCell<AuthorizationManager>,
98    // Note: to reduce the risk of future maintainers accidentally adding a new
99    // way of observing the diagnostic level (which may vary between different
100    // replicas of the host, thus causing divergence) there are no borrow
101    // helpers for it and the only method to use it is inside the
102    // `with_debug_mode` callback that switches to the shadow budget.
103    diagnostic_level: RefCell<DiagnosticLevel>,
104    base_prng: RefCell<Option<Prng>>,
105    // Auth-recording mode generates pseudorandom nonces to populate its output.
106    // We'd like these to be deterministic from one run to the next, but also
107    // completely isolated from any use of the user-accessible PRNGs (either
108    // base or local) such that contracts behave exactly the same whether or not
109    // they're recording auth. Therefore this task gets its own PRNG, seeded when
110    // the base PRNG is seeded (as a derived PRNG).
111    #[cfg(any(test, feature = "recording_mode"))]
112    recording_auth_nonce_prng: RefCell<Option<ChaCha20Rng>>,
113    // Some tests _of the host_ rely on pseudorandom _input_ data. For these cases we attach
114    // yet another unmetered PRNG to the host.
115    #[cfg(any(test, feature = "testutils"))]
116    test_prng: RefCell<Option<ChaCha20Rng>>,
117    // Note: we're not going to charge metering for testutils because it's out of the scope
118    // of what users will be charged for in production -- it's scaffolding for testing a contract,
119    // but shouldn't be charged to the contract itself (and will never be compiled-in to
120    // production hosts)
121    #[cfg(any(test, feature = "testutils"))]
122    contracts: RefCell<std::collections::BTreeMap<Hash, Rc<dyn ContractFunctionSet>>>,
123    // Store a copy of the `AuthorizationManager` for the last host function
124    // invocation. In order to emulate the production behavior in tests, we reset
125    // authorization manager after every invocation (as it's not meant to be
126    // shared between invocations).
127    // This enables test-only functions that allow checking if the authorization
128    // has happened or has been recorded.
129    #[cfg(any(test, feature = "testutils"))]
130    previous_authorization_manager: RefCell<Option<AuthorizationManager>>,
131    // Store a hook that we will call with various lifecycle events during
132    // the host's execution. No guarantees are made about the stability of this
133    // interface, it exists strictly for internal testing of the host.
134    #[doc(hidden)]
135    trace_hook: RefCell<Option<TraceHook>>,
136    // Store a simple contract invocation hook for public usage.
137    // The hook triggers when the top-level contract invocation
138    // starts and when it ends.
139    #[doc(hidden)]
140    #[cfg(any(test, feature = "testutils"))]
141    top_contract_invocation_hook: RefCell<Option<ContractInvocationHook>>,
142
143    // A utility to help us measure certain key events we're interested
144    // in observing the coverage of. Only written-to, never read, it
145    // exists only so that we can observe in aggregated code-coverage
146    // measurements whether the lines of code that write to its fields are
147    // covered.
148    #[doc(hidden)]
149    #[cfg(any(test, feature = "testutils"))]
150    coverage_scoreboard: RefCell<CoverageScoreboard>,
151
152    #[doc(hidden)]
153    #[cfg(any(test, feature = "recording_mode"))]
154    suppress_diagnostic_events: RefCell<bool>,
155
156    // This flag marks the call of `build_module_cache` that would happen
157    // in enforcing mode. In recording mode we need to use this flag to
158    // determine whether we need to rebuild module cache after the host
159    // invocation has been done.
160    #[doc(hidden)]
161    #[cfg(any(test, feature = "recording_mode"))]
162    need_to_build_module_cache: RefCell<bool>,
163}
164
165// Host is a newtype on Rc<HostImpl> so we can impl Env for it below.
166#[derive(Clone)]
167pub struct Host(Rc<HostImpl>);
168
169#[allow(clippy::derivable_impls)]
170impl Default for Host {
171    fn default() -> Self {
172        #[cfg(all(not(target_family = "wasm"), feature = "tracy"))]
173        let _client = tracy_client::Client::start();
174        Self(Default::default())
175    }
176}
177
178macro_rules! impl_checked_borrow_helpers {
179    ($field:ident, $t:ty, $borrow:ident, $borrow_mut:ident) => {
180        impl Host {
181            #[allow(dead_code, unused_imports)]
182            pub(crate) fn $borrow(&self) -> Result<std::cell::Ref<'_, $t>, HostError> {
183                use crate::host::error::TryBorrowOrErr;
184                self.0.$field.try_borrow_or_err_with(
185                    self,
186                    concat!("host.0.", stringify!($field), ".try_borrow failed"),
187                )
188            }
189            #[allow(dead_code, unused_imports)]
190            pub(crate) fn $borrow_mut(&self) -> Result<std::cell::RefMut<'_, $t>, HostError> {
191                use crate::host::error::TryBorrowOrErr;
192                self.0.$field.try_borrow_mut_or_err_with(
193                    self,
194                    concat!("host.0.", stringify!($field), ".try_borrow_mut failed"),
195                )
196            }
197        }
198    };
199}
200impl_checked_borrow_helpers!(
201    module_cache,
202    Option<ModuleCache>,
203    try_borrow_module_cache,
204    try_borrow_module_cache_mut
205);
206impl_checked_borrow_helpers!(
207    shared_linker,
208    Option<wasmi::Linker<Host>>,
209    try_borrow_linker,
210    try_borrow_linker_mut
211);
212impl_checked_borrow_helpers!(
213    source_account,
214    Option<AccountId>,
215    try_borrow_source_account,
216    try_borrow_source_account_mut
217);
218impl_checked_borrow_helpers!(
219    ledger,
220    Option<LedgerInfo>,
221    try_borrow_ledger,
222    try_borrow_ledger_mut
223);
224impl_checked_borrow_helpers!(
225    objects,
226    Vec<HostObject>,
227    try_borrow_objects,
228    try_borrow_objects_mut
229);
230impl_checked_borrow_helpers!(storage, Storage, try_borrow_storage, try_borrow_storage_mut);
231impl_checked_borrow_helpers!(
232    context_stack,
233    Vec<Context>,
234    try_borrow_context_stack,
235    try_borrow_context_stack_mut
236);
237impl_checked_borrow_helpers!(
238    events,
239    InternalEventsBuffer,
240    try_borrow_events,
241    try_borrow_events_mut
242);
243impl_checked_borrow_helpers!(
244    authorization_manager,
245    AuthorizationManager,
246    try_borrow_authorization_manager,
247    try_borrow_authorization_manager_mut
248);
249
250// Note: diagnostic_mode borrow helpers are _not_ defined here to reduce the
251// risk of future maintainers accidentally revealing any way of observing the
252// diagnostic level in user code (which may vary between different replicas of
253// the host).
254
255impl_checked_borrow_helpers!(
256    base_prng,
257    Option<Prng>,
258    try_borrow_base_prng,
259    try_borrow_base_prng_mut
260);
261
262#[cfg(any(test, feature = "recording_mode"))]
263impl_checked_borrow_helpers!(
264    recording_auth_nonce_prng,
265    Option<ChaCha20Rng>,
266    try_borrow_recording_auth_nonce_prng,
267    try_borrow_recording_auth_nonce_prng_mut
268);
269
270#[cfg(any(test, feature = "testutils"))]
271impl_checked_borrow_helpers!(
272    test_prng,
273    Option<ChaCha20Rng>,
274    try_borrow_test_prng,
275    try_borrow_test_prng_mut
276);
277
278#[cfg(any(test, feature = "testutils"))]
279impl_checked_borrow_helpers!(contracts, std::collections::BTreeMap<Hash, Rc<dyn ContractFunctionSet>>, try_borrow_contracts, try_borrow_contracts_mut);
280
281#[cfg(any(test, feature = "testutils"))]
282impl_checked_borrow_helpers!(
283    previous_authorization_manager,
284    Option<AuthorizationManager>,
285    try_borrow_previous_authorization_manager,
286    try_borrow_previous_authorization_manager_mut
287);
288
289impl_checked_borrow_helpers!(
290    trace_hook,
291    Option<TraceHook>,
292    try_borrow_trace_hook,
293    try_borrow_trace_hook_mut
294);
295
296#[cfg(any(test, feature = "testutils"))]
297impl_checked_borrow_helpers!(
298    top_contract_invocation_hook,
299    Option<ContractInvocationHook>,
300    try_borrow_top_contract_invocation_hook,
301    try_borrow_top_contract_invocation_hook_mut
302);
303
304#[cfg(any(test, feature = "testutils"))]
305impl_checked_borrow_helpers!(
306    coverage_scoreboard,
307    CoverageScoreboard,
308    try_borrow_coverage_scoreboard,
309    try_borrow_coverage_scoreboard_mut
310);
311
312#[cfg(any(test, feature = "recording_mode"))]
313impl_checked_borrow_helpers!(
314    suppress_diagnostic_events,
315    bool,
316    try_borrow_suppress_diagnostic_events,
317    try_borrow_suppress_diagnostic_events_mut
318);
319
320#[cfg(any(test, feature = "recording_mode"))]
321impl_checked_borrow_helpers!(
322    need_to_build_module_cache,
323    bool,
324    try_borrow_need_to_build_module_cache,
325    try_borrow_need_to_build_module_cache_mut
326);
327
328impl Debug for HostImpl {
329    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
330        write!(f, "HostImpl(...)")
331    }
332}
333
334impl Debug for Host {
335    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
336        write!(f, "Host({:x})", Rc::<HostImpl>::as_ptr(&self.0) as usize)
337    }
338}
339
340impl Host {
341    pub fn vec_new_from_linear_memory_mem<M: CustomContextVM>(
342        &self,
343        m: M,
344        vals_pos: U32Val,
345        len: U32Val,
346    ) -> Result<VecObject, HostError> {
347        let MemFnArgsCustomVm { pos, len, .. } = self.get_mem_fn_args_custom_vm(&m, vals_pos, len);
348        Vec::<Val>::charge_bulk_init_cpy(len as u64, self)?;
349        let mut vals: Vec<Val> = vec![Val::VOID.to_val(); len as usize];
350        // charge for conversion from bytes to `Val`s
351        self.charge_budget(
352            ContractCostType::MemCpy,
353            Some((len as u64).saturating_mul(8)),
354        )?;
355        self.metered_vm_read_vals_from_linear_memory_mem::<8, Val, M>(
356            &m,
357            pos,
358            vals.as_mut_slice(),
359            |buf| Ok(Val::from_payload(u64::from_le_bytes(*buf))),
360        )?;
361
362        for v in vals.iter() {
363            self.check_val_integrity(*v)?;
364        }
365        self.add_host_object(HostVec::from_vec(vals)?)
366    }
367
368    pub fn map_unpack_to_linear_memory_fn_mem<M: CustomContextVM>(
369        &self,
370        m: &mut M,
371        map: MapObject,
372        keys_pos: U32Val,
373        vals_pos: U32Val,
374        len: U32Val,
375    ) -> Result<Void, HostError> {
376        let MemFnArgsCustomVmMut {
377            pos: keys_pos, len, ..
378        } = self.get_mem_fn_args_custom_vm_mut(m, keys_pos, len);
379        self.visit_obj(map, |mapobj: &HostMap| {
380            if mapobj.len() != len as usize {
381                return Err(self.err(
382                    ScErrorType::Object,
383                    ScErrorCode::UnexpectedSize,
384                    "differing host map and output slice lengths when unpacking map to linear memory",
385                    &[],
386                ));
387            }
388            // Step 1: check all key symbols.
389            self.metered_vm_scan_slices_in_linear_memory_mem(
390                m,
391                keys_pos,
392                len as usize,
393                |n, slice| {
394                    let sym = Symbol::try_from(
395                        mapobj.get_at_index(n, self).map_err(|he|
396                            if he.error.is_type(ScErrorType::Budget) {
397                                he
398                            } else {
399                                self.err(
400                                    ScErrorType::Object,
401                                    ScErrorCode::IndexBounds,
402                                    "vector out of bounds while unpacking map to linear memory",
403                                    &[],
404                                )
405                            }
406                        )?.0
407                    )?;
408                    self.check_symbol_matches(slice, sym)?;
409                    Ok(())
410                },
411            )?;
412
413            // Step 2: write all vals.
414            // charges memcpy of converting map entries into bytes
415            self.charge_budget(ContractCostType::MemCpy, Some((len as u64).saturating_mul(8)))?;
416
417            self.metered_vm_write_vals_to_linear_memory_mem(
418                m,
419                vals_pos.into(),
420                mapobj.map.as_slice(),
421                |pair| {
422                    Ok(u64::to_le_bytes(
423                        pair.1.get_payload(),
424                    ))
425                },
426            )?;
427
428            Ok(())
429        })?;
430
431        Ok(Val::VOID)
432    }
433
434    pub fn bytes_new_from_linear_memory_mem<M: CustomContextVM>(
435        &self,
436        m: M,
437        lm_pos: U32Val,
438        len: U32Val,
439    ) -> Result<BytesObject, HostError> {
440        self.memobj_new_from_linear_memory_mem::<ScBytes, M>(&m, lm_pos, len)
441    }
442
443    pub fn string_new_from_linear_memory_mem<M: CustomContextVM>(
444        &self,
445        m: M,
446        lm_pos: U32Val,
447        len: U32Val,
448    ) -> Result<StringObject, HostError> {
449        self.memobj_new_from_linear_memory_mem::<ScString, M>(&m, lm_pos, len)
450    }
451
452    pub fn symbol_new_from_linear_memory_mem<M: CustomContextVM>(
453        &self,
454        m: M,
455        lm_pos: U32Val,
456        len: U32Val,
457    ) -> Result<SymbolObject, HostError> {
458        self.memobj_new_from_linear_memory_mem::<ScSymbol, M>(&m, lm_pos, len)
459    }
460
461    pub fn symbol_index_in_linear_memory_mem<M: CustomContextVM>(
462        &self,
463        m: M,
464        sym: Symbol,
465        lm_pos: U32Val,
466        len: U32Val,
467    ) -> Result<U32Val, HostError> {
468        let MemFnArgsCustomVm { mem, pos, len } = self.get_mem_fn_args_custom_vm(&m, lm_pos, len);
469        let mut found = None;
470        self.metered_vm_scan_slices_in_linear_memory_mem(mem, pos, len as usize, |i, slice| {
471            if self.symbol_matches(slice, sym)? {
472                if found.is_none() {
473                    found = Some(self.usize_to_u32(i)?)
474                }
475            }
476            Ok(())
477        })?;
478        match found {
479            None => Err(self.err(
480                ScErrorType::Value,
481                ScErrorCode::MissingValue,
482                "symbol not found in linear memory slices",
483                &[sym.to_val()],
484            )),
485            Some(idx) => Ok(U32Val::from(idx)),
486        }
487    }
488
489    pub fn vec_unpack_to_linear_memory_mem<M: CustomContextVM>(
490        &self,
491        m: &mut M,
492        vec: VecObject,
493        vals_pos: U32Val,
494        len: U32Val,
495    ) -> Result<Void, HostError> {
496        let MemFnArgsCustomVmMut { mem, pos, len } =
497            self.get_mem_fn_args_custom_vm_mut(m, vals_pos, len);
498        self.visit_obj(vec, |vecobj: &HostVec| {
499            if vecobj.len() != len as usize {
500                return Err(self.err(
501                    ScErrorType::Object,
502                    ScErrorCode::UnexpectedSize,
503                    "differing host vector and output vector lengths when unpacking vec to linear memory",
504                    &[],
505                ));
506            }
507            // charges memcpy of converting vec entries into bytes
508            self.charge_budget(ContractCostType::MemCpy, Some((len as u64).saturating_mul(8)))?;
509            self.metered_vm_write_vals_to_linear_memory_mem(
510                mem,
511                pos,
512                vecobj.as_slice(),
513                |x| {
514                    Ok(u64::to_le_bytes(
515                        x.get_payload(),
516                    ))
517                },
518            )
519        })?;
520        Ok(Val::VOID)
521    }
522
523    pub fn bytes_copy_to_linear_memory_mem<M: CustomContextVM>(
524        &self,
525        m: &mut M,
526        b: BytesObject,
527        b_pos: U32Val,
528        lm_pos: U32Val,
529        len: U32Val,
530    ) -> Result<Void, HostError> {
531        self.memobj_copy_to_linear_memory_mem::<ScBytes, M>(m, b, b_pos, lm_pos, len)?;
532        Ok(Val::VOID)
533    }
534
535    /// Constructs a new [`Host`] that will use the provided [`Storage`] for
536    /// contract-data access functions such as
537    /// [`Env::get_contract_data`].
538    pub fn with_storage_and_budget(storage: Storage, budget: Budget) -> Self {
539        #[cfg(all(not(target_family = "wasm"), feature = "tracy"))]
540        let _client = tracy_client::Client::start();
541        Self(Rc::new(HostImpl {
542            module_cache: RefCell::new(None),
543            shared_linker: RefCell::new(None),
544            source_account: RefCell::new(None),
545            ledger: RefCell::new(None),
546            objects: Default::default(),
547            storage: RefCell::new(storage),
548            context_stack: Default::default(),
549            budget,
550            events: Default::default(),
551            authorization_manager: RefCell::new(
552                AuthorizationManager::new_enforcing_without_authorizations(),
553            ),
554            diagnostic_level: Default::default(),
555            base_prng: RefCell::new(None),
556            #[cfg(any(test, feature = "recording_mode"))]
557            recording_auth_nonce_prng: RefCell::new(None),
558            #[cfg(any(test, feature = "testutils"))]
559            test_prng: RefCell::new(None),
560            #[cfg(any(test, feature = "testutils"))]
561            contracts: Default::default(),
562            #[cfg(any(test, feature = "testutils"))]
563            previous_authorization_manager: RefCell::new(None),
564            trace_hook: RefCell::new(None),
565            #[cfg(any(test, feature = "testutils"))]
566            top_contract_invocation_hook: RefCell::new(None),
567            #[cfg(any(test, feature = "testutils"))]
568            coverage_scoreboard: Default::default(),
569            #[cfg(any(test, feature = "recording_mode"))]
570            suppress_diagnostic_events: RefCell::new(false),
571            #[cfg(any(test, feature = "recording_mode"))]
572            need_to_build_module_cache: RefCell::new(false),
573        }))
574    }
575
576    pub fn build_module_cache_if_needed(&self) -> Result<(), HostError> {
577        if self.get_ledger_protocol_version()? >= ModuleCache::MIN_LEDGER_VERSION
578            && self.try_borrow_module_cache()?.is_none()
579        {
580            let cache = ModuleCache::new(self)?;
581            let linker = cache.make_linker(self)?;
582            *self.try_borrow_module_cache_mut()? = Some(cache);
583            *self.try_borrow_linker_mut()? = Some(linker);
584        }
585        Ok(())
586    }
587
588    #[cfg(any(test, feature = "recording_mode"))]
589    pub fn in_storage_recording_mode(&self) -> Result<bool, HostError> {
590        if let crate::storage::FootprintMode::Recording(_) = self.try_borrow_storage()?.mode {
591            Ok(true)
592        } else {
593            Ok(false)
594        }
595    }
596
597    #[cfg(any(test, feature = "recording_mode"))]
598    pub fn clear_module_cache(&self) -> Result<(), HostError> {
599        *self.try_borrow_module_cache_mut()? = None;
600        *self.try_borrow_linker_mut()? = None;
601        Ok(())
602    }
603
604    #[cfg(any(test, feature = "recording_mode"))]
605    pub fn rebuild_module_cache(&self) -> Result<(), HostError> {
606        self.clear_module_cache()?;
607        self.build_module_cache_if_needed()
608    }
609
610    pub fn set_source_account(&self, source_account: AccountId) -> Result<(), HostError> {
611        *self.try_borrow_source_account_mut()? = Some(source_account);
612        Ok(())
613    }
614
615    #[cfg(any(test, feature = "testutils"))]
616    pub fn remove_source_account(&self) -> Result<(), HostError> {
617        *self.try_borrow_source_account_mut()? = None;
618        Ok(())
619    }
620
621    #[cfg(any(test, feature = "testutils"))]
622    pub(crate) fn source_account_id(&self) -> Result<Option<AccountId>, HostError> {
623        self.try_borrow_source_account()?.metered_clone(self)
624    }
625
626    #[cfg(any(test, feature = "testutils"))]
627    pub(crate) fn with_test_prng<T>(
628        &self,
629        f: impl FnOnce(&mut ChaCha20Rng) -> Result<T, HostError>,
630    ) -> Result<T, HostError> {
631        let mut opt = self.try_borrow_test_prng_mut()?;
632        if let Some(p) = opt.as_mut() {
633            f(p)
634        } else {
635            Err(self.err(
636                ScErrorType::Context,
637                ScErrorCode::InternalError,
638                "missing test PRNG",
639                &[],
640            ))
641        }
642    }
643
644    #[cfg(any(test, feature = "recording_mode"))]
645    pub(crate) fn with_recording_auth_nonce_prng<T>(
646        &self,
647        f: impl FnOnce(&mut ChaCha20Rng) -> Result<T, HostError>,
648    ) -> Result<T, HostError> {
649        let mut opt = self.try_borrow_recording_auth_nonce_prng_mut()?;
650        if let Some(p) = opt.as_mut() {
651            f(p)
652        } else {
653            Err(self.err(
654                ScErrorType::Context,
655                ScErrorCode::InternalError,
656                "missing recording-auth nonce PRNG",
657                &[],
658            ))
659        }
660    }
661
662    pub fn source_account_address(&self) -> Result<Option<AddressObject>, HostError> {
663        if let Some(acc) = self.try_borrow_source_account()?.as_ref() {
664            Ok(Some(self.add_host_object(ScAddress::Account(
665                acc.metered_clone(self)?,
666            ))?))
667        } else {
668            Ok(None)
669        }
670    }
671
672    #[cfg(any(test, feature = "recording_mode"))]
673    pub fn switch_to_enforcing_storage(&self) -> Result<(), HostError> {
674        self.with_mut_storage(|storage| {
675            storage.mode = crate::storage::FootprintMode::Enforcing;
676            Ok(())
677        })
678    }
679
680    #[cfg(any(test, feature = "recording_mode"))]
681    pub fn switch_to_recording_auth(&self, disable_non_root_auth: bool) -> Result<(), HostError> {
682        *self.try_borrow_authorization_manager_mut()? =
683            AuthorizationManager::new_recording(disable_non_root_auth);
684        Ok(())
685    }
686
687    pub fn set_authorization_entries(
688        &self,
689        auth_entries: Vec<soroban_env_common::xdr::SorobanAuthorizationEntry>,
690    ) -> Result<(), HostError> {
691        let new_auth_manager = AuthorizationManager::new_enforcing(self, auth_entries)?;
692        *self.try_borrow_authorization_manager_mut()? = new_auth_manager;
693        Ok(())
694    }
695
696    #[allow(unused_variables)]
697    pub fn set_base_prng_seed(&self, seed: prng::Seed) -> Result<(), HostError> {
698        let mut base_prng = Prng::new_from_seed(seed, self.budget_ref())?;
699        // NB: we must _create_ these PRNGs whether or not we're in a build that
700        // stores / reveals them, so that the base_prng is left in the same state
701        // regardless of build configuration.
702        let recording_auth_nonce_prng = base_prng.unmetered_raw_sub_prng();
703        let test_prng = base_prng.unmetered_raw_sub_prng();
704        #[cfg(any(test, feature = "testutils"))]
705        {
706            *self.try_borrow_test_prng_mut()? = Some(test_prng);
707        }
708        #[cfg(any(test, feature = "recording_mode"))]
709        {
710            *self.try_borrow_recording_auth_nonce_prng_mut()? = Some(recording_auth_nonce_prng);
711        }
712        *self.try_borrow_base_prng_mut()? = Some(base_prng);
713        Ok(())
714    }
715
716    pub fn get_ledger_info(&self) -> Result<Option<LedgerInfo>, HostError> {
717        let info = self.try_borrow_ledger()?;
718
719        Ok(info.clone())
720    }
721
722    pub fn set_ledger_info(&self, info: LedgerInfo) -> Result<(), HostError> {
723        *self.try_borrow_ledger_mut()? = Some(info);
724        Ok(())
725    }
726
727    pub fn with_ledger_info<F, T>(&self, f: F) -> Result<T, HostError>
728    where
729        F: FnOnce(&LedgerInfo) -> Result<T, HostError>,
730    {
731        match self.try_borrow_ledger()?.as_ref() {
732            None => Err(self.err(
733                ScErrorType::Context,
734                ScErrorCode::InternalError,
735                "missing ledger info",
736                &[],
737            )),
738            Some(li) => f(li),
739        }
740    }
741
742    pub fn with_mut_ledger_info<F>(&self, mut f: F) -> Result<(), HostError>
743    where
744        F: FnMut(&mut LedgerInfo),
745    {
746        match self.try_borrow_ledger_mut()?.as_mut() {
747            None => Err(self.err(
748                ScErrorType::Context,
749                ScErrorCode::InternalError,
750                "missing ledger info",
751                &[],
752            )),
753            Some(li) => {
754                f(li);
755                Ok(())
756            }
757        }
758    }
759
760    pub fn get_ledger_protocol_version(&self) -> Result<u32, HostError> {
761        self.with_ledger_info(|li| Ok(li.protocol_version))
762    }
763
764    pub(crate) fn budget_ref(&self) -> &Budget {
765        &self.0.budget
766    }
767
768    pub fn budget_cloned(&self) -> Budget {
769        self.0.budget.clone()
770    }
771
772    pub fn charge_budget(&self, ty: ContractCostType, input: Option<u64>) -> Result<(), HostError> {
773        self.0.budget.charge(ty, input)
774    }
775
776    pub fn set_shadow_budget_limits(&self, cpu: u64, mem: u64) -> Result<(), HostError> {
777        self.0.budget.set_shadow_limits(cpu, mem)
778    }
779
780    pub fn set_diagnostic_level(&self, diagnostic_level: DiagnosticLevel) -> Result<(), HostError> {
781        *self.0.diagnostic_level.try_borrow_mut_or_err()? = diagnostic_level;
782        Ok(())
783    }
784
785    // As above, avoids having to import DiagnosticLevel.
786    pub fn enable_debug(&self) -> Result<(), HostError> {
787        self.set_diagnostic_level(DiagnosticLevel::Debug)
788    }
789
790    /// Wraps a `budget.with_shadow_mode` call with a check against the
791    /// diagnostic level. This wrapper should be used for any work that is part
792    /// of the production workflow but in debug mode, i.e. diagnostic related
793    /// work (logging, or any operations on diagnostic events).
794    ///
795    /// Note: to help minimize the risk of divergence based on accidental
796    /// observation of the diagnostic level in any context _other_ than this
797    /// callback, we make two things at least inconvenient enough to maybe cause
798    /// people to come to this comment and read it:
799    ///
800    ///   1. We avoid having any other direct way of observing the flag.
801    ///   2. We eat all errors and return no values from the closure.
802    ///
803    /// If you need to observe a value from the execution of debug mode, you can
804    /// of course still mutate a mutable reference pointing outside the closure,
805    /// but be _absolutely certain_ any observation you thereby make of the
806    /// debug-level _only_ flows into other functions that are themselves
807    /// debug-mode-guarded and/or only write results into debug state (eg.
808    /// diagnostic events).
809    pub(crate) fn with_debug_mode<F>(&self, f: F)
810    where
811        F: FnOnce() -> Result<(), HostError>,
812    {
813        if let Ok(cell) = self.0.diagnostic_level.try_borrow_or_err() {
814            if matches!(*cell, DiagnosticLevel::Debug) {
815                return self.budget_ref().with_shadow_mode(f);
816            }
817        }
818    }
819
820    /// Calls the provided function while ensuring that no diagnostic events are
821    /// recorded.
822    /// This is useful for emulating operations only for the sake of budget
823    /// accounting in recording mode.
824    #[cfg(any(test, feature = "recording_mode"))]
825    pub(crate) fn with_suppressed_diagnostic_events<F>(&self, f: F) -> Result<(), HostError>
826    where
827        F: FnOnce() -> Result<(), HostError>,
828    {
829        *self.try_borrow_suppress_diagnostic_events_mut()? = true;
830        f()?;
831        *self.try_borrow_suppress_diagnostic_events_mut()? = false;
832        Ok(())
833    }
834
835    /// Returns whether the Host can be finished by calling
836    /// [`Host::try_finish`].
837    ///
838    /// Returns true if the host reference is unique, refcount = 1.
839    pub fn can_finish(&self) -> bool {
840        Rc::strong_count(&self.0) == 1
841    }
842
843    /// Accept a _unique_ (refcount = 1) host reference and destroy the
844    /// underlying [`HostImpl`], returning its finalized components containing
845    /// processing side effects  to the caller as a tuple wrapped in `Ok(...)`.
846    ///
847    /// Use [`Host::can_finish`] to determine before calling the function if it
848    /// will succeed.
849    pub fn try_finish(self) -> Result<(Storage, Events), HostError> {
850        let events = self.try_borrow_events()?.externalize(&self)?;
851        Rc::try_unwrap(self.0)
852            .map(|host_impl| {
853                let storage = host_impl.storage.into_inner();
854                (storage, events)
855            })
856            .map_err(|_| {
857                Error::from_type_and_code(ScErrorType::Context, ScErrorCode::InternalError).into()
858            })
859    }
860}
861
862macro_rules! call_trace_env_call {
863    ($self:expr, $($arg:expr),*) => {
864        if $self.tracing_enabled()
865        {
866            $self.trace_env_call(function_short_name!(), &[$(&$arg),*])?;
867        }
868    };
869}
870
871macro_rules! call_trace_env_ret {
872    ($self:expr, $arg:expr) => {{
873        if $self.tracing_enabled() {
874            let dyn_res: Result<&dyn core::fmt::Debug, &HostError> = match &$arg {
875                Ok(ref ok) => Ok(ok),
876                Err(err) => Err(err),
877            };
878            $self.trace_env_ret(function_short_name!(), &dyn_res)?;
879        }
880    }};
881}
882
883// Notes on metering: these are called from the guest and thus charged on the VM instructions.
884impl EnvBase for Host {
885    type Error = HostError;
886
887    fn error_from_error_val(&self, e: soroban_env_common::Error) -> Self::Error {
888        self.error(e, "promoting Error to HostError", &[])
889    }
890
891    fn check_obj_integrity(&self, obj: Object) -> Result<(), HostError> {
892        use crate::{xdr, Tag};
893        self.visit_obj_untyped(obj, |hobj| match (hobj, obj.to_val().get_tag()) {
894            (HostObject::Vec(_), Tag::VecObject)
895            | (HostObject::Map(_), Tag::MapObject)
896            | (HostObject::U64(_), Tag::U64Object)
897            | (HostObject::I64(_), Tag::I64Object)
898            | (HostObject::TimePoint(_), Tag::TimepointObject)
899            | (HostObject::Duration(_), Tag::DurationObject)
900            | (HostObject::U128(_), Tag::U128Object)
901            | (HostObject::I128(_), Tag::I128Object)
902            | (HostObject::U256(_), Tag::U256Object)
903            | (HostObject::I256(_), Tag::I256Object)
904            | (HostObject::Bytes(_), Tag::BytesObject)
905            | (HostObject::String(_), Tag::StringObject)
906            | (HostObject::Symbol(_), Tag::SymbolObject)
907            | (HostObject::Address(_), Tag::AddressObject) => Ok(()),
908            _ => Err(self.err(
909                xdr::ScErrorType::Value,
910                xdr::ScErrorCode::InvalidInput,
911                "mis-tagged object reference",
912                &[],
913            )),
914        })
915    }
916
917    // This function is somewhat subtle.
918    //
919    // It exists to allow the client of the (VmCaller)Env interface(s) to
920    // essentially _reject_ an error returned by one of the Result-returning
921    // methods on the trait, choosing to panic instead. But doing so in some way
922    // that the trait defines, rather than calling panic in the client.
923    //
924    // The only client we expect to _do_ this is a non-builtin user contract
925    // compiled natively for local testing (and thus linked directly to `Host`).
926    // In a wasm build of a user contract, we already encourage users to think
927    // of `Env::Error` as infallible by literally defining `Guest::Error` as the
928    // `Infallible` type (which makes sense: we trap the user's VM on such
929    // errors, don't resume it at all). But in a non-wasm, native build of a
930    // user contract, `Env=Host` and `Env::Error=HostError`, an inhabited type
931    // you can observe. So the user might actually have a code path returning
932    // from such an error that is suddenly non-dead and receiving an
933    // `Env::Error=HostError`, which (to maintain continuity with the VM case)
934    // they then _want_ to treat as impossible-to-have-occurred just like
935    // `Guest::Error`. They can panic, but that doesn't quite maintain the
936    // illusion properly. Instead they should call this method to "reject the
937    // error".
938    //
939    // When such a "rejected error" occurs, we do panic, but only after checking
940    // to see if we're in a `TestContract` invocation, and if so storing the
941    // error's Error value in that frame, such that `Host::call_n` can recover
942    // the Error when it _catches_ the panic and converts it back to an error.
943    //
944    // It might seem like we ought to `std::panic::panic_any(e)` here, making
945    // the panic carry a `HostError` or `Error` and catching it by dynamic type
946    // inspection in the `call_n` catch logic. The reason we don't do so is that
947    // `panic_any` will not provide a nice printable value to the `PanicInfo`,
948    // it constructs, so when/if the panic makes it to a top-level printout it
949    // will display a relatively ugly message like "thread panicked at Box<dyn
950    // Any>" to stderr, when it is much more useful to the user if we have it
951    // print the result of HostError::Debug, with its glorious Error,
952    // site-of-origin backtrace and debug log.
953    //
954    // To get it to do that, we have to call `panic!()`, not `panic_any`.
955    // Personally I think this is a glaring weakness of `panic_any` but we are
956    // not in a position to improve it.
957    #[cfg(feature = "testutils")]
958    fn escalate_error_to_panic(&self, e: Self::Error) -> ! {
959        let _ = self.with_current_frame_opt(|f| {
960            if let Some(Frame::TestContract(frame)) = f {
961                if let Ok(mut panic) = frame.panic.try_borrow_mut() {
962                    *panic = Some(e.error);
963                }
964            }
965            Ok(())
966        });
967        let escalation = self.error(e.error, "escalating error to panic", &[]);
968        panic!("{:?}", escalation)
969    }
970
971    fn augment_err_result<T>(&self, mut x: Result<T, Self::Error>) -> Result<T, Self::Error> {
972        if let Err(e) = &mut x {
973            if e.info.is_none() {
974                e.info = self.maybe_get_debug_info()
975            }
976        }
977        x
978    }
979
980    fn tracing_enabled(&self) -> bool {
981        match self.try_borrow_trace_hook() {
982            Ok(hook) => hook.is_some(),
983            Err(_) => false,
984        }
985    }
986
987    fn trace_env_call(&self, fname: &'static str, args: &[&dyn Debug]) -> Result<(), HostError> {
988        self.call_any_lifecycle_hook(TraceEvent::EnvCall(fname, args))
989    }
990
991    fn trace_env_ret(
992        &self,
993        fname: &'static str,
994        res: &Result<&dyn Debug, &HostError>,
995    ) -> Result<(), HostError> {
996        self.call_any_lifecycle_hook(TraceEvent::EnvRet(fname, res))
997    }
998
999    fn check_same_env(&self, other: &Self) -> Result<(), Self::Error> {
1000        if Rc::ptr_eq(&self.0, &other.0) {
1001            Ok(())
1002        } else {
1003            Err(self.err(
1004                ScErrorType::Context,
1005                ScErrorCode::InternalError,
1006                "check_same_env on different Hosts",
1007                &[],
1008            ))
1009        }
1010    }
1011
1012    fn bytes_copy_from_slice(
1013        &self,
1014        b: BytesObject,
1015        b_pos: U32Val,
1016        slice: &[u8],
1017    ) -> Result<BytesObject, HostError> {
1018        call_trace_env_call!(self, b, b_pos, slice.len());
1019        let res = self.memobj_copy_from_slice::<ScBytes>(b, b_pos, slice);
1020        call_trace_env_ret!(self, res);
1021        res
1022    }
1023
1024    fn bytes_copy_to_slice(
1025        &self,
1026        b: BytesObject,
1027        b_pos: U32Val,
1028        slice: &mut [u8],
1029    ) -> Result<(), HostError> {
1030        call_trace_env_call!(self, b, b_pos, slice.len());
1031        let res = self.memobj_copy_to_slice::<ScBytes>(b, b_pos, slice);
1032        call_trace_env_ret!(self, res);
1033        res
1034    }
1035
1036    fn string_copy_to_slice(
1037        &self,
1038        b: StringObject,
1039        b_pos: U32Val,
1040        slice: &mut [u8],
1041    ) -> Result<(), HostError> {
1042        call_trace_env_call!(self, b, b_pos, slice.len());
1043        let res = self.memobj_copy_to_slice::<ScString>(b, b_pos, slice);
1044        call_trace_env_ret!(self, res);
1045        res
1046    }
1047
1048    fn symbol_copy_to_slice(
1049        &self,
1050        s: SymbolObject,
1051        b_pos: U32Val,
1052        slice: &mut [u8],
1053    ) -> Result<(), HostError> {
1054        call_trace_env_call!(self, s, b_pos, slice.len());
1055        let res = self.memobj_copy_to_slice::<ScSymbol>(s, b_pos, slice);
1056        call_trace_env_ret!(self, res);
1057        res
1058    }
1059
1060    fn bytes_new_from_slice(&self, mem: &[u8]) -> Result<BytesObject, HostError> {
1061        call_trace_env_call!(self, mem.len());
1062        let res = self.add_host_object(self.scbytes_from_slice(mem)?);
1063        call_trace_env_ret!(self, res);
1064        res
1065    }
1066
1067    fn string_new_from_slice(&self, s: &[u8]) -> Result<StringObject, HostError> {
1068        call_trace_env_call!(self, s.len());
1069        let res = self.add_host_object(ScString(self.metered_slice_to_vec(s)?.try_into()?));
1070        call_trace_env_ret!(self, res);
1071        res
1072    }
1073
1074    fn symbol_new_from_slice(&self, s: &[u8]) -> Result<SymbolObject, HostError> {
1075        call_trace_env_call!(self, s.len());
1076        // Note: this whole function could be replaced by `ScSymbol::try_from_bytes`
1077        // in order to avoid duplication. It is duplicated in order to support
1078        // a slightly different check order for the sake of observation consistency.
1079        self.charge_budget(ContractCostType::MemCmp, Some(s.len() as u64))?;
1080        for b in s {
1081            SymbolSmall::validate_byte(*b).map_err(|_| {
1082                self.err(
1083                    ScErrorType::Value,
1084                    ScErrorCode::InvalidInput,
1085                    "byte is not allowed in Symbol",
1086                    &[(*b as u32).into()],
1087                )
1088            })?;
1089        }
1090        let res = self.add_host_object(ScSymbol(self.metered_slice_to_vec(s)?.try_into()?));
1091        call_trace_env_ret!(self, res);
1092        res
1093    }
1094
1095    fn map_new_from_slices(&self, keys: &[&str], vals: &[Val]) -> Result<MapObject, HostError> {
1096        call_trace_env_call!(self, keys.len());
1097        if keys.len() != vals.len() {
1098            return Err(self.err(
1099                ScErrorType::Object,
1100                ScErrorCode::UnexpectedSize,
1101                "differing key and value slice lengths when creating map from slices",
1102                &[],
1103            ));
1104        }
1105        Vec::<(Val, Val)>::charge_bulk_init_cpy(keys.len() as u64, self)?;
1106        let map_vec = keys
1107            .iter()
1108            .zip(vals.iter().copied())
1109            .map(|(key_str, val)| {
1110                let sym = Symbol::try_from_val(self, key_str)?;
1111                self.check_val_integrity(val)?;
1112                Ok((sym.to_val(), val))
1113            })
1114            .collect::<Result<Vec<(Val, Val)>, HostError>>()?;
1115        let map = HostMap::from_map(map_vec, self)?;
1116        let res = self.add_host_object(map);
1117        call_trace_env_ret!(self, res);
1118        res
1119    }
1120
1121    fn map_unpack_to_slice(
1122        &self,
1123        map: MapObject,
1124        keys: &[&str],
1125        vals: &mut [Val],
1126    ) -> Result<Void, HostError> {
1127        call_trace_env_call!(self, map, keys.len());
1128        if keys.len() != vals.len() {
1129            return Err(self.err(
1130                ScErrorType::Object,
1131                ScErrorCode::UnexpectedSize,
1132                "differing key and value slice lengths when unpacking map to slice",
1133                &[],
1134            ));
1135        }
1136        self.visit_obj(map, |hm: &HostMap| {
1137            if hm.len() != vals.len() {
1138                return Err(self.err(
1139                    ScErrorType::Object,
1140                    ScErrorCode::UnexpectedSize,
1141                    "differing host map and output slice lengths when unpacking map to slice",
1142                    &[],
1143                ));
1144            }
1145
1146            for (ik, mk) in keys.iter().zip(hm.keys(self)?) {
1147                let sym: Symbol = mk.try_into()?;
1148                self.check_symbol_matches(ik.as_bytes(), sym)?;
1149            }
1150
1151            metered_clone::charge_shallow_copy::<Val>(keys.len() as u64, self)?;
1152            for (iv, mv) in vals.iter_mut().zip(hm.values(self)?) {
1153                *iv = *mv;
1154            }
1155            Ok(())
1156        })?;
1157        let res = Ok(Val::VOID);
1158        call_trace_env_ret!(self, res);
1159        res
1160    }
1161
1162    fn vec_new_from_slice(&self, vals: &[Val]) -> Result<VecObject, Self::Error> {
1163        call_trace_env_call!(self, vals.len());
1164        let vec = HostVec::from_exact_iter(vals.iter().cloned(), self.budget_ref())?;
1165        for v in vec.iter() {
1166            self.check_val_integrity(*v)?;
1167        }
1168        let res = self.add_host_object(vec);
1169        call_trace_env_ret!(self, res);
1170        res
1171    }
1172
1173    fn vec_unpack_to_slice(&self, vec: VecObject, vals: &mut [Val]) -> Result<Void, Self::Error> {
1174        call_trace_env_call!(self, vec, vals.len());
1175        self.visit_obj(vec, |hv: &HostVec| {
1176            if hv.len() != vals.len() {
1177                return Err(self.err(
1178                    ScErrorType::Object,
1179                    ScErrorCode::UnexpectedSize,
1180                    "differing host vector and output vector lengths when unpacking vec to slice",
1181                    &[],
1182                ));
1183            }
1184            metered_clone::charge_shallow_copy::<Val>(hv.len() as u64, self)?;
1185            vals.copy_from_slice(hv.as_slice());
1186            Ok(())
1187        })?;
1188        let res = Ok(Val::VOID);
1189        call_trace_env_ret!(self, res);
1190        res
1191    }
1192
1193    fn symbol_index_in_strs(&self, sym: Symbol, slices: &[&str]) -> Result<U32Val, Self::Error> {
1194        call_trace_env_call!(self, sym, slices.len());
1195        let mut found = None;
1196        for (i, slice) in slices.iter().enumerate() {
1197            if self.symbol_matches(slice.as_bytes(), sym)? && found.is_none() {
1198                found = Some(i)
1199            }
1200        }
1201        let res = match found {
1202            None => Err(self.err(
1203                ScErrorType::Value,
1204                ScErrorCode::InvalidInput,
1205                "symbol not found in slice of strs",
1206                &[sym.to_val()],
1207            )),
1208            Some(idx) => Ok(U32Val::from(self.usize_to_u32(idx)?)),
1209        };
1210        call_trace_env_ret!(self, res);
1211        res
1212    }
1213
1214    fn log_from_slice(&self, msg: &str, vals: &[Val]) -> Result<Void, HostError> {
1215        call_trace_env_call!(self, msg.len(), vals.len());
1216        self.log_diagnostics(msg, vals);
1217        let res = Ok(Void::from(()));
1218        call_trace_env_ret!(self, res);
1219        res
1220    }
1221
1222    fn check_protocol_version_lower_bound(&self, lower: u32) -> Result<(), Self::Error> {
1223        self.with_ledger_info(|li| {
1224            let proto = li.protocol_version;
1225            if proto < lower {
1226                Err(self.err(
1227                    ScErrorType::Context,
1228                    ScErrorCode::IndexBounds,
1229                    "ledger protocol {} is less than specified lower bound {}",
1230                    &[Val::from_u32(proto).into(), Val::from_u32(lower).into()],
1231                ))
1232            } else {
1233                Ok(())
1234            }
1235        })
1236    }
1237
1238    fn check_protocol_version_upper_bound(&self, upper: u32) -> Result<(), Self::Error> {
1239        self.with_ledger_info(|li| {
1240            let proto = li.protocol_version;
1241            if proto > upper {
1242                Err(self.err(
1243                    ScErrorType::Context,
1244                    ScErrorCode::IndexBounds,
1245                    "ledger protocol {} is larger than specified upper bound {}",
1246                    &[Val::from_u32(proto).into(), Val::from_u32(upper).into()],
1247                ))
1248            } else {
1249                Ok(())
1250            }
1251        })
1252    }
1253}
1254
1255impl VmCallerEnv for Host {
1256    type VmUserState = Host;
1257
1258    // region: "context" module functions
1259
1260    // Notes on metering: covered by the components
1261    fn log_from_linear_memory(
1262        &self,
1263        vmcaller: &mut VmCaller<Host>,
1264        msg_pos: U32Val,
1265        msg_len: U32Val,
1266        vals_pos: U32Val,
1267        vals_len: U32Val,
1268    ) -> Result<Void, HostError> {
1269        self.with_debug_mode(|| {
1270            let MemFnArgs { vm, pos, len } = self.get_mem_fn_args(msg_pos, msg_len)?;
1271            Vec::<u8>::charge_bulk_init_cpy(len as u64, self)?;
1272            let mut msg: Vec<u8> = vec![0u8; len as usize];
1273            self.metered_vm_read_bytes_from_linear_memory(vmcaller, &vm, pos, &mut msg)?;
1274            // `String::from_utf8_lossy` iternally allocates a `String` which is a `Vec<u8>`
1275            Vec::<u8>::charge_bulk_init_cpy(len as u64, self)?;
1276            let msg = String::from_utf8_lossy(&msg);
1277
1278            let MemFnArgs { vm, pos, len } = self.get_mem_fn_args(vals_pos, vals_len)?;
1279            Vec::<Val>::charge_bulk_init_cpy((len as u64).saturating_add(1), self)?;
1280            let mut vals: Vec<Val> = vec![Val::VOID.to_val(); len as usize];
1281            // charge for conversion from bytes to `Val`s
1282            self.charge_budget(
1283                ContractCostType::MemCpy,
1284                Some((len as u64).saturating_mul(8)),
1285            )?;
1286            self.metered_vm_read_vals_from_linear_memory::<8, Val>(
1287                vmcaller,
1288                &vm,
1289                pos,
1290                vals.as_mut_slice(),
1291                |buf| self.relative_to_absolute(Val::from_payload(u64::from_le_bytes(*buf))),
1292            )?;
1293            self.log_diagnostics(&msg, &vals);
1294            Ok(())
1295        });
1296
1297        Ok(Val::VOID)
1298    }
1299
1300    // Metered: covered by `visit`.
1301    fn obj_cmp(&self, _vmcaller: &mut VmCaller<Host>, a: Val, b: Val) -> Result<i64, HostError> {
1302        let res = match {
1303            match (Object::try_from(a), Object::try_from(b)) {
1304                // We were given two objects: compare them.
1305                (Ok(a), Ok(b)) => self.visit_obj_untyped(a, |ao| {
1306                    // They might each be None but that's ok, None compares less than Some.
1307                    self.visit_obj_untyped(b, |bo| Ok(Some(self.compare(&ao, &bo)?)))
1308                })?,
1309
1310                // We were given an object and a non-object: try a small-value comparison.
1311                (Ok(a), Err(_)) => self
1312                    .visit_obj_untyped(a, |aobj| aobj.try_compare_to_small(self.as_budget(), b))?,
1313                // Same as previous case, but reversing the resulting order.
1314                (Err(_), Ok(b)) => self.visit_obj_untyped(b, |bobj| {
1315                    let ord = bobj.try_compare_to_small(self.as_budget(), a)?;
1316                    Ok(match ord {
1317                        Some(Ordering::Less) => Some(Ordering::Greater),
1318                        Some(Ordering::Greater) => Some(Ordering::Less),
1319                        other => other,
1320                    })
1321                })?,
1322                // We should have been given at least one object.
1323                (Err(_), Err(_)) => {
1324                    return Err(self.err(
1325                        ScErrorType::Value,
1326                        ScErrorCode::UnexpectedType,
1327                        "two non-object args to obj_cmp",
1328                        &[a, b],
1329                    ));
1330                }
1331            }
1332        } {
1333            // If any of the above got us a result, great, use it.
1334            Some(res) => res,
1335
1336            // Otherwise someone gave us an object and a non-paired value (not a small-value
1337            // case of the same type). Order these by their ScValType.
1338            None => {
1339                let atype = a.get_tag().get_scval_type();
1340                let btype = b.get_tag().get_scval_type();
1341                if atype == btype {
1342                    // This shouldn't have happened, but if it does there's a logic error.
1343                    return Err(self.err(
1344                        ScErrorType::Value,
1345                        ScErrorCode::InternalError,
1346                        "equal-tagged values rejected by small-value obj_cmp",
1347                        &[a, b],
1348                    ));
1349                }
1350                atype.cmp(&btype)
1351            }
1352        };
1353        // Finally, translate Ordering::Foo to a number to return to caller.
1354        Ok(match res {
1355            Ordering::Less => -1,
1356            Ordering::Equal => 0,
1357            Ordering::Greater => 1,
1358        })
1359    }
1360
1361    fn contract_event(
1362        &self,
1363        _vmcaller: &mut VmCaller<Host>,
1364        topics: VecObject,
1365        data: Val,
1366    ) -> Result<Void, HostError> {
1367        self.record_contract_event(ContractEventType::Contract, topics, data)?;
1368        Ok(Val::VOID)
1369    }
1370
1371    fn get_ledger_version(&self, _vmcaller: &mut VmCaller<Host>) -> Result<U32Val, Self::Error> {
1372        Ok(self.get_ledger_protocol_version()?.into())
1373    }
1374
1375    fn get_ledger_sequence(&self, _vmcaller: &mut VmCaller<Host>) -> Result<U32Val, Self::Error> {
1376        self.with_ledger_info(|li| Ok(li.sequence_number.into()))
1377    }
1378
1379    fn get_ledger_timestamp(&self, _vmcaller: &mut VmCaller<Host>) -> Result<U64Val, Self::Error> {
1380        self.with_ledger_info(|li| Ok(U64Val::try_from_val(self, &li.timestamp)?))
1381    }
1382
1383    fn fail_with_error(
1384        &self,
1385        _vmcaller: &mut VmCaller<Self::VmUserState>,
1386        error: Error,
1387    ) -> Result<Void, Self::Error> {
1388        if error.is_type(ScErrorType::Contract) {
1389            Err(self.error(
1390                error,
1391                "failing with contract error",
1392                &[U32Val::from(error.get_code()).to_val()],
1393            ))
1394        } else {
1395            Err(self.err(
1396                ScErrorType::Context,
1397                ScErrorCode::UnexpectedType,
1398                "contract attempted to fail with non-ContractError error code",
1399                &[error.to_val()],
1400            ))
1401        }
1402    }
1403
1404    fn get_ledger_network_id(
1405        &self,
1406        _vmcaller: &mut VmCaller<Host>,
1407    ) -> Result<BytesObject, Self::Error> {
1408        self.with_ledger_info(|li| {
1409            // FIXME: cache this and a few other such IDs: https://github.com/stellar/rs-soroban-env/issues/681
1410            self.add_host_object(self.scbytes_from_slice(li.network_id.as_slice())?)
1411        })
1412    }
1413
1414    // Notes on metering: covered by the components.
1415    fn get_current_contract_address(
1416        &self,
1417        _vmcaller: &mut VmCaller<Host>,
1418    ) -> Result<AddressObject, HostError> {
1419        // FIXME: cache this and a few other such IDs: https://github.com/stellar/rs-soroban-env/issues/681
1420        self.add_host_object(ScAddress::Contract(
1421            self.get_current_contract_id_internal()?,
1422        ))
1423    }
1424
1425    fn get_max_live_until_ledger(
1426        &self,
1427        _vmcaller: &mut VmCaller<Host>,
1428    ) -> Result<U32Val, Self::Error> {
1429        Ok(self.max_live_until_ledger()?.into())
1430    }
1431
1432    // endregion: "context" module functions
1433
1434    // region: "int" module functions
1435
1436    impl_wrapping_obj_from_num!(obj_from_u64, u64, U64Object, u64);
1437    impl_wrapping_obj_to_num!(obj_to_u64, u64, U64Object, u64);
1438    impl_wrapping_obj_from_num!(obj_from_i64, i64, I64Object, i64);
1439    impl_wrapping_obj_to_num!(obj_to_i64, i64, I64Object, i64);
1440    impl_wrapping_obj_from_num!(timepoint_obj_from_u64, TimePoint, TimepointObject, u64);
1441    impl_wrapping_obj_to_num!(timepoint_obj_to_u64, TimePoint, TimepointObject, u64);
1442    impl_wrapping_obj_from_num!(duration_obj_from_u64, Duration, DurationObject, u64);
1443    impl_wrapping_obj_to_num!(duration_obj_to_u64, Duration, DurationObject, u64);
1444
1445    fn obj_from_u128_pieces(
1446        &self,
1447        _vmcaller: &mut VmCaller<Self::VmUserState>,
1448        hi: u64,
1449        lo: u64,
1450    ) -> Result<U128Object, Self::Error> {
1451        self.add_host_object(int128_helpers::u128_from_pieces(hi, lo))
1452    }
1453
1454    fn obj_to_u128_lo64(
1455        &self,
1456        _vmcaller: &mut VmCaller<Self::VmUserState>,
1457        obj: U128Object,
1458    ) -> Result<u64, Self::Error> {
1459        self.visit_obj(obj, |u: &u128| Ok(int128_helpers::u128_lo(*u)))
1460    }
1461
1462    fn obj_to_u128_hi64(
1463        &self,
1464        _vmcaller: &mut VmCaller<Self::VmUserState>,
1465        obj: U128Object,
1466    ) -> Result<u64, Self::Error> {
1467        self.visit_obj(obj, |u: &u128| Ok(int128_helpers::u128_hi(*u)))
1468    }
1469
1470    fn obj_from_i128_pieces(
1471        &self,
1472        _vmcaller: &mut VmCaller<Self::VmUserState>,
1473        hi: i64,
1474        lo: u64,
1475    ) -> Result<I128Object, Self::Error> {
1476        self.add_host_object(int128_helpers::i128_from_pieces(hi, lo))
1477    }
1478
1479    fn obj_to_i128_lo64(
1480        &self,
1481        _vmcaller: &mut VmCaller<Self::VmUserState>,
1482        obj: I128Object,
1483    ) -> Result<u64, Self::Error> {
1484        self.visit_obj(obj, |i: &i128| Ok(int128_helpers::i128_lo(*i)))
1485    }
1486
1487    fn obj_to_i128_hi64(
1488        &self,
1489        _vmcaller: &mut VmCaller<Self::VmUserState>,
1490        obj: I128Object,
1491    ) -> Result<i64, Self::Error> {
1492        self.visit_obj(obj, |i: &i128| Ok(int128_helpers::i128_hi(*i)))
1493    }
1494
1495    fn obj_from_u256_pieces(
1496        &self,
1497        _vmcaller: &mut VmCaller<Self::VmUserState>,
1498        hi_hi: u64,
1499        hi_lo: u64,
1500        lo_hi: u64,
1501        lo_lo: u64,
1502    ) -> Result<U256Object, Self::Error> {
1503        self.add_host_object(u256_from_pieces(hi_hi, hi_lo, lo_hi, lo_lo))
1504    }
1505
1506    fn u256_val_from_be_bytes(
1507        &self,
1508        _vmcaller: &mut VmCaller<Self::VmUserState>,
1509        bytes: BytesObject,
1510    ) -> Result<U256Val, HostError> {
1511        let num = self.visit_obj(bytes, |b: &ScBytes| {
1512            Ok(U256::from_be_bytes(self.fixed_length_bytes_from_slice(
1513                "U256 bytes",
1514                b.as_slice(),
1515            )?))
1516        })?;
1517        self.map_err(U256Val::try_from_val(self, &num))
1518    }
1519
1520    fn u256_val_to_be_bytes(
1521        &self,
1522        _vmcaller: &mut VmCaller<Self::VmUserState>,
1523        val: U256Val,
1524    ) -> Result<BytesObject, HostError> {
1525        if let Ok(so) = U256Small::try_from(val) {
1526            self.add_host_object(self.scbytes_from_slice(&U256::from(so).to_be_bytes())?)
1527        } else {
1528            let obj = val.try_into()?;
1529            let scb = self.visit_obj(obj, |u: &U256| self.scbytes_from_slice(&u.to_be_bytes()))?;
1530            self.add_host_object(scb)
1531        }
1532    }
1533
1534    fn obj_to_u256_hi_hi(
1535        &self,
1536        _vmcaller: &mut VmCaller<Self::VmUserState>,
1537        obj: U256Object,
1538    ) -> Result<u64, HostError> {
1539        self.visit_obj(obj, |u: &U256| {
1540            let (hi_hi, _, _, _) = u256_into_pieces(*u);
1541            Ok(hi_hi)
1542        })
1543    }
1544
1545    fn obj_to_u256_hi_lo(
1546        &self,
1547        _vmcaller: &mut VmCaller<Self::VmUserState>,
1548        obj: U256Object,
1549    ) -> Result<u64, HostError> {
1550        self.visit_obj(obj, |u: &U256| {
1551            let (_, hi_lo, _, _) = u256_into_pieces(*u);
1552            Ok(hi_lo)
1553        })
1554    }
1555
1556    fn obj_to_u256_lo_hi(
1557        &self,
1558        _vmcaller: &mut VmCaller<Self::VmUserState>,
1559        obj: U256Object,
1560    ) -> Result<u64, HostError> {
1561        self.visit_obj(obj, |u: &U256| {
1562            let (_, _, lo_hi, _) = u256_into_pieces(*u);
1563            Ok(lo_hi)
1564        })
1565    }
1566
1567    fn obj_to_u256_lo_lo(
1568        &self,
1569        _vmcaller: &mut VmCaller<Self::VmUserState>,
1570        obj: U256Object,
1571    ) -> Result<u64, HostError> {
1572        self.visit_obj(obj, |u: &U256| {
1573            let (_, _, _, lo_lo) = u256_into_pieces(*u);
1574            Ok(lo_lo)
1575        })
1576    }
1577
1578    fn obj_from_i256_pieces(
1579        &self,
1580        _vmcaller: &mut VmCaller<Self::VmUserState>,
1581        hi_hi: i64,
1582        hi_lo: u64,
1583        lo_hi: u64,
1584        lo_lo: u64,
1585    ) -> Result<I256Object, Self::Error> {
1586        self.add_host_object(i256_from_pieces(hi_hi, hi_lo, lo_hi, lo_lo))
1587    }
1588
1589    fn i256_val_from_be_bytes(
1590        &self,
1591        _vmcaller: &mut VmCaller<Self::VmUserState>,
1592        bytes: BytesObject,
1593    ) -> Result<I256Val, HostError> {
1594        let num = self.visit_obj(bytes, |b: &ScBytes| {
1595            Ok(I256::from_be_bytes(self.fixed_length_bytes_from_slice(
1596                "I256 bytes",
1597                b.as_slice(),
1598            )?))
1599        })?;
1600        I256Val::try_from_val(self, &num).map_err(|_| ConversionError.into())
1601    }
1602
1603    fn i256_val_to_be_bytes(
1604        &self,
1605        _vmcaller: &mut VmCaller<Self::VmUserState>,
1606        val: I256Val,
1607    ) -> Result<BytesObject, HostError> {
1608        if let Ok(so) = I256Small::try_from(val) {
1609            self.add_host_object(self.scbytes_from_slice(&I256::from(so).to_be_bytes())?)
1610        } else {
1611            let obj = val.try_into()?;
1612            let scb = self.visit_obj(obj, |i: &I256| self.scbytes_from_slice(&i.to_be_bytes()))?;
1613            self.add_host_object(scb)
1614        }
1615    }
1616
1617    fn obj_to_i256_hi_hi(
1618        &self,
1619        _vmcaller: &mut VmCaller<Self::VmUserState>,
1620        obj: I256Object,
1621    ) -> Result<i64, HostError> {
1622        self.visit_obj(obj, |i: &I256| {
1623            let (hi_hi, _, _, _) = i256_into_pieces(*i);
1624            Ok(hi_hi)
1625        })
1626    }
1627
1628    fn obj_to_i256_hi_lo(
1629        &self,
1630        _vmcaller: &mut VmCaller<Self::VmUserState>,
1631        obj: I256Object,
1632    ) -> Result<u64, HostError> {
1633        self.visit_obj(obj, |i: &I256| {
1634            let (_, hi_lo, _, _) = i256_into_pieces(*i);
1635            Ok(hi_lo)
1636        })
1637    }
1638
1639    fn obj_to_i256_lo_hi(
1640        &self,
1641        _vmcaller: &mut VmCaller<Self::VmUserState>,
1642        obj: I256Object,
1643    ) -> Result<u64, HostError> {
1644        self.visit_obj(obj, |i: &I256| {
1645            let (_, _, lo_hi, _) = i256_into_pieces(*i);
1646            Ok(lo_hi)
1647        })
1648    }
1649
1650    fn obj_to_i256_lo_lo(
1651        &self,
1652        _vmcaller: &mut VmCaller<Self::VmUserState>,
1653        obj: I256Object,
1654    ) -> Result<u64, HostError> {
1655        self.visit_obj(obj, |i: &I256| {
1656            let (_, _, _, lo_lo) = i256_into_pieces(*i);
1657            Ok(lo_lo)
1658        })
1659    }
1660
1661    impl_bignum_host_fns!(u256_add, checked_add, U256, U256Val, Int256AddSub);
1662    impl_bignum_host_fns!(u256_sub, checked_sub, U256, U256Val, Int256AddSub);
1663    impl_bignum_host_fns!(u256_mul, checked_mul, U256, U256Val, Int256Mul);
1664    impl_bignum_host_fns!(u256_div, checked_div, U256, U256Val, Int256Div);
1665    impl_bignum_host_fns!(
1666        u256_rem_euclid,
1667        checked_rem_euclid,
1668        U256,
1669        U256Val,
1670        Int256Div
1671    );
1672    impl_bignum_host_fns_rhs_u32!(u256_pow, checked_pow, U256, U256Val, Int256Pow);
1673    impl_bignum_host_fns_rhs_u32!(u256_shl, checked_shl, U256, U256Val, Int256Shift);
1674    impl_bignum_host_fns_rhs_u32!(u256_shr, checked_shr, U256, U256Val, Int256Shift);
1675
1676    impl_bignum_host_fns!(i256_add, checked_add, I256, I256Val, Int256AddSub);
1677    impl_bignum_host_fns!(i256_sub, checked_sub, I256, I256Val, Int256AddSub);
1678    impl_bignum_host_fns!(i256_mul, checked_mul, I256, I256Val, Int256Mul);
1679    impl_bignum_host_fns!(i256_div, checked_div, I256, I256Val, Int256Div);
1680    impl_bignum_host_fns!(
1681        i256_rem_euclid,
1682        checked_rem_euclid,
1683        I256,
1684        I256Val,
1685        Int256Div
1686    );
1687    impl_bignum_host_fns_rhs_u32!(i256_pow, checked_pow, I256, I256Val, Int256Pow);
1688    impl_bignum_host_fns_rhs_u32!(i256_shl, checked_shl, I256, I256Val, Int256Shift);
1689    impl_bignum_host_fns_rhs_u32!(i256_shr, checked_shr, I256, I256Val, Int256Shift);
1690
1691    // endregion: "int" module functions
1692    // region: "map" module functions
1693
1694    fn map_new(&self, _vmcaller: &mut VmCaller<Host>) -> Result<MapObject, HostError> {
1695        self.add_host_object(HostMap::new())
1696    }
1697
1698    fn map_put(
1699        &self,
1700        _vmcaller: &mut VmCaller<Host>,
1701        m: MapObject,
1702        k: Val,
1703        v: Val,
1704    ) -> Result<MapObject, HostError> {
1705        let mnew = self.visit_obj(m, |hm: &HostMap| hm.insert(k, v, self))?;
1706        self.add_host_object(mnew)
1707    }
1708
1709    fn map_get(
1710        &self,
1711        _vmcaller: &mut VmCaller<Host>,
1712        m: MapObject,
1713        k: Val,
1714    ) -> Result<Val, HostError> {
1715        self.visit_obj(m, |hm: &HostMap| {
1716            hm.get(&k, self)?.copied().ok_or_else(|| {
1717                self.err(
1718                    ScErrorType::Object,
1719                    ScErrorCode::MissingValue,
1720                    "map key not found in map_get",
1721                    &[m.to_val(), k],
1722                )
1723            })
1724        })
1725    }
1726
1727    fn map_del(
1728        &self,
1729        _vmcaller: &mut VmCaller<Host>,
1730        m: MapObject,
1731        k: Val,
1732    ) -> Result<MapObject, HostError> {
1733        match self.visit_obj(m, |hm: &HostMap| hm.remove(&k, self))? {
1734            Some((mnew, _)) => Ok(self.add_host_object(mnew)?),
1735            None => Err(self.err(
1736                ScErrorType::Object,
1737                ScErrorCode::MissingValue,
1738                "map key not found in map_del",
1739                &[m.to_val(), k],
1740            )),
1741        }
1742    }
1743
1744    fn map_len(&self, _vmcaller: &mut VmCaller<Host>, m: MapObject) -> Result<U32Val, HostError> {
1745        let len = self.visit_obj(m, |hm: &HostMap| Ok(hm.len()))?;
1746        self.usize_to_u32val(len)
1747    }
1748
1749    fn map_has(
1750        &self,
1751        _vmcaller: &mut VmCaller<Host>,
1752        m: MapObject,
1753        k: Val,
1754    ) -> Result<Bool, HostError> {
1755        self.visit_obj(m, |hm: &HostMap| Ok(hm.contains_key(&k, self)?.into()))
1756    }
1757
1758    fn map_key_by_pos(
1759        &self,
1760        _vmcaller: &mut VmCaller<Host>,
1761        m: MapObject,
1762        i: U32Val,
1763    ) -> Result<Val, HostError> {
1764        let i: u32 = i.into();
1765        self.visit_obj(m, |hm: &HostMap| {
1766            hm.get_at_index(i as usize, self).map(|r| r.0)
1767        })
1768    }
1769
1770    fn map_val_by_pos(
1771        &self,
1772        _vmcaller: &mut VmCaller<Host>,
1773        m: MapObject,
1774        i: U32Val,
1775    ) -> Result<Val, HostError> {
1776        let i: u32 = i.into();
1777        self.visit_obj(m, |hm: &HostMap| {
1778            hm.get_at_index(i as usize, self).map(|r| r.1)
1779        })
1780    }
1781
1782    fn map_keys(
1783        &self,
1784        _vmcaller: &mut VmCaller<Host>,
1785        m: MapObject,
1786    ) -> Result<VecObject, HostError> {
1787        let vec = self.visit_obj(m, |hm: &HostMap| {
1788            HostVec::from_exact_iter(hm.keys(self)?.cloned(), self.budget_ref())
1789        })?;
1790        self.add_host_object(vec)
1791    }
1792
1793    fn map_values(
1794        &self,
1795        _vmcaller: &mut VmCaller<Host>,
1796        m: MapObject,
1797    ) -> Result<VecObject, HostError> {
1798        let vec = self.visit_obj(m, |hm: &HostMap| {
1799            HostVec::from_exact_iter(hm.values(self)?.cloned(), self.budget_ref())
1800        })?;
1801        self.add_host_object(vec)
1802    }
1803
1804    fn map_new_from_linear_memory(
1805        &self,
1806        vmcaller: &mut VmCaller<Host>,
1807        keys_pos: U32Val,
1808        vals_pos: U32Val,
1809        len: U32Val,
1810    ) -> Result<MapObject, HostError> {
1811        // Step 1: extract all key symbols.
1812        let MemFnArgs {
1813            vm,
1814            pos: keys_pos,
1815            len,
1816        } = self.get_mem_fn_args(keys_pos, len)?;
1817        let mut key_syms = Vec::<Symbol>::with_metered_capacity(len as usize, self)?;
1818        self.metered_vm_scan_slices_in_linear_memory(
1819            vmcaller,
1820            &vm,
1821            keys_pos,
1822            len as usize,
1823            |_n, slice| {
1824                // Optimization note: this does an unnecessary `ScVal` roundtrip.
1825                // We should just use `Symbol::try_from_val` on the slice instead.
1826                self.charge_budget(ContractCostType::MemCpy, Some(slice.len() as u64))?;
1827                let scsym = ScSymbol(slice.try_into()?);
1828                let sym = Symbol::try_from(self.to_valid_host_val(&ScVal::Symbol(scsym))?)?;
1829                key_syms.push(sym);
1830                Ok(())
1831            },
1832        )?;
1833
1834        // Step 2: extract all val Vals.
1835        let vals_pos: u32 = vals_pos.into();
1836        Vec::<Val>::charge_bulk_init_cpy(len as u64, self)?;
1837        let mut vals: Vec<Val> = vec![Val::VOID.into(); len as usize];
1838        // charge for conversion from bytes to `Val`s
1839        self.charge_budget(
1840            ContractCostType::MemCpy,
1841            Some((len as u64).saturating_mul(8)),
1842        )?;
1843        self.metered_vm_read_vals_from_linear_memory::<8, Val>(
1844            vmcaller,
1845            &vm,
1846            vals_pos,
1847            vals.as_mut_slice(),
1848            |buf| self.relative_to_absolute(Val::from_payload(u64::from_le_bytes(*buf))),
1849        )?;
1850        for v in vals.iter() {
1851            self.check_val_integrity(*v)?;
1852        }
1853
1854        // Step 3: turn pairs into a map.
1855        let pair_iter = key_syms
1856            .iter()
1857            .map(|s| s.to_val())
1858            .zip(vals.iter().cloned());
1859        let map = HostMap::from_exact_iter(pair_iter, self)?;
1860        self.add_host_object(map)
1861    }
1862
1863    fn map_unpack_to_linear_memory(
1864        &self,
1865        vmcaller: &mut VmCaller<Host>,
1866        map: MapObject,
1867        keys_pos: U32Val,
1868        vals_pos: U32Val,
1869        len: U32Val,
1870    ) -> Result<Void, HostError> {
1871        let MemFnArgs {
1872            vm,
1873            pos: keys_pos,
1874            len,
1875        } = self.get_mem_fn_args(keys_pos, len)?;
1876        self.visit_obj(map, |mapobj: &HostMap| {
1877            if mapobj.len() != len as usize {
1878                return Err(self.err(
1879                    ScErrorType::Object,
1880                    ScErrorCode::UnexpectedSize,
1881                    "differing host map and output slice lengths when unpacking map to linear memory",
1882                    &[],
1883                ));
1884            }
1885            // Step 1: check all key symbols.
1886            self.metered_vm_scan_slices_in_linear_memory(
1887                vmcaller,
1888                &vm,
1889                keys_pos,
1890                len as usize,
1891                |n, slice| {
1892                    let sym = Symbol::try_from(
1893                        mapobj.get_at_index(n, self).map_err(|he|
1894                            if he.error.is_type(ScErrorType::Budget) {
1895                                he
1896                            } else {
1897                                self.err(
1898                                    ScErrorType::Object,
1899                                    ScErrorCode::IndexBounds,
1900                                    "vector out of bounds while unpacking map to linear memory",
1901                                    &[],
1902                                )
1903                            }
1904                        )?.0
1905                    )?;
1906                    self.check_symbol_matches(slice, sym)?;
1907                    Ok(())
1908                },
1909            )?;
1910
1911            // Step 2: write all vals.
1912            // charges memcpy of converting map entries into bytes
1913            self.charge_budget(ContractCostType::MemCpy, Some((len as u64).saturating_mul(8)))?;
1914            self.metered_vm_write_vals_to_linear_memory(
1915                vmcaller,
1916                &vm,
1917                vals_pos.into(),
1918                mapobj.map.as_slice(),
1919                |pair| {
1920                    Ok(u64::to_le_bytes(
1921                        self.absolute_to_relative(pair.1)?.get_payload(),
1922                    ))
1923                },
1924            )?;
1925            Ok(())
1926        })?;
1927
1928        Ok(Val::VOID)
1929    }
1930
1931    // endregion: "map" module functions
1932    // region: "vec" module functions
1933
1934    fn vec_new(&self, _vmcaller: &mut VmCaller<Host>) -> Result<VecObject, HostError> {
1935        self.add_host_object(HostVec::new())
1936    }
1937
1938    fn vec_put(
1939        &self,
1940        _vmcaller: &mut VmCaller<Host>,
1941        v: VecObject,
1942        i: U32Val,
1943        x: Val,
1944    ) -> Result<VecObject, HostError> {
1945        let i: u32 = i.into();
1946        let vnew = self.visit_obj(v, |hv: &HostVec| {
1947            self.validate_index_lt_bound(i, hv.len())?;
1948            hv.set(i as usize, x, self.as_budget())
1949        })?;
1950        self.add_host_object(vnew)
1951    }
1952
1953    fn vec_get(
1954        &self,
1955        _vmcaller: &mut VmCaller<Host>,
1956        v: VecObject,
1957        i: U32Val,
1958    ) -> Result<Val, HostError> {
1959        let i: u32 = i.into();
1960        self.visit_obj(v, |hv: &HostVec| {
1961            self.validate_index_lt_bound(i, hv.len())?;
1962            hv.get(i as usize, self.as_budget()).map(|r| *r)
1963        })
1964    }
1965
1966    fn vec_del(
1967        &self,
1968        _vmcaller: &mut VmCaller<Host>,
1969        v: VecObject,
1970        i: U32Val,
1971    ) -> Result<VecObject, HostError> {
1972        let i: u32 = i.into();
1973        let vnew = self.visit_obj(v, |hv: &HostVec| {
1974            self.validate_index_lt_bound(i, hv.len())?;
1975            hv.remove(i as usize, self.as_budget())
1976        })?;
1977        self.add_host_object(vnew)
1978    }
1979
1980    fn vec_len(&self, _vmcaller: &mut VmCaller<Host>, v: VecObject) -> Result<U32Val, HostError> {
1981        let len = self.visit_obj(v, |hv: &HostVec| Ok(hv.len()))?;
1982        self.usize_to_u32val(len)
1983    }
1984
1985    fn vec_push_front(
1986        &self,
1987        _vmcaller: &mut VmCaller<Host>,
1988        v: VecObject,
1989        x: Val,
1990    ) -> Result<VecObject, HostError> {
1991        let vnew = self.visit_obj(v, |hv: &HostVec| hv.push_front(x, self.as_budget()))?;
1992        self.add_host_object(vnew)
1993    }
1994
1995    fn vec_pop_front(
1996        &self,
1997        _vmcaller: &mut VmCaller<Host>,
1998        v: VecObject,
1999    ) -> Result<VecObject, HostError> {
2000        let vnew = self.visit_obj(v, |hv: &HostVec| hv.pop_front(self.as_budget()))?;
2001        self.add_host_object(vnew)
2002    }
2003
2004    fn vec_push_back(
2005        &self,
2006        _vmcaller: &mut VmCaller<Host>,
2007        v: VecObject,
2008        x: Val,
2009    ) -> Result<VecObject, HostError> {
2010        let vnew = self.visit_obj(v, |hv: &HostVec| hv.push_back(x, self.as_budget()))?;
2011        self.add_host_object(vnew)
2012    }
2013
2014    fn vec_pop_back(
2015        &self,
2016        _vmcaller: &mut VmCaller<Host>,
2017        v: VecObject,
2018    ) -> Result<VecObject, HostError> {
2019        let vnew = self.visit_obj(v, |hv: &HostVec| hv.pop_back(self.as_budget()))?;
2020        self.add_host_object(vnew)
2021    }
2022
2023    fn vec_front(&self, _vmcaller: &mut VmCaller<Host>, v: VecObject) -> Result<Val, HostError> {
2024        self.visit_obj(v, |hv: &HostVec| {
2025            hv.front(self.as_budget()).map(|hval| *hval)
2026        })
2027    }
2028
2029    fn vec_back(&self, _vmcaller: &mut VmCaller<Host>, v: VecObject) -> Result<Val, HostError> {
2030        self.visit_obj(v, |hv: &HostVec| {
2031            hv.back(self.as_budget()).map(|hval| *hval)
2032        })
2033    }
2034
2035    fn vec_insert(
2036        &self,
2037        _vmcaller: &mut VmCaller<Host>,
2038        v: VecObject,
2039        i: U32Val,
2040        x: Val,
2041    ) -> Result<VecObject, HostError> {
2042        let i: u32 = i.into();
2043        let vnew = self.visit_obj(v, |hv: &HostVec| {
2044            self.validate_index_le_bound(i, hv.len())?;
2045            hv.insert(i as usize, x, self.as_budget())
2046        })?;
2047        self.add_host_object(vnew)
2048    }
2049
2050    fn vec_append(
2051        &self,
2052        _vmcaller: &mut VmCaller<Host>,
2053        v1: VecObject,
2054        v2: VecObject,
2055    ) -> Result<VecObject, HostError> {
2056        let vnew = self.visit_obj(v1, |hv1: &HostVec| {
2057            self.visit_obj(v2, |hv2: &HostVec| {
2058                if hv1.len() > u32::MAX as usize - hv2.len() {
2059                    Err(self.err_arith_overflow())
2060                } else {
2061                    hv1.append(hv2, self.as_budget())
2062                }
2063            })
2064        })?;
2065        self.add_host_object(vnew)
2066    }
2067
2068    fn vec_slice(
2069        &self,
2070        _vmcaller: &mut VmCaller<Host>,
2071        v: VecObject,
2072        start: U32Val,
2073        end: U32Val,
2074    ) -> Result<VecObject, HostError> {
2075        let start: u32 = start.into();
2076        let end: u32 = end.into();
2077        let vnew = self.visit_obj(v, |hv: &HostVec| {
2078            let range = self.valid_range_from_start_end_bound(start, end, hv.len())?;
2079            hv.slice(range, self.as_budget())
2080        })?;
2081        self.add_host_object(vnew)
2082    }
2083
2084    fn vec_first_index_of(
2085        &self,
2086        _vmcaller: &mut VmCaller<Host>,
2087        v: VecObject,
2088        x: Val,
2089    ) -> Result<Val, Self::Error> {
2090        self.visit_obj(v, |hv: &HostVec| {
2091            Ok(
2092                match hv.first_index_of(|other| self.compare(&x, other), self.as_budget())? {
2093                    Some(u) => self.usize_to_u32val(u)?.into(),
2094                    None => Val::VOID.into(),
2095                },
2096            )
2097        })
2098    }
2099
2100    fn vec_last_index_of(
2101        &self,
2102        _vmcaller: &mut VmCaller<Host>,
2103        v: VecObject,
2104        x: Val,
2105    ) -> Result<Val, Self::Error> {
2106        self.visit_obj(v, |hv: &HostVec| {
2107            Ok(
2108                match hv.last_index_of(|other| self.compare(&x, other), self.as_budget())? {
2109                    Some(u) => self.usize_to_u32val(u)?.into(),
2110                    None => Val::VOID.into(),
2111                },
2112            )
2113        })
2114    }
2115
2116    fn vec_binary_search(
2117        &self,
2118        _vmcaller: &mut VmCaller<Host>,
2119        v: VecObject,
2120        x: Val,
2121    ) -> Result<u64, Self::Error> {
2122        self.visit_obj(v, |hv: &HostVec| {
2123            let res = hv.binary_search_by(|probe| self.compare(probe, &x), self.as_budget())?;
2124            self.u64_from_binary_search_result(res)
2125        })
2126    }
2127
2128    fn vec_new_from_linear_memory(
2129        &self,
2130        vmcaller: &mut VmCaller<Host>,
2131        vals_pos: U32Val,
2132        len: U32Val,
2133    ) -> Result<VecObject, HostError> {
2134        let MemFnArgs { vm, pos, len } = self.get_mem_fn_args(vals_pos, len)?;
2135        Vec::<Val>::charge_bulk_init_cpy(len as u64, self)?;
2136        let mut vals: Vec<Val> = vec![Val::VOID.to_val(); len as usize];
2137        // charge for conversion from bytes to `Val`s
2138        self.charge_budget(
2139            ContractCostType::MemCpy,
2140            Some((len as u64).saturating_mul(8)),
2141        )?;
2142        self.metered_vm_read_vals_from_linear_memory::<8, Val>(
2143            vmcaller,
2144            &vm,
2145            pos,
2146            vals.as_mut_slice(),
2147            |buf| self.relative_to_absolute(Val::from_payload(u64::from_le_bytes(*buf))),
2148        )?;
2149        for v in vals.iter() {
2150            self.check_val_integrity(*v)?;
2151        }
2152        self.add_host_object(HostVec::from_vec(vals)?)
2153    }
2154
2155    fn vec_unpack_to_linear_memory(
2156        &self,
2157        vmcaller: &mut VmCaller<Host>,
2158        vec: VecObject,
2159        vals_pos: U32Val,
2160        len: U32Val,
2161    ) -> Result<Void, HostError> {
2162        let MemFnArgs { vm, pos, len } = self.get_mem_fn_args(vals_pos, len)?;
2163        self.visit_obj(vec, |vecobj: &HostVec| {
2164            if vecobj.len() != len as usize {
2165                return Err(self.err(
2166                    ScErrorType::Object,
2167                    ScErrorCode::UnexpectedSize,
2168                    "differing host vector and output vector lengths when unpacking vec to linear memory",
2169                    &[],
2170                ));
2171            }
2172            // charges memcpy of converting vec entries into bytes
2173            self.charge_budget(ContractCostType::MemCpy, Some((len as u64).saturating_mul(8)))?;
2174            self.metered_vm_write_vals_to_linear_memory(
2175                vmcaller,
2176                &vm,
2177                pos,
2178                vecobj.as_slice(),
2179                |x| {
2180                    Ok(u64::to_le_bytes(
2181                        self.absolute_to_relative(*x)?.get_payload(),
2182                    ))
2183                },
2184            )
2185        })?;
2186        Ok(Val::VOID)
2187    }
2188
2189    // endregion: "vec" module functions
2190    // region: "ledger" module functions
2191
2192    // Notes on metering: covered by components
2193    fn put_contract_data(
2194        &self,
2195        _vmcaller: &mut VmCaller<Host>,
2196        k: Val,
2197        v: Val,
2198        t: StorageType,
2199    ) -> Result<Void, HostError> {
2200        match t {
2201            StorageType::Temporary | StorageType::Persistent => {
2202                self.put_contract_data_into_ledger(k, v, t)?
2203            }
2204            StorageType::Instance => self.with_mut_instance_storage(|s| {
2205                s.map = s.map.insert(k, v, self)?;
2206                Ok(())
2207            })?,
2208        };
2209
2210        Ok(Val::VOID)
2211    }
2212
2213    // Notes on metering: covered by components
2214    fn has_contract_data(
2215        &self,
2216        _vmcaller: &mut VmCaller<Host>,
2217        k: Val,
2218        t: StorageType,
2219    ) -> Result<Bool, HostError> {
2220        let res = match t {
2221            StorageType::Temporary | StorageType::Persistent => {
2222                let key = self.storage_key_from_val(k, t.try_into()?)?;
2223                self.try_borrow_storage_mut()?
2224                    .has_with_host(&key, self, Some(k))?
2225            }
2226            StorageType::Instance => {
2227                self.with_instance_storage(|s| Ok(s.map.get(&k, self)?.is_some()))?
2228            }
2229        };
2230
2231        Ok(Val::from_bool(res))
2232    }
2233
2234    // Notes on metering: covered by components
2235    fn get_contract_data(
2236        &self,
2237        _vmcaller: &mut VmCaller<Host>,
2238        k: Val,
2239        t: StorageType,
2240    ) -> Result<Val, HostError> {
2241        match t {
2242            StorageType::Temporary | StorageType::Persistent => {
2243                let key = self.storage_key_from_val(k, t.try_into()?)?;
2244                let entry = self
2245                    .try_borrow_storage_mut()?
2246                    .get_with_host(&key, self, Some(k))?;
2247                match &entry.data {
2248                    LedgerEntryData::ContractData(e) => Ok(self.to_valid_host_val(&e.val)?),
2249                    _ => Err(self.err(
2250                        ScErrorType::Storage,
2251                        ScErrorCode::InternalError,
2252                        "expected contract data ledger entry",
2253                        &[],
2254                    )),
2255                }
2256            }
2257            StorageType::Instance => self.with_instance_storage(|s| {
2258                s.map
2259                    .get(&k, self)?
2260                    .ok_or_else(|| {
2261                        self.err(
2262                            ScErrorType::Storage,
2263                            ScErrorCode::MissingValue,
2264                            "key is missing from instance storage",
2265                            &[k],
2266                        )
2267                    })
2268                    .copied()
2269            }),
2270        }
2271    }
2272
2273    // Notes on metering: covered by components
2274    fn del_contract_data(
2275        &self,
2276        _vmcaller: &mut VmCaller<Host>,
2277        k: Val,
2278        t: StorageType,
2279    ) -> Result<Void, HostError> {
2280        match t {
2281            StorageType::Temporary | StorageType::Persistent => {
2282                let key = self.storage_key_from_val(k, t.try_into()?)?;
2283                self.try_borrow_storage_mut()?
2284                    .del_with_host(&key, self, Some(k))?;
2285            }
2286            StorageType::Instance => {
2287                self.with_mut_instance_storage(|s| {
2288                    if let Some((new_map, _)) = s.map.remove(&k, self)? {
2289                        s.map = new_map;
2290                    }
2291                    Ok(())
2292                })?;
2293            }
2294        }
2295
2296        Ok(Val::VOID)
2297    }
2298
2299    // Notes on metering: covered by components
2300    fn extend_contract_data_ttl(
2301        &self,
2302        _vmcaller: &mut VmCaller<Host>,
2303        k: Val,
2304        t: StorageType,
2305        threshold: U32Val,
2306        extend_to: U32Val,
2307    ) -> Result<Void, HostError> {
2308        if matches!(t, StorageType::Instance) {
2309            return Err(self.err(
2310                ScErrorType::Storage,
2311                ScErrorCode::InvalidAction,
2312                "instance storage should be extended via `extend_current_contract_instance_and_code_ttl` function only",
2313                &[],
2314            ))?;
2315        }
2316        let key = self.storage_key_from_val(k, t.try_into()?)?;
2317        self.try_borrow_storage_mut()?.extend_ttl(
2318            self,
2319            key,
2320            threshold.into(),
2321            extend_to.into(),
2322            Some(k),
2323        )?;
2324        Ok(Val::VOID)
2325    }
2326
2327    fn extend_current_contract_instance_and_code_ttl(
2328        &self,
2329        _vmcaller: &mut VmCaller<Host>,
2330        threshold: U32Val,
2331        extend_to: U32Val,
2332    ) -> Result<Void, HostError> {
2333        let contract_id = self.get_current_contract_id_internal()?;
2334        let key = self.contract_instance_ledger_key(&contract_id)?;
2335        self.extend_contract_instance_ttl_from_contract_id(
2336            key.clone(),
2337            threshold.into(),
2338            extend_to.into(),
2339        )?;
2340        self.extend_contract_code_ttl_from_contract_id(key, threshold.into(), extend_to.into())?;
2341        Ok(Val::VOID)
2342    }
2343
2344    fn extend_contract_instance_ttl(
2345        &self,
2346        _vmcaller: &mut VmCaller<Self::VmUserState>,
2347        contract: AddressObject,
2348        threshold: U32Val,
2349        extend_to: U32Val,
2350    ) -> Result<Void, Self::Error> {
2351        let contract_id = self.contract_id_from_address(contract)?;
2352        let key = self.contract_instance_ledger_key(&contract_id)?;
2353
2354        self.extend_contract_instance_ttl_from_contract_id(
2355            key,
2356            threshold.into(),
2357            extend_to.into(),
2358        )?;
2359
2360        Ok(Val::VOID)
2361    }
2362
2363    fn extend_contract_instance_and_code_ttl(
2364        &self,
2365        _vmcaller: &mut VmCaller<Self::VmUserState>,
2366        contract: AddressObject,
2367        threshold: U32Val,
2368        extend_to: U32Val,
2369    ) -> Result<Void, Self::Error> {
2370        let contract_id = self.contract_id_from_address(contract)?;
2371        let key = self.contract_instance_ledger_key(&contract_id)?;
2372        self.extend_contract_instance_ttl_from_contract_id(
2373            key.clone(),
2374            threshold.into(),
2375            extend_to.into(),
2376        )?;
2377        self.extend_contract_code_ttl_from_contract_id(key, threshold.into(), extend_to.into())?;
2378        Ok(Val::VOID)
2379    }
2380
2381    fn extend_contract_code_ttl(
2382        &self,
2383        _vmcaller: &mut VmCaller<Self::VmUserState>,
2384        contract: AddressObject,
2385        threshold: U32Val,
2386        extend_to: U32Val,
2387    ) -> Result<Void, Self::Error> {
2388        let contract_id = self.contract_id_from_address(contract)?;
2389        let key = self.contract_instance_ledger_key(&contract_id)?;
2390
2391        self.extend_contract_code_ttl_from_contract_id(key, threshold.into(), extend_to.into())?;
2392
2393        Ok(Val::VOID)
2394    }
2395
2396    // Notes on metering: covered by the components.
2397    fn create_contract(
2398        &self,
2399        _vmcaller: &mut VmCaller<Host>,
2400        deployer: AddressObject,
2401        wasm_hash: BytesObject,
2402        salt: BytesObject,
2403    ) -> Result<AddressObject, HostError> {
2404        let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress {
2405            address: self.visit_obj(deployer, |addr: &ScAddress| addr.metered_clone(self))?,
2406            salt: self.u256_from_bytesobj_input("contract_id_salt", salt)?,
2407        });
2408        let executable =
2409            ContractExecutable::Wasm(self.hash_from_bytesobj_input("wasm_hash", wasm_hash)?);
2410        let args = CreateContractArgs {
2411            contract_id_preimage,
2412            executable,
2413        };
2414        self.create_contract_internal(Some(deployer), args)
2415    }
2416
2417    // Notes on metering: covered by the components.
2418    fn create_asset_contract(
2419        &self,
2420        _vmcaller: &mut VmCaller<Host>,
2421        serialized_asset: BytesObject,
2422    ) -> Result<AddressObject, HostError> {
2423        let asset: Asset = self.metered_from_xdr_obj(serialized_asset)?;
2424        let contract_id_preimage = ContractIdPreimage::Asset(asset);
2425        let executable = ContractExecutable::StellarAsset;
2426        let args = CreateContractArgs {
2427            contract_id_preimage,
2428            executable,
2429        };
2430        // Asset contracts don't need any deployer authorization (they're tied
2431        // to the asset issuers instead).
2432        self.create_contract_internal(None, args)
2433    }
2434
2435    // Notes on metering: covered by the components.
2436    fn get_contract_id(
2437        &self,
2438        _vmcaller: &mut VmCaller<Host>,
2439        deployer: AddressObject,
2440        salt: BytesObject,
2441    ) -> Result<AddressObject, HostError> {
2442        let hash_id = self.get_contract_id_hash(deployer, salt)?;
2443        self.add_host_object(ScAddress::Contract(hash_id))
2444    }
2445
2446    // Notes on metering: covered by the components.
2447    fn get_asset_contract_id(
2448        &self,
2449        _vmcaller: &mut VmCaller<Host>,
2450        serialized_asset: BytesObject,
2451    ) -> Result<AddressObject, HostError> {
2452        let asset: Asset = self.metered_from_xdr_obj(serialized_asset)?;
2453        let hash_id = self.get_asset_contract_id_hash(asset)?;
2454        self.add_host_object(ScAddress::Contract(hash_id))
2455    }
2456
2457    fn upload_wasm(
2458        &self,
2459        _vmcaller: &mut VmCaller<Host>,
2460        wasm: BytesObject,
2461    ) -> Result<BytesObject, HostError> {
2462        let wasm_vec =
2463            self.visit_obj(wasm, |bytes: &ScBytes| bytes.as_vec().metered_clone(self))?;
2464        self.upload_contract_wasm(wasm_vec)
2465    }
2466
2467    fn update_current_contract_wasm(
2468        &self,
2469        _vmcaller: &mut VmCaller<Host>,
2470        hash: BytesObject,
2471    ) -> Result<Void, HostError> {
2472        let wasm_hash = self.hash_from_bytesobj_input("wasm_hash", hash)?;
2473        if !self.wasm_exists(&wasm_hash)? {
2474            return Err(self.err(
2475                ScErrorType::Storage,
2476                ScErrorCode::MissingValue,
2477                "Wasm does not exist",
2478                &[hash.to_val()],
2479            ));
2480        }
2481        let curr_contract_id = self.get_current_contract_id_internal()?;
2482        let key = self.contract_instance_ledger_key(&curr_contract_id)?;
2483        let old_instance = self.retrieve_contract_instance_from_storage(&key)?;
2484        let new_executable = ContractExecutable::Wasm(wasm_hash);
2485        self.emit_update_contract_event(&old_instance.executable, &new_executable)?;
2486        self.store_contract_instance(Some(new_executable), None, curr_contract_id, &key)?;
2487        Ok(Val::VOID)
2488    }
2489
2490    // endregion: "ledger" module functions
2491    // region: "call" module functions
2492
2493    // Notes on metering: here covers the args unpacking. The actual VM work is changed at lower layers.
2494    fn call(
2495        &self,
2496        _vmcaller: &mut VmCaller<Host>,
2497        contract_address: AddressObject,
2498        func: Symbol,
2499        args: VecObject,
2500    ) -> Result<Val, HostError> {
2501        let argvec = self.call_args_from_obj(args)?;
2502        // this is the recommended path of calling a contract, with `reentry`
2503        // always set `ContractReentryMode::Prohibited`
2504        let res = self.call_n_internal(
2505            &self.contract_id_from_address(contract_address)?,
2506            func,
2507            argvec.as_slice(),
2508            ContractReentryMode::Prohibited,
2509            false,
2510        );
2511        if let Err(e) = &res {
2512            self.error(
2513                e.error,
2514                "contract call failed",
2515                &[func.to_val(), args.to_val()],
2516            );
2517        }
2518        res
2519    }
2520
2521    // Notes on metering: covered by the components.
2522    fn try_call(
2523        &self,
2524        _vmcaller: &mut VmCaller<Host>,
2525        contract_address: AddressObject,
2526        func: Symbol,
2527        args: VecObject,
2528    ) -> Result<Val, HostError> {
2529        let argvec = self.call_args_from_obj(args)?;
2530        // this is the "loosened" path of calling a contract.
2531        // TODO: A `reentry` flag will be passed from `try_call` into here.
2532        // For now, we are passing in `ContractReentryMode::Prohibited` to disable
2533        // reentry.
2534        let res = self.call_n_internal(
2535            &self.contract_id_from_address(contract_address)?,
2536            func,
2537            argvec.as_slice(),
2538            ContractReentryMode::Prohibited,
2539            false,
2540        );
2541        match res {
2542            Ok(rv) => Ok(rv),
2543            Err(e) => {
2544                self.error(
2545                    e.error,
2546                    "contract try_call failed",
2547                    &[func.to_val(), args.to_val()],
2548                );
2549                // Only allow to gracefully handle the recoverable errors.
2550                // Non-recoverable errors should still cause guest to panic and
2551                // abort execution.
2552                if e.is_recoverable() {
2553                    // Pass contract error _codes_ through, while switching
2554                    // from Err(ce) to Ok(ce), i.e. recovering.
2555                    if e.error.is_type(ScErrorType::Contract) {
2556                        Ok(e.error.to_val())
2557                    } else {
2558                        // Narrow all the remaining host errors down to a single
2559                        // error type. We don't want to expose the granular host
2560                        // errors to the guest, consistently with how every
2561                        // other host function works. This reduces the risk of
2562                        // implementation being 'locked' into specific error
2563                        // codes due to them being exposed to the guest and
2564                        // hashed into blockchain.
2565                        // The granular error codes are still observable with
2566                        // diagnostic events.
2567                        Ok(Error::from_type_and_code(
2568                            ScErrorType::Context,
2569                            ScErrorCode::InvalidAction,
2570                        )
2571                        .to_val())
2572                    }
2573                } else {
2574                    Err(e)
2575                }
2576            }
2577        }
2578    }
2579
2580    // endregion: "call" module functions
2581    // region: "buf" module functions
2582
2583    // Notes on metering: covered by components
2584    fn serialize_to_bytes(
2585        &self,
2586        _vmcaller: &mut VmCaller<Host>,
2587        v: Val,
2588    ) -> Result<BytesObject, HostError> {
2589        let scv = self.from_host_val(v)?;
2590        let mut buf = Vec::<u8>::new();
2591        metered_write_xdr(self.budget_ref(), &scv, &mut buf)?;
2592        self.add_host_object(self.scbytes_from_vec(buf)?)
2593    }
2594
2595    // Notes on metering: covered by components
2596    fn deserialize_from_bytes(
2597        &self,
2598        _vmcaller: &mut VmCaller<Host>,
2599        b: BytesObject,
2600    ) -> Result<Val, HostError> {
2601        let scv = self.visit_obj(b, |hv: &ScBytes| {
2602            self.metered_from_xdr::<ScVal>(hv.as_slice())
2603        })?;
2604        // Metering bug: the representation check is not metered,
2605        // so if the value is not valid, we won't charge anything for
2606        // walking the `ScVal`. Since `to_host_val` performs validation
2607        // and has proper metering, next protocol version should just
2608        // call `to_host_val` directly.
2609        if Val::can_represent_scval_recursive(&scv) {
2610            self.to_host_val(&scv)
2611        } else {
2612            Err(self.err(
2613                ScErrorType::Value,
2614                ScErrorCode::UnexpectedType,
2615                "Deserialized ScVal type cannot be represented as Val",
2616                &[(scv.discriminant() as i32).into()],
2617            ))
2618        }
2619    }
2620
2621    fn string_copy_to_linear_memory(
2622        &self,
2623        vmcaller: &mut VmCaller<Host>,
2624        s: StringObject,
2625        s_pos: U32Val,
2626        lm_pos: U32Val,
2627        len: U32Val,
2628    ) -> Result<Void, HostError> {
2629        self.memobj_copy_to_linear_memory::<ScString>(vmcaller, s, s_pos, lm_pos, len)?;
2630        Ok(Val::VOID)
2631    }
2632
2633    fn symbol_copy_to_linear_memory(
2634        &self,
2635        vmcaller: &mut VmCaller<Host>,
2636        s: SymbolObject,
2637        s_pos: U32Val,
2638        lm_pos: U32Val,
2639        len: U32Val,
2640    ) -> Result<Void, HostError> {
2641        self.memobj_copy_to_linear_memory::<ScSymbol>(vmcaller, s, s_pos, lm_pos, len)?;
2642        Ok(Val::VOID)
2643    }
2644
2645    fn bytes_copy_to_linear_memory(
2646        &self,
2647        vmcaller: &mut VmCaller<Host>,
2648        b: BytesObject,
2649        b_pos: U32Val,
2650        lm_pos: U32Val,
2651        len: U32Val,
2652    ) -> Result<Void, HostError> {
2653        self.memobj_copy_to_linear_memory::<ScBytes>(vmcaller, b, b_pos, lm_pos, len)?;
2654        Ok(Val::VOID)
2655    }
2656
2657    fn bytes_copy_from_linear_memory(
2658        &self,
2659        vmcaller: &mut VmCaller<Host>,
2660        b: BytesObject,
2661        b_pos: U32Val,
2662        lm_pos: U32Val,
2663        len: U32Val,
2664    ) -> Result<BytesObject, HostError> {
2665        self.memobj_copy_from_linear_memory::<ScBytes>(vmcaller, b, b_pos, lm_pos, len)
2666    }
2667
2668    fn bytes_new_from_linear_memory(
2669        &self,
2670        vmcaller: &mut VmCaller<Host>,
2671        lm_pos: U32Val,
2672        len: U32Val,
2673    ) -> Result<BytesObject, HostError> {
2674        self.memobj_new_from_linear_memory::<ScBytes>(vmcaller, lm_pos, len)
2675    }
2676
2677    fn string_new_from_linear_memory(
2678        &self,
2679        vmcaller: &mut VmCaller<Host>,
2680        lm_pos: U32Val,
2681        len: U32Val,
2682    ) -> Result<StringObject, HostError> {
2683        self.memobj_new_from_linear_memory::<ScString>(vmcaller, lm_pos, len)
2684    }
2685
2686    fn symbol_new_from_linear_memory(
2687        &self,
2688        vmcaller: &mut VmCaller<Host>,
2689        lm_pos: U32Val,
2690        len: U32Val,
2691    ) -> Result<SymbolObject, HostError> {
2692        self.memobj_new_from_linear_memory::<ScSymbol>(vmcaller, lm_pos, len)
2693    }
2694
2695    // Metering: covered by `metered_vm_scan_slices_in_linear_memory` and `symbol_matches`.
2696    fn symbol_index_in_linear_memory(
2697        &self,
2698        vmcaller: &mut VmCaller<Host>,
2699        sym: Symbol,
2700        lm_pos: U32Val,
2701        len: U32Val,
2702    ) -> Result<U32Val, HostError> {
2703        let MemFnArgs { vm, pos, len } = self.get_mem_fn_args(lm_pos, len)?;
2704        let mut found = None;
2705        self.metered_vm_scan_slices_in_linear_memory(
2706            vmcaller,
2707            &vm,
2708            pos,
2709            len as usize,
2710            |i, slice| {
2711                if self.symbol_matches(slice, sym)? {
2712                    if found.is_none() {
2713                        found = Some(self.usize_to_u32(i)?)
2714                    }
2715                }
2716                Ok(())
2717            },
2718        )?;
2719        match found {
2720            None => Err(self.err(
2721                ScErrorType::Value,
2722                ScErrorCode::MissingValue,
2723                "symbol not found in linear memory slices",
2724                &[sym.to_val()],
2725            )),
2726            Some(idx) => Ok(U32Val::from(idx)),
2727        }
2728    }
2729
2730    // Notes on metering: covered by `add_host_object`
2731    fn bytes_new(&self, _vmcaller: &mut VmCaller<Host>) -> Result<BytesObject, HostError> {
2732        self.add_host_object(self.scbytes_from_vec(Vec::<u8>::new())?)
2733    }
2734
2735    // Notes on metering: `get_mut` is free
2736    fn bytes_put(
2737        &self,
2738        _vmcaller: &mut VmCaller<Host>,
2739        b: BytesObject,
2740        iv: U32Val,
2741        u: U32Val,
2742    ) -> Result<BytesObject, HostError> {
2743        let i: u32 = iv.into();
2744        let u = self.u8_from_u32val_input("u", u)?;
2745        let vnew = self.visit_obj(b, |hv: &ScBytes| {
2746            let mut vnew: Vec<u8> = hv.metered_clone(self)?.into();
2747            match vnew.get_mut(i as usize) {
2748                None => Err(self.err(
2749                    ScErrorType::Object,
2750                    ScErrorCode::IndexBounds,
2751                    "bytes_put out of bounds",
2752                    &[iv.to_val()],
2753                )),
2754                Some(v) => {
2755                    *v = u;
2756                    Ok(ScBytes(vnew.try_into()?))
2757                }
2758            }
2759        })?;
2760        self.add_host_object(vnew)
2761    }
2762
2763    // Notes on metering: `get` is free
2764    fn bytes_get(
2765        &self,
2766        _vmcaller: &mut VmCaller<Host>,
2767        b: BytesObject,
2768        iv: U32Val,
2769    ) -> Result<U32Val, HostError> {
2770        let i: u32 = iv.into();
2771        self.visit_obj(b, |hv: &ScBytes| {
2772            hv.get(i as usize)
2773                .map(|u| U32Val::from(u32::from(*u)))
2774                .ok_or_else(|| {
2775                    self.err(
2776                        ScErrorType::Object,
2777                        ScErrorCode::IndexBounds,
2778                        "bytes_get out of bounds",
2779                        &[iv.to_val()],
2780                    )
2781                })
2782        })
2783    }
2784
2785    fn bytes_del(
2786        &self,
2787        _vmcaller: &mut VmCaller<Host>,
2788        b: BytesObject,
2789        i: U32Val,
2790    ) -> Result<BytesObject, HostError> {
2791        let i: u32 = i.into();
2792        let vnew = self.visit_obj(b, |hv: &ScBytes| {
2793            self.validate_index_lt_bound(i, hv.len())?;
2794            let mut vnew: Vec<u8> = hv.metered_clone(self)?.into();
2795            // len > i has been verified above but use checked_sub just in case
2796            let n_elts = (hv.len() as u64).checked_sub(i as u64).ok_or_else(|| {
2797                Error::from_type_and_code(ScErrorType::Context, ScErrorCode::InternalError)
2798            })?;
2799            // remove elements incurs the cost of moving bytes, it does not incur
2800            // allocation/deallocation
2801            metered_clone::charge_shallow_copy::<u8>(n_elts, self)?;
2802            vnew.remove(i as usize);
2803            Ok(ScBytes(vnew.try_into()?))
2804        })?;
2805        self.add_host_object(vnew)
2806    }
2807
2808    // Notes on metering: `len` is free
2809    fn bytes_len(
2810        &self,
2811        _vmcaller: &mut VmCaller<Host>,
2812        b: BytesObject,
2813    ) -> Result<U32Val, HostError> {
2814        let len = self.visit_obj(b, |hv: &ScBytes| Ok(hv.len()))?;
2815        self.usize_to_u32val(len)
2816    }
2817
2818    // Notes on metering: `len` is free
2819    fn string_len(
2820        &self,
2821        _vmcaller: &mut VmCaller<Host>,
2822        b: StringObject,
2823    ) -> Result<U32Val, HostError> {
2824        let len = self.visit_obj(b, |hv: &ScString| Ok(hv.len()))?;
2825        self.usize_to_u32val(len)
2826    }
2827
2828    // Notes on metering: `len` is free
2829    fn symbol_len(
2830        &self,
2831        _vmcaller: &mut VmCaller<Host>,
2832        b: SymbolObject,
2833    ) -> Result<U32Val, HostError> {
2834        let len = self.visit_obj(b, |hv: &ScSymbol| Ok(hv.len()))?;
2835        self.usize_to_u32val(len)
2836    }
2837
2838    // Notes on metering: `push` is free
2839    fn bytes_push(
2840        &self,
2841        _vmcaller: &mut VmCaller<Host>,
2842        b: BytesObject,
2843        u: U32Val,
2844    ) -> Result<BytesObject, HostError> {
2845        let u = self.u8_from_u32val_input("u", u)?;
2846        let vnew = self.visit_obj(b, |hv: &ScBytes| {
2847            // we allocate the new vector to be able to hold `len + 1` bytes, so that the push
2848            // will not trigger a reallocation, causing data to be cloned twice.
2849            let len = self.validate_usize_sum_fits_in_u32(hv.len(), 1)?;
2850            let mut vnew = Vec::<u8>::with_metered_capacity(len, self)?;
2851            vnew.extend_from_slice(hv.as_slice());
2852            vnew.push(u);
2853            Ok(ScBytes(vnew.try_into()?))
2854        })?;
2855        self.add_host_object(vnew)
2856    }
2857
2858    // Notes on metering: `pop` is free
2859    fn bytes_pop(
2860        &self,
2861        _vmcaller: &mut VmCaller<Host>,
2862        b: BytesObject,
2863    ) -> Result<BytesObject, HostError> {
2864        let vnew = self.visit_obj(b, |hv: &ScBytes| {
2865            let mut vnew: Vec<u8> = hv.metered_clone(self)?.into();
2866            // Popping will not trigger reallocation. Here we don't charge anything since this is
2867            // just a `len` reduction.
2868            if vnew.pop().is_none() {
2869                return Err(self.err(
2870                    ScErrorType::Object,
2871                    ScErrorCode::IndexBounds,
2872                    "bytes_pop out of bounds",
2873                    &[],
2874                ));
2875            }
2876            Ok(ScBytes(vnew.try_into()?))
2877        })?;
2878        self.add_host_object(vnew)
2879    }
2880
2881    // Notes on metering: `first` is free
2882    fn bytes_front(
2883        &self,
2884        _vmcaller: &mut VmCaller<Host>,
2885        b: BytesObject,
2886    ) -> Result<U32Val, HostError> {
2887        self.visit_obj(b, |hv: &ScBytes| {
2888            hv.first()
2889                .map(|u| U32Val::from(u32::from(*u)))
2890                .ok_or_else(|| {
2891                    self.err(
2892                        ScErrorType::Object,
2893                        ScErrorCode::IndexBounds,
2894                        "bytes_front out of bounds",
2895                        &[],
2896                    )
2897                })
2898        })
2899    }
2900
2901    // Notes on metering: `last` is free
2902    fn bytes_back(
2903        &self,
2904        _vmcaller: &mut VmCaller<Host>,
2905        b: BytesObject,
2906    ) -> Result<U32Val, HostError> {
2907        self.visit_obj(b, |hv: &ScBytes| {
2908            hv.last()
2909                .map(|u| U32Val::from(u32::from(*u)))
2910                .ok_or_else(|| {
2911                    self.err(
2912                        ScErrorType::Object,
2913                        ScErrorCode::IndexBounds,
2914                        "bytes_back out of bounds",
2915                        &[],
2916                    )
2917                })
2918        })
2919    }
2920
2921    fn bytes_insert(
2922        &self,
2923        _vmcaller: &mut VmCaller<Host>,
2924        b: BytesObject,
2925        i: U32Val,
2926        u: U32Val,
2927    ) -> Result<BytesObject, HostError> {
2928        let i: u32 = i.into();
2929        let u = self.u8_from_u32val_input("u", u)?;
2930        let vnew = self.visit_obj(b, |hv: &ScBytes| {
2931            self.validate_index_le_bound(i, hv.len())?;
2932            // we allocate the new vector to be able to hold `len + 1` bytes, so that the insert
2933            // will not trigger a reallocation, causing data to be cloned twice.
2934            let len = self.validate_usize_sum_fits_in_u32(hv.len(), 1)?;
2935            let mut vnew = Vec::<u8>::with_metered_capacity(len, self)?;
2936            vnew.extend_from_slice(hv.as_slice());
2937            vnew.insert(i as usize, u);
2938            Ok(ScBytes(vnew.try_into()?))
2939        })?;
2940        self.add_host_object(vnew)
2941    }
2942
2943    fn bytes_append(
2944        &self,
2945        _vmcaller: &mut VmCaller<Host>,
2946        b1: BytesObject,
2947        b2: BytesObject,
2948    ) -> Result<BytesObject, HostError> {
2949        let vnew = self.visit_obj(b1, |sb1: &ScBytes| {
2950            self.visit_obj(b2, |sb2: &ScBytes| {
2951                // we allocate large enough memory to hold the new combined vector, so that
2952                // allocation only happens once, and charge for it upfront.
2953                let len = self.validate_usize_sum_fits_in_u32(sb1.len(), sb2.len())?;
2954                let mut vnew = Vec::<u8>::with_metered_capacity(len, self)?;
2955                vnew.extend_from_slice(sb1.as_slice());
2956                vnew.extend_from_slice(sb2.as_slice());
2957                Ok(vnew)
2958            })
2959        })?;
2960        self.add_host_object(ScBytes(vnew.try_into()?))
2961    }
2962
2963    fn bytes_slice(
2964        &self,
2965        _vmcaller: &mut VmCaller<Host>,
2966        b: BytesObject,
2967        start: U32Val,
2968        end: U32Val,
2969    ) -> Result<BytesObject, HostError> {
2970        let start: u32 = start.into();
2971        let end: u32 = end.into();
2972        let vnew = self.visit_obj(b, |hv: &ScBytes| {
2973            let range = self.valid_range_from_start_end_bound(start, end, hv.len())?;
2974            self.metered_slice_to_vec(
2975                &hv.as_slice()
2976                    .get(range)
2977                    .ok_or_else(|| self.err_oob_object_index(None))?,
2978            )
2979        })?;
2980        self.add_host_object(self.scbytes_from_vec(vnew)?)
2981    }
2982
2983    // endregion: "buf" module functions
2984    // region: "crypto" module functions
2985
2986    // Notes on metering: covered by components.
2987    fn compute_hash_sha256(
2988        &self,
2989        _vmcaller: &mut VmCaller<Host>,
2990        x: BytesObject,
2991    ) -> Result<BytesObject, HostError> {
2992        let hash = self.sha256_hash_from_bytesobj_input(x)?;
2993        self.add_host_object(self.scbytes_from_vec(hash)?)
2994    }
2995
2996    // Notes on metering: covered by components.
2997    fn compute_hash_keccak256(
2998        &self,
2999        _vmcaller: &mut VmCaller<Host>,
3000        x: BytesObject,
3001    ) -> Result<BytesObject, HostError> {
3002        let hash = self.keccak256_hash_from_bytesobj_input(x)?;
3003        self.add_host_object(self.scbytes_from_vec(hash)?)
3004    }
3005
3006    // Notes on metering: covered by components.
3007    fn verify_sig_ed25519(
3008        &self,
3009        _vmcaller: &mut VmCaller<Host>,
3010        k: BytesObject,
3011        x: BytesObject,
3012        s: BytesObject,
3013    ) -> Result<Void, HostError> {
3014        let verifying_key = self.ed25519_pub_key_from_bytesobj_input(k)?;
3015        let sig = self.ed25519_signature_from_bytesobj_input("sig", s)?;
3016        let res = self.visit_obj(x, |payload: &ScBytes| {
3017            self.verify_sig_ed25519_internal(payload.as_slice(), &verifying_key, &sig)
3018        });
3019        Ok(res?.into())
3020    }
3021
3022    fn recover_key_ecdsa_secp256k1(
3023        &self,
3024        _vmcaller: &mut VmCaller<Host>,
3025        msg_digest: BytesObject,
3026        signature: BytesObject,
3027        recovery_id: U32Val,
3028    ) -> Result<BytesObject, HostError> {
3029        let sig = self.ecdsa_signature_from_bytesobj_input::<k256::Secp256k1>(signature)?;
3030        let rid = self.secp256k1_recovery_id_from_u32val(recovery_id)?;
3031        let hash = self.hash_from_bytesobj_input("msg_digest", msg_digest)?;
3032        let rk = self.recover_key_ecdsa_secp256k1_internal(&hash, &sig, rid)?;
3033        self.add_host_object(rk)
3034    }
3035
3036    fn verify_sig_ecdsa_secp256r1(
3037        &self,
3038        _vmcaller: &mut VmCaller<Host>,
3039        public_key: BytesObject,
3040        msg_digest: BytesObject,
3041        signature: BytesObject,
3042    ) -> Result<Void, HostError> {
3043        let pk = self.secp256r1_public_key_from_bytesobj_input(public_key)?;
3044        let sig = self.ecdsa_signature_from_bytesobj_input::<p256::NistP256>(signature)?;
3045        let msg_hash = self.hash_from_bytesobj_input("msg_digest", msg_digest)?;
3046        let res = self.secp256r1_verify_signature(&pk, &msg_hash, &sig)?;
3047        Ok(res.into())
3048    }
3049
3050    // endregion: "crypto" module functions
3051    // region: "test" module functions
3052
3053    fn dummy0(&self, _vmcaller: &mut VmCaller<Self::VmUserState>) -> Result<Val, Self::Error> {
3054        Ok(().into())
3055    }
3056
3057    fn protocol_gated_dummy(
3058        &self,
3059        _vmcaller: &mut VmCaller<Self::VmUserState>,
3060    ) -> Result<Val, Self::Error> {
3061        Ok(().into())
3062    }
3063
3064    // endregion: "test" module functions
3065    // region: "address" module functions
3066
3067    fn require_auth_for_args(
3068        &self,
3069        _vmcaller: &mut VmCaller<Self::VmUserState>,
3070        address: AddressObject,
3071        args: VecObject,
3072    ) -> Result<Void, Self::Error> {
3073        let args = self.visit_obj(args, |a: &HostVec| a.to_vec(self.budget_ref()))?;
3074        Ok(self
3075            .try_borrow_authorization_manager()?
3076            .require_auth(self, address, args)?
3077            .into())
3078    }
3079
3080    fn require_auth(
3081        &self,
3082        _vmcaller: &mut VmCaller<Self::VmUserState>,
3083        address: AddressObject,
3084    ) -> Result<Void, Self::Error> {
3085        let args = self.with_current_frame(|f| {
3086            let args = match f {
3087                Frame::ContractVM { args, .. } => args,
3088                Frame::HostFunction(_) => {
3089                    return Err(self.err(
3090                        ScErrorType::Context,
3091                        ScErrorCode::InternalError,
3092                        "require_auth is not suppported for host fns",
3093                        &[],
3094                    ));
3095                }
3096                Frame::StellarAssetContract(_, _, args, _) => args,
3097                #[cfg(any(test, feature = "testutils"))]
3098                Frame::TestContract(c) => &c.args,
3099            };
3100            args.metered_clone(self)
3101        })?;
3102
3103        Ok(self
3104            .try_borrow_authorization_manager()?
3105            .require_auth(self, address, args)?
3106            .into())
3107    }
3108
3109    fn authorize_as_curr_contract(
3110        &self,
3111        _vmcaller: &mut VmCaller<Self::VmUserState>,
3112        auth_entries: VecObject,
3113    ) -> Result<Void, HostError> {
3114        Ok(self
3115            .try_borrow_authorization_manager()?
3116            .add_invoker_contract_auth_with_curr_contract_as_invoker(self, auth_entries)?
3117            .into())
3118    }
3119
3120    fn address_to_strkey(
3121        &self,
3122        _vmcaller: &mut VmCaller<Self::VmUserState>,
3123        address: AddressObject,
3124    ) -> Result<StringObject, Self::Error> {
3125        let strkey = self.visit_obj(address, |addr: &ScAddress| {
3126            // Approximate the strkey encoding cost with two vector allocations:
3127            // one for the payload size (32-byte key/hash + 3 bytes for
3128            // version/checksum) and  another one for the base32 encoding of
3129            // the payload.
3130            const PAYLOAD_LEN: u64 = 32 + 3;
3131            Vec::<u8>::charge_bulk_init_cpy(PAYLOAD_LEN + (PAYLOAD_LEN * 8 + 4) / 5, self)?;
3132            let strkey = match addr {
3133                ScAddress::Account(acc_id) => {
3134                    let AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(ed25519))) = acc_id;
3135                    let strkey = stellar_strkey::Strkey::PublicKeyEd25519(
3136                        stellar_strkey::ed25519::PublicKey(ed25519.metered_clone(self)?),
3137                    );
3138                    strkey
3139                }
3140                ScAddress::Contract(Hash(h)) => stellar_strkey::Strkey::Contract(
3141                    stellar_strkey::Contract(h.metered_clone(self)?),
3142                ),
3143            };
3144            Ok(strkey.to_string())
3145        })?;
3146        self.add_host_object(ScString(strkey.try_into()?))
3147    }
3148
3149    fn strkey_to_address(
3150        &self,
3151        _vmcaller: &mut VmCaller<Self::VmUserState>,
3152        strkey_obj: Val,
3153    ) -> Result<AddressObject, Self::Error> {
3154        let strkey_obj = Object::try_from(strkey_obj).map_err(|_| {
3155            self.err(
3156                ScErrorType::Value,
3157                ScErrorCode::UnexpectedType,
3158                "strkey is not an object",
3159                &[strkey_obj],
3160            )
3161        })?;
3162        let sc_addr = self.visit_obj_untyped(strkey_obj, |key_obj: &HostObject| {
3163            let key = match key_obj {
3164                HostObject::Bytes(b) => b.as_slice(),
3165                HostObject::String(s) => s.as_slice(),
3166                _ => {
3167                    return Err(self.err(
3168                        ScErrorType::Value,
3169                        ScErrorCode::UnexpectedType,
3170                        "strkey is not a string or bytes object",
3171                        &[strkey_obj.to_val()],
3172                    ));
3173                }
3174            };
3175            const PAYLOAD_LEN: u64 = 32 + 3;
3176            let expected_key_len = (PAYLOAD_LEN * 8 + 4) / 5;
3177            if expected_key_len != key.len() as u64 {
3178                return Err(self.err(
3179                    ScErrorType::Value,
3180                    ScErrorCode::InvalidInput,
3181                    "unexpected strkey length",
3182                    &[strkey_obj.to_val()],
3183                ));
3184            }
3185            // Charge for the key copy to string.
3186            Vec::<u8>::charge_bulk_init_cpy(key.len() as u64, self)?;
3187            let key_str = String::from_utf8_lossy(key);
3188            // Approximate the decoding cost as two vector allocations for the
3189            // expected payload length (the strkey library does one extra copy).
3190            Vec::<u8>::charge_bulk_init_cpy(PAYLOAD_LEN + PAYLOAD_LEN, self)?;
3191            let strkey = stellar_strkey::Strkey::from_string(&key_str).map_err(|_| {
3192                self.err(
3193                    ScErrorType::Value,
3194                    ScErrorCode::InvalidInput,
3195                    "couldn't process the string as strkey",
3196                    &[strkey_obj.to_val()],
3197                )
3198            })?;
3199            match strkey {
3200                stellar_strkey::Strkey::PublicKeyEd25519(pk) => Ok(ScAddress::Account(AccountId(
3201                    PublicKey::PublicKeyTypeEd25519(Uint256(pk.0)),
3202                ))),
3203
3204                stellar_strkey::Strkey::Contract(c) => Ok(ScAddress::Contract(Hash(c.0))),
3205                _ => {
3206                    return Err(self.err(
3207                        ScErrorType::Value,
3208                        ScErrorCode::InvalidInput,
3209                        "incorrect strkey type",
3210                        &[strkey_obj.to_val()],
3211                    ));
3212                }
3213            }
3214        })?;
3215        self.add_host_object(sc_addr)
3216    }
3217
3218    // endregion: "address" module functions
3219    // region: "prng" module functions
3220
3221    fn prng_reseed(
3222        &self,
3223        _vmcaller: &mut VmCaller<Self::VmUserState>,
3224        seed: BytesObject,
3225    ) -> Result<Void, Self::Error> {
3226        self.visit_obj(seed, |bytes: &ScBytes| {
3227            let slice: &[u8] = bytes.as_ref();
3228            self.charge_budget(ContractCostType::MemCpy, Some(prng::SEED_BYTES))?;
3229            if let Ok(seed32) = slice.try_into() {
3230                self.with_current_prng(|prng| {
3231                    *prng = Prng::new_from_seed(seed32, self.budget_ref())?;
3232                    Ok(())
3233                })?;
3234                Ok(Val::VOID)
3235            } else if let Ok(len) = u32::try_from(slice.len()) {
3236                Err(self.err(
3237                    ScErrorType::Value,
3238                    ScErrorCode::UnexpectedSize,
3239                    "Unexpected size of BytesObject in prng_reseed",
3240                    &[U32Val::from(len).to_val()],
3241                ))
3242            } else {
3243                Err(self.err(
3244                    ScErrorType::Value,
3245                    ScErrorCode::UnexpectedSize,
3246                    "Unexpected size of BytesObject in prng_reseed",
3247                    &[],
3248                ))
3249            }
3250        })
3251    }
3252
3253    fn prng_bytes_new(
3254        &self,
3255        _vmcaller: &mut VmCaller<Self::VmUserState>,
3256        length: U32Val,
3257    ) -> Result<BytesObject, Self::Error> {
3258        self.add_host_object(
3259            self.with_current_prng(|prng| prng.bytes_new(length.into(), self.as_budget()))?,
3260        )
3261    }
3262
3263    fn prng_u64_in_inclusive_range(
3264        &self,
3265        _vmcaller: &mut VmCaller<Self::VmUserState>,
3266        lo: u64,
3267        hi: u64,
3268    ) -> Result<u64, Self::Error> {
3269        self.with_current_prng(|prng| prng.u64_in_inclusive_range(lo..=hi, self.as_budget()))
3270    }
3271
3272    fn prng_vec_shuffle(
3273        &self,
3274        _vmcaller: &mut VmCaller<Self::VmUserState>,
3275        vec: VecObject,
3276    ) -> Result<VecObject, Self::Error> {
3277        let vnew = self.visit_obj(vec, |v: &HostVec| {
3278            self.with_current_prng(|prng| prng.vec_shuffle(v, self.as_budget()))
3279        })?;
3280        self.add_host_object(vnew)
3281    }
3282    // endregion: "prng" module functions
3283}
3284
3285#[cfg(feature = "bench")]
3286impl Host {
3287    // Testing interface to create values directly for later use via Env functions.
3288    // It needs to be a `pub` method because benches are considered a separate crate.
3289    pub fn inject_val(&self, v: &ScVal) -> Result<Val, HostError> {
3290        self.to_host_val(v).map(Into::into)
3291    }
3292}
3293
3294#[cfg(any(test, feature = "testutils"))]
3295impl Host {
3296    /// Sets a hook to track top-level contract invocations.
3297    /// The hook triggers right before the top-level contract invocation
3298    /// starts and right after it ends.
3299    /// 'Top-level contract invocation' happens when the host creates
3300    /// the first context frame that belongs to a contract, which includes
3301    /// both direct host function calls (`call`/`try_call`), and test
3302    /// utilities such as `with_test_contract_frame` or
3303    /// `call_account_contract_check_auth`.
3304    pub fn set_top_contract_invocation_hook(
3305        &self,
3306        hook: Option<ContractInvocationHook>,
3307    ) -> Result<(), HostError> {
3308        *self.try_borrow_top_contract_invocation_hook_mut()? = hook;
3309        Ok(())
3310    }
3311
3312    /// Helper for mutating the [`Budget`] held in this [`Host`], either to
3313    /// allocate it on contract creation or to deplete it on callbacks from
3314    /// the VM or host functions.
3315    #[allow(dead_code)]
3316    pub fn with_budget<T, F>(&self, f: F) -> Result<T, HostError>
3317    where
3318        F: FnOnce(Budget) -> Result<T, HostError>,
3319    {
3320        f(self.0.budget.clone())
3321    }
3322
3323    /// Returns the ledger number until a contract with given address lives
3324    /// (inclusive).
3325    pub fn get_contract_instance_live_until_ledger(
3326        &self,
3327        contract: AddressObject,
3328    ) -> Result<u32, HostError> {
3329        let contract_id = self.contract_id_from_address(contract)?;
3330        let key = self.contract_instance_ledger_key(&contract_id)?;
3331        let (_, live_until) = self
3332            .try_borrow_storage_mut()?
3333            .get_with_live_until_ledger(&key, self, None)?;
3334        live_until.ok_or_else(|| {
3335            self.err(
3336                ScErrorType::Storage,
3337                ScErrorCode::InternalError,
3338                "unexpected contract instance without TTL",
3339                &[contract.into()],
3340            )
3341        })
3342    }
3343
3344    /// Returns the ledger number until contract code entry for contract with
3345    /// given address lives (inclusive).
3346    pub fn get_contract_code_live_until_ledger(
3347        &self,
3348        contract: AddressObject,
3349    ) -> Result<u32, HostError> {
3350        let contract_id = self.contract_id_from_address(contract)?;
3351        let key = self.contract_instance_ledger_key(&contract_id)?;
3352        match self
3353            .retrieve_contract_instance_from_storage(&key)?
3354            .executable
3355        {
3356            ContractExecutable::Wasm(wasm_hash) => {
3357                let key = self.contract_code_ledger_key(&wasm_hash)?;
3358                let (_, live_until) = self
3359                    .try_borrow_storage_mut()?
3360                    .get_with_live_until_ledger(&key, self, None)?;
3361                live_until.ok_or_else(|| {
3362                    self.err(
3363                        ScErrorType::Storage,
3364                        ScErrorCode::InternalError,
3365                        "unexpected contract code without TTL for a contract",
3366                        &[contract.into()],
3367                    )
3368                })
3369            }
3370            ContractExecutable::StellarAsset => Err(self.err(
3371                ScErrorType::Storage,
3372                ScErrorCode::InvalidInput,
3373                "Stellar Asset Contracts don't have contract code",
3374                &[],
3375            )),
3376        }
3377    }
3378
3379    /// Returns the ledger number until a current contract's data entry
3380    /// with given key and storage type lives (inclusive).
3381    /// Instance storage type is not supported by this function, use
3382    /// `get_contract_instance_live_until_ledger` instead.
3383    pub fn get_contract_data_live_until_ledger(
3384        &self,
3385        key: Val,
3386        storage_type: StorageType,
3387    ) -> Result<u32, HostError> {
3388        let ledger_key = match storage_type {
3389            StorageType::Temporary | StorageType::Persistent => {
3390                self.storage_key_from_val(key, storage_type.try_into()?)?
3391            }
3392            StorageType::Instance => {
3393                return Err(self.err(
3394                    ScErrorType::Storage,
3395                    ScErrorCode::InvalidAction,
3396                    "`get_contract_data_live_until_ledger` doesn't support instance storage, use `get_contract_instance_live_until_ledger` instead.",
3397                    &[],
3398                ));
3399            }
3400        };
3401        let (_, live_until) = self.try_borrow_storage_mut()?.get_with_live_until_ledger(
3402            &ledger_key,
3403            self,
3404            Some(key),
3405        )?;
3406        live_until.ok_or_else(|| {
3407            self.err(
3408                ScErrorType::Storage,
3409                ScErrorCode::InternalError,
3410                "unexpected contract data without TTL",
3411                &[key],
3412            )
3413        })
3414    }
3415}
3416
3417impl Host {
3418    #[allow(dead_code)]
3419    pub(crate) fn set_trace_hook(&self, hook: Option<TraceHook>) -> Result<(), HostError> {
3420        self.call_any_lifecycle_hook(TraceEvent::End)?;
3421        *self.try_borrow_trace_hook_mut()? = hook;
3422        self.call_any_lifecycle_hook(TraceEvent::Begin)?;
3423        Ok(())
3424    }
3425
3426    pub(crate) fn call_any_lifecycle_hook(&self, event: TraceEvent) -> Result<(), HostError> {
3427        match &*self.try_borrow_trace_hook()? {
3428            Some(hook) => hook(self, event),
3429            None => Ok(()),
3430        }
3431    }
3432}