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