odra_core/
host.rs

1//! A module that provides the interface for interacting with the host environment.
2
3use crate::address::Addressable;
4use crate::gas_report::GasReport;
5use crate::{
6    call_result::CallResult, entry_point_callback::EntryPointsCaller, CallDef, ContractCallResult,
7    ContractEnv, EventError, VmError
8};
9#[cfg(not(target_arch = "wasm32"))]
10use crate::{consts, contract::OdraContract, contract_def::HasIdent};
11use crate::{prelude::*, utils};
12use casper_event_standard::EventInstance;
13use casper_types::{
14    bytesrepr::{Bytes, FromBytes, ToBytes},
15    CLTyped, PublicKey, RuntimeArgs, U512
16};
17
18/// A host side reference to a contract.
19pub trait HostRef {
20    /// Creates a new host side reference to a contract.
21    fn new(address: Address, env: HostEnv) -> Self;
22    /// Creates a new host reference with attached tokens, based on the current instance.
23    ///
24    /// If there are tokens attached to the current instance, the tokens will be attached
25    /// to the next contract call.
26    fn with_tokens(&self, tokens: U512) -> Self;
27    /// Returns the address of the contract.
28    fn address(&self) -> &Address;
29    /// Returns the host environment.
30    fn env(&self) -> &HostEnv;
31    /// Returns the n-th event emitted by the contract.
32    ///
33    /// If the event is not found or the type does not match, returns `EventError::EventNotFound`.
34    fn get_event<T>(&self, index: i32) -> Result<T, EventError>
35    where
36        T: FromBytes + EventInstance + 'static;
37    /// Returns a detailed information about the last call of the contract.
38    fn last_call(&self) -> ContractCallResult;
39}
40
41impl<T: HostRef> Addressable for T {
42    fn address(&self) -> &Address {
43        HostRef::address(self)
44    }
45}
46
47/// Trait for loading a contract from the host environment.
48///
49/// Similar to [Deployer], but does not deploy a new contract, but loads an existing one.
50pub trait HostRefLoader<T: HostRef> {
51    /// Loads an existing contract from the host environment.
52    fn load(env: &HostEnv, address: Address) -> T;
53}
54
55/// A type which can provide an [EntryPointsCaller].
56pub trait EntryPointsCallerProvider {
57    /// Returns an [EntryPointsCaller] for the given host environment.
58    fn entry_points_caller(env: &HostEnv) -> EntryPointsCaller;
59}
60
61/// A type which can deploy a contract.
62///
63/// Before any interaction with the contract, it must be deployed, either
64/// on a virtual machine or on a real blockchain.
65///
66/// The `Deployer` trait provides a simple way to deploy a contract.
67#[cfg(not(target_arch = "wasm32"))]
68pub trait Deployer<R: OdraContract>: Sized {
69    /// Deploys a contract with given init args.
70    ///
71    /// If the `init_args` is not [NoArgs], the contract is deployed and initialized
72    /// by calling the constructor. Otherwise no constructor is called.
73    ///
74    /// The default [OdraConfig] is used for deployment.
75    ///
76    /// Returns a host reference to the deployed contract.
77    fn deploy(env: &HostEnv, init_args: R::InitArgs) -> R::HostRef;
78
79    /// Tries to deploy a contract with given init args.
80    ///
81    /// Similar to `deploy`, but returns a result instead of panicking.
82    fn try_deploy(env: &HostEnv, init_args: R::InitArgs) -> OdraResult<R::HostRef>;
83
84    /// Deploys a contract with given init args and configuration.
85    ///
86    /// Returns a host reference to the deployed contract.
87    fn deploy_with_cfg<T: OdraConfig>(env: &HostEnv, init_args: R::InitArgs, cfg: T) -> R::HostRef;
88
89    /// Tries to deploy a contract with given init args and configuration.
90    ///
91    /// Similar to `deploy_with_cfg`, but returns a result instead of panicking.
92    fn try_deploy_with_cfg<T: OdraConfig>(
93        env: &HostEnv,
94        init_args: R::InitArgs,
95        cfg: T
96    ) -> OdraResult<R::HostRef>;
97}
98
99/// A type which can be used as initialization arguments for a contract.
100pub trait InitArgs: Into<RuntimeArgs> {}
101
102/// Default implementation of [InitArgs]. Should be used when the contract
103/// does not require initialization arguments.
104///
105/// Precisely, it means the constructor function has not been defined,
106/// or does not require any arguments.
107pub struct NoArgs;
108
109impl InitArgs for NoArgs {}
110
111impl From<NoArgs> for RuntimeArgs {
112    fn from(_: NoArgs) -> Self {
113        RuntimeArgs::new()
114    }
115}
116
117/// A configuration for a contract.
118///
119/// The configuration every contract written in Odra expects.
120/// Read more: [https://odra.dev/docs/backends/casper/#wasm-arguments]
121pub trait OdraConfig {
122    /// Returns the package hash of the contract.
123    ///
124    /// Used to set the `odra_cfg_package_hash_key_name` key at the contract initialization.
125    fn package_hash(&self) -> String;
126    /// Returns true if the contract should be deployed as upgradable.
127    ///
128    /// If true, the `odra_cfg_is_upgradable` key is set to `true` at the contract initialization.
129    fn is_upgradable(&self) -> bool;
130    /// If true and the key `odra_cfg_package_hash_key_name` already exists, it should be overwritten.
131    fn allow_key_override(&self) -> bool;
132}
133
134#[cfg(not(target_arch = "wasm32"))]
135/// Default configuration for a contract.
136struct DefaultOdraConfig {
137    name: String
138}
139
140#[cfg(not(target_arch = "wasm32"))]
141impl OdraConfig for DefaultOdraConfig {
142    fn package_hash(&self) -> String {
143        self.name.clone()
144    }
145
146    fn is_upgradable(&self) -> bool {
147        false
148    }
149
150    fn allow_key_override(&self) -> bool {
151        true
152    }
153}
154
155#[cfg(not(target_arch = "wasm32"))]
156impl<R: OdraContract> Deployer<R> for R {
157    fn deploy(
158        env: &HostEnv,
159        init_args: <R as OdraContract>::InitArgs
160    ) -> <R as OdraContract>::HostRef {
161        let contract_ident = R::HostRef::ident();
162        match Self::try_deploy(env, init_args) {
163            Ok(contract) => contract,
164            Err(OdraError::ExecutionError(ExecutionError::MissingArg)) => {
165                core::panic!("Invalid init args for contract {}.", contract_ident)
166            }
167            Err(e) => core::panic!("Contract init failed {:?}", e)
168        }
169    }
170
171    fn try_deploy(
172        env: &HostEnv,
173        init_args: <R as OdraContract>::InitArgs
174    ) -> OdraResult<<R as OdraContract>::HostRef> {
175        Self::try_deploy_with_cfg(
176            env,
177            init_args,
178            DefaultOdraConfig {
179                name: R::HostRef::ident()
180            }
181        )
182    }
183
184    fn deploy_with_cfg<T: OdraConfig>(
185        env: &HostEnv,
186        init_args: <R as OdraContract>::InitArgs,
187        cfg: T
188    ) -> <R as OdraContract>::HostRef {
189        let contract_ident = R::HostRef::ident();
190        match Self::try_deploy_with_cfg(env, init_args, cfg) {
191            Ok(contract) => contract,
192            Err(OdraError::ExecutionError(ExecutionError::MissingArg)) => {
193                core::panic!("Invalid init args for contract {}.", contract_ident)
194            }
195            Err(e) => core::panic!("Contract init failed {:?}", e)
196        }
197    }
198
199    fn try_deploy_with_cfg<T: OdraConfig>(
200        env: &HostEnv,
201        init_args: <R as OdraContract>::InitArgs,
202        cfg: T
203    ) -> OdraResult<<R as OdraContract>::HostRef> {
204        let contract_ident = R::HostRef::ident();
205        let caller = R::HostRef::entry_points_caller(env);
206
207        let mut init_args = init_args.into();
208        init_args.insert(consts::IS_UPGRADABLE_ARG, cfg.is_upgradable())?;
209        init_args.insert(consts::ALLOW_KEY_OVERRIDE_ARG, cfg.allow_key_override())?;
210        init_args.insert(
211            consts::PACKAGE_HASH_KEY_NAME_ARG,
212            format!("{}_package_hash", cfg.package_hash())
213        )?;
214
215        let address = env.new_contract(&contract_ident, init_args, caller)?;
216        Ok(R::HostRef::new(address, env.clone()))
217    }
218}
219
220#[cfg(not(target_arch = "wasm32"))]
221impl<T: OdraContract> HostRefLoader<T::HostRef> for T {
222    fn load(env: &HostEnv, address: Address) -> T::HostRef {
223        let caller = T::HostRef::entry_points_caller(env);
224        let contract_name = T::HostRef::ident();
225        env.register_contract(address, contract_name, caller);
226        T::HostRef::new(address, env.clone())
227    }
228}
229
230/// The `HostContext` trait defines the interface for interacting with the host environment.
231#[cfg_attr(test, mockall::automock)]
232pub trait HostContext {
233    /// Sets the caller address for the current contract execution.
234    fn set_caller(&self, caller: Address);
235
236    /// Sets the gas limit for the current contract execution.
237    fn set_gas(&self, gas: u64);
238
239    /// Returns the caller address for the current contract execution.
240    fn caller(&self) -> Address;
241
242    /// Returns the account address at the specified index.
243    fn get_account(&self, index: usize) -> Address;
244
245    /// Returns the CSPR balance of the specified address.
246    fn balance_of(&self, address: &Address) -> U512;
247
248    /// Advances the block time by the specified time difference.
249    fn advance_block_time(&self, time_diff: u64);
250
251    /// Returns the current block time.
252    fn block_time(&self) -> u64;
253
254    /// Returns the event bytes for the specified contract address and index.
255    fn get_event(&self, contract_address: &Address, index: u32) -> Result<Bytes, EventError>;
256
257    /// Returns the number of emitted events for the specified contract address.
258    fn get_events_count(&self, contract_address: &Address) -> u32;
259
260    /// Calls a contract at the specified address with the given call definition.
261    fn call_contract(
262        &self,
263        address: &Address,
264        call_def: CallDef,
265        use_proxy: bool
266    ) -> OdraResult<Bytes>;
267
268    /// Creates a new contract with the specified name, initialization arguments, and entry points caller.
269    fn new_contract(
270        &self,
271        name: &str,
272        init_args: RuntimeArgs,
273        entry_points_caller: EntryPointsCaller
274    ) -> OdraResult<Address>;
275
276    /// Registers an existing contract with the specified address, name, and entry points caller.
277    fn register_contract(
278        &self,
279        address: Address,
280        contract_name: String,
281        entry_points_caller: EntryPointsCaller
282    );
283
284    /// Returns the contract environment.
285    fn contract_env(&self) -> ContractEnv;
286
287    /// Returns the gas report for the current contract execution.
288    fn gas_report(&self) -> GasReport;
289
290    /// Returns the gas cost of the last contract call.
291    fn last_call_gas_cost(&self) -> u64;
292
293    /// Signs the specified message with the given address and returns the signature.
294    fn sign_message(&self, message: &Bytes, address: &Address) -> Bytes;
295
296    /// Returns the public key associated with the specified address.
297    fn public_key(&self, address: &Address) -> PublicKey;
298
299    /// Transfers the specified amount of CSPR from the current caller to the specified address.
300    fn transfer(&self, to: Address, amount: U512) -> OdraResult<()>;
301}
302
303/// Represents the host environment for executing smart contracts.
304///
305/// It provides methods for interacting with the underlying host context and managing
306/// the execution of contracts.
307#[derive(Clone)]
308pub struct HostEnv {
309    backend: Rc<RefCell<dyn HostContext>>,
310    last_call_result: Rc<RefCell<Option<CallResult>>>,
311    deployed_contracts: Rc<RefCell<Vec<Address>>>,
312    events_count: Rc<RefCell<BTreeMap<Address, u32>>> // contract_address -> events_count
313}
314
315impl HostEnv {
316    /// Creates a new `HostEnv` instance with the specified backend.
317    pub fn new(backend: Rc<RefCell<dyn HostContext>>) -> HostEnv {
318        HostEnv {
319            backend,
320            last_call_result: RefCell::new(None).into(),
321            deployed_contracts: RefCell::new(vec![]).into(),
322            events_count: Rc::new(RefCell::new(Default::default()))
323        }
324    }
325
326    /// Returns the account address at the specified index.
327    pub fn get_account(&self, index: usize) -> Address {
328        let backend = self.backend.borrow();
329        backend.get_account(index)
330    }
331
332    /// Sets the caller address for the current contract execution.
333    pub fn set_caller(&self, address: Address) {
334        if address.is_contract() {
335            panic!("Caller cannot be a contract: {:?}", address)
336        }
337        let backend = self.backend.borrow();
338        backend.set_caller(address)
339    }
340
341    /// Advances the block time by the specified time difference.
342    pub fn advance_block_time(&self, time_diff: u64) {
343        let backend = self.backend.borrow();
344        backend.advance_block_time(time_diff)
345    }
346
347    /// Returns the current block time.
348    pub fn block_time(&self) -> u64 {
349        let backend = self.backend.borrow();
350        backend.block_time()
351    }
352
353    /// Registers a new contract with the specified name, initialization arguments, and entry points caller.
354    pub fn new_contract(
355        &self,
356        name: &str,
357        init_args: RuntimeArgs,
358        entry_points_caller: EntryPointsCaller
359    ) -> OdraResult<Address> {
360        let backend = self.backend.borrow();
361        let deployed_contract = backend.new_contract(name, init_args, entry_points_caller)?;
362
363        self.deployed_contracts.borrow_mut().push(deployed_contract);
364        self.events_count.borrow_mut().insert(deployed_contract, 0);
365        Ok(deployed_contract)
366    }
367
368    /// Registers an existing contract with the specified address, name and entry points caller.
369    /// Similar to `new_contract`, but skips the deployment phase.
370    pub fn register_contract(
371        &self,
372        address: Address,
373        contract_name: String,
374        entry_points_caller: EntryPointsCaller
375    ) {
376        let backend = self.backend.borrow();
377        backend.register_contract(address, contract_name, entry_points_caller);
378        self.deployed_contracts.borrow_mut().push(address);
379    }
380
381    /// Calls a contract at the specified address with the given call definition.
382    pub fn call_contract<T: FromBytes + CLTyped>(
383        &self,
384        address: Address,
385        call_def: CallDef
386    ) -> OdraResult<T> {
387        let use_proxy = T::cl_type() != <()>::cl_type() || !call_def.amount().is_zero();
388        let call_result = self.raw_call_contract(address, call_def, use_proxy);
389        call_result.map(|bytes| {
390            T::from_bytes(&bytes)
391                .map(|(obj, _)| obj)
392                .map_err(|_| OdraError::VmError(VmError::Deserialization))
393        })?
394    }
395
396    /// Calls a contract at the specified address with the given call definition. Returns raw,
397    /// not serialized bytes.
398    pub fn raw_call_contract(
399        &self,
400        address: Address,
401        call_def: CallDef,
402        use_proxy: bool
403    ) -> OdraResult<Bytes> {
404        let backend = self.backend.borrow();
405        let call_result = backend.call_contract(&address, call_def, use_proxy);
406
407        let mut events_map: BTreeMap<Address, Vec<Bytes>> = BTreeMap::new();
408        let mut binding = self.events_count.borrow_mut();
409
410        // Go through all contracts and collect their events
411        self.deployed_contracts
412            .borrow()
413            .iter()
414            .for_each(|contract_address| {
415                if binding.get(contract_address).is_none() {
416                    binding.insert(
417                        *contract_address,
418                        backend.get_events_count(contract_address)
419                    );
420                }
421                let events_count = binding.get_mut(contract_address).unwrap();
422                let old_events_last_id = *events_count;
423                let new_events_count = backend.get_events_count(contract_address);
424                let mut events = vec![];
425                for event_id in old_events_last_id..new_events_count {
426                    let event = backend.get_event(contract_address, event_id).unwrap();
427                    events.push(event);
428                }
429
430                events_map.insert(*contract_address, events);
431
432                *events_count = new_events_count;
433            });
434
435        let last_call_gas_cost = backend.last_call_gas_cost();
436
437        self.last_call_result.replace(Some(CallResult::new(
438            address,
439            backend.caller(),
440            last_call_gas_cost,
441            call_result.clone(),
442            events_map
443        )));
444
445        call_result
446    }
447
448    /// Returns the gas cost of the last contract call.
449    pub fn contract_env(&self) -> ContractEnv {
450        self.backend.borrow().contract_env()
451    }
452
453    /// Prints the gas report for the current contract execution.
454    pub fn gas_report(&self) -> GasReport {
455        self.backend.borrow().gas_report().clone()
456    }
457
458    /// Returns the CSPR balance of the specified address.
459    pub fn balance_of<T: Addressable>(&self, address: &T) -> U512 {
460        let backend = self.backend.borrow();
461        backend.balance_of(address.address())
462    }
463
464    /// Retrieves an event with the specified index from the specified contract.
465    ///
466    /// # Returns
467    ///
468    /// Returns the event as an instance of the specified type, or an error if the event
469    /// couldn't be retrieved or parsed.
470    pub fn get_event<T: FromBytes + EventInstance, R: Addressable>(
471        &self,
472        contract_address: &R,
473        index: i32
474    ) -> Result<T, EventError> {
475        let contract_address = contract_address.address();
476        let backend = self.backend.borrow();
477        let events_count = self.events_count(contract_address);
478        let event_absolute_position = crate::utils::event_absolute_position(events_count, index)
479            .ok_or(EventError::IndexOutOfBounds)?;
480
481        let bytes = backend.get_event(contract_address, event_absolute_position)?;
482        T::from_bytes(&bytes)
483            .map_err(|_| EventError::Parsing)
484            .map(|r| r.0)
485    }
486
487    /// Retrieves a raw event (serialized) with the specified index from the specified contract.
488    pub fn get_event_bytes<T: Addressable>(
489        &self,
490        contract_address: &T,
491        index: u32
492    ) -> Result<Bytes, EventError> {
493        let backend = self.backend.borrow();
494        backend.get_event(contract_address.address(), index)
495    }
496
497    /// Returns the names of all events emitted by the specified contract.
498    pub fn event_names<T: Addressable>(&self, contract_address: &T) -> Vec<String> {
499        let backend = self.backend.borrow();
500        let events_count = backend.get_events_count(contract_address.address());
501
502        (0..events_count)
503            .map(|event_id| {
504                backend
505                    .get_event(contract_address.address(), event_id)
506                    .and_then(|bytes| utils::extract_event_name(&bytes))
507                    .unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
508            })
509            .collect()
510    }
511
512    /// Returns all events emitted by the specified contract.
513    pub fn events<T: Addressable>(&self, contract_address: &T) -> Vec<Bytes> {
514        let backend = self.backend.borrow();
515        let contract_address = contract_address.address();
516        let events_count = backend.get_events_count(contract_address);
517        (0..events_count)
518            .map(|event_id| {
519                backend
520                    .get_event(contract_address, event_id)
521                    .unwrap_or_else(|e| {
522                        panic!(
523                            "Couldn't get event at address {:?} with id {}: {:?}",
524                            &contract_address, event_id, e
525                        )
526                    })
527            })
528            .collect()
529    }
530
531    /// Returns the number of events emitted by the specified contract.
532    pub fn events_count<T: Addressable>(&self, contract_address: &T) -> u32 {
533        let backend = self.backend.borrow();
534        backend.get_events_count(contract_address.address())
535    }
536
537    /// Returns true if the specified event was emitted by the specified contract.
538    pub fn emitted_event<T: ToBytes + EventInstance, R: Addressable>(
539        &self,
540        contract_address: &R,
541        event: &T
542    ) -> bool {
543        let contract_address = contract_address.address();
544        let events_count = self.events_count(contract_address);
545        let event_bytes = Bytes::from(
546            event
547                .to_bytes()
548                .unwrap_or_else(|_| panic!("Couldn't serialize event"))
549        );
550        (0..events_count)
551            .map(|event_id| {
552                self.get_event_bytes(contract_address, event_id)
553                    .unwrap_or_else(|e| {
554                        panic!(
555                            "Couldn't get event at address {:?} with id {}: {:?}",
556                            &contract_address, event_id, e
557                        )
558                    })
559            })
560            .any(|bytes| bytes == event_bytes)
561    }
562
563    /// Returns true if an event with the specified name was emitted by the specified contract.
564    pub fn emitted<T: AsRef<str>, R: Addressable>(
565        &self,
566        contract_address: &R,
567        event_name: T
568    ) -> bool {
569        let events_count = self.events_count(contract_address);
570        (0..events_count)
571            .map(|event_id| {
572                self.get_event_bytes(contract_address, event_id)
573                    .unwrap_or_else(|e| {
574                        panic!(
575                            "Couldn't get event at address {:?} with id {}: {:?}",
576                            contract_address.address(),
577                            event_id,
578                            e
579                        )
580                    })
581            })
582            .any(|bytes| {
583                utils::extract_event_name(&bytes)
584                    .unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
585                    .as_str()
586                    == event_name.as_ref()
587            })
588    }
589
590    /// Returns the last call result for the specified contract.
591    pub fn last_call_result(&self, contract_address: Address) -> ContractCallResult {
592        self.last_call_result
593            .borrow()
594            .clone()
595            .unwrap()
596            .contract_last_call(contract_address)
597    }
598
599    /// Signs the specified message with the private key of the specified address.
600    pub fn sign_message(&self, message: &Bytes, address: &Address) -> Bytes {
601        let backend = self.backend.borrow();
602        backend.sign_message(message, address)
603    }
604
605    /// Returns the public key associated with the specified address.
606    pub fn public_key(&self, address: &Address) -> PublicKey {
607        let backend = self.backend.borrow();
608        backend.public_key(address)
609    }
610
611    /// Returns the caller address for the current contract execution.
612    pub fn caller(&self) -> Address {
613        let backend = self.backend.borrow();
614        backend.caller()
615    }
616
617    /// Sets the gas limit for the current contract execution.
618    pub fn set_gas(&self, gas: u64) {
619        let backend = self.backend.borrow();
620        backend.set_gas(gas)
621    }
622
623    /// Transfers the specified amount of CSPR from the current caller to the specified address.
624    pub fn transfer(&self, to: Address, amount: U512) -> OdraResult<()> {
625        if to.is_contract() {
626            return Err(OdraError::ExecutionError(
627                ExecutionError::TransferToContract
628            ));
629        }
630        let backend = self.backend.borrow();
631        backend.transfer(to, amount)
632    }
633}
634
635#[cfg(test)]
636mod test {
637    use core::fmt::Debug;
638
639    use super::*;
640    use casper_event_standard::Event;
641    use casper_types::{account::AccountHash, ContractPackageHash};
642    use mockall::{mock, predicate};
643    use std::sync::Mutex;
644
645    static IDENT_MTX: Mutex<()> = Mutex::new(());
646    static EPC_MTX: Mutex<()> = Mutex::new(());
647
648    #[derive(Debug, Event, PartialEq)]
649    struct TestEv {}
650
651    mock! {
652        TestRef {}
653        impl HasIdent for TestRef {
654            fn ident() -> String;
655        }
656        impl EntryPointsCallerProvider for TestRef {
657            fn entry_points_caller(env: &HostEnv) -> EntryPointsCaller;
658        }
659        impl HostRef for TestRef {
660            fn new(address: Address, env: HostEnv) -> Self;
661            fn with_tokens(&self, tokens: U512) -> Self;
662            fn address(&self) -> &Address;
663            fn env(&self) -> &HostEnv;
664            fn get_event<T>(&self, index: i32) -> Result<T, EventError> where T: FromBytes + EventInstance + 'static;
665            fn last_call(&self) -> ContractCallResult;
666        }
667    }
668
669    impl crate::ContractRef for MockTestRef {
670        fn new(_env: Rc<ContractEnv>, _address: Address) -> Self {
671            unimplemented!()
672        }
673        fn address(&self) -> &Address {
674            unimplemented!()
675        }
676    }
677
678    impl OdraContract for MockTestRef {
679        type HostRef = MockTestRef;
680
681        type ContractRef = MockTestRef;
682
683        type InitArgs = NoArgs;
684    }
685
686    mock! {
687        Ev {}
688        impl Into<RuntimeArgs> for Ev {
689            fn into(self) -> RuntimeArgs;
690        }
691    }
692
693    #[test]
694    fn test_deploy_with_default_args() {
695        // MockTestRef::ident() and  MockTestRef::entry_points_caller() are static and can't be safely used
696        // from multiple tests at the same time. Should be to protected with a Mutex. Each function has
697        // a separate Mutex.
698        // https://github.com/asomers/mockall/blob/master/mockall/tests/mock_struct_with_static_method.rs
699        let _i = IDENT_MTX.lock();
700        let _e = EPC_MTX.lock();
701
702        // stubs
703        let indent_ctx = MockTestRef::ident_context();
704        indent_ctx.expect().returning(|| "TestRef".to_string());
705
706        let epc_ctx = MockTestRef::entry_points_caller_context();
707        epc_ctx
708            .expect()
709            .returning(|h| EntryPointsCaller::new(h.clone(), vec![], |_, _| Ok(Bytes::default())));
710
711        // check if TestRef::new() is called exactly once
712        let instance_ctx = MockTestRef::new_context();
713        instance_ctx
714            .expect()
715            .times(1)
716            .returning(|_, _| MockTestRef::default());
717
718        let mut ctx = MockHostContext::new();
719        ctx.expect_new_contract()
720            .returning(|_, _, _| Ok(Address::Account(AccountHash::new([0; 32]))));
721        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
722        MockTestRef::deploy(&env, NoArgs);
723    }
724
725    #[test]
726    fn test_load_ref() {
727        // MockTestRef::ident() and MockTestRef::entry_points_caller() are static and can't be safely used
728        // from multiple tests at the same time. Should be to protected with a Mutex. Each function has
729        // a separate Mutex.
730        // https://github.com/asomers/mockall/blob/master/mockall/tests/mock_struct_with_static_method.rs
731        let _e = EPC_MTX.lock();
732        let _i = IDENT_MTX.lock();
733
734        // stubs
735        let epc_ctx = MockTestRef::entry_points_caller_context();
736        epc_ctx
737            .expect()
738            .returning(|h| EntryPointsCaller::new(h.clone(), vec![], |_, _| Ok(Bytes::default())));
739        let indent_ctx = MockTestRef::ident_context();
740        indent_ctx.expect().returning(|| "TestRef".to_string());
741
742        let mut ctx = MockHostContext::new();
743        ctx.expect_register_contract().returning(|_, _, _| ());
744        ctx.expect_get_events_count().returning(|_| 0);
745
746        // check if TestRef::new() is called exactly once
747        let instance_ctx = MockTestRef::new_context();
748        instance_ctx
749            .expect()
750            .times(1)
751            .returning(|_, _| MockTestRef::default());
752
753        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
754        let address = Address::Account(AccountHash::new([0; 32]));
755        MockTestRef::load(&env, address);
756    }
757
758    #[test]
759    fn test_host_env() {
760        let mut ctx = MockHostContext::new();
761        ctx.expect_new_contract()
762            .returning(|_, _, _| Ok(Address::Account(AccountHash::new([0; 32]))));
763        ctx.expect_caller()
764            .returning(|| Address::Account(AccountHash::new([2; 32])))
765            .times(1);
766        ctx.expect_gas_report().returning(GasReport::new).times(1);
767        ctx.expect_set_gas().returning(|_| ()).times(1);
768
769        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
770
771        assert_eq!(env.caller(), Address::Account(AccountHash::new([2; 32])));
772        // should call the `HostContext`
773        env.gas_report();
774        env.set_gas(1_000u64)
775    }
776
777    #[test]
778    fn test_successful_transfer_to_account() {
779        // Given a host context that successfully transfers tokens.
780        let mut ctx = MockHostContext::new();
781        ctx.expect_transfer().returning(|_, _| Ok(()));
782        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
783
784        let addr = Address::Account(AccountHash::new([0; 32]));
785        // When transfer 100 tokens to an account.
786        let result = env.transfer(addr, 100.into());
787        // Then the transfer should be successful.
788        assert!(result.is_ok());
789    }
790
791    #[test]
792    fn test_failing_transfer_to_account() {
793        // Given a host context that fails to transfer tokens.
794        let mut ctx = MockHostContext::new();
795        ctx.expect_transfer()
796            .returning(|_, _| Err(OdraError::ExecutionError(ExecutionError::UnwrapError)));
797        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
798
799        let addr = Address::Account(AccountHash::new([0; 32]));
800        // When transfer 100 tokens to an account.
801        let result = env.transfer(addr, 100.into());
802        // Then the transfer should fail.
803        assert_eq!(
804            result.err(),
805            Some(OdraError::ExecutionError(ExecutionError::UnwrapError))
806        );
807    }
808
809    #[test]
810    fn test_transfer_to_contract() {
811        // Given a host context that successfully transfers tokens.
812        let mut ctx = MockHostContext::new();
813        ctx.expect_transfer().returning(|_, _| Ok(()));
814        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
815
816        let addr = Address::Contract(ContractPackageHash::new([0; 32]));
817        // When transfer 100 tokens to a contract.
818        let result = env.transfer(addr, 100.into());
819        // Then the transfer should fail.
820        assert_eq!(
821            result,
822            Err(OdraError::ExecutionError(
823                ExecutionError::TransferToContract
824            ))
825        );
826    }
827
828    #[test]
829    fn test_get_event() {
830        let addr = Address::Account(AccountHash::new([0; 32]));
831
832        let mut ctx = MockHostContext::new();
833        // there are 2 events emitted by the contract
834        ctx.expect_get_events_count().returning(|_| 2);
835        // get_event() at index 0 will return an invalid event
836        ctx.expect_get_event()
837            .with(predicate::always(), predicate::eq(0))
838            .returning(|_, _| Ok(vec![1].into()));
839        // get_event() at index 1 will return an valid event
840        ctx.expect_get_event()
841            .with(predicate::always(), predicate::eq(1))
842            .returning(|_, _| Ok(TestEv {}.to_bytes().unwrap().into()));
843
844        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
845
846        assert_eq!(env.get_event(&addr, 1), Ok(TestEv {}));
847        assert_eq!(env.get_event(&addr, -1), Ok(TestEv {}));
848        assert_eq!(
849            env.get_event::<TestEv, _>(&addr, 0),
850            Err(EventError::Parsing)
851        );
852        assert_eq!(
853            env.get_event::<TestEv, _>(&addr, -2),
854            Err(EventError::Parsing)
855        );
856        assert_eq!(
857            env.get_event::<TestEv, _>(&addr, 2),
858            Err(EventError::IndexOutOfBounds)
859        );
860        assert_eq!(
861            env.get_event::<TestEv, _>(&addr, -3),
862            Err(EventError::IndexOutOfBounds)
863        );
864    }
865
866    #[test]
867    fn test_events_works() {
868        let addr = Address::Account(AccountHash::new([0; 32]));
869
870        let mut ctx = MockHostContext::new();
871        // there are 2 events emitted by the contract
872        ctx.expect_get_events_count().returning(|_| 2);
873        // get_event() at index 0 will return an invalid event
874        ctx.expect_get_event()
875            .with(predicate::always(), predicate::eq(0))
876            .returning(|_, _| Ok(vec![1].into()));
877        // get_event() at index 1 will return an valid event
878        ctx.expect_get_event()
879            .with(predicate::always(), predicate::eq(1))
880            .returning(|_, _| Ok(vec![1, 0, 1].into()));
881
882        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
883
884        assert_eq!(
885            env.events(&addr),
886            vec![vec![1].into(), vec![1, 0, 1].into()]
887        );
888    }
889
890    #[test]
891    #[should_panic(
892        expected = "Couldn't get event at address Account(AccountHash(0000000000000000000000000000000000000000000000000000000000000000)) with id 0: CouldntExtractEventData"
893    )]
894    fn test_events_fails() {
895        let addr = Address::Account(AccountHash::new([0; 32]));
896
897        let mut ctx = MockHostContext::new();
898        // there are 2 events emitted by the contract
899        ctx.expect_get_events_count().returning(|_| 2);
900        // get_event() at index 0 panics
901        ctx.expect_get_event()
902            .with(predicate::always(), predicate::eq(0))
903            .returning(|_, _| Err(EventError::CouldntExtractEventData));
904
905        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
906
907        env.events(&addr);
908    }
909
910    #[test]
911    fn test_emitted() {
912        let addr = Address::Account(AccountHash::new([0; 32]));
913        let mut ctx = MockHostContext::new();
914
915        ctx.expect_get_events_count().returning(|_| 1);
916        ctx.expect_get_event()
917            .returning(|_, _| Ok(TestEv {}.to_bytes().unwrap().into()));
918
919        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
920        assert!(env.emitted(&addr, "TestEv"));
921        assert!(!env.emitted(&addr, "AnotherEvent"));
922    }
923}