soroban_env_host/
host.rs

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