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