odra_core/
host.rs

1//! A module that provides the interface for interacting with the host environment.
2
3mod deployed_contracts;
4
5use crate::address::Addressable;
6use crate::gas_report::GasReport;
7use crate::host::deployed_contracts::DeployedContract;
8use crate::{
9    call_result::CallResult, entry_point_callback::EntryPointsCaller, CallDef, ContractCallResult,
10    ContractEnv, EventError, VmError
11};
12#[cfg(not(target_arch = "wasm32"))]
13use crate::{consts, contract::OdraContract, contract_def::HasIdent};
14use crate::{prelude::*, utils};
15use casper_event_standard::EventInstance;
16use casper_types::{
17    bytesrepr::{Bytes, FromBytes, ToBytes},
18    CLTyped, PublicKey, RuntimeArgs, U512
19};
20
21/// A host side reference to a contract.
22pub trait HostRef {
23    /// Creates a new host side reference to a contract.
24    fn new(address: Address, env: HostEnv) -> Self;
25    /// Creates a new host reference with attached tokens, based on the current instance.
26    ///
27    /// If there are tokens attached to the current instance, the tokens will be attached
28    /// to the next contract call.
29    fn with_tokens(&self, tokens: U512) -> Self;
30    /// Returns the address of the contract.
31    fn contract_address(&self) -> Address;
32    /// Returns the host environment.
33    fn env(&self) -> &HostEnv;
34    /// Returns the n-th event emitted by the contract.
35    ///
36    /// If the event is not found or the type does not match, returns `EventError::EventNotFound`.
37    fn get_event<T>(&self, index: i32) -> Result<T, EventError>
38    where
39        T: FromBytes + EventInstance + 'static;
40    /// Returns a detailed information about the last call of the contract.
41    fn last_call(&self) -> ContractCallResult;
42}
43
44impl<T: HostRef> Addressable for T {
45    fn address(&self) -> Address {
46        HostRef::contract_address(self)
47    }
48}
49
50/// Trait for loading a contract from the host environment.
51///
52/// Similar to [Deployer], but does not deploy a new contract, but loads an existing one.
53pub trait HostRefLoader<T: HostRef> {
54    /// Loads an existing contract from the host environment.
55    fn load(env: &HostEnv, address: Address) -> T;
56}
57
58/// A type which can provide an [EntryPointsCaller].
59pub trait EntryPointsCallerProvider {
60    /// Returns an [EntryPointsCaller] for the given host environment.
61    fn entry_points_caller(env: &HostEnv) -> EntryPointsCaller;
62}
63
64/// A type which can deploy a contract.
65///
66/// Before any interaction with the contract, it must be deployed, either
67/// on a virtual machine or on a real blockchain.
68///
69/// The `Deployer` trait provides a simple way to deploy a contract.
70#[cfg(not(target_arch = "wasm32"))]
71pub trait Deployer<R: OdraContract>: Sized {
72    /// Deploys a contract with given init args.
73    ///
74    /// If the `init_args` is not [NoArgs], the contract is deployed and initialized
75    /// by calling the constructor. Otherwise no constructor is called.
76    ///
77    /// The default [OdraConfig] is used for deployment.
78    ///
79    /// Returns a host reference to the deployed contract.
80    fn deploy(env: &HostEnv, init_args: R::InitArgs) -> R::HostRef;
81
82    /// Tries to deploy a contract with given init args.
83    ///
84    /// Similar to `deploy`, but returns a result instead of panicking.
85    fn try_deploy(env: &HostEnv, init_args: R::InitArgs) -> OdraResult<R::HostRef>;
86
87    /// Deploys a contract with given init args and configuration.
88    ///
89    /// Returns a host reference to the deployed contract.
90    fn deploy_with_cfg(env: &HostEnv, init_args: R::InitArgs, cfg: InstallConfig) -> R::HostRef;
91
92    /// Tries to deploy a contract with given init args and configuration.
93    ///
94    /// Similar to `deploy_with_cfg`, but returns a result instead of panicking.
95    fn try_deploy_with_cfg(
96        env: &HostEnv,
97        init_args: R::InitArgs,
98        cfg: InstallConfig
99    ) -> OdraResult<R::HostRef>;
100
101    /// Tries to upgrade a contract with given init args.
102    fn try_upgrade(
103        env: &HostEnv,
104        address: Address,
105        init_args: R::UpgradeArgs
106    ) -> OdraResult<R::HostRef>;
107
108    /// Tries to upgrade a contract with given init args and configuration
109    fn try_upgrade_with_cfg(
110        env: &HostEnv,
111        address: Address,
112        upgrade_args: R::UpgradeArgs,
113        cfg: UpgradeConfig
114    ) -> OdraResult<R::HostRef>;
115}
116
117/// A type which can be used as initialization arguments for a contract.
118pub trait InitArgs: Into<RuntimeArgs> {}
119/// A type which can be used as upgrade arguments for a contract.
120pub trait UpgradeArgs: Into<RuntimeArgs> {}
121
122/// Default implementation of [InitArgs]. Should be used when the contract
123/// does not require initialization arguments.
124///
125/// Precisely, it means the constructor function has not been defined,
126/// or does not require any arguments.
127pub struct NoArgs;
128
129impl InitArgs for NoArgs {}
130
131impl UpgradeArgs for NoArgs {}
132
133impl From<NoArgs> for RuntimeArgs {
134    fn from(_: NoArgs) -> Self {
135        RuntimeArgs::new()
136    }
137}
138
139/// A configuration for a contract.
140///
141/// The configuration every contract written in Odra expects.
142/// Read more: [https://odra.dev/docs/backends/casper/#wasm-arguments]
143#[cfg(not(target_arch = "wasm32"))]
144pub struct InstallConfig {
145    /// Returns the package hash of the contract.
146    ///
147    /// Used to set the `odra_cfg_package_hash_key_name` key at the contract initialization.
148    pub package_named_key: String,
149    /// Returns true if the contract should be deployed as upgradable.
150    ///
151    /// If true, the `odra_cfg_is_upgradable` key is set to `true` at the contract initialization.
152    pub is_upgradable: bool,
153    /// If true and the key `odra_cfg_package_hash_key_name` already exists, it should be overwritten.
154    pub allow_key_override: bool
155}
156
157/// A configuration for upgrading contract.
158///
159/// The configuration every contract upgrade written in Odra expects.
160/// Read more: [https://odra.dev/docs/backends/casper/#wasm-arguments]
161#[cfg(not(target_arch = "wasm32"))]
162pub struct UpgradeConfig {
163    /// Returns the package hash of the contract.
164    ///
165    /// Used to set the `odra_cfg_package_hash_key_name` key at the contract initialization.
166    pub package_named_key: String,
167    /// Create a new upgrade group for the contract. Set it to `true` if you want to upgrade a contract
168    /// Which was deployed using Odra 2.2 or earlier, or not using Odra at all.
169    pub force_create_upgrade_group: bool,
170    /// If true and the key `odra_cfg_package_hash_key_name` already exists, it should be overwritten.
171    pub allow_key_override: bool
172}
173
174#[cfg(not(target_arch = "wasm32"))]
175impl InstallConfig {
176    /// Returns new InstallConfig
177    pub fn new<T: HasIdent>(is_upgradable: bool, allow_key_override: bool) -> Self {
178        InstallConfig {
179            package_named_key: T::ident(),
180            is_upgradable,
181            allow_key_override
182        }
183    }
184
185    /// Returns new InstallConfig configured for default upgradable contract
186    pub fn upgradable<T: HasIdent>() -> Self {
187        InstallConfig::new::<T>(true, true)
188    }
189}
190
191#[cfg(not(target_arch = "wasm32"))]
192impl UpgradeConfig {
193    /// Returns new UpgradeConfig with default values.
194    /// It is by default upgradable and allows key override.
195    pub fn new<T: HasIdent>() -> Self {
196        UpgradeConfig {
197            package_named_key: T::ident(),
198            force_create_upgrade_group: false,
199            allow_key_override: true
200        }
201    }
202}
203
204#[cfg(not(target_arch = "wasm32"))]
205impl<R: OdraContract> Deployer<R> for R {
206    fn deploy(
207        env: &HostEnv,
208        init_args: <R as OdraContract>::InitArgs
209    ) -> <R as OdraContract>::HostRef {
210        let contract_ident = R::HostRef::ident();
211        match Self::try_deploy(env, init_args) {
212            Ok(contract) => contract,
213            Err(OdraError::ExecutionError(ExecutionError::MissingArg)) => {
214                core::panic!("Invalid init args for contract {}.", contract_ident)
215            }
216            Err(e) => core::panic!("Contract init failed {:?}", e)
217        }
218    }
219
220    fn try_deploy(
221        env: &HostEnv,
222        init_args: <R as OdraContract>::InitArgs
223    ) -> OdraResult<<R as OdraContract>::HostRef> {
224        Self::try_deploy_with_cfg(
225            env,
226            init_args,
227            InstallConfig::new::<<R as OdraContract>::HostRef>(false, true)
228        )
229    }
230
231    fn deploy_with_cfg(
232        env: &HostEnv,
233        init_args: <R as OdraContract>::InitArgs,
234        cfg: InstallConfig
235    ) -> <R as OdraContract>::HostRef {
236        let contract_ident = R::HostRef::ident();
237        match Self::try_deploy_with_cfg(env, init_args, cfg) {
238            Ok(contract) => contract,
239            Err(OdraError::ExecutionError(ExecutionError::MissingArg)) => {
240                core::panic!("Invalid init args for contract {}.", contract_ident)
241            }
242            Err(e) => core::panic!("Contract init failed {:?}", e)
243        }
244    }
245
246    fn try_deploy_with_cfg(
247        env: &HostEnv,
248        init_args: <R as OdraContract>::InitArgs,
249        cfg: InstallConfig
250    ) -> OdraResult<<R as OdraContract>::HostRef> {
251        let contract_ident = R::HostRef::ident();
252        let caller = R::HostRef::entry_points_caller(env);
253
254        let mut init_args = init_args.into();
255        init_args.insert(consts::IS_UPGRADABLE_ARG, cfg.is_upgradable)?;
256        init_args.insert(consts::IS_UPGRADE_ARG, false)?;
257        init_args.insert(consts::ALLOW_KEY_OVERRIDE_ARG, cfg.allow_key_override)?;
258        init_args.insert(
259            consts::PACKAGE_HASH_KEY_NAME_ARG,
260            format!("{}_package_hash", cfg.package_named_key)
261        )?;
262
263        let address = env.new_contract(&contract_ident, init_args, caller)?;
264        Ok(R::HostRef::new(address, env.clone()))
265    }
266
267    fn try_upgrade(
268        env: &HostEnv,
269        contract_to_upgrade: Address,
270        upgrade_args: <R as OdraContract>::UpgradeArgs
271    ) -> OdraResult<<R as OdraContract>::HostRef> {
272        Self::try_upgrade_with_cfg(
273            env,
274            contract_to_upgrade,
275            upgrade_args,
276            UpgradeConfig::new::<<R as OdraContract>::HostRef>()
277        )
278    }
279
280    fn try_upgrade_with_cfg(
281        env: &HostEnv,
282        contract_to_upgrade: Address,
283        upgrade_args: <R as OdraContract>::UpgradeArgs,
284        cfg: UpgradeConfig
285    ) -> OdraResult<<R as OdraContract>::HostRef> {
286        let mut upgrade_args = upgrade_args.into();
287        upgrade_args.insert(consts::IS_UPGRADE_ARG, true)?;
288        upgrade_args.insert(
289            consts::PACKAGE_HASH_TO_UPGRADE_ARG,
290            contract_to_upgrade.value()
291        )?;
292        upgrade_args.insert(consts::ALLOW_KEY_OVERRIDE_ARG, cfg.allow_key_override)?;
293        upgrade_args.insert(
294            consts::PACKAGE_HASH_KEY_NAME_ARG,
295            format!("{}_package_hash", cfg.package_named_key)
296        )?;
297        upgrade_args.insert(consts::CREATE_UPGRADE_GROUP, cfg.force_create_upgrade_group)?;
298        let contract_ident = R::HostRef::ident();
299        let entry_points_caller = R::HostRef::entry_points_caller(env);
300
301        let address = env.upgrade_contract(
302            &contract_ident,
303            contract_to_upgrade,
304            upgrade_args,
305            entry_points_caller
306        )?;
307        Ok(HostRef::new(address, env.clone()))
308    }
309}
310
311#[cfg(not(target_arch = "wasm32"))]
312impl<T: OdraContract> HostRefLoader<T::HostRef> for T {
313    fn load(env: &HostEnv, address: Address) -> T::HostRef {
314        let caller = T::HostRef::entry_points_caller(env);
315        let contract_name = T::HostRef::ident();
316        env.register_contract(address, contract_name, caller);
317        T::HostRef::new(address, env.clone())
318    }
319}
320
321/// The `HostContext` trait defines the interface for interacting with the host environment.
322#[cfg_attr(test, mockall::automock)]
323pub trait HostContext {
324    /// Sets the caller address for the current contract execution.
325    fn set_caller(&self, caller: Address);
326
327    /// Sets the gas limit for the current contract execution.
328    fn set_gas(&self, gas: u64);
329
330    /// Returns the caller address for the current contract execution.
331    fn caller(&self) -> Address;
332
333    /// Returns the account address at the specified index.
334    fn get_account(&self, index: usize) -> Address;
335
336    /// Returns the validator public key.
337    fn get_validator(&self, index: usize) -> PublicKey;
338
339    /// The validator at the given index will withdraw all funds and be removed from the validator set.
340    fn remove_validator(&self, index: usize);
341
342    /// Returns the CSPR balance of the specified address.
343    fn balance_of(&self, address: &Address) -> U512;
344
345    /// Advances the block time by the specified time difference.
346    fn advance_block_time(&self, time_diff: u64);
347
348    /// Advances the block time by the specified time difference and processes auctions.
349    fn advance_with_auctions(&self, time_diff: u64);
350
351    /// Time between auctions in milliseconds.
352    fn auction_delay(&self) -> u64;
353
354    /// Time for the funds to be transferred back to the delegator after undelegation in milliseconds.
355    fn unbonding_delay(&self) -> u64;
356
357    /// Returns the delegated amount for the specified delegator and validator.
358    fn delegated_amount(&self, delegator: Address, validator: PublicKey) -> U512;
359
360    /// Returns the current block time.
361    fn block_time(&self) -> u64;
362
363    /// Returns the event bytes for the specified contract address and index.
364    fn get_event(&self, contract_address: &Address, index: u32) -> Result<Bytes, EventError>;
365
366    /// Returns the native event bytes for the specified contract address and index.
367    fn get_native_event(&self, contract_address: &Address, index: u32)
368        -> Result<Bytes, EventError>;
369
370    /// Returns the number of emitted events for the specified contract address.
371    fn get_events_count(&self, contract_address: &Address) -> Result<u32, EventError>;
372
373    /// Returns the number of emitted native events for the specified contract address.
374    fn get_native_events_count(&self, contract_address: &Address) -> Result<u32, EventError>;
375
376    /// Calls a contract at the specified address with the given call definition.
377    fn call_contract(
378        &self,
379        address: &Address,
380        call_def: CallDef,
381        use_proxy: bool
382    ) -> OdraResult<Bytes>;
383
384    /// Creates a new contract with the specified name, initialization arguments, and entry points caller.
385    fn new_contract(
386        &self,
387        name: &str,
388        init_args: RuntimeArgs,
389        entry_points_caller: EntryPointsCaller
390    ) -> OdraResult<Address>;
391
392    /// Upgrades an existing contract with a new one with given upgrade arguments and new entry
393    /// points caller.
394    fn upgrade_contract(
395        &self,
396        name: &str,
397        contract_to_upgrade: Address,
398        upgrade_args: RuntimeArgs,
399        entry_points_caller: EntryPointsCaller
400    ) -> OdraResult<Address>;
401
402    /// Registers an existing contract with the specified address, name, and entry points caller.
403    fn register_contract(
404        &self,
405        address: Address,
406        contract_name: String,
407        entry_points_caller: EntryPointsCaller
408    );
409
410    /// Returns the contract environment.
411    fn contract_env(&self) -> ContractEnv;
412
413    /// Returns the gas report for the current contract execution.
414    fn gas_report(&self) -> GasReport;
415
416    /// Returns the gas cost of the last contract call.
417    fn last_call_gas_cost(&self) -> u64;
418
419    /// Signs the specified message with the given address and returns the signature.
420    fn sign_message(&self, message: &Bytes, address: &Address) -> Bytes;
421
422    /// Returns the public key associated with the specified address.
423    fn public_key(&self, address: &Address) -> PublicKey;
424
425    /// Transfers the specified amount of CSPR from the current caller to the specified address.
426    fn transfer(&self, to: Address, amount: U512) -> OdraResult<()>;
427}
428
429/// Represents the host environment for executing smart contracts.
430///
431/// It provides methods for interacting with the underlying host context and managing
432/// the execution of contracts.
433#[derive(Clone)]
434pub struct HostEnv {
435    backend: Rc<RefCell<dyn HostContext>>,
436    last_call_result: Rc<RefCell<Option<CallResult>>>,
437    deployed_contracts: Rc<RefCell<BTreeMap<Address, DeployedContract>>>,
438    captures_events: Rc<RefCell<bool>>
439}
440
441impl HostEnv {
442    /// Creates a new `HostEnv` instance with the specified backend.
443    pub fn new(backend: Rc<RefCell<dyn HostContext>>) -> HostEnv {
444        HostEnv {
445            backend,
446            last_call_result: RefCell::new(None).into(),
447            deployed_contracts: RefCell::new(Default::default()).into(),
448            captures_events: Rc::new(RefCell::new(true))
449        }
450    }
451
452    /// Sets the `captures_events` flag, which determines whether events should be captured.
453    pub fn set_captures_events(&self, captures: bool) {
454        *self.captures_events.borrow_mut() = captures;
455        if captures {
456            // Initialize events for all deployed contracts if capturing is enabled
457            let contract_addresses: Vec<Address> =
458                self.deployed_contracts.borrow().keys().copied().collect();
459
460            for contract_address in contract_addresses {
461                self.init_events(&contract_address);
462            }
463        }
464    }
465
466    /// Returns the account address at the specified index.
467    pub fn get_account(&self, index: usize) -> Address {
468        let backend = self.backend.borrow();
469        backend.get_account(index)
470    }
471
472    /// Returns the validator public key.
473    pub fn get_validator(&self, index: usize) -> PublicKey {
474        let backend = self.backend.borrow();
475        backend.get_validator(index)
476    }
477
478    /// Sets the caller address for the current contract execution.
479    pub fn set_caller(&self, address: Address) {
480        if address.is_contract() {
481            panic!("Caller cannot be a contract: {:?}", address)
482        }
483        let backend = self.backend.borrow();
484        backend.set_caller(address)
485    }
486
487    /// Advances the block time by the specified time difference in milliseconds.
488    pub fn advance_block_time(&self, time_diff: u64) {
489        let backend = self.backend.borrow();
490        backend.advance_block_time(time_diff)
491    }
492
493    /// Advances the block time by the specified time difference in milliseconds
494    /// and processes auctions.
495    pub fn advance_with_auctions(&self, time_diff: u64) {
496        let backend = self.backend.borrow();
497        backend.advance_with_auctions(time_diff);
498    }
499
500    /// Returns the era length in milliseconds.
501    pub fn auction_delay(&self) -> u64 {
502        let backend = self.backend.borrow();
503        backend.auction_delay()
504    }
505
506    /// Returns the delay between unstaking and the transfer of funds back to the delegator in milliseconds.
507    pub fn unbonding_delay(&self) -> u64 {
508        let backend = self.backend.borrow();
509        backend.unbonding_delay()
510    }
511
512    /// Returns the amount of CSPR delegated to the specified validator by the specified delegator.
513    pub fn delegated_amount(&self, delegator: Address, validator: PublicKey) -> U512 {
514        let backend = self.backend.borrow();
515        backend.delegated_amount(delegator, validator)
516    }
517
518    /// Evicts the validator at the specified index from the validator set.
519    pub fn remove_validator(&self, index: usize) {
520        let backend = self.backend.borrow();
521        backend.remove_validator(index);
522    }
523
524    /// Returns the current block time in milliseconds.
525    pub fn block_time(&self) -> u64 {
526        let backend = self.backend.borrow();
527        backend.block_time()
528    }
529
530    /// Returns the current block time in milliseconds.
531    pub fn block_time_millis(&self) -> u64 {
532        let backend = self.backend.borrow();
533        backend.block_time()
534    }
535
536    /// Returns the current block time in seconds.
537    pub fn block_time_secs(&self) -> u64 {
538        let backend = self.backend.borrow();
539        backend.block_time().checked_div(1000).unwrap()
540    }
541
542    /// Registers a new contract with the specified name, initialization arguments, and entry points caller.
543    pub fn new_contract(
544        &self,
545        name: &str,
546        init_args: RuntimeArgs,
547        entry_points_caller: EntryPointsCaller
548    ) -> OdraResult<Address> {
549        // Filter "upgrade" from EntryPointsCaller
550        let mut entry_points_caller = entry_points_caller.clone();
551        entry_points_caller.remove_entry_point("upgrade");
552
553        let backend = self.backend.borrow();
554        let contract_address = backend.new_contract(name, init_args, entry_points_caller)?;
555
556        self.deployed_contracts
557            .borrow_mut()
558            .insert(contract_address, DeployedContract::new(contract_address));
559        Ok(contract_address)
560    }
561
562    /// Upgrades an existing contract with a new one with given upgrade arguments and new entry
563    /// points caller.
564    pub fn upgrade_contract(
565        &self,
566        name: &str,
567        contract_to_upgrade: Address,
568        upgrade_args: RuntimeArgs,
569        entry_points_caller: EntryPointsCaller
570    ) -> OdraResult<Address> {
571        // Filter "init" from EntryPointsCaller
572        let mut entry_points_caller = entry_points_caller.clone();
573        entry_points_caller.remove_entry_point("init");
574
575        let backend = self.backend.borrow();
576        let upgraded_contract = backend.upgrade_contract(
577            name,
578            contract_to_upgrade,
579            upgrade_args,
580            entry_points_caller
581        )?;
582        let mut contracts = self.deployed_contracts.borrow_mut();
583        let contract = contracts.get_mut(&upgraded_contract).unwrap();
584        contract.current_version += 1;
585        // CES events are intact, but native events are connected to a contract, not a package.
586        contract.native_events_count = 0;
587        Ok(upgraded_contract)
588    }
589
590    /// Registers an existing contract with the specified address, name and entry points caller.
591    /// Similar to `new_contract`, but skips the deployment phase.
592    pub fn register_contract(
593        &self,
594        address: Address,
595        contract_name: String,
596        entry_points_caller: EntryPointsCaller
597    ) {
598        let backend = self.backend.borrow();
599        backend.register_contract(address, contract_name, entry_points_caller);
600        self.deployed_contracts
601            .borrow_mut()
602            .insert(address, DeployedContract::new(address));
603    }
604
605    /// Calls a contract at the specified address with the given call definition.
606    pub fn call_contract<T: FromBytes + CLTyped>(
607        &self,
608        address: Address,
609        call_def: CallDef
610    ) -> OdraResult<T> {
611        let use_proxy = T::cl_type() != <()>::cl_type() || !call_def.amount().is_zero();
612        let call_result = self.raw_call_contract(address, call_def, use_proxy);
613        call_result.map(|bytes| {
614            T::from_bytes(&bytes)
615                .map(|(obj, _)| obj)
616                .map_err(|_| OdraError::VmError(VmError::Deserialization))
617        })?
618    }
619
620    /// Calls a contract at the specified address with the given call definition. Returns raw,
621    /// not serialized bytes.
622    pub fn raw_call_contract(
623        &self,
624        address: Address,
625        call_def: CallDef,
626        use_proxy: bool
627    ) -> OdraResult<Bytes> {
628        let call_result = {
629            let backend = self.backend.borrow();
630            backend.call_contract(&address, call_def, use_proxy)
631        };
632
633        let mut events_map: BTreeMap<Address, Vec<Bytes>> = BTreeMap::new();
634        let mut native_events_map: BTreeMap<Address, Vec<Bytes>> = BTreeMap::new();
635
636        let captures_events = *self.captures_events.borrow();
637        if captures_events {
638            // Go through all contracts and collect their events
639            self.deployed_contracts.borrow_mut().iter_mut().for_each(
640                |(contract_address, contract)| {
641                    let events = self.last_events(contract);
642                    let native_events = self.last_native_events(contract);
643                    events_map.insert(*contract_address, events);
644                    native_events_map.insert(*contract_address, native_events);
645                }
646            );
647        }
648
649        let backend = self.backend.borrow();
650        let last_call_gas_cost = backend.last_call_gas_cost();
651
652        self.last_call_result.replace(Some(CallResult::new(
653            address,
654            backend.caller(),
655            last_call_gas_cost,
656            call_result.clone(),
657            events_map,
658            native_events_map
659        )));
660
661        call_result
662    }
663
664    /// Returns the gas cost of the last contract call.
665    pub fn contract_env(&self) -> ContractEnv {
666        self.backend.borrow().contract_env()
667    }
668
669    /// Prints the gas report for the current contract execution.
670    pub fn gas_report(&self) -> GasReport {
671        self.backend.borrow().gas_report().clone()
672    }
673
674    /// Returns the CSPR balance of the specified address.
675    pub fn balance_of<T: Addressable>(&self, addr: &T) -> U512 {
676        let backend = self.backend.borrow();
677        backend.balance_of(&addr.address())
678    }
679
680    /// Retrieves an event with the specified index from the specified contract.
681    ///
682    /// # Returns
683    ///
684    /// Returns the event as an instance of the specified type, or an error if the event
685    /// couldn't be retrieved or parsed.
686    pub fn get_event<T: FromBytes + EventInstance, R: Addressable>(
687        &self,
688        addr: &R,
689        index: i32
690    ) -> Result<T, EventError> {
691        let contract_address = addr.address();
692        let backend = self.backend.borrow();
693        let events_count = self.events_count(&contract_address);
694        let event_absolute_position = crate::utils::event_absolute_position(events_count, index)
695            .ok_or(EventError::IndexOutOfBounds)?;
696
697        let bytes = backend.get_event(&contract_address, event_absolute_position)?;
698        let (event, remainder) = T::from_bytes(&bytes).map_err(|_| EventError::Parsing)?;
699
700        if remainder.is_empty() {
701            Ok(event)
702        } else {
703            Err(EventError::Formatting)
704        }
705    }
706
707    /// Retrieves a native event with the specified index from the specified contract.
708    ///
709    /// # Returns
710    ///
711    /// Returns the event as an instance of the specified type, or an error if the event
712    /// couldn't be retrieved or parsed.
713    pub fn get_native_event<T: FromBytes + EventInstance, R: Addressable>(
714        &self,
715        addr: &R,
716        index: i32
717    ) -> Result<T, EventError> {
718        let contract_address = addr.address();
719        let backend = self.backend.borrow();
720        let events_count = self.native_events_count(&contract_address);
721        let event_absolute_position = crate::utils::event_absolute_position(events_count, index)
722            .ok_or(EventError::IndexOutOfBounds)?;
723
724        let bytes = backend.get_native_event(&contract_address, event_absolute_position)?;
725        T::from_bytes(&bytes)
726            .map_err(|_| EventError::Parsing)
727            .map(|r| r.0)
728    }
729
730    /// Retrieves a raw event (serialized) with the specified index from the specified contract.
731    pub fn get_event_bytes<T: Addressable>(
732        &self,
733        addr: &T,
734        index: u32
735    ) -> Result<Bytes, EventError> {
736        let backend = self.backend.borrow();
737        backend.get_event(&addr.address(), index)
738    }
739
740    /// Retrieves a raw native event (serialized) with the specified index from the specified contract.
741    pub fn get_native_event_bytes<T: Addressable>(
742        &self,
743        addr: &T,
744        index: u32
745    ) -> Result<Bytes, EventError> {
746        let backend = self.backend.borrow();
747        backend.get_native_event(&addr.address(), index)
748    }
749
750    /// Returns the names of all events emitted by the specified contract.
751    pub fn event_names<T: Addressable>(&self, addr: &T) -> Vec<String> {
752        let events_count = self.events_count(addr);
753
754        let backend = self.backend.borrow();
755        (0..events_count)
756            .map(|event_id| {
757                backend
758                    .get_event(&addr.address(), event_id)
759                    .and_then(|bytes| utils::extract_event_name(&bytes))
760                    .unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
761            })
762            .collect()
763    }
764
765    /// Returns all events emitted by the specified contract.
766    pub fn events<T: Addressable>(&self, addr: &T) -> Vec<Bytes> {
767        let backend = self.backend.borrow();
768        let contract_address = addr.address();
769        let events_count = backend
770            .get_events_count(&contract_address)
771            .unwrap_or_default();
772        (0..events_count)
773            .map(|event_id| {
774                backend
775                    .get_event(&contract_address, event_id)
776                    .unwrap_or_else(|e| {
777                        panic!(
778                            "Couldn't get event at address {:?} with id {}: {:?}",
779                            &contract_address, event_id, e
780                        )
781                    })
782            })
783            .collect()
784    }
785
786    /// Returns the number of events emitted by the specified contract.
787    pub fn events_count<T: Addressable>(&self, addr: &T) -> u32 {
788        let backend = self.backend.borrow();
789        backend
790            .get_events_count(&addr.address())
791            .unwrap_or_default()
792    }
793
794    /// Returns the number of native events emitted by the specified contract.
795    pub fn native_events_count<T: Addressable>(&self, addr: &T) -> u32 {
796        let backend = self.backend.borrow();
797        backend
798            .get_native_events_count(&addr.address())
799            .unwrap_or_default()
800    }
801
802    /// Returns true if the specified event was emitted by the specified contract.
803    pub fn emitted_event<T: ToBytes + EventInstance, R: Addressable>(
804        &self,
805        addr: &R,
806        event: T
807    ) -> bool {
808        let contract_address = addr.address();
809        let events_count = self.events_count(addr);
810
811        let event_bytes = Bytes::from(
812            event
813                .to_bytes()
814                .unwrap_or_else(|_| panic!("Couldn't serialize event"))
815        );
816
817        (0..events_count)
818            .map(|event_id| {
819                self.get_event_bytes(&contract_address, event_id)
820                    .unwrap_or_else(|e| {
821                        panic!(
822                            "Couldn't get event at address {:?} with id {}: {:?}",
823                            &contract_address, event_id, e
824                        )
825                    })
826            })
827            .any(|bytes| bytes == event_bytes)
828    }
829
830    /// Returns true if the specified event was emitted by the specified contract.
831    pub fn emitted_native_event<T: ToBytes + EventInstance, R: Addressable>(
832        &self,
833        addr: &R,
834        event: T
835    ) -> bool {
836        let contract_address = addr.address();
837        let events_count = self.native_events_count(addr);
838        if events_count > 0 {
839            let event_bytes = Bytes::from(
840                event
841                    .to_bytes()
842                    .unwrap_or_else(|_| panic!("Couldn't serialize event"))
843            );
844            (0..events_count)
845                .map(|event_id| {
846                    self.get_native_event_bytes(&contract_address, event_id)
847                        .unwrap_or_else(|e| {
848                            panic!(
849                                "Couldn't get event at address {:?} with id {}: {:?}",
850                                &contract_address, event_id, e
851                            )
852                        })
853                })
854                .any(|bytes| bytes == event_bytes)
855        } else {
856            false
857        }
858    }
859
860    /// Returns true if an event with the specified name was emitted by the specified contract.
861    pub fn emitted<T: AsRef<str>, R: Addressable>(&self, addr: &R, event_name: T) -> bool {
862        let events_count = self.events_count(addr);
863
864        (0..events_count)
865            .map(|event_id| {
866                self.get_event_bytes(addr, event_id).unwrap_or_else(|e| {
867                    panic!(
868                        "Couldn't get event at address {:?} with id {}: {:?}",
869                        addr.address(),
870                        event_id,
871                        e
872                    )
873                })
874            })
875            .any(|bytes| {
876                utils::extract_event_name(&bytes)
877                    .unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
878                    .as_str()
879                    == event_name.as_ref()
880            })
881    }
882    /// Returns true if a native event with the specified name was emitted by the specified contract.
883    pub fn emitted_native<T: AsRef<str>, R: Addressable>(&self, addr: &R, event_name: T) -> bool {
884        let events_count = self.native_events_count(addr);
885
886        (0..events_count)
887            .map(|event_id| {
888                self.get_native_event_bytes(addr, event_id)
889                    .unwrap_or_else(|e| {
890                        panic!(
891                            "Couldn't get event at address {:?} with id {}: {:?}",
892                            addr.address(),
893                            event_id,
894                            e
895                        )
896                    })
897            })
898            .any(|bytes| {
899                utils::extract_event_name(&bytes)
900                    .unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
901                    .as_str()
902                    == event_name.as_ref()
903            })
904    }
905
906    /// Returns the last call result for the specified contract.
907    pub fn last_call_result(&self, contract_address: Address) -> ContractCallResult {
908        self.last_call_result
909            .borrow()
910            .clone()
911            .unwrap()
912            .contract_last_call(contract_address)
913    }
914
915    /// Signs the specified message with the private key of the specified address.
916    pub fn sign_message(&self, message: &Bytes, address: &Address) -> Bytes {
917        let backend = self.backend.borrow();
918        backend.sign_message(message, address)
919    }
920
921    /// Returns the public key associated with the specified address.
922    pub fn public_key(&self, address: &Address) -> PublicKey {
923        let backend = self.backend.borrow();
924        backend.public_key(address)
925    }
926
927    /// Returns the caller address for the current contract execution.
928    pub fn caller(&self) -> Address {
929        let backend = self.backend.borrow();
930        backend.caller()
931    }
932
933    /// Sets the gas limit for the current contract execution.
934    pub fn set_gas(&self, gas: u64) {
935        let backend = self.backend.borrow();
936        backend.set_gas(gas)
937    }
938
939    /// Transfers the specified amount of CSPR from the current caller to the specified address.
940    pub fn transfer(&self, to: Address, amount: U512) -> OdraResult<()> {
941        if to.is_contract() {
942            return Err(OdraError::ExecutionError(
943                ExecutionError::TransferToContract
944            ));
945        }
946        let backend = self.backend.borrow();
947        backend.transfer(to, amount)
948    }
949
950    fn last_events(&self, contract: &mut DeployedContract) -> Vec<Bytes> {
951        let old_count = contract.events_count;
952        let new_count = self.events_count(&contract.address);
953        let mut events = vec![];
954        for count in old_count..new_count {
955            let event = self.get_event_bytes(&contract.address, count).unwrap();
956            events.push(event);
957        }
958
959        contract.events_count = new_count;
960        events
961    }
962
963    fn last_native_events(&self, contract: &mut DeployedContract) -> Vec<Bytes> {
964        let old_count = contract.native_events_count;
965        let new_count = self.native_events_count(&contract.address);
966        let mut events = vec![];
967        for count in old_count..new_count {
968            let event = self
969                .get_native_event_bytes(&contract.address, count)
970                .unwrap();
971            events.push(event);
972        }
973
974        contract.native_events_count = new_count;
975        events
976    }
977
978    fn init_events(&self, contract_address: &Address) {
979        // First, check if initialization is needed and get event counts
980        let needs_init = {
981            let contracts = self.deployed_contracts.borrow();
982            contracts
983                .get(contract_address)
984                .map(|contract| !contract.events_initialized)
985                .unwrap_or(false)
986        };
987
988        if needs_init {
989            // Get event counts while not holding any borrows
990            let events_count = self.events_count(contract_address);
991            let native_events_count = self.native_events_count(contract_address);
992
993            // Now update the contract
994            let mut contracts = self.deployed_contracts.borrow_mut();
995            let contract = contracts.get_mut(contract_address).unwrap();
996            contract.events_count = events_count;
997            contract.native_events_count = native_events_count;
998            contract.events_initialized = true;
999        }
1000    }
1001}
1002
1003#[cfg(test)]
1004mod test {
1005    use core::fmt::Debug;
1006
1007    use super::*;
1008    use casper_event_standard::Event;
1009    use casper_types::account::AccountHash;
1010    use casper_types::contracts::ContractPackageHash;
1011    use mockall::{mock, predicate};
1012    use std::sync::Mutex;
1013
1014    static IDENT_MTX: Mutex<()> = Mutex::new(());
1015    static EPC_MTX: Mutex<()> = Mutex::new(());
1016
1017    #[derive(Debug, Event, PartialEq)]
1018    struct TestEv {}
1019
1020    mock! {
1021        TestRef {}
1022        impl HasIdent for TestRef {
1023            fn ident() -> String;
1024        }
1025        impl EntryPointsCallerProvider for TestRef {
1026            fn entry_points_caller(env: &HostEnv) -> EntryPointsCaller;
1027        }
1028        impl HostRef for TestRef {
1029            fn new(address: Address, env: HostEnv) -> Self;
1030            fn with_tokens(&self, tokens: U512) -> Self;
1031            fn contract_address(&self) -> Address;
1032            fn env(&self) -> &HostEnv;
1033            fn get_event<T>(&self, index: i32) -> Result<T, EventError> where T: FromBytes + EventInstance + 'static;
1034            fn last_call(&self) -> ContractCallResult;
1035        }
1036    }
1037
1038    impl crate::ContractRef for MockTestRef {
1039        fn new(_env: Rc<ContractEnv>, _address: Address) -> Self {
1040            unimplemented!()
1041        }
1042        fn address(&self) -> &Address {
1043            unimplemented!()
1044        }
1045
1046        fn with_tokens(&self, _tokens: U512) -> Self {
1047            unimplemented!()
1048        }
1049    }
1050
1051    impl OdraContract for MockTestRef {
1052        type HostRef = MockTestRef;
1053
1054        type ContractRef = MockTestRef;
1055
1056        type InitArgs = NoArgs;
1057
1058        type UpgradeArgs = NoArgs;
1059    }
1060
1061    mock! {
1062        Ev {}
1063        impl Into<RuntimeArgs> for Ev {
1064            fn into(self) -> RuntimeArgs;
1065        }
1066    }
1067
1068    #[test]
1069    fn test_deploy_with_default_args() {
1070        // MockTestRef::ident() and  MockTestRef::entry_points_caller() are static and can't be safely used
1071        // from multiple tests at the same time. Should be to protected with a Mutex. Each function has
1072        // a separate Mutex.
1073        // https://github.com/asomers/mockall/blob/master/mockall/tests/mock_struct_with_static_method.rs
1074        let _i = IDENT_MTX.lock();
1075        let _e = EPC_MTX.lock();
1076
1077        // stubs
1078        let indent_ctx = MockTestRef::ident_context();
1079        indent_ctx.expect().returning(|| "TestRef".to_string());
1080
1081        let epc_ctx = MockTestRef::entry_points_caller_context();
1082        epc_ctx
1083            .expect()
1084            .returning(|h| EntryPointsCaller::new(h.clone(), vec![], |_, _| Ok(Bytes::default())));
1085
1086        // check if TestRef::new() is called exactly once
1087        let instance_ctx = MockTestRef::new_context();
1088        instance_ctx
1089            .expect()
1090            .times(1)
1091            .returning(|_, _| MockTestRef::default());
1092
1093        let mut ctx = MockHostContext::new();
1094        ctx.expect_new_contract()
1095            .returning(|_, _, _| Ok(Address::Account(AccountHash::new([0; 32]))));
1096        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1097        MockTestRef::deploy(&env, NoArgs);
1098    }
1099
1100    #[test]
1101    fn test_load_ref() {
1102        // MockTestRef::ident() and MockTestRef::entry_points_caller() are static and can't be safely used
1103        // from multiple tests at the same time. Should be to protected with a Mutex. Each function has
1104        // a separate Mutex.
1105        // https://github.com/asomers/mockall/blob/master/mockall/tests/mock_struct_with_static_method.rs
1106        let _e = EPC_MTX.lock();
1107        let _i = IDENT_MTX.lock();
1108
1109        // stubs
1110        let epc_ctx = MockTestRef::entry_points_caller_context();
1111        epc_ctx
1112            .expect()
1113            .returning(|h| EntryPointsCaller::new(h.clone(), vec![], |_, _| Ok(Bytes::default())));
1114        let indent_ctx = MockTestRef::ident_context();
1115        indent_ctx.expect().returning(|| "TestRef".to_string());
1116
1117        let mut ctx = MockHostContext::new();
1118        ctx.expect_register_contract().returning(|_, _, _| ());
1119        ctx.expect_get_events_count().returning(|_| Ok(0));
1120        ctx.expect_get_native_events_count().returning(|_| Ok(0));
1121
1122        // check if TestRef::new() is called exactly once
1123        let instance_ctx = MockTestRef::new_context();
1124        instance_ctx
1125            .expect()
1126            .times(1)
1127            .returning(|_, _| MockTestRef::default());
1128
1129        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1130        let address = Address::Account(AccountHash::new([0; 32]));
1131        MockTestRef::load(&env, address);
1132    }
1133
1134    #[test]
1135    fn test_host_env() {
1136        let mut ctx = MockHostContext::new();
1137        ctx.expect_new_contract()
1138            .returning(|_, _, _| Ok(Address::Account(AccountHash::new([0; 32]))));
1139        ctx.expect_caller()
1140            .returning(|| Address::Account(AccountHash::new([2; 32])))
1141            .times(1);
1142        ctx.expect_gas_report().returning(GasReport::new).times(1);
1143        ctx.expect_set_gas().returning(|_| ()).times(1);
1144
1145        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1146
1147        assert_eq!(env.caller(), Address::Account(AccountHash::new([2; 32])));
1148        // should call the `HostContext`
1149        env.gas_report();
1150        env.set_gas(1_000u64)
1151    }
1152
1153    #[test]
1154    fn test_successful_transfer_to_account() {
1155        // Given a host context that successfully transfers tokens.
1156        let mut ctx = MockHostContext::new();
1157        ctx.expect_transfer().returning(|_, _| Ok(()));
1158        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1159
1160        let addr = Address::Account(AccountHash::new([0; 32]));
1161        // When transfer 100 tokens to an account.
1162        let result = env.transfer(addr, 100.into());
1163        // Then the transfer should be successful.
1164        assert!(result.is_ok());
1165    }
1166
1167    #[test]
1168    fn test_failing_transfer_to_account() {
1169        // Given a host context that fails to transfer tokens.
1170        let mut ctx = MockHostContext::new();
1171        ctx.expect_transfer()
1172            .returning(|_, _| Err(OdraError::ExecutionError(ExecutionError::UnwrapError)));
1173        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1174
1175        let addr = Address::Account(AccountHash::new([0; 32]));
1176        // When transfer 100 tokens to an account.
1177        let result = env.transfer(addr, 100.into());
1178        // Then the transfer should fail.
1179        assert_eq!(
1180            result.err(),
1181            Some(OdraError::ExecutionError(ExecutionError::UnwrapError))
1182        );
1183    }
1184
1185    #[test]
1186    fn test_transfer_to_contract() {
1187        // Given a host context that successfully transfers tokens.
1188        let mut ctx = MockHostContext::new();
1189        ctx.expect_transfer().returning(|_, _| Ok(()));
1190        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1191
1192        let addr = Address::Contract(ContractPackageHash::new([0; 32]));
1193        // When transfer 100 tokens to a contract.
1194        let result = env.transfer(addr, 100.into());
1195        // Then the transfer should fail.
1196        assert_eq!(
1197            result,
1198            Err(OdraError::ExecutionError(
1199                ExecutionError::TransferToContract
1200            ))
1201        );
1202    }
1203
1204    #[test]
1205    fn test_get_event() {
1206        let addr = Address::Account(AccountHash::new([0; 32]));
1207
1208        let mut ctx = MockHostContext::new();
1209        // there are 2 events emitted by the contract
1210        ctx.expect_get_events_count().returning(|_| Ok(2));
1211        // get_event() at index 0 will return an invalid event
1212        ctx.expect_get_event()
1213            .with(predicate::always(), predicate::eq(0))
1214            .returning(|_, _| Ok(vec![1].into()));
1215        // get_event() at index 1 will return an valid event
1216        ctx.expect_get_event()
1217            .with(predicate::always(), predicate::eq(1))
1218            .returning(|_, _| Ok(TestEv {}.to_bytes().unwrap().into()));
1219
1220        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1221
1222        assert_eq!(env.get_event(&addr, 1), Ok(TestEv {}));
1223        assert_eq!(env.get_event(&addr, -1), Ok(TestEv {}));
1224        assert_eq!(
1225            env.get_event::<TestEv, _>(&addr, 0),
1226            Err(EventError::Parsing)
1227        );
1228        assert_eq!(
1229            env.get_event::<TestEv, _>(&addr, -2),
1230            Err(EventError::Parsing)
1231        );
1232        assert_eq!(
1233            env.get_event::<TestEv, _>(&addr, 2),
1234            Err(EventError::IndexOutOfBounds)
1235        );
1236        assert_eq!(
1237            env.get_event::<TestEv, _>(&addr, -3),
1238            Err(EventError::IndexOutOfBounds)
1239        );
1240    }
1241
1242    #[test]
1243    fn test_events_works() {
1244        let addr = Address::Account(AccountHash::new([0; 32]));
1245
1246        let mut ctx = MockHostContext::new();
1247        // there are 2 events emitted by the contract
1248        ctx.expect_get_events_count().returning(|_| Ok(2));
1249        // get_event() at index 0 will return an invalid event
1250        ctx.expect_get_event()
1251            .with(predicate::always(), predicate::eq(0))
1252            .returning(|_, _| Ok(vec![1].into()));
1253        // get_event() at index 1 will return an valid event
1254        ctx.expect_get_event()
1255            .with(predicate::always(), predicate::eq(1))
1256            .returning(|_, _| Ok(vec![1, 0, 1].into()));
1257
1258        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1259
1260        assert_eq!(
1261            env.events(&addr),
1262            vec![vec![1].into(), vec![1, 0, 1].into()]
1263        );
1264    }
1265
1266    #[test]
1267    #[should_panic(
1268        expected = "Couldn't get event at address Account(AccountHash(0000000000000000000000000000000000000000000000000000000000000000)) with id 0: CouldntExtractEventData"
1269    )]
1270    fn test_events_fails() {
1271        let addr = Address::Account(AccountHash::new([0; 32]));
1272
1273        let mut ctx = MockHostContext::new();
1274        // there are 2 events emitted by the contract
1275        ctx.expect_get_events_count().returning(|_| Ok(2));
1276        // get_event() at index 0 panics
1277        ctx.expect_get_event()
1278            .with(predicate::always(), predicate::eq(0))
1279            .returning(|_, _| Err(EventError::CouldntExtractEventData));
1280
1281        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1282
1283        env.events(&addr);
1284    }
1285
1286    #[test]
1287    fn test_emitted() {
1288        let addr = Address::Account(AccountHash::new([0; 32]));
1289        let mut ctx = MockHostContext::new();
1290
1291        ctx.expect_get_events_count().returning(|_| Ok(1));
1292        ctx.expect_get_event()
1293            .returning(|_, _| Ok(TestEv {}.to_bytes().unwrap().into()));
1294
1295        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1296        assert!(env.emitted(&addr, "TestEv"));
1297        assert!(!env.emitted(&addr, "AnotherEvent"));
1298    }
1299}