Skip to main content

soroban_sdk/
env.rs

1use core::convert::Infallible;
2
3#[cfg(target_family = "wasm")]
4pub mod internal {
5    use core::convert::Infallible;
6
7    pub use soroban_env_guest::*;
8    pub type EnvImpl = Guest;
9    pub type MaybeEnvImpl = Guest;
10
11    // In the Guest case, Env::Error is already Infallible so there is no work
12    // to do to "reject an error": if an error occurs in the environment, the
13    // host will trap our VM and we'll never get here at all.
14    pub(crate) fn reject_err<T>(_env: &Guest, r: Result<T, Infallible>) -> Result<T, Infallible> {
15        r
16    }
17}
18
19#[cfg(not(target_family = "wasm"))]
20pub mod internal {
21    use core::convert::Infallible;
22
23    pub use soroban_env_host::*;
24    pub type EnvImpl = Host;
25    pub type MaybeEnvImpl = Option<Host>;
26
27    // When we have `feature="testutils"` (or are in cfg(test)) we enable feature
28    // `soroban-env-{common,host}/testutils` which in turn adds the helper method
29    // `Env::escalate_error_to_panic` to the Env trait.
30    //
31    // When this is available we want to use it, because it works in concert
32    // with a _different_ part of the host that's also `testutils`-gated: the
33    // mechanism for emulating the WASM VM error-handling semantics with native
34    // contracts. In particular when a WASM contract calls a host function that
35    // fails with some error E, the host traps the VM (not returning to it at
36    // all) and propagates E to the caller of the contract. This is simulated in
37    // the native case by returning a (nontrivial) error E to us here, which we
38    // then "reject" back to the host, which stores E in a temporary cell inside
39    // any `TestContract` frame in progress and then _panics_, unwinding back to
40    // a panic-catcher it installed when invoking the `TestContract` frame, and
41    // then extracting E from the frame and returning it to its caller. This
42    // simulates the "crash, but catching the error" behavior of the WASM case.
43    // This only works if we panic via `escalate_error_to_panic`.
44    //
45    // (The reason we don't just panic_any() here and let the panic-catcher do a
46    // type-based catch is that there might _be_ no panic-catcher around us, and
47    // we want to print out a nice error message in that case too, which
48    // panic_any() does not do us the favor of producing. This is all very
49    // subtle. See also soroban_env_host::Host::escalate_error_to_panic.)
50    #[cfg(any(test, feature = "testutils"))]
51    pub(crate) fn reject_err<T>(env: &Host, r: Result<T, HostError>) -> Result<T, Infallible> {
52        r.map_err(|e| env.escalate_error_to_panic(e))
53    }
54
55    // When we're _not_ in a cfg enabling `soroban-env-{common,host}/testutils`,
56    // there is no `Env::escalate_error_to_panic` to call, so we just panic
57    // here. But this is ok because in that case there is also no multi-contract
58    // calling machinery set up, nor probably any panic-catcher installed that
59    // we need to hide error values for the benefit of. Any panic in this case
60    // is probably going to unwind completely anyways. No special case needed.
61    #[cfg(not(any(test, feature = "testutils")))]
62    pub(crate) fn reject_err<T>(_env: &Host, r: Result<T, HostError>) -> Result<T, Infallible> {
63        r.map_err(|e| panic!("{:?}", e))
64    }
65
66    #[doc(hidden)]
67    impl<F, T> Convert<F, T> for super::Env
68    where
69        EnvImpl: Convert<F, T>,
70    {
71        type Error = <EnvImpl as Convert<F, T>>::Error;
72        fn convert(&self, f: F) -> Result<T, Self::Error> {
73            self.env_impl.convert(f)
74        }
75    }
76}
77
78pub use internal::xdr;
79pub use internal::ContractTtlExtension;
80pub use internal::ConversionError;
81pub use internal::EnvBase;
82pub use internal::Error;
83pub use internal::MapObject;
84pub use internal::SymbolStr;
85pub use internal::TryFromVal;
86pub use internal::TryIntoVal;
87pub use internal::Val;
88pub use internal::VecObject;
89
90pub trait IntoVal<E: internal::Env, T> {
91    fn into_val(&self, e: &E) -> T;
92}
93
94pub trait FromVal<E: internal::Env, T> {
95    fn from_val(e: &E, v: &T) -> Self;
96}
97
98impl<E: internal::Env, T, U> FromVal<E, T> for U
99where
100    U: TryFromVal<E, T>,
101{
102    fn from_val(e: &E, v: &T) -> Self {
103        U::try_from_val(e, v).unwrap_optimized()
104    }
105}
106
107impl<E: internal::Env, T, U> IntoVal<E, T> for U
108where
109    T: FromVal<E, Self>,
110{
111    fn into_val(&self, e: &E) -> T {
112        T::from_val(e, self)
113    }
114}
115
116use crate::auth::InvokerContractAuthEntry;
117use crate::custom_account::CustomAccount;
118use crate::unwrap::UnwrapInfallible;
119use crate::unwrap::UnwrapOptimized;
120use crate::InvokeError;
121use crate::{
122    crypto::Crypto, deploy::Deployer, events::Events, ledger::Ledger, logs::Logs, prng::Prng,
123    storage::Storage, Address, Vec,
124};
125use internal::{
126    AddressObject, Bool, BytesObject, DurationObject, I128Object, I256Object, I256Val, I64Object,
127    MuxedAddressObject, StorageType, StringObject, Symbol, SymbolObject, TimepointObject,
128    U128Object, U256Object, U256Val, U32Val, U64Object, U64Val, Void,
129};
130
131#[doc(hidden)]
132#[derive(Clone)]
133pub struct MaybeEnv {
134    maybe_env_impl: internal::MaybeEnvImpl,
135    #[cfg(any(test, feature = "testutils"))]
136    test_state: Option<EnvTestState>,
137}
138
139#[cfg(target_family = "wasm")]
140impl TryFrom<MaybeEnv> for Env {
141    type Error = Infallible;
142
143    fn try_from(_value: MaybeEnv) -> Result<Self, Self::Error> {
144        Ok(Env {
145            env_impl: internal::EnvImpl {},
146        })
147    }
148}
149
150impl Default for MaybeEnv {
151    fn default() -> Self {
152        Self::none()
153    }
154}
155
156#[cfg(target_family = "wasm")]
157impl MaybeEnv {
158    // separate function to be const
159    pub const fn none() -> Self {
160        Self {
161            maybe_env_impl: internal::EnvImpl {},
162        }
163    }
164}
165
166#[cfg(not(target_family = "wasm"))]
167impl MaybeEnv {
168    // separate function to be const
169    pub const fn none() -> Self {
170        Self {
171            maybe_env_impl: None,
172            #[cfg(any(test, feature = "testutils"))]
173            test_state: None,
174        }
175    }
176}
177
178#[cfg(target_family = "wasm")]
179impl From<Env> for MaybeEnv {
180    fn from(value: Env) -> Self {
181        MaybeEnv {
182            maybe_env_impl: value.env_impl,
183        }
184    }
185}
186
187#[cfg(not(target_family = "wasm"))]
188impl TryFrom<MaybeEnv> for Env {
189    type Error = ConversionError;
190
191    fn try_from(value: MaybeEnv) -> Result<Self, Self::Error> {
192        if let Some(env_impl) = value.maybe_env_impl {
193            Ok(Env {
194                env_impl,
195                #[cfg(any(test, feature = "testutils"))]
196                test_state: value.test_state.unwrap_or_default(),
197            })
198        } else {
199            Err(ConversionError)
200        }
201    }
202}
203
204#[cfg(not(target_family = "wasm"))]
205impl From<Env> for MaybeEnv {
206    fn from(value: Env) -> Self {
207        MaybeEnv {
208            maybe_env_impl: Some(value.env_impl.clone()),
209            #[cfg(any(test, feature = "testutils"))]
210            test_state: Some(value.test_state.clone()),
211        }
212    }
213}
214
215/// The [Env] type provides access to the environment the contract is executing
216/// within.
217///
218/// The [Env] provides access to information about the currently executing
219/// contract, who invoked it, contract data, functions for signing, hashing,
220/// etc.
221///
222/// Most types require access to an [Env] to be constructed or converted.
223#[derive(Clone)]
224pub struct Env {
225    env_impl: internal::EnvImpl,
226    #[cfg(any(test, feature = "testutils"))]
227    test_state: EnvTestState,
228}
229
230impl Default for Env {
231    #[cfg(not(any(test, feature = "testutils")))]
232    fn default() -> Self {
233        Self {
234            env_impl: Default::default(),
235        }
236    }
237
238    #[cfg(any(test, feature = "testutils"))]
239    fn default() -> Self {
240        Self::new_with_config(EnvTestConfig::default())
241    }
242}
243
244#[cfg(any(test, feature = "testutils"))]
245#[derive(Default, Clone)]
246struct LastEnv {
247    test_name: String,
248    number: usize,
249}
250
251#[cfg(any(test, feature = "testutils"))]
252thread_local! {
253    static LAST_ENV: RefCell<Option<LastEnv>> = RefCell::new(None);
254}
255
256#[cfg(any(test, feature = "testutils"))]
257#[derive(Clone, Default)]
258struct EnvTestState {
259    test_name: Option<String>,
260    number: usize,
261    config: EnvTestConfig,
262    generators: Rc<RefCell<Generators>>,
263    auth_snapshot: Rc<RefCell<AuthSnapshot>>,
264    snapshot: Option<Rc<LedgerSnapshot>>,
265}
266
267/// Config for changing the default behavior of the Env when used in tests.
268#[cfg(any(test, feature = "testutils"))]
269#[derive(Clone)]
270pub struct EnvTestConfig {
271    /// Capture a test snapshot when the Env is dropped, causing a test snapshot
272    /// JSON file to be written to disk when the Env is no longer referenced.
273    /// Defaults to true.
274    pub capture_snapshot_at_drop: bool,
275    // NOTE: Next time a field needs to be added to EnvTestConfig it will be a breaking change,
276    // take the opportunity to make the current field private, new fields private, and settable via
277    // functions. Why: So that it is the last time a breaking change is needed to the type.
278}
279
280#[cfg(any(test, feature = "testutils"))]
281impl Default for EnvTestConfig {
282    fn default() -> Self {
283        Self {
284            capture_snapshot_at_drop: true,
285        }
286    }
287}
288
289impl Env {
290    /// Panic with the given error.
291    ///
292    /// Equivalent to `panic!`, but with an error value instead of a string.
293    #[doc(hidden)]
294    #[cfg(feature = "experimental_spec_shaking_v2")]
295    #[inline(always)]
296    pub fn panic_with_error<I>(&self, error: I) -> !
297    where
298        I: Into<internal::Error> + crate::SpecShakingMarker,
299    {
300        I::spec_shaking_marker();
301        self.panic_with_error_inner(error.into())
302    }
303
304    /// Panic with the given error.
305    ///
306    /// Equivalent to `panic!`, but with an error value instead of a string.
307    #[doc(hidden)]
308    #[cfg(not(feature = "experimental_spec_shaking_v2"))]
309    #[inline(always)]
310    pub fn panic_with_error(&self, error: impl Into<internal::Error>) -> ! {
311        self.panic_with_error_inner(error.into())
312    }
313
314    #[inline(always)]
315    fn panic_with_error_inner(&self, error: internal::Error) -> ! {
316        _ = internal::Env::fail_with_error(self, error);
317        #[cfg(target_family = "wasm")]
318        core::arch::wasm32::unreachable();
319        #[cfg(not(target_family = "wasm"))]
320        unreachable!();
321    }
322
323    /// Get a [Storage] for accessing and updating persistent data owned by the
324    /// currently executing contract.
325    #[inline(always)]
326    pub fn storage(&self) -> Storage {
327        Storage::new(self)
328    }
329
330    /// Get [Events] for publishing events associated with the
331    /// currently executing contract.
332    #[inline(always)]
333    pub fn events(&self) -> Events {
334        Events::new(self)
335    }
336
337    /// Get a [Ledger] for accessing the current ledger.
338    #[inline(always)]
339    pub fn ledger(&self) -> Ledger {
340        Ledger::new(self)
341    }
342
343    /// Get a deployer for deploying contracts.
344    #[inline(always)]
345    pub fn deployer(&self) -> Deployer {
346        Deployer::new(self)
347    }
348
349    /// Get an accessor for functions used for custom account implementation.
350    ///
351    /// The accessor's methods may only be called within `__check_auth` contract
352    /// function.
353    #[inline(always)]
354    pub fn custom_account(&self) -> CustomAccount {
355        CustomAccount::new(self)
356    }
357
358    /// Get a [Crypto] for accessing the current cryptographic functions.
359    #[inline(always)]
360    pub fn crypto(&self) -> Crypto {
361        Crypto::new(self)
362    }
363
364    /// # ⚠️ Hazardous Materials
365    ///
366    /// Get a [CryptoHazmat][crate::crypto::CryptoHazmat] for accessing the
367    /// cryptographic functions that are not generally recommended. Using them
368    /// incorrectly can introduce security vulnerabilities. Use [Crypto] if
369    /// possible.
370    #[cfg_attr(any(test, feature = "hazmat-crypto"), visibility::make(pub))]
371    #[cfg_attr(feature = "docs", doc(cfg(feature = "hazmat-crypto")))]
372    #[inline(always)]
373    pub(crate) fn crypto_hazmat(&self) -> crate::crypto::CryptoHazmat {
374        crate::crypto::CryptoHazmat::new(self)
375    }
376
377    /// Get a [Prng] for accessing the current functions which provide pseudo-randomness.
378    ///
379    /// # Warning
380    ///
381    /// **The pseudo-random generator returned is not suitable for
382    /// security-sensitive work.**
383    #[inline(always)]
384    pub fn prng(&self) -> Prng {
385        Prng::new(self)
386    }
387
388    /// Get the Address object corresponding to the current executing contract.
389    pub fn current_contract_address(&self) -> Address {
390        let address = internal::Env::get_current_contract_address(self).unwrap_infallible();
391        unsafe { Address::unchecked_new(self.clone(), address) }
392    }
393
394    #[doc(hidden)]
395    pub(crate) fn require_auth_for_args(&self, address: &Address, args: Vec<Val>) {
396        internal::Env::require_auth_for_args(self, address.to_object(), args.to_object())
397            .unwrap_infallible();
398    }
399
400    #[doc(hidden)]
401    pub(crate) fn require_auth(&self, address: &Address) {
402        internal::Env::require_auth(self, address.to_object()).unwrap_infallible();
403    }
404
405    /// Invokes a function of a contract that is registered in the [Env].
406    ///
407    /// # Panics
408    ///
409    /// Will panic if the `contract_id` does not match a registered contract,
410    /// `func` does not match a function of the referenced contract, or the
411    /// number of `args` do not match the argument count of the referenced
412    /// contract function.
413    ///
414    /// Will panic if the contract that is invoked fails or aborts in anyway.
415    ///
416    /// Will panic if the value returned from the contract cannot be converted
417    /// into the type `T`.
418    pub fn invoke_contract<T>(
419        &self,
420        contract_address: &Address,
421        func: &crate::Symbol,
422        args: Vec<Val>,
423    ) -> T
424    where
425        T: TryFromVal<Env, Val>,
426    {
427        let rv = internal::Env::call(
428            self,
429            contract_address.to_object(),
430            func.to_symbol_val(),
431            args.to_object(),
432        )
433        .unwrap_infallible();
434        T::try_from_val(self, &rv)
435            .map_err(|_| ConversionError)
436            .unwrap()
437    }
438
439    /// Invokes a function of a contract that is registered in the [Env],
440    /// returns an error if the invocation fails for any reason.
441    pub fn try_invoke_contract<T, E>(
442        &self,
443        contract_address: &Address,
444        func: &crate::Symbol,
445        args: Vec<Val>,
446    ) -> Result<Result<T, T::Error>, Result<E, InvokeError>>
447    where
448        T: TryFromVal<Env, Val>,
449        E: TryFrom<Error>,
450        E::Error: Into<InvokeError>,
451    {
452        let rv = internal::Env::try_call(
453            self,
454            contract_address.to_object(),
455            func.to_symbol_val(),
456            args.to_object(),
457        )
458        .unwrap_infallible();
459        match internal::Error::try_from_val(self, &rv) {
460            Ok(err) => Err(E::try_from(err).map_err(Into::into)),
461            Err(ConversionError) => Ok(T::try_from_val(self, &rv)),
462        }
463    }
464
465    /// Authorizes sub-contract calls on behalf of the current contract.
466    ///
467    /// All the direct calls that the current contract performs are always
468    /// considered to have been authorized. This is only needed to authorize
469    /// deeper calls that originate from the next contract call from the current
470    /// contract.
471    ///
472    /// For example, if the contract A calls contract B, contract
473    /// B calls contract C and contract C calls `A.require_auth()`, then an
474    /// entry corresponding to C call has to be passed in `auth_entries`. It
475    /// doesn't matter if contract B called `require_auth` or not. If contract A
476    /// calls contract B again, then `authorize_as_current_contract` has to be
477    /// called again with the respective entries.
478    ///
479    /// When testing a contract call that uses `authorize_as_current_contract`, avoid
480    /// using [`mock_all_auths_allowing_non_root_auth`][Self::mock_all_auths_allowing_non_root_auth].
481    /// A test that uses this mock will not fail if a missing or incorrect
482    /// `authorize_as_current_contract` call is present. It is recommended to use other
483    /// authorization mocking functions like [`mock_auths`][Self::mock_auths]
484    /// or [`set_auths`][Self::set_auths] if needed.
485    ///
486    /// ### Examples
487    /// ```
488    /// use soroban_sdk::{
489    ///     auth::{ContractContext, InvokerContractAuthEntry, SubContractInvocation},
490    ///     contract, contractimpl, vec, Address, Env, IntoVal, Symbol,
491    /// };
492    ///
493    /// // Contract C performs authorization for addr
494    /// #[contract]
495    /// pub struct ContractC;
496    ///
497    /// #[contractimpl]
498    /// impl ContractC {
499    ///     pub fn do_auth(_env: Env, addr: Address, amount: i128) -> i128 {
500    ///         addr.require_auth();
501    ///         amount
502    ///     }
503    /// }
504    ///
505    /// // Contract B performs authorization for `addr` and invokes Contract C with
506    /// // `addr` and `amount` as arguments.
507    /// #[contract]
508    /// pub struct ContractB;
509    ///
510    /// #[contractimpl]
511    /// impl ContractB {
512    ///     pub fn call_c(env: Env, addr: Address, contract_c: Address, amount: i128) -> i128 {
513    ///         addr.require_auth();
514    ///         ContractCClient::new(&env, &contract_c).do_auth(&addr, &amount)
515    ///     }
516    /// }
517    ///
518    /// // Contract A authorizes Contract B to call Contract C on its behalf with `addr` and
519    /// // `amount` as arguments.
520    /// #[contract]
521    /// pub struct ContractA;
522    ///
523    /// #[contractimpl]
524    /// impl ContractA {
525    ///     pub fn call_b(env: Env, contract_b: Address, contract_c: Address, amount: i128) -> i128 {
526    ///         let curr_contract = env.current_contract_address();
527    ///         // Authorize the sub-call Contract B makes to Contract C
528    ///         env.authorize_as_current_contract(vec![
529    ///             &env,
530    ///             InvokerContractAuthEntry::Contract(SubContractInvocation {
531    ///                 context: ContractContext {
532    ///                     contract: contract_c.clone(),
533    ///                     fn_name: Symbol::new(&env, "do_auth"),
534    ///                     args: vec![&env, curr_contract.into_val(&env), amount.into_val(&env)],
535    ///                 },
536    ///                 sub_invocations: vec![&env],
537    ///             }),
538    ///         ]);
539    ///         ContractBClient::new(&env, &contract_b).call_c(&curr_contract, &contract_c, &amount)
540    ///     }
541    /// }
542    ///
543    /// #[test]
544    /// fn test() {
545    /// # }
546    /// # fn main() {
547    ///     let env = Env::default();
548    ///     let contract_a = env.register(ContractA, ());
549    ///     let contract_b = env.register(ContractB, ());
550    ///     let contract_c = env.register(ContractC, ());
551    ///
552    ///     // Auths are not mocked to ensure `authorize_as_current_contract`
553    ///     // is working as intended. If Contract A includes additional auths, consider
554    ///     // using `mock_auths` or `set_auths` for those authorizations.
555    ///
556    ///     let client = ContractAClient::new(&env, &contract_a);
557    ///     let result = client.call_b(&contract_b, &contract_c, &100);
558    ///     assert_eq!(result, 100);
559    /// }
560    /// ```
561    pub fn authorize_as_current_contract(&self, auth_entries: Vec<InvokerContractAuthEntry>) {
562        internal::Env::authorize_as_curr_contract(self, auth_entries.to_object())
563            .unwrap_infallible();
564    }
565
566    /// Get the [Logs] for logging debug events.
567    #[inline(always)]
568    #[deprecated(note = "use [Env::logs]")]
569    #[doc(hidden)]
570    pub fn logger(&self) -> Logs {
571        self.logs()
572    }
573
574    /// Get the [Logs] for logging debug events.
575    #[inline(always)]
576    pub fn logs(&self) -> Logs {
577        Logs::new(self)
578    }
579}
580
581#[doc(hidden)]
582#[cfg(not(target_family = "wasm"))]
583impl Env {
584    pub(crate) fn is_same_env(&self, other: &Self) -> bool {
585        self.env_impl.is_same(&other.env_impl)
586    }
587}
588
589#[cfg(any(test, feature = "testutils"))]
590use crate::testutils::cost_estimate::CostEstimate;
591#[cfg(any(test, feature = "testutils"))]
592use crate::{
593    auth,
594    testutils::{
595        budget::Budget, cost_estimate::NetworkInvocationResourceLimits, default_ledger_info,
596        Address as _, AuthSnapshot, AuthorizedInvocation, ContractFunctionSet, EventsSnapshot,
597        Generators, Ledger as _, MockAuth, MockAuthContract, Register, Snapshot,
598        SnapshotSourceInput, StellarAssetContract, StellarAssetIssuer,
599    },
600    Bytes, BytesN, ConstructorArgs,
601};
602#[cfg(any(test, feature = "testutils"))]
603use core::{cell::RefCell, cell::RefMut};
604#[cfg(any(test, feature = "testutils"))]
605use internal::{InvocationEvent, InvocationResourceLimits};
606#[cfg(any(test, feature = "testutils"))]
607use soroban_ledger_snapshot::LedgerSnapshot;
608#[cfg(any(test, feature = "testutils"))]
609use std::{path::Path, rc::Rc};
610#[cfg(any(test, feature = "testutils"))]
611use xdr::{LedgerEntry, LedgerKey, LedgerKeyContractData, SorobanAuthorizationEntry};
612
613#[cfg(any(test, feature = "testutils"))]
614#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
615impl Env {
616    #[doc(hidden)]
617    pub fn in_contract(&self) -> bool {
618        self.env_impl.has_frame().unwrap()
619    }
620
621    #[doc(hidden)]
622    pub fn host(&self) -> &internal::Host {
623        &self.env_impl
624    }
625
626    #[doc(hidden)]
627    pub(crate) fn with_generator<T>(&self, f: impl FnOnce(RefMut<'_, Generators>) -> T) -> T {
628        f((*self.test_state.generators).borrow_mut())
629    }
630
631    /// Create an Env with the test config.
632    pub fn new_with_config(config: EnvTestConfig) -> Env {
633        struct EmptySnapshotSource();
634
635        impl internal::storage::SnapshotSource for EmptySnapshotSource {
636            fn get(
637                &self,
638                _key: &Rc<xdr::LedgerKey>,
639            ) -> Result<Option<(Rc<xdr::LedgerEntry>, Option<u32>)>, soroban_env_host::HostError>
640            {
641                Ok(None)
642            }
643        }
644
645        let rf = Rc::new(EmptySnapshotSource());
646
647        Env::new_for_testutils(config, rf, None, None, None)
648    }
649
650    /// Change the test config of an Env.
651    pub fn set_config(&mut self, config: EnvTestConfig) {
652        self.test_state.config = config;
653    }
654
655    /// Used by multiple constructors to configure test environments consistently.
656    fn new_for_testutils(
657        config: EnvTestConfig,
658        recording_footprint: Rc<dyn internal::storage::SnapshotSource>,
659        generators: Option<Rc<RefCell<Generators>>>,
660        ledger_info: Option<internal::LedgerInfo>,
661        snapshot: Option<Rc<LedgerSnapshot>>,
662    ) -> Env {
663        // Store in the Env the name of the test it is for, and a number so that within a test
664        // where one or more Env's have been created they can be uniquely identified relative to
665        // each other.
666
667        let test_name = match std::thread::current().name() {
668            // When doc tests are running they're all run with the thread name main. There's no way
669            // to detect which doc test is being run.
670            Some(name) if name != "main" => Some(name.to_owned()),
671            _ => None,
672        };
673        let number = if let Some(ref test_name) = test_name {
674            LAST_ENV.with_borrow_mut(|l| {
675                if let Some(last_env) = l.as_mut() {
676                    if test_name != &last_env.test_name {
677                        last_env.test_name = test_name.clone();
678                        last_env.number = 1;
679                        1
680                    } else {
681                        let next_number = last_env.number + 1;
682                        last_env.number = next_number;
683                        next_number
684                    }
685                } else {
686                    *l = Some(LastEnv {
687                        test_name: test_name.clone(),
688                        number: 1,
689                    });
690                    1
691                }
692            })
693        } else {
694            1
695        };
696
697        let storage = internal::storage::Storage::with_recording_footprint(recording_footprint);
698        let budget = internal::budget::Budget::default();
699        let env_impl = internal::EnvImpl::with_storage_and_budget(storage, budget.clone());
700        env_impl
701            .set_source_account(xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(
702                xdr::Uint256([0; 32]),
703            )))
704            .unwrap();
705        env_impl
706            .set_diagnostic_level(internal::DiagnosticLevel::Debug)
707            .unwrap();
708        env_impl.set_base_prng_seed([0; 32]).unwrap();
709
710        let auth_snapshot = Rc::new(RefCell::new(AuthSnapshot::default()));
711        let auth_snapshot_in_hook = auth_snapshot.clone();
712        env_impl
713            .set_invocation_hook(Some(Rc::new(move |host, event| {
714                match event {
715                    InvocationEvent::Start => {}
716                    InvocationEvent::Finish => {
717                        let new_auths = host
718                            .get_authenticated_authorizations()
719                            // If an error occurs getting the authenticated authorizations
720                            // it means that no auth has occurred.
721                            .unwrap();
722                        (*auth_snapshot_in_hook).borrow_mut().0.push(new_auths);
723                    }
724                }
725            })))
726            .unwrap();
727        env_impl.enable_invocation_metering();
728        env_impl
729            .set_invocation_resource_limits(Some(InvocationResourceLimits::mainnet()))
730            .unwrap();
731
732        let env = Env {
733            env_impl,
734            test_state: EnvTestState {
735                test_name,
736                number,
737                config,
738                generators: generators.unwrap_or_default(),
739                snapshot,
740                auth_snapshot,
741            },
742        };
743
744        let ledger_info = ledger_info.unwrap_or_else(default_ledger_info);
745        env.ledger().set(ledger_info);
746
747        env
748    }
749
750    /// Returns the resources metered during the last top level contract
751    /// invocation.    
752    ///
753    /// In order to get non-`None` results, `enable_invocation_metering` has to
754    /// be called and at least one invocation has to happen after that.
755    ///
756    /// Take the return value with a grain of salt. The returned resources mostly
757    /// correspond only to the operations that have happened during the host
758    /// invocation, i.e. this won't try to simulate the work that happens in
759    /// production scenarios (e.g. certain XDR roundtrips). This also doesn't try
760    /// to model resources related to the transaction size.
761    ///
762    /// The returned value is as useful as the preceding setup, e.g. if a test
763    /// contract is used instead of a Wasm contract, all the costs related to
764    /// VM instantiation and execution, as well as Wasm reads/rent bumps will be
765    /// missed.
766    ///
767    /// While the resource metering may be useful for contract optimization,
768    /// keep in mind that resource and fee estimation may be imprecise. Use
769    /// simulation with RPC in order to get the exact resources for submitting
770    /// the transactions to the network.    
771    pub fn cost_estimate(&self) -> CostEstimate {
772        CostEstimate::new(self.clone())
773    }
774
775    /// Register a contract with the [Env] for testing.
776    ///
777    /// Pass the contract type when the contract is defined in the current crate
778    /// and is being registered natively. Pass the contract wasm bytes when the
779    /// contract has been loaded as wasm.
780    ///
781    /// Pass the arguments for the contract's constructor, or `()` if none. For
782    /// contracts with a constructor, use the contract's generated `Args` type
783    /// to construct the arguments with the appropriate types for invoking
784    /// the constructor during registration.
785    ///
786    /// Returns the address of the registered contract that is the same as the
787    /// contract id passed in.
788    ///
789    /// If you need to specify the address the contract should be registered at,
790    /// use [`Env::register_at`].
791    ///
792    /// ### Examples
793    /// Register a contract defined in the current crate, by specifying the type
794    /// name:
795    /// ```
796    /// use soroban_sdk::{contract, contractimpl, testutils::Address as _, Address, BytesN, Env, Symbol};
797    ///
798    /// #[contract]
799    /// pub struct Contract;
800    ///
801    /// #[contractimpl]
802    /// impl Contract {
803    ///     pub fn __constructor(_env: Env, _input: u32) {
804    ///     }
805    /// }
806    ///
807    /// #[test]
808    /// fn test() {
809    /// # }
810    /// # fn main() {
811    ///     let env = Env::default();
812    ///     let contract_id = env.register(Contract, ContractArgs::__constructor(&123,));
813    /// }
814    /// ```
815    /// Register a contract wasm, by specifying the wasm bytes:
816    /// ```
817    /// use soroban_sdk::{testutils::Address as _, Address, BytesN, Env};
818    ///
819    /// const WASM: &[u8] = include_bytes!("../doctest_fixtures/contract.wasm");
820    ///
821    /// #[test]
822    /// fn test() {
823    /// # }
824    /// # fn main() {
825    ///     let env = Env::default();
826    ///     let contract_id = env.register(WASM, ());
827    /// }
828    /// ```
829    pub fn register<'a, C, A>(&self, contract: C, constructor_args: A) -> Address
830    where
831        C: Register,
832        A: ConstructorArgs,
833    {
834        contract.register(self, None, constructor_args)
835    }
836
837    /// Register a contract with the [Env] for testing.
838    ///
839    /// Passing a contract ID for the first arguments registers the contract
840    /// with that contract ID.
841    ///
842    /// Registering a contract that is already registered replaces it.
843    /// Use re-registration with caution as it does not exist in the real
844    /// (on-chain) environment. Specifically, the new contract's constructor
845    /// will be called again during re-registration. That behavior only exists
846    /// for this test utility and is not reproducible on-chain, where contract
847    /// Wasm updates don't cause constructor to be called.
848    ///
849    /// Pass the contract type when the contract is defined in the current crate
850    /// and is being registered natively. Pass the contract wasm bytes when the
851    /// contract has been loaded as wasm.
852    ///
853    /// Returns the address of the registered contract that is the same as the
854    /// contract id passed in.
855    ///
856    /// ### Examples
857    /// Register a contract defined in the current crate, by specifying the type
858    /// name:
859    /// ```
860    /// use soroban_sdk::{contract, contractimpl, testutils::Address as _, Address, BytesN, Env, Symbol};
861    ///
862    /// #[contract]
863    /// pub struct Contract;
864    ///
865    /// #[contractimpl]
866    /// impl Contract {
867    ///     pub fn __constructor(_env: Env, _input: u32) {
868    ///     }
869    /// }
870    ///
871    /// #[test]
872    /// fn test() {
873    /// # }
874    /// # fn main() {
875    ///     let env = Env::default();
876    ///     let contract_id = Address::generate(&env);
877    ///     env.register_at(&contract_id, Contract, (123_u32,));
878    /// }
879    /// ```
880    /// Register a contract wasm, by specifying the wasm bytes:
881    /// ```
882    /// use soroban_sdk::{testutils::Address as _, Address, BytesN, Env};
883    ///
884    /// const WASM: &[u8] = include_bytes!("../doctest_fixtures/contract.wasm");
885    ///
886    /// #[test]
887    /// fn test() {
888    /// # }
889    /// # fn main() {
890    ///     let env = Env::default();
891    ///     let contract_id = Address::generate(&env);
892    ///     env.register_at(&contract_id, WASM, ());
893    /// }
894    /// ```
895    pub fn register_at<C, A>(
896        &self,
897        contract_id: &Address,
898        contract: C,
899        constructor_args: A,
900    ) -> Address
901    where
902        C: Register,
903        A: ConstructorArgs,
904    {
905        contract.register(self, contract_id, constructor_args)
906    }
907
908    /// Register a contract with the [Env] for testing.
909    ///
910    /// Passing a contract ID for the first arguments registers the contract
911    /// with that contract ID. Providing `None` causes the Env to generate a new
912    /// contract ID that is assigned to the contract.
913    ///
914    /// If a contract has a constructor defined, then it will be called with
915    /// no arguments. If a constructor takes arguments, use `register`.
916    ///
917    /// Registering a contract that is already registered replaces it.
918    /// Use re-registration with caution as it does not exist in the real
919    /// (on-chain) environment. Specifically, the new contract's constructor
920    /// will be called again during re-registration. That behavior only exists
921    /// for this test utility and is not reproducible on-chain, where contract
922    /// Wasm updates don't cause constructor to be called.
923    ///
924    /// Returns the address of the registered contract.
925    ///
926    /// ### Examples
927    /// ```
928    /// use soroban_sdk::{contract, contractimpl, BytesN, Env, Symbol};
929    ///
930    /// #[contract]
931    /// pub struct HelloContract;
932    ///
933    /// #[contractimpl]
934    /// impl HelloContract {
935    ///     pub fn hello(env: Env, recipient: Symbol) -> Symbol {
936    ///         todo!()
937    ///     }
938    /// }
939    ///
940    /// #[test]
941    /// fn test() {
942    /// # }
943    /// # fn main() {
944    ///     let env = Env::default();
945    ///     let contract_id = env.register_contract(None, HelloContract);
946    /// }
947    /// ```
948    #[deprecated(note = "use `register`")]
949    pub fn register_contract<'a, T: ContractFunctionSet + 'static>(
950        &self,
951        contract_id: impl Into<Option<&'a Address>>,
952        contract: T,
953    ) -> Address {
954        self.register_contract_with_constructor(contract_id, contract, ())
955    }
956
957    /// Register a contract with the [Env] for testing.
958    ///
959    /// This acts the in the same fashion as `register_contract`, but allows
960    /// passing arguments to the contract's constructor.
961    ///
962    /// Passing a contract ID for the first arguments registers the contract
963    /// with that contract ID. Providing `None` causes the Env to generate a new
964    /// contract ID that is assigned to the contract.
965    ///
966    /// Registering a contract that is already registered replaces it.
967    /// Use re-registration with caution as it does not exist in the real
968    /// (on-chain) environment. Specifically, the new contract's constructor
969    /// will be called again during re-registration. That behavior only exists
970    /// for this test utility and is not reproducible on-chain, where contract
971    /// Wasm updates don't cause constructor to be called.
972    ///
973    /// Returns the address of the registered contract.
974    pub(crate) fn register_contract_with_constructor<
975        'a,
976        T: ContractFunctionSet + 'static,
977        A: ConstructorArgs,
978    >(
979        &self,
980        contract_id: impl Into<Option<&'a Address>>,
981        contract: T,
982        constructor_args: A,
983    ) -> Address {
984        struct InternalContractFunctionSet<T: ContractFunctionSet>(pub(crate) T);
985        impl<T: ContractFunctionSet> internal::ContractFunctionSet for InternalContractFunctionSet<T> {
986            fn call(
987                &self,
988                func: &Symbol,
989                env_impl: &internal::EnvImpl,
990                args: &[Val],
991            ) -> Option<Val> {
992                let env = Env {
993                    env_impl: env_impl.clone(),
994                    test_state: Default::default(),
995                };
996                self.0.call(
997                    crate::Symbol::try_from_val(&env, func)
998                        .unwrap_infallible()
999                        .to_string()
1000                        .as_str(),
1001                    env,
1002                    args,
1003                )
1004            }
1005        }
1006
1007        let contract_id = if let Some(contract_id) = contract_id.into() {
1008            contract_id.clone()
1009        } else {
1010            Address::generate(self)
1011        };
1012        // Convert the constructor arguments before switching auth managers, so
1013        // that a panic during conversion cannot leave the environment stuck in
1014        // recording auth. This matches the wasm registration path.
1015        let constructor_args = constructor_args.into_val(self).to_object();
1016        let prev_auth_manager = self.env_impl.snapshot_auth_manager().unwrap();
1017        self.env_impl
1018            .switch_to_recording_auth_inherited_from_snapshot(&prev_auth_manager)
1019            .unwrap();
1020        let register_result = self.env_impl.register_test_contract_with_constructor(
1021            contract_id.to_object(),
1022            Rc::new(InternalContractFunctionSet(contract)),
1023            constructor_args,
1024        );
1025        self.env_impl.set_auth_manager(prev_auth_manager).unwrap();
1026        register_result.unwrap();
1027        contract_id
1028    }
1029
1030    /// Register a contract in a Wasm file with the [Env] for testing.
1031    ///
1032    /// Passing a contract ID for the first arguments registers the contract
1033    /// with that contract ID. Providing `None` causes the Env to generate a new
1034    /// contract ID that is assigned to the contract.
1035    ///
1036    /// Registering a contract that is already registered replaces it.
1037    /// Use re-registration with caution as it does not exist in the real
1038    /// (on-chain) environment. Specifically, the new contract's constructor
1039    /// will be called again during re-registration. That behavior only exists
1040    /// for this test utility and is not reproducible on-chain, where contract
1041    /// Wasm updates don't cause constructor to be called.
1042    ///
1043    /// Returns the address of the registered contract.
1044    ///
1045    /// ### Examples
1046    /// ```
1047    /// use soroban_sdk::{BytesN, Env};
1048    ///
1049    /// const WASM: &[u8] = include_bytes!("../doctest_fixtures/contract.wasm");
1050    ///
1051    /// #[test]
1052    /// fn test() {
1053    /// # }
1054    /// # fn main() {
1055    ///     let env = Env::default();
1056    ///     env.register_contract_wasm(None, WASM);
1057    /// }
1058    /// ```
1059    #[deprecated(note = "use `register`")]
1060    pub fn register_contract_wasm<'a>(
1061        &self,
1062        contract_id: impl Into<Option<&'a Address>>,
1063        contract_wasm: impl IntoVal<Env, Bytes>,
1064    ) -> Address {
1065        let wasm_hash: BytesN<32> = self.deployer().upload_contract_wasm(contract_wasm);
1066        self.register_contract_with_optional_contract_id_and_executable(
1067            contract_id,
1068            xdr::ContractExecutable::Wasm(xdr::Hash(wasm_hash.into())),
1069            crate::vec![&self],
1070        )
1071    }
1072
1073    /// Register a contract in a Wasm file with the [Env] for testing.
1074    ///
1075    /// This acts the in the same fashion as `register_contract`, but allows
1076    /// passing arguments to the contract's constructor.
1077    ///
1078    /// Passing a contract ID for the first arguments registers the contract
1079    /// with that contract ID. Providing `None` causes the Env to generate a new
1080    /// contract ID that is assigned to the contract.
1081    ///
1082    /// Registering a contract that is already registered replaces it.
1083    /// Use re-registration with caution as it does not exist in the real
1084    /// (on-chain) environment. Specifically, the new contract's constructor
1085    /// will be called again during re-registration. That behavior only exists
1086    /// for this test utility and is not reproducible on-chain, where contract
1087    /// Wasm updates don't cause constructor to be called.
1088    ///
1089    /// Returns the address of the registered contract.
1090    pub(crate) fn register_contract_wasm_with_constructor<'a>(
1091        &self,
1092        contract_id: impl Into<Option<&'a Address>>,
1093        contract_wasm: impl IntoVal<Env, Bytes>,
1094        constructor_args: impl ConstructorArgs,
1095    ) -> Address {
1096        let wasm_hash: BytesN<32> = self.deployer().upload_contract_wasm(contract_wasm);
1097        self.register_contract_with_optional_contract_id_and_executable(
1098            contract_id,
1099            xdr::ContractExecutable::Wasm(xdr::Hash(wasm_hash.into())),
1100            constructor_args.into_val(self),
1101        )
1102    }
1103
1104    /// Register the built-in Stellar Asset Contract with provided admin address.
1105    ///
1106    /// Returns a utility struct that contains the contract ID of the registered
1107    /// token contract, as well as methods to read and update issuer flags.
1108    ///
1109    /// The contract will wrap a randomly-generated Stellar asset. This function
1110    /// is useful for using in the tests when an arbitrary token contract
1111    /// instance is needed.
1112    pub fn register_stellar_asset_contract_v2(&self, admin: Address) -> StellarAssetContract {
1113        let issuer_pk = self.with_generator(|mut g| g.address());
1114        let issuer_id = xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(
1115            issuer_pk.clone(),
1116        )));
1117
1118        let k = Rc::new(xdr::LedgerKey::Account(xdr::LedgerKeyAccount {
1119            account_id: issuer_id.clone(),
1120        }));
1121
1122        if self.host().get_ledger_entry(&k).unwrap().is_none() {
1123            let v = Rc::new(xdr::LedgerEntry {
1124                data: xdr::LedgerEntryData::Account(xdr::AccountEntry {
1125                    account_id: issuer_id.clone(),
1126                    balance: 0,
1127                    flags: 0,
1128                    home_domain: Default::default(),
1129                    inflation_dest: None,
1130                    num_sub_entries: 0,
1131                    seq_num: xdr::SequenceNumber(0),
1132                    thresholds: xdr::Thresholds([1; 4]),
1133                    signers: xdr::VecM::default(),
1134                    ext: xdr::AccountEntryExt::V0,
1135                }),
1136                last_modified_ledger_seq: 0,
1137                ext: xdr::LedgerEntryExt::V0,
1138            });
1139            self.host().add_ledger_entry(&k, &v, None).unwrap();
1140        }
1141
1142        let asset = xdr::Asset::CreditAlphanum4(xdr::AlphaNum4 {
1143            asset_code: xdr::AssetCode4([b'a', b'a', b'a', 0]),
1144            issuer: issuer_id.clone(),
1145        });
1146        let create = xdr::HostFunction::CreateContract(xdr::CreateContractArgs {
1147            contract_id_preimage: xdr::ContractIdPreimage::Asset(asset.clone()),
1148            executable: xdr::ContractExecutable::StellarAsset,
1149        });
1150
1151        let token_id: Address = self
1152            .env_impl
1153            .invoke_function(create)
1154            .unwrap()
1155            .try_into_val(self)
1156            .unwrap();
1157
1158        let prev_auth_manager = self.env_impl.snapshot_auth_manager().unwrap();
1159        self.env_impl
1160            .switch_to_recording_auth_inherited_from_snapshot(&prev_auth_manager)
1161            .unwrap();
1162        let admin_result = self.try_invoke_contract::<(), Error>(
1163            &token_id,
1164            &soroban_sdk_macros::internal_symbol_short!("set_admin"),
1165            (admin,).try_into_val(self).unwrap(),
1166        );
1167        self.env_impl.set_auth_manager(prev_auth_manager).unwrap();
1168        admin_result.unwrap().unwrap();
1169
1170        let issuer = StellarAssetIssuer::new(self.clone(), issuer_id);
1171        StellarAssetContract::new(token_id, issuer, asset)
1172    }
1173
1174    /// Register the built-in Stellar Asset Contract with provided admin address.
1175    ///
1176    /// Returns the contract ID of the registered token contract.
1177    ///
1178    /// The contract will wrap a randomly-generated Stellar asset. This function
1179    /// is useful for using in the tests when an arbitrary token contract
1180    /// instance is needed.
1181    #[deprecated(note = "use [Env::register_stellar_asset_contract_v2]")]
1182    pub fn register_stellar_asset_contract(&self, admin: Address) -> Address {
1183        self.register_stellar_asset_contract_v2(admin).address()
1184    }
1185
1186    fn register_contract_with_optional_contract_id_and_executable<'a>(
1187        &self,
1188        contract_id: impl Into<Option<&'a Address>>,
1189        executable: xdr::ContractExecutable,
1190        constructor_args: Vec<Val>,
1191    ) -> Address {
1192        if let Some(contract_id) = contract_id.into() {
1193            self.register_contract_with_contract_id_and_executable(
1194                contract_id,
1195                executable,
1196                constructor_args,
1197            );
1198            contract_id.clone()
1199        } else {
1200            self.register_contract_with_source(executable, constructor_args)
1201        }
1202    }
1203
1204    fn register_contract_with_source(
1205        &self,
1206        executable: xdr::ContractExecutable,
1207        constructor_args: Vec<Val>,
1208    ) -> Address {
1209        let args_vec: std::vec::Vec<xdr::ScVal> =
1210            constructor_args.iter().map(|v| v.into_val(self)).collect();
1211        let constructor_args = args_vec.try_into().unwrap();
1212        let prev_auth_manager = self.env_impl.snapshot_auth_manager().unwrap();
1213        self.env_impl
1214            .switch_to_recording_auth_inherited_from_snapshot(&prev_auth_manager)
1215            .unwrap();
1216        let create_result = self
1217            .env_impl
1218            .invoke_function(xdr::HostFunction::CreateContractV2(
1219                xdr::CreateContractArgsV2 {
1220                    contract_id_preimage: xdr::ContractIdPreimage::Address(
1221                        xdr::ContractIdPreimageFromAddress {
1222                            address: xdr::ScAddress::Contract(xdr::ContractId(xdr::Hash(
1223                                self.with_generator(|mut g| g.address()),
1224                            ))),
1225                            salt: xdr::Uint256([0; 32]),
1226                        },
1227                    ),
1228                    executable,
1229                    constructor_args,
1230                },
1231            ));
1232
1233        self.env_impl.set_auth_manager(prev_auth_manager).unwrap();
1234
1235        create_result.unwrap().try_into_val(self).unwrap()
1236    }
1237
1238    /// Set authorizations and signatures in the environment which will be
1239    /// consumed by contracts when they invoke [`Address::require_auth`] or
1240    /// [`Address::require_auth_for_args`] functions.
1241    ///
1242    /// Requires valid signatures for the authorization to be successful.
1243    ///
1244    /// This function can also be called on contract clients.
1245    ///
1246    /// To mock auth for testing, without requiring valid signatures, use
1247    /// [`mock_all_auths`][Self::mock_all_auths] or
1248    /// [`mock_auths`][Self::mock_auths]. If mocking of auths is enabled,
1249    /// calling [`set_auths`][Self::set_auths] disables any mocking.
1250    pub fn set_auths(&self, auths: &[SorobanAuthorizationEntry]) {
1251        self.env_impl
1252            .set_authorization_entries(auths.to_vec())
1253            .unwrap();
1254    }
1255
1256    /// Mock authorizations in the environment which will cause matching invokes
1257    /// of [`Address::require_auth`] and [`Address::require_auth_for_args`] to
1258    /// pass.
1259    ///
1260    /// This function can also be called on contract clients.
1261    ///
1262    /// Authorizations not matching a mocked auth will fail.
1263    ///
1264    /// To mock all auths, use [`mock_all_auths`][Self::mock_all_auths].
1265    ///
1266    /// ### Examples
1267    /// ```
1268    /// use soroban_sdk::{contract, contractimpl, Env, Address, testutils::{Address as _, MockAuth, MockAuthInvoke}, IntoVal};
1269    ///
1270    /// #[contract]
1271    /// pub struct HelloContract;
1272    ///
1273    /// #[contractimpl]
1274    /// impl HelloContract {
1275    ///     pub fn hello(env: Env, from: Address) {
1276    ///         from.require_auth();
1277    ///         // TODO
1278    ///     }
1279    /// }
1280    ///
1281    /// #[test]
1282    /// fn test() {
1283    /// # }
1284    /// # fn main() {
1285    ///     let env = Env::default();
1286    ///     let contract_id = env.register(HelloContract, ());
1287    ///
1288    ///     let client = HelloContractClient::new(&env, &contract_id);
1289    ///     let addr = Address::generate(&env);
1290    ///     client.mock_auths(&[
1291    ///         MockAuth {
1292    ///             address: &addr,
1293    ///             invoke: &MockAuthInvoke {
1294    ///                 contract: &contract_id,
1295    ///                 fn_name: "hello",
1296    ///                 args: (&addr,).into_val(&env),
1297    ///                 sub_invokes: &[],
1298    ///             },
1299    ///         },
1300    ///     ]).hello(&addr);
1301    /// }
1302    /// ```
1303    pub fn mock_auths(&self, auths: &[MockAuth]) {
1304        for a in auths {
1305            self.register_at(a.address, MockAuthContract, ());
1306        }
1307        let auths = auths
1308            .iter()
1309            .cloned()
1310            .map(Into::into)
1311            .collect::<std::vec::Vec<_>>();
1312        self.env_impl.set_authorization_entries(auths).unwrap();
1313    }
1314
1315    /// Mock all calls to the [`Address::require_auth`] and
1316    /// [`Address::require_auth_for_args`] functions in invoked contracts,
1317    /// having them succeed as if authorization was provided.
1318    ///
1319    /// When mocking is enabled, if the [`Address`] being authorized is the
1320    /// address of a contract, that contract's `__check_auth` function will not
1321    /// be called, and the contract does not need to exist or be registered in
1322    /// the test.
1323    ///
1324    /// When mocking is enabled, if the [`Address`] being authorized is the
1325    /// address of an account, the account does not need to exist.
1326    ///
1327    /// This function can also be called on contract clients.
1328    ///
1329    /// To disable mocking, see [`set_auths`][Self::set_auths].
1330    ///
1331    /// To access a list of auths that have occurred, see [`auths`][Self::auths].
1332    ///
1333    /// It is not currently possible to mock a subset of auths.
1334    ///
1335    /// A test that uses `mock_all_auths` without verifying the resulting
1336    /// authorization tree via [`auths`][Self::auths] can pass even when a contract
1337    /// is missing a `require_auth` check. Use [`auths`][Self::auths] after
1338    /// the contract call to assert that the expected authorizations were required.
1339    ///
1340    /// ### Examples
1341    /// ```
1342    /// use soroban_sdk::{contract, contractimpl, Env, Address, IntoVal, symbol_short};
1343    /// use soroban_sdk::testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation};
1344    ///
1345    /// #[contract]
1346    /// pub struct HelloContract;
1347    ///
1348    /// #[contractimpl]
1349    /// impl HelloContract {
1350    ///     pub fn hello(env: Env, from: Address) {
1351    ///         from.require_auth();
1352    ///         // TODO
1353    ///     }
1354    /// }
1355    ///
1356    /// #[test]
1357    /// fn test() {
1358    /// # }
1359    /// # fn main() {
1360    ///     let env = Env::default();
1361    ///     let contract_id = env.register(HelloContract, ());
1362    ///
1363    ///     env.mock_all_auths();
1364    ///
1365    ///     let client = HelloContractClient::new(&env, &contract_id);
1366    ///     let addr = Address::generate(&env);
1367    ///     client.hello(&addr);
1368    ///
1369    ///     // Verify that the expected authorization was required.
1370    ///     assert_eq!(
1371    ///         env.auths(),
1372    ///         [(
1373    ///             addr.clone(),
1374    ///             AuthorizedInvocation {
1375    ///                 function: AuthorizedFunction::Contract((
1376    ///                     contract_id,
1377    ///                     symbol_short!("hello"),
1378    ///                     (&addr,).into_val(&env),
1379    ///                 )),
1380    ///                 sub_invocations: [].into(),
1381    ///             }
1382    ///         )]
1383    ///     );
1384    /// }
1385    /// ```
1386    pub fn mock_all_auths(&self) {
1387        self.env_impl.switch_to_recording_auth(true).unwrap();
1388    }
1389
1390    /// A version of [`mock_all_auths`][Self::mock_all_auths] that allows authorizations that are not
1391    /// present in the root invocation.
1392    ///
1393    /// Refer to [`mock_all_auths`][Self::mock_all_auths] documentation for general information and
1394    /// prefer using [`mock_all_auths`][Self::mock_all_auths] unless non-root authorization is required.
1395    ///
1396    /// The only difference from [`mock_all_auths`][Self::mock_all_auths] is that this won't return an
1397    /// error when `require_auth` hasn't been called in the root invocation for
1398    /// any given address. This is useful to test contracts that bundle calls to
1399    /// another contract without atomicity requirements (i.e. any contract call
1400    /// can be frontrun).
1401    ///
1402    /// ### Examples
1403    /// ```
1404    /// use soroban_sdk::{contract, contractimpl, Env, Address, testutils::Address as _};
1405    ///
1406    /// #[contract]
1407    /// pub struct ContractA;
1408    ///
1409    /// #[contractimpl]
1410    /// impl ContractA {
1411    ///     pub fn do_auth(env: Env, addr: Address) {
1412    ///         addr.require_auth();
1413    ///     }
1414    /// }
1415    /// #[contract]
1416    /// pub struct ContractB;
1417    ///
1418    /// #[contractimpl]
1419    /// impl ContractB {
1420    ///     pub fn call_a(env: Env, contract_a: Address, addr: Address) {
1421    ///         // Notice there is no `require_auth` call here.
1422    ///         ContractAClient::new(&env, &contract_a).do_auth(&addr);
1423    ///     }
1424    /// }
1425    /// #[test]
1426    /// fn test() {
1427    /// # }
1428    /// # fn main() {
1429    ///     let env = Env::default();
1430    ///     let contract_a = env.register(ContractA, ());
1431    ///     let contract_b = env.register(ContractB, ());
1432    ///     // The regular `env.mock_all_auths()` would result in the call
1433    ///     // failure.
1434    ///     env.mock_all_auths_allowing_non_root_auth();
1435    ///
1436    ///     let client = ContractBClient::new(&env, &contract_b);
1437    ///     let addr = Address::generate(&env);
1438    ///     client.call_a(&contract_a, &addr);
1439    /// }
1440    /// ```
1441    pub fn mock_all_auths_allowing_non_root_auth(&self) {
1442        self.env_impl.switch_to_recording_auth(false).unwrap();
1443    }
1444
1445    /// Returns a list of authorization trees that were seen during the last
1446    /// contract or authorized host function invocation.
1447    ///
1448    /// Use this in tests to verify that the expected authorizations with the
1449    /// expected arguments are required.
1450    ///
1451    /// The return value is a vector of authorizations represented by tuples of
1452    /// `(address, AuthorizedInvocation)`. `AuthorizedInvocation` describes the
1453    /// tree of `require_auth_for_args(address, args)` from the contract
1454    /// functions (or `require_auth` with all the arguments of the function
1455    /// invocation). It also might contain the authorized host functions (
1456    /// currently CreateContract is the only such function) in case if
1457    /// corresponding host functions have been called.
1458    ///
1459    /// Refer to documentation for `AuthorizedInvocation` for detailed
1460    /// information on its contents.
1461    ///
1462    /// The order of the returned vector is defined by the order of
1463    /// [`Address::require_auth`] calls. Repeated calls to
1464    /// [`Address::require_auth`] with the same address and args in the same
1465    /// tree of contract invocations will appear only once in the vector.
1466    ///
1467    /// ### Examples
1468    /// ```
1469    /// use soroban_sdk::{contract, contractimpl, testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation}, symbol_short, Address, Symbol, Env, IntoVal};
1470    ///
1471    /// #[contract]
1472    /// pub struct Contract;
1473    ///
1474    /// #[contractimpl]
1475    /// impl Contract {
1476    ///     pub fn transfer(env: Env, address: Address, amount: i128) {
1477    ///         address.require_auth();
1478    ///     }
1479    ///     pub fn transfer2(env: Env, address: Address, amount: i128) {
1480    ///         address.require_auth_for_args((amount / 2,).into_val(&env));
1481    ///     }
1482    /// }
1483    ///
1484    /// #[test]
1485    /// fn test() {
1486    /// # }
1487    /// # #[cfg(feature = "testutils")]
1488    /// # fn main() {
1489    ///     let env = Env::default();
1490    ///     let contract_id = env.register(Contract, ());
1491    ///     let client = ContractClient::new(&env, &contract_id);
1492    ///     env.mock_all_auths();
1493    ///     let address = Address::generate(&env);
1494    ///     client.transfer(&address, &1000_i128);
1495    ///     assert_eq!(
1496    ///         env.auths(),
1497    ///         [(
1498    ///             address.clone(),
1499    ///             AuthorizedInvocation {
1500    ///                 function: AuthorizedFunction::Contract((
1501    ///                     client.address.clone(),
1502    ///                     symbol_short!("transfer"),
1503    ///                     (&address, 1000_i128,).into_val(&env)
1504    ///                 )),
1505    ///                 sub_invocations: [].into()
1506    ///             }
1507    ///         )]
1508    ///     );
1509    ///
1510    ///     client.transfer2(&address, &1000_i128);
1511    ///     assert_eq!(
1512    ///         env.auths(),
1513    ///         [(
1514    ///             address.clone(),
1515    ///             AuthorizedInvocation {
1516    ///                 function: AuthorizedFunction::Contract((
1517    ///                     client.address.clone(),
1518    ///                     symbol_short!("transfer2"),
1519    ///                     // `transfer2` requires auth for (amount / 2) == (1000 / 2) == 500.
1520    ///                     (500_i128,).into_val(&env)
1521    ///                 )),
1522    ///                 sub_invocations: [].into()
1523    ///             }
1524    ///         )]
1525    ///     );
1526    /// }
1527    /// # #[cfg(not(feature = "testutils"))]
1528    /// # fn main() { }
1529    /// ```
1530    pub fn auths(&self) -> std::vec::Vec<(Address, AuthorizedInvocation)> {
1531        (*self.test_state.auth_snapshot)
1532            .borrow()
1533            .0
1534            .last()
1535            .cloned()
1536            .unwrap_or_default()
1537            .into_iter()
1538            .map(|(sc_addr, invocation)| {
1539                (
1540                    xdr::ScVal::Address(sc_addr).try_into_val(self).unwrap(),
1541                    AuthorizedInvocation::from_xdr(self, &invocation),
1542                )
1543            })
1544            .collect()
1545    }
1546
1547    /// Invokes the special `__check_auth` function of contracts that implement
1548    /// the custom account interface.
1549    ///
1550    /// `__check_auth` can't be called outside of the host-managed `require_auth`
1551    /// calls. This test utility allows testing custom account contracts without
1552    /// the need to setup complex contract call trees and enabling the enforcing
1553    /// auth on the host side.
1554    ///
1555    /// This function requires to provide the template argument for error. Use
1556    /// `soroban_sdk::Error` if `__check_auth` doesn't return a special
1557    /// contract error and use the error with `contracterror` attribute
1558    /// otherwise.
1559    ///
1560    /// ### Examples
1561    /// ```
1562    /// use soroban_sdk::{contract, contracterror, contractimpl, testutils::{Address as _, BytesN as _}, vec, auth::Context, BytesN, Env, Vec, Val};
1563    ///
1564    /// #[contracterror]
1565    /// #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
1566    /// #[repr(u32)]
1567    /// pub enum NoopAccountError {
1568    ///     SomeError = 1,
1569    /// }
1570    /// #[contract]
1571    /// struct NoopAccountContract;
1572    /// #[contractimpl]
1573    /// impl NoopAccountContract {
1574    ///
1575    ///     #[allow(non_snake_case)]
1576    ///     pub fn __check_auth(
1577    ///         _env: Env,
1578    ///         _signature_payload: BytesN<32>,
1579    ///         signature: Val,
1580    ///         _auth_context: Vec<Context>,
1581    ///     ) -> Result<(), NoopAccountError> {
1582    ///         if signature.is_void() {
1583    ///             Err(NoopAccountError::SomeError)
1584    ///         } else {
1585    ///             Ok(())
1586    ///         }
1587    ///     }
1588    /// }
1589    /// #[test]
1590    /// fn test() {
1591    /// # }
1592    /// # fn main() {
1593    ///     let e: Env = Default::default();
1594    ///     let account_contract = NoopAccountContractClient::new(&e, &e.register(NoopAccountContract, ()));
1595    ///     // Non-successful call of `__check_auth` with a `contracterror` error.
1596    ///     assert_eq!(
1597    ///         e.try_invoke_contract_check_auth::<NoopAccountError>(
1598    ///             &account_contract.address,
1599    ///             &BytesN::from_array(&e, &[0; 32]),
1600    ///             ().into(),
1601    ///             &vec![&e],
1602    ///         ),
1603    ///         // The inner `Result` is for conversion error and will be Ok
1604    ///         // as long as a valid error type used.
1605    ///         Err(Ok(NoopAccountError::SomeError))
1606    ///     );
1607    ///     // Successful call of `__check_auth` with a `soroban_sdk::InvokeError`
1608    ///     // error - this should be compatible with any error type.
1609    ///     assert_eq!(
1610    ///         e.try_invoke_contract_check_auth::<soroban_sdk::InvokeError>(
1611    ///             &account_contract.address,
1612    ///             &BytesN::from_array(&e, &[0; 32]),
1613    ///             0_i32.into(),
1614    ///             &vec![&e],
1615    ///         ),
1616    ///         Ok(())
1617    ///     );
1618    /// }
1619    /// ```
1620    pub fn try_invoke_contract_check_auth<E>(
1621        &self,
1622        contract: &Address,
1623        signature_payload: &BytesN<32>,
1624        signature: Val,
1625        auth_context: &Vec<auth::Context>,
1626    ) -> Result<(), Result<E, InvokeError>>
1627    where
1628        E: TryFrom<Error>,
1629        E::Error: Into<InvokeError>,
1630    {
1631        let args = Vec::from_array(
1632            self,
1633            [signature_payload.to_val(), signature, auth_context.to_val()],
1634        );
1635        let res = self
1636            .host()
1637            .call_account_contract_check_auth(contract.to_object(), args.to_object());
1638        match res {
1639            Ok(rv) => Ok(rv.into_val(self)),
1640            Err(e) => Err(e.error.try_into().map_err(Into::into)),
1641        }
1642    }
1643
1644    fn register_contract_with_contract_id_and_executable(
1645        &self,
1646        contract_address: &Address,
1647        executable: xdr::ContractExecutable,
1648        constructor_args: Vec<Val>,
1649    ) {
1650        let contract_id = contract_address.contract_id();
1651        let data_key = xdr::ScVal::LedgerKeyContractInstance;
1652        let key = Rc::new(LedgerKey::ContractData(LedgerKeyContractData {
1653            contract: xdr::ScAddress::Contract(contract_id.clone()),
1654            key: data_key.clone(),
1655            durability: xdr::ContractDataDurability::Persistent,
1656        }));
1657
1658        let instance = xdr::ScContractInstance {
1659            executable,
1660            storage: Default::default(),
1661        };
1662
1663        let entry = Rc::new(LedgerEntry {
1664            ext: xdr::LedgerEntryExt::V0,
1665            last_modified_ledger_seq: 0,
1666            data: xdr::LedgerEntryData::ContractData(xdr::ContractDataEntry {
1667                contract: xdr::ScAddress::Contract(contract_id.clone()),
1668                key: data_key,
1669                val: xdr::ScVal::ContractInstance(instance),
1670                durability: xdr::ContractDataDurability::Persistent,
1671                ext: xdr::ExtensionPoint::V0,
1672            }),
1673        });
1674        let live_until_ledger = self.ledger().sequence() + 1;
1675        self.host()
1676            .add_ledger_entry(&key, &entry, Some(live_until_ledger))
1677            .unwrap();
1678        let prev_auth_manager = self.env_impl.snapshot_auth_manager().unwrap();
1679        self.env_impl
1680            .switch_to_recording_auth_inherited_from_snapshot(&prev_auth_manager)
1681            .unwrap();
1682        let call_result = self.env_impl.call_constructor_for_stored_contract_unsafe(
1683            &contract_id,
1684            constructor_args.to_object(),
1685        );
1686        self.env_impl.set_auth_manager(prev_auth_manager).unwrap();
1687        call_result.unwrap();
1688    }
1689
1690    /// Run the function as if executed by the given contract ID.
1691    ///
1692    /// Used to write or read contract data, or take other actions in tests for
1693    /// setting up tests or asserting on internal state.
1694    ///
1695    /// ### Examples
1696    /// ```
1697    /// use soroban_sdk::{contract, contractimpl, Env, Symbol};
1698    ///
1699    /// #[contract]
1700    /// pub struct HelloContract;
1701    ///
1702    /// #[contractimpl]
1703    /// impl HelloContract {
1704    ///     pub fn set_storage(env: Env, key: Symbol, val: Symbol) {
1705    ///         env.storage().persistent().set(&key, &val);
1706    ///     }
1707    /// }
1708    ///
1709    /// #[test]
1710    /// fn test() {
1711    /// # }
1712    /// # fn main() {
1713    ///     let env = Env::default();
1714    ///     let contract_id = env.register(HelloContract, ());
1715    ///     let client = HelloContractClient::new(&env, &contract_id);
1716    ///
1717    ///     let key = Symbol::new(&env, "foo");
1718    ///     let val = Symbol::new(&env, "bar");
1719    ///
1720    ///     // Set storage using the contract
1721    ///     client.set_storage(&key, &val);
1722    ///
1723    ///     // Successfully read the storage key
1724    ///     let result = env.as_contract(&contract_id, || {
1725    ///         env.storage()
1726    ///             .persistent()
1727    ///             .get::<Symbol, Symbol>(&key)
1728    ///             .unwrap()
1729    ///     });
1730    ///     assert_eq!(result, val);
1731    /// }
1732    /// ```
1733    pub fn as_contract<T>(&self, id: &Address, f: impl FnOnce() -> T) -> T {
1734        let id = id.contract_id();
1735        let func = Symbol::from_small_str("");
1736        let mut t: Option<T> = None;
1737        self.env_impl
1738            .with_test_contract_frame(id, func, || {
1739                t = Some(f());
1740                Ok(().into())
1741            })
1742            .unwrap();
1743        t.unwrap()
1744    }
1745
1746    /// Run the function as if executed by the given contract ID. Returns an
1747    /// error if the function execution fails for any reason.
1748    ///
1749    /// Used to write or read contract data, or take other actions in tests for
1750    /// setting up tests or asserting on internal state.
1751    ///
1752    /// ### Examples
1753    /// ```
1754    /// use soroban_sdk::{contract, contractimpl, xdr::{ScErrorCode, ScErrorType}, Env, Error, Symbol};
1755    ///
1756    /// #[contract]
1757    /// pub struct HelloContract;
1758    ///
1759    /// #[contractimpl]
1760    /// impl HelloContract {
1761    ///     pub fn set_storage(env: Env, key: Symbol, val: Symbol) {
1762    ///         env.storage().persistent().set(&key, &val);
1763    ///     }
1764    /// }
1765    ///
1766    /// #[test]
1767    /// fn test() {
1768    /// # }
1769    /// # fn main() {
1770    ///     let env = Env::default();
1771    ///     let contract_id = env.register(HelloContract, ());
1772    ///     let client = HelloContractClient::new(&env, &contract_id);
1773    ///
1774    ///     let key = Symbol::new(&env, "foo");
1775    ///     let val = Symbol::new(&env, "bar");
1776    ///
1777    ///     // Set storage using the contract
1778    ///     client.set_storage(&key, &val);
1779    ///
1780    ///     // Successfully read the storage key
1781    ///     let result = env.try_as_contract::<Symbol, Error>(&contract_id, || {
1782    ///         env.storage()
1783    ///             .persistent()
1784    ///             .get::<Symbol, Symbol>(&key)
1785    ///             .unwrap()
1786    ///     });
1787    ///     assert_eq!(result, Ok(val));
1788    ///
1789    ///     // Attempting to extend TTL of a non-existent key throws an error
1790    ///     let new_key = Symbol::new(&env, "baz");
1791    ///     let result = env.try_as_contract(&contract_id, || {
1792    ///         env.storage().persistent().extend_ttl(&new_key, 1, 100);
1793    ///     });
1794    ///     assert_eq!(
1795    ///         result,
1796    ///         Err(Ok(Error::from_type_and_code(
1797    ///             ScErrorType::Storage,
1798    ///             ScErrorCode::MissingValue
1799    ///         )))
1800    ///     );
1801    /// }
1802    /// ```
1803    pub fn try_as_contract<T, E>(
1804        &self,
1805        id: &Address,
1806        f: impl FnOnce() -> T,
1807    ) -> Result<T, Result<E, InvokeError>>
1808    where
1809        E: TryFrom<Error>,
1810        E::Error: Into<InvokeError>,
1811    {
1812        let id = id.contract_id();
1813        let func = Symbol::from_small_str("");
1814        let mut t: Option<T> = None;
1815        let result = self.env_impl.try_with_test_contract_frame(id, func, || {
1816            t = Some(f());
1817            Ok(().into())
1818        });
1819
1820        match result {
1821            Ok(_) => Ok(t.unwrap()),
1822            Err(e) => Err(E::try_from(e.error).map_err(Into::into)),
1823        }
1824    }
1825
1826    /// Creates a new Env loaded with the [`Snapshot`].
1827    ///
1828    /// The ledger info and state in the snapshot are loaded into the Env.
1829    ///
1830    /// Events, as an output source only, are not loaded into the Env.
1831    pub fn from_snapshot(s: Snapshot) -> Env {
1832        Env::new_for_testutils(
1833            EnvTestConfig::default(),
1834            Rc::new(s.ledger.clone()),
1835            Some(Rc::new(RefCell::new(s.generators))),
1836            Some(s.ledger.ledger_info()),
1837            Some(Rc::new(s.ledger.clone())),
1838        )
1839    }
1840
1841    /// Creates a new Env loaded with the ledger snapshot loaded from the file.
1842    ///
1843    /// The ledger info and state in the snapshot are loaded into the Env.
1844    ///
1845    /// Events, as an output source only, are not loaded into the Env.
1846    ///
1847    /// ### Panics
1848    ///
1849    /// If there is any error reading the file.
1850    pub fn from_snapshot_file(p: impl AsRef<Path>) -> Env {
1851        Self::from_snapshot(Snapshot::read_file(p).unwrap())
1852    }
1853
1854    /// Create a snapshot from the Env's current state.
1855    pub fn to_snapshot(&self) -> Snapshot {
1856        Snapshot {
1857            generators: (*self.test_state.generators).borrow().clone(),
1858            auth: (*self.test_state.auth_snapshot).borrow().clone(),
1859            ledger: self.to_ledger_snapshot(),
1860            events: self.to_events_snapshot(),
1861        }
1862    }
1863
1864    /// Create a snapshot file from the Env's current state.
1865    ///
1866    /// ### Panics
1867    ///
1868    /// If there is any error writing the file.
1869    pub fn to_snapshot_file(&self, p: impl AsRef<Path>) {
1870        self.to_snapshot().write_file(p).unwrap();
1871    }
1872
1873    /// Creates a new Env loaded with the snapshot source.
1874    ///
1875    /// The ledger info and state from the snapshot source are loaded into the Env.
1876    pub fn from_ledger_snapshot(input: impl Into<SnapshotSourceInput>) -> Env {
1877        let SnapshotSourceInput {
1878            source,
1879            ledger_info,
1880            snapshot,
1881        } = input.into();
1882
1883        Env::new_for_testutils(
1884            EnvTestConfig::default(), // TODO: Allow setting the config.
1885            source,
1886            None,
1887            ledger_info,
1888            snapshot,
1889        )
1890    }
1891
1892    /// Creates a new Env loaded with the ledger snapshot loaded from the file.
1893    ///
1894    /// ### Panics
1895    ///
1896    /// If there is any error reading the file.
1897    pub fn from_ledger_snapshot_file(p: impl AsRef<Path>) -> Env {
1898        Self::from_ledger_snapshot(LedgerSnapshot::read_file(p).unwrap())
1899    }
1900
1901    /// Create a snapshot from the Env's current state.
1902    pub fn to_ledger_snapshot(&self) -> LedgerSnapshot {
1903        let snapshot = self.test_state.snapshot.clone().unwrap_or_default();
1904        let mut snapshot = (*snapshot).clone();
1905        snapshot.set_ledger_info(self.ledger().get());
1906        snapshot.update_entries(&self.host().get_stored_entries().unwrap());
1907        snapshot
1908    }
1909
1910    /// Create a snapshot file from the Env's current state.
1911    ///
1912    /// ### Panics
1913    ///
1914    /// If there is any error writing the file.
1915    pub fn to_ledger_snapshot_file(&self, p: impl AsRef<Path>) {
1916        self.to_ledger_snapshot().write_file(p).unwrap();
1917    }
1918
1919    /// Create an events snapshot from the Env's current state.
1920    pub(crate) fn to_events_snapshot(&self) -> EventsSnapshot {
1921        EventsSnapshot(
1922            self.host()
1923                .get_events()
1924                .unwrap()
1925                .0
1926                .into_iter()
1927                .filter(|e| match e.event.type_ {
1928                    // Keep only system and contract events, because event
1929                    // snapshots are used in test snapshots, and intended to be
1930                    // stable over time because the goal is to record meaningful
1931                    // observable behaviors. Diagnostic events are observable,
1932                    // but events have no stability guarantees and are intended
1933                    // to be used by developers when debugging, tracing, and
1934                    // observing, not by systems that integrate.
1935                    xdr::ContractEventType::System | xdr::ContractEventType::Contract => true,
1936                    xdr::ContractEventType::Diagnostic => false,
1937                })
1938                .map(Into::into)
1939                .collect(),
1940        )
1941    }
1942
1943    /// Get the budget that tracks the resources consumed for the environment.
1944    #[deprecated(note = "use cost_estimate().budget()")]
1945    pub fn budget(&self) -> Budget {
1946        Budget::new(self.env_impl.budget_cloned())
1947    }
1948}
1949
1950#[cfg(any(test, feature = "testutils"))]
1951impl Drop for Env {
1952    fn drop(&mut self) {
1953        // If the env impl (Host) is finishable, that means this Env is the last
1954        // Env to hold a reference to the Host. The Env should only write a test
1955        // snapshot at that point when no other references to the host exist,
1956        // because it is only when there are no other references that the host
1957        // is being dropped.
1958        if self.env_impl.can_finish() && self.test_state.config.capture_snapshot_at_drop {
1959            self.to_test_snapshot_file();
1960        }
1961    }
1962}
1963
1964#[doc(hidden)]
1965#[cfg(any(test, feature = "testutils"))]
1966impl Env {
1967    /// Create a snapshot file for the currently executing test.
1968    ///
1969    /// Writes the file to the `test_snapshots/{test-name}.N.json` path where
1970    /// `N` is incremented for each unique `Env` in the test.
1971    ///
1972    /// Use to record the observable behavior of a test, and changes to that
1973    /// behavior over time. Commit the test snapshot file to version control and
1974    /// watch for changes in it on contract change, SDK upgrade, protocol
1975    /// upgrade, and other important events.
1976    ///
1977    /// No file will be created if the environment has no meaningful data such
1978    /// as stored entries or events.
1979    ///
1980    /// ### Panics
1981    ///
1982    /// If there is any error writing the file.
1983    pub(crate) fn to_test_snapshot_file(&self) {
1984        let snapshot = self.to_snapshot();
1985
1986        // Don't write a snapshot that has no data in it.
1987        if snapshot.ledger.entries().into_iter().count() == 0
1988            && snapshot.events.0.is_empty()
1989            && snapshot.auth.0.is_empty()
1990        {
1991            return;
1992        }
1993
1994        // Determine path to write test snapshots to.
1995        let Some(test_name) = &self.test_state.test_name else {
1996            // If there's no test name, we're not in a test context, so don't write snapshots.
1997            return;
1998        };
1999        let number = self.test_state.number;
2000        // Break up the test name into directories, using :: as the separator.
2001        // The :: module separator cannot be written into the filename because
2002        // some operating systems (e.g. Windows) do not allow the : character in
2003        // filenames.
2004        let test_name_path = test_name
2005            .split("::")
2006            .map(|p| std::path::Path::new(p).to_path_buf())
2007            .reduce(|p0, p1| p0.join(p1))
2008            .expect("test name to not be empty");
2009        let dir = std::path::Path::new("test_snapshots");
2010        let p = dir
2011            .join(&test_name_path)
2012            .with_extension(format!("{number}.json"));
2013
2014        // Write test snapshots to file.
2015        eprintln!("Writing test snapshot file for test {test_name:?} to {p:?}.");
2016        snapshot.write_file(p).unwrap();
2017    }
2018}
2019
2020#[doc(hidden)]
2021impl internal::EnvBase for Env {
2022    type Error = Infallible;
2023
2024    // This exists to allow code in conversion paths to upgrade an Error to an
2025    // Env::Error with some control granted to the underlying Env (and panic
2026    // paths kept out of the host). We delegate this to our env_impl and then,
2027    // since our own Error type is Infallible, immediately throw it into either
2028    // the env_impl's Error escalation path (if testing), or just plain panic.
2029    #[cfg(not(target_family = "wasm"))]
2030    fn error_from_error_val(&self, e: crate::Error) -> Self::Error {
2031        let host_err = self.env_impl.error_from_error_val(e);
2032        #[cfg(any(test, feature = "testutils"))]
2033        self.env_impl.escalate_error_to_panic(host_err);
2034        #[cfg(not(any(test, feature = "testutils")))]
2035        panic!("{:?}", host_err);
2036    }
2037
2038    // When targeting wasm we don't even need to do that, just delegate to
2039    // the Guest's impl, which calls core::arch::wasm32::unreachable.
2040    #[cfg(target_family = "wasm")]
2041    #[allow(unreachable_code)]
2042    fn error_from_error_val(&self, e: crate::Error) -> Self::Error {
2043        self.env_impl.error_from_error_val(e)
2044    }
2045
2046    fn check_protocol_version_lower_bound(&self, v: u32) -> Result<(), Self::Error> {
2047        Ok(self
2048            .env_impl
2049            .check_protocol_version_lower_bound(v)
2050            .unwrap_optimized())
2051    }
2052
2053    fn check_protocol_version_upper_bound(&self, v: u32) -> Result<(), Self::Error> {
2054        Ok(self
2055            .env_impl
2056            .check_protocol_version_upper_bound(v)
2057            .unwrap_optimized())
2058    }
2059
2060    // Note: the function `escalate_error_to_panic` only exists _on the `Env`
2061    // trait_ when the feature `soroban-env-common/testutils` is enabled. This
2062    // is because the host wants to never have this function even _compiled in_
2063    // when building for production, as it might be accidentally called (we have
2064    // mistakenly done so with conversion and comparison traits in the past).
2065    //
2066    // As a result, we only implement it here (fairly meaninglessly) when we're
2067    // in `cfg(test)` (which enables `soroban-env-host/testutils` thus
2068    // `soroban-env-common/testutils`) or when we've had our own `testutils`
2069    // feature enabled (which does the same).
2070    //
2071    // See the `internal::reject_err` functions above for more detail about what
2072    // it actually does (when implemented for real, on the host). In this
2073    // not-very-serious impl, since `Self::Error` is `Infallible`, this instance
2074    // can never actually be called and so its body is just a trivial
2075    // transformation from one empty type to another, for Type System Reasons.
2076    #[cfg(any(test, feature = "testutils"))]
2077    fn escalate_error_to_panic(&self, e: Self::Error) -> ! {
2078        match e {}
2079    }
2080
2081    fn bytes_copy_from_slice(
2082        &self,
2083        b: BytesObject,
2084        b_pos: U32Val,
2085        slice: &[u8],
2086    ) -> Result<BytesObject, Self::Error> {
2087        Ok(self
2088            .env_impl
2089            .bytes_copy_from_slice(b, b_pos, slice)
2090            .unwrap_optimized())
2091    }
2092
2093    fn bytes_copy_to_slice(
2094        &self,
2095        b: BytesObject,
2096        b_pos: U32Val,
2097        slice: &mut [u8],
2098    ) -> Result<(), Self::Error> {
2099        Ok(self
2100            .env_impl
2101            .bytes_copy_to_slice(b, b_pos, slice)
2102            .unwrap_optimized())
2103    }
2104
2105    fn bytes_new_from_slice(&self, slice: &[u8]) -> Result<BytesObject, Self::Error> {
2106        Ok(self.env_impl.bytes_new_from_slice(slice).unwrap_optimized())
2107    }
2108
2109    fn log_from_slice(&self, msg: &str, args: &[Val]) -> Result<Void, Self::Error> {
2110        Ok(self.env_impl.log_from_slice(msg, args).unwrap_optimized())
2111    }
2112
2113    fn string_copy_to_slice(
2114        &self,
2115        b: StringObject,
2116        b_pos: U32Val,
2117        slice: &mut [u8],
2118    ) -> Result<(), Self::Error> {
2119        Ok(self
2120            .env_impl
2121            .string_copy_to_slice(b, b_pos, slice)
2122            .unwrap_optimized())
2123    }
2124
2125    fn symbol_copy_to_slice(
2126        &self,
2127        b: SymbolObject,
2128        b_pos: U32Val,
2129        mem: &mut [u8],
2130    ) -> Result<(), Self::Error> {
2131        Ok(self
2132            .env_impl
2133            .symbol_copy_to_slice(b, b_pos, mem)
2134            .unwrap_optimized())
2135    }
2136
2137    fn string_new_from_slice(&self, slice: &[u8]) -> Result<StringObject, Self::Error> {
2138        Ok(self
2139            .env_impl
2140            .string_new_from_slice(slice)
2141            .unwrap_optimized())
2142    }
2143
2144    fn symbol_new_from_slice(&self, slice: &[u8]) -> Result<SymbolObject, Self::Error> {
2145        Ok(self
2146            .env_impl
2147            .symbol_new_from_slice(slice)
2148            .unwrap_optimized())
2149    }
2150
2151    fn map_new_from_slices(&self, keys: &[&str], vals: &[Val]) -> Result<MapObject, Self::Error> {
2152        Ok(self
2153            .env_impl
2154            .map_new_from_slices(keys, vals)
2155            .unwrap_optimized())
2156    }
2157
2158    fn map_unpack_to_slice(
2159        &self,
2160        map: MapObject,
2161        keys: &[&str],
2162        vals: &mut [Val],
2163    ) -> Result<Void, Self::Error> {
2164        Ok(self
2165            .env_impl
2166            .map_unpack_to_slice(map, keys, vals)
2167            .unwrap_optimized())
2168    }
2169
2170    fn vec_new_from_slice(&self, vals: &[Val]) -> Result<VecObject, Self::Error> {
2171        Ok(self.env_impl.vec_new_from_slice(vals).unwrap_optimized())
2172    }
2173
2174    fn vec_unpack_to_slice(&self, vec: VecObject, vals: &mut [Val]) -> Result<Void, Self::Error> {
2175        Ok(self
2176            .env_impl
2177            .vec_unpack_to_slice(vec, vals)
2178            .unwrap_optimized())
2179    }
2180
2181    fn symbol_index_in_strs(&self, key: Symbol, strs: &[&str]) -> Result<U32Val, Self::Error> {
2182        Ok(self
2183            .env_impl
2184            .symbol_index_in_strs(key, strs)
2185            .unwrap_optimized())
2186    }
2187}
2188
2189///////////////////////////////////////////////////////////////////////////////
2190/// X-macro use: impl Env for SDK's Env
2191///////////////////////////////////////////////////////////////////////////////
2192
2193// This is a helper macro used only by impl_env_for_sdk below. It consumes a
2194// token-tree of the form:
2195//
2196//  {fn $fn_id:ident $args:tt -> $ret:ty}
2197//
2198// and produces the the corresponding method definition to be used in the
2199// SDK's Env implementation of the Env (calling through to the corresponding
2200// guest or host implementation).
2201macro_rules! sdk_function_helper {
2202    {$mod_id:ident, fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty}
2203    =>
2204    {
2205        fn $fn_id(&self, $($arg:$type),*) -> Result<$ret, Self::Error> {
2206            internal::reject_err(&self.env_impl, self.env_impl.$fn_id($($arg),*))
2207        }
2208    };
2209}
2210
2211// This is a callback macro that pattern-matches the token-tree passed by the
2212// x-macro (call_macro_with_all_host_functions) and produces a suite of
2213// forwarding-method definitions, which it places in the body of the declaration
2214// of the implementation of Env for the SDK's Env.
2215macro_rules! impl_env_for_sdk {
2216    {
2217        $(
2218            // This outer pattern matches a single 'mod' block of the token-tree
2219            // passed from the x-macro to this macro. It is embedded in a `$()*`
2220            // pattern-repetition matcher so that it will match all provided
2221            // 'mod' blocks provided.
2222            $(#[$mod_attr:meta])*
2223            mod $mod_id:ident $mod_str:literal
2224            {
2225                $(
2226                    // This inner pattern matches a single function description
2227                    // inside a 'mod' block in the token-tree passed from the
2228                    // x-macro to this macro. It is embedded in a `$()*`
2229                    // pattern-repetition matcher so that it will match all such
2230                    // descriptions.
2231                    $(#[$fn_attr:meta])*
2232                    { $fn_str:literal, $($min_proto:literal)?, $($max_proto:literal)?, fn $fn_id:ident $args:tt -> $ret:ty }
2233                )*
2234            }
2235        )*
2236    }
2237
2238    =>  // The part of the macro above this line is a matcher; below is its expansion.
2239
2240    {
2241        // This macro expands to a single item: the implementation of Env for
2242        // the SDK's Env struct used by client contract code running in a WASM VM.
2243        #[doc(hidden)]
2244        impl internal::Env for Env
2245        {
2246            $(
2247                $(
2248                    // This invokes the guest_function_helper! macro above
2249                    // passing only the relevant parts of the declaration
2250                    // matched by the inner pattern above. It is embedded in two
2251                    // nested `$()*` pattern-repetition expanders that
2252                    // correspond to the pattern-repetition matchers in the
2253                    // match section, but we ignore the structure of the 'mod'
2254                    // block repetition-level from the outer pattern in the
2255                    // expansion, flattening all functions from all 'mod' blocks
2256                    // into the implementation of Env for Guest.
2257                    sdk_function_helper!{$mod_id, fn $fn_id $args -> $ret}
2258                )*
2259            )*
2260        }
2261    };
2262}
2263
2264// Here we invoke the x-macro passing generate_env_trait as its callback macro.
2265internal::call_macro_with_all_host_functions! { impl_env_for_sdk }