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            for (contract_address, _) in self.deployed_contracts.borrow().iter() {
458                self.init_events(contract_address);
459            }
460        }
461    }
462
463    /// Returns the account address at the specified index.
464    pub fn get_account(&self, index: usize) -> Address {
465        let backend = self.backend.borrow();
466        backend.get_account(index)
467    }
468
469    /// Returns the validator public key.
470    pub fn get_validator(&self, index: usize) -> PublicKey {
471        let backend = self.backend.borrow();
472        backend.get_validator(index)
473    }
474
475    /// Sets the caller address for the current contract execution.
476    pub fn set_caller(&self, address: Address) {
477        if address.is_contract() {
478            panic!("Caller cannot be a contract: {:?}", address)
479        }
480        let backend = self.backend.borrow();
481        backend.set_caller(address)
482    }
483
484    /// Advances the block time by the specified time difference in milliseconds.
485    pub fn advance_block_time(&self, time_diff: u64) {
486        let backend = self.backend.borrow();
487        backend.advance_block_time(time_diff)
488    }
489
490    /// Advances the block time by the specified time difference in milliseconds
491    /// and processes auctions.
492    pub fn advance_with_auctions(&self, time_diff: u64) {
493        let backend = self.backend.borrow();
494        backend.advance_with_auctions(time_diff);
495    }
496
497    /// Returns the era length in milliseconds.
498    pub fn auction_delay(&self) -> u64 {
499        let backend = self.backend.borrow();
500        backend.auction_delay()
501    }
502
503    /// Returns the delay between unstaking and the transfer of funds back to the delegator in milliseconds.
504    pub fn unbonding_delay(&self) -> u64 {
505        let backend = self.backend.borrow();
506        backend.unbonding_delay()
507    }
508
509    /// Returns the amount of CSPR delegated to the specified validator by the specified delegator.
510    pub fn delegated_amount(&self, delegator: Address, validator: PublicKey) -> U512 {
511        let backend = self.backend.borrow();
512        backend.delegated_amount(delegator, validator)
513    }
514
515    /// Evicts the validator at the specified index from the validator set.
516    pub fn remove_validator(&self, index: usize) {
517        let backend = self.backend.borrow();
518        backend.remove_validator(index);
519    }
520
521    /// Returns the current block time in milliseconds.
522    pub fn block_time(&self) -> u64 {
523        let backend = self.backend.borrow();
524        backend.block_time()
525    }
526
527    /// Returns the current block time in milliseconds.
528    pub fn block_time_millis(&self) -> u64 {
529        let backend = self.backend.borrow();
530        backend.block_time()
531    }
532
533    /// Returns the current block time in seconds.
534    pub fn block_time_secs(&self) -> u64 {
535        let backend = self.backend.borrow();
536        backend.block_time().checked_div(1000).unwrap()
537    }
538
539    /// Registers a new contract with the specified name, initialization arguments, and entry points caller.
540    pub fn new_contract(
541        &self,
542        name: &str,
543        init_args: RuntimeArgs,
544        entry_points_caller: EntryPointsCaller
545    ) -> OdraResult<Address> {
546        // Filter "upgrade" from EntryPointsCaller
547        let mut entry_points_caller = entry_points_caller.clone();
548        entry_points_caller.remove_entry_point("upgrade");
549
550        let backend = self.backend.borrow();
551        let contract_address = backend.new_contract(name, init_args, entry_points_caller)?;
552
553        self.deployed_contracts
554            .borrow_mut()
555            .insert(contract_address, DeployedContract::new(contract_address));
556        Ok(contract_address)
557    }
558
559    /// Upgrades an existing contract with a new one with given upgrade arguments and new entry
560    /// points caller.
561    pub fn upgrade_contract(
562        &self,
563        name: &str,
564        contract_to_upgrade: Address,
565        upgrade_args: RuntimeArgs,
566        entry_points_caller: EntryPointsCaller
567    ) -> OdraResult<Address> {
568        // Filter "init" from EntryPointsCaller
569        let mut entry_points_caller = entry_points_caller.clone();
570        entry_points_caller.remove_entry_point("init");
571
572        let backend = self.backend.borrow();
573        let upgraded_contract = backend.upgrade_contract(
574            name,
575            contract_to_upgrade,
576            upgrade_args,
577            entry_points_caller
578        )?;
579        let mut contracts = self.deployed_contracts.borrow_mut();
580        let contract = contracts.get_mut(&upgraded_contract).unwrap();
581        contract.current_version += 1;
582        // CES events are intact, but native events are connected to a contract, not a package.
583        contract.native_events_count = 0;
584        Ok(upgraded_contract)
585    }
586
587    /// Registers an existing contract with the specified address, name and entry points caller.
588    /// Similar to `new_contract`, but skips the deployment phase.
589    pub fn register_contract(
590        &self,
591        address: Address,
592        contract_name: String,
593        entry_points_caller: EntryPointsCaller
594    ) {
595        let backend = self.backend.borrow();
596        backend.register_contract(address, contract_name, entry_points_caller);
597        self.deployed_contracts
598            .borrow_mut()
599            .insert(address, DeployedContract::new(address));
600    }
601
602    /// Calls a contract at the specified address with the given call definition.
603    pub fn call_contract<T: FromBytes + CLTyped>(
604        &self,
605        address: Address,
606        call_def: CallDef
607    ) -> OdraResult<T> {
608        let use_proxy = T::cl_type() != <()>::cl_type() || !call_def.amount().is_zero();
609        let call_result = self.raw_call_contract(address, call_def, use_proxy);
610        call_result.map(|bytes| {
611            T::from_bytes(&bytes)
612                .map(|(obj, _)| obj)
613                .map_err(|_| OdraError::VmError(VmError::Deserialization))
614        })?
615    }
616
617    /// Calls a contract at the specified address with the given call definition. Returns raw,
618    /// not serialized bytes.
619    pub fn raw_call_contract(
620        &self,
621        address: Address,
622        call_def: CallDef,
623        use_proxy: bool
624    ) -> OdraResult<Bytes> {
625        let call_result = {
626            let backend = self.backend.borrow();
627            backend.call_contract(&address, call_def, use_proxy)
628        };
629
630        let mut events_map: BTreeMap<Address, Vec<Bytes>> = BTreeMap::new();
631        let mut native_events_map: BTreeMap<Address, Vec<Bytes>> = BTreeMap::new();
632
633        let captures_events = *self.captures_events.borrow();
634        if captures_events {
635            // Go through all contracts and collect their events
636            self.deployed_contracts.borrow_mut().iter_mut().for_each(
637                |(contract_address, contract)| {
638                    let events = self.last_events(contract);
639                    let native_events = self.last_native_events(contract);
640                    events_map.insert(*contract_address, events);
641                    native_events_map.insert(*contract_address, native_events);
642                }
643            );
644        }
645
646        let backend = self.backend.borrow();
647        let last_call_gas_cost = backend.last_call_gas_cost();
648
649        self.last_call_result.replace(Some(CallResult::new(
650            address,
651            backend.caller(),
652            last_call_gas_cost,
653            call_result.clone(),
654            events_map,
655            native_events_map
656        )));
657
658        call_result
659    }
660
661    /// Returns the gas cost of the last contract call.
662    pub fn contract_env(&self) -> ContractEnv {
663        self.backend.borrow().contract_env()
664    }
665
666    /// Prints the gas report for the current contract execution.
667    pub fn gas_report(&self) -> GasReport {
668        self.backend.borrow().gas_report().clone()
669    }
670
671    /// Returns the CSPR balance of the specified address.
672    pub fn balance_of<T: Addressable>(&self, addr: &T) -> U512 {
673        let backend = self.backend.borrow();
674        backend.balance_of(&addr.address())
675    }
676
677    /// Retrieves an event with the specified index from the specified contract.
678    ///
679    /// # Returns
680    ///
681    /// Returns the event as an instance of the specified type, or an error if the event
682    /// couldn't be retrieved or parsed.
683    pub fn get_event<T: FromBytes + EventInstance, R: Addressable>(
684        &self,
685        addr: &R,
686        index: i32
687    ) -> Result<T, EventError> {
688        let contract_address = addr.address();
689        let backend = self.backend.borrow();
690        let events_count = self.events_count(&contract_address);
691        let event_absolute_position = crate::utils::event_absolute_position(events_count, index)
692            .ok_or(EventError::IndexOutOfBounds)?;
693
694        let bytes = backend.get_event(&contract_address, event_absolute_position)?;
695        T::from_bytes(&bytes)
696            .map_err(|_| EventError::Parsing)
697            .map(|r| r.0)
698    }
699
700    /// Retrieves a native event with the specified index from the specified contract.
701    ///
702    /// # Returns
703    ///
704    /// Returns the event as an instance of the specified type, or an error if the event
705    /// couldn't be retrieved or parsed.
706    pub fn get_native_event<T: FromBytes + EventInstance, R: Addressable>(
707        &self,
708        addr: &R,
709        index: i32
710    ) -> Result<T, EventError> {
711        let contract_address = addr.address();
712        let backend = self.backend.borrow();
713        let events_count = self.native_events_count(&contract_address);
714        let event_absolute_position = crate::utils::event_absolute_position(events_count, index)
715            .ok_or(EventError::IndexOutOfBounds)?;
716
717        let bytes = backend.get_native_event(&contract_address, event_absolute_position)?;
718        T::from_bytes(&bytes)
719            .map_err(|_| EventError::Parsing)
720            .map(|r| r.0)
721    }
722
723    /// Retrieves a raw event (serialized) with the specified index from the specified contract.
724    pub fn get_event_bytes<T: Addressable>(
725        &self,
726        addr: &T,
727        index: u32
728    ) -> Result<Bytes, EventError> {
729        let backend = self.backend.borrow();
730        backend.get_event(&addr.address(), index)
731    }
732
733    /// Retrieves a raw native event (serialized) with the specified index from the specified contract.
734    pub fn get_native_event_bytes<T: Addressable>(
735        &self,
736        addr: &T,
737        index: u32
738    ) -> Result<Bytes, EventError> {
739        let backend = self.backend.borrow();
740        backend.get_native_event(&addr.address(), index)
741    }
742
743    /// Returns the names of all events emitted by the specified contract.
744    pub fn event_names<T: Addressable>(&self, addr: &T) -> Vec<String> {
745        let events_count = self.events_count(addr);
746
747        let backend = self.backend.borrow();
748        (0..events_count)
749            .map(|event_id| {
750                backend
751                    .get_event(&addr.address(), event_id)
752                    .and_then(|bytes| utils::extract_event_name(&bytes))
753                    .unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
754            })
755            .collect()
756    }
757
758    /// Returns all events emitted by the specified contract.
759    pub fn events<T: Addressable>(&self, addr: &T) -> Vec<Bytes> {
760        let backend = self.backend.borrow();
761        let contract_address = addr.address();
762        let events_count = backend
763            .get_events_count(&contract_address)
764            .unwrap_or_default();
765        (0..events_count)
766            .map(|event_id| {
767                backend
768                    .get_event(&contract_address, event_id)
769                    .unwrap_or_else(|e| {
770                        panic!(
771                            "Couldn't get event at address {:?} with id {}: {:?}",
772                            &contract_address, event_id, e
773                        )
774                    })
775            })
776            .collect()
777    }
778
779    /// Returns the number of events emitted by the specified contract.
780    pub fn events_count<T: Addressable>(&self, addr: &T) -> u32 {
781        let backend = self.backend.borrow();
782        backend
783            .get_events_count(&addr.address())
784            .unwrap_or_default()
785    }
786
787    /// Returns the number of native events emitted by the specified contract.
788    pub fn native_events_count<T: Addressable>(&self, addr: &T) -> u32 {
789        let backend = self.backend.borrow();
790        backend
791            .get_native_events_count(&addr.address())
792            .unwrap_or_default()
793    }
794
795    /// Returns true if the specified event was emitted by the specified contract.
796    pub fn emitted_event<T: ToBytes + EventInstance, R: Addressable>(
797        &self,
798        addr: &R,
799        event: T
800    ) -> bool {
801        let contract_address = addr.address();
802        let events_count = self.events_count(addr);
803
804        let event_bytes = Bytes::from(
805            event
806                .to_bytes()
807                .unwrap_or_else(|_| panic!("Couldn't serialize event"))
808        );
809
810        (0..events_count)
811            .map(|event_id| {
812                self.get_event_bytes(&contract_address, event_id)
813                    .unwrap_or_else(|e| {
814                        panic!(
815                            "Couldn't get event at address {:?} with id {}: {:?}",
816                            &contract_address, event_id, e
817                        )
818                    })
819            })
820            .any(|bytes| bytes == event_bytes)
821    }
822
823    /// Returns true if the specified event was emitted by the specified contract.
824    pub fn emitted_native_event<T: ToBytes + EventInstance, R: Addressable>(
825        &self,
826        addr: &R,
827        event: T
828    ) -> bool {
829        let contract_address = addr.address();
830        let events_count = self.native_events_count(addr);
831        if events_count > 0 {
832            let event_bytes = Bytes::from(
833                event
834                    .to_bytes()
835                    .unwrap_or_else(|_| panic!("Couldn't serialize event"))
836            );
837            (0..events_count)
838                .map(|event_id| {
839                    self.get_native_event_bytes(&contract_address, event_id)
840                        .unwrap_or_else(|e| {
841                            panic!(
842                                "Couldn't get event at address {:?} with id {}: {:?}",
843                                &contract_address, event_id, e
844                            )
845                        })
846                })
847                .any(|bytes| bytes == event_bytes)
848        } else {
849            false
850        }
851    }
852
853    /// Returns true if an event with the specified name was emitted by the specified contract.
854    pub fn emitted<T: AsRef<str>, R: Addressable>(&self, addr: &R, event_name: T) -> bool {
855        let events_count = self.events_count(addr);
856
857        (0..events_count)
858            .map(|event_id| {
859                self.get_event_bytes(addr, event_id).unwrap_or_else(|e| {
860                    panic!(
861                        "Couldn't get event at address {:?} with id {}: {:?}",
862                        addr.address(),
863                        event_id,
864                        e
865                    )
866                })
867            })
868            .any(|bytes| {
869                utils::extract_event_name(&bytes)
870                    .unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
871                    .as_str()
872                    == event_name.as_ref()
873            })
874    }
875    /// Returns true if a native event with the specified name was emitted by the specified contract.
876    pub fn emitted_native<T: AsRef<str>, R: Addressable>(&self, addr: &R, event_name: T) -> bool {
877        let events_count = self.native_events_count(addr);
878
879        (0..events_count)
880            .map(|event_id| {
881                self.get_native_event_bytes(addr, event_id)
882                    .unwrap_or_else(|e| {
883                        panic!(
884                            "Couldn't get event at address {:?} with id {}: {:?}",
885                            addr.address(),
886                            event_id,
887                            e
888                        )
889                    })
890            })
891            .any(|bytes| {
892                utils::extract_event_name(&bytes)
893                    .unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
894                    .as_str()
895                    == event_name.as_ref()
896            })
897    }
898
899    /// Returns the last call result for the specified contract.
900    pub fn last_call_result(&self, contract_address: Address) -> ContractCallResult {
901        self.last_call_result
902            .borrow()
903            .clone()
904            .unwrap()
905            .contract_last_call(contract_address)
906    }
907
908    /// Signs the specified message with the private key of the specified address.
909    pub fn sign_message(&self, message: &Bytes, address: &Address) -> Bytes {
910        let backend = self.backend.borrow();
911        backend.sign_message(message, address)
912    }
913
914    /// Returns the public key associated with the specified address.
915    pub fn public_key(&self, address: &Address) -> PublicKey {
916        let backend = self.backend.borrow();
917        backend.public_key(address)
918    }
919
920    /// Returns the caller address for the current contract execution.
921    pub fn caller(&self) -> Address {
922        let backend = self.backend.borrow();
923        backend.caller()
924    }
925
926    /// Sets the gas limit for the current contract execution.
927    pub fn set_gas(&self, gas: u64) {
928        let backend = self.backend.borrow();
929        backend.set_gas(gas)
930    }
931
932    /// Transfers the specified amount of CSPR from the current caller to the specified address.
933    pub fn transfer(&self, to: Address, amount: U512) -> OdraResult<()> {
934        if to.is_contract() {
935            return Err(OdraError::ExecutionError(
936                ExecutionError::TransferToContract
937            ));
938        }
939        let backend = self.backend.borrow();
940        backend.transfer(to, amount)
941    }
942
943    fn last_events(&self, contract: &mut DeployedContract) -> Vec<Bytes> {
944        let old_count = contract.events_count;
945        let new_count = self.events_count(&contract.address);
946        let mut events = vec![];
947        for count in old_count..new_count {
948            let event = self.get_event_bytes(&contract.address, count).unwrap();
949            events.push(event);
950        }
951
952        contract.events_count = new_count;
953        events
954    }
955
956    fn last_native_events(&self, contract: &mut DeployedContract) -> Vec<Bytes> {
957        let old_count = contract.native_events_count;
958        let new_count = self.native_events_count(&contract.address);
959        let mut events = vec![];
960        for count in old_count..new_count {
961            let event = self
962                .get_native_event_bytes(&contract.address, count)
963                .unwrap();
964            events.push(event);
965        }
966
967        contract.native_events_count = new_count;
968        events
969    }
970
971    fn init_events(&self, contract_address: &Address) {
972        let mut contracts = self.deployed_contracts.borrow_mut();
973        let contract = contracts.get_mut(contract_address).unwrap();
974
975        if !contract.events_initialized {
976            contract.events_count = self.events_count(contract_address);
977            contract.native_events_count = self.native_events_count(contract_address);
978            contract.events_initialized = true;
979        }
980    }
981}
982
983#[cfg(test)]
984mod test {
985    use core::fmt::Debug;
986
987    use super::*;
988    use casper_event_standard::Event;
989    use casper_types::account::AccountHash;
990    use casper_types::contracts::ContractPackageHash;
991    use mockall::{mock, predicate};
992    use std::sync::Mutex;
993
994    static IDENT_MTX: Mutex<()> = Mutex::new(());
995    static EPC_MTX: Mutex<()> = Mutex::new(());
996
997    #[derive(Debug, Event, PartialEq)]
998    struct TestEv {}
999
1000    mock! {
1001        TestRef {}
1002        impl HasIdent for TestRef {
1003            fn ident() -> String;
1004        }
1005        impl EntryPointsCallerProvider for TestRef {
1006            fn entry_points_caller(env: &HostEnv) -> EntryPointsCaller;
1007        }
1008        impl HostRef for TestRef {
1009            fn new(address: Address, env: HostEnv) -> Self;
1010            fn with_tokens(&self, tokens: U512) -> Self;
1011            fn contract_address(&self) -> Address;
1012            fn env(&self) -> &HostEnv;
1013            fn get_event<T>(&self, index: i32) -> Result<T, EventError> where T: FromBytes + EventInstance + 'static;
1014            fn last_call(&self) -> ContractCallResult;
1015        }
1016    }
1017
1018    impl crate::ContractRef for MockTestRef {
1019        fn new(_env: Rc<ContractEnv>, _address: Address) -> Self {
1020            unimplemented!()
1021        }
1022        fn address(&self) -> &Address {
1023            unimplemented!()
1024        }
1025
1026        fn with_tokens(&self, _tokens: U512) -> Self {
1027            unimplemented!()
1028        }
1029    }
1030
1031    impl OdraContract for MockTestRef {
1032        type HostRef = MockTestRef;
1033
1034        type ContractRef = MockTestRef;
1035
1036        type InitArgs = NoArgs;
1037
1038        type UpgradeArgs = NoArgs;
1039    }
1040
1041    mock! {
1042        Ev {}
1043        impl Into<RuntimeArgs> for Ev {
1044            fn into(self) -> RuntimeArgs;
1045        }
1046    }
1047
1048    #[test]
1049    fn test_deploy_with_default_args() {
1050        // MockTestRef::ident() and  MockTestRef::entry_points_caller() are static and can't be safely used
1051        // from multiple tests at the same time. Should be to protected with a Mutex. Each function has
1052        // a separate Mutex.
1053        // https://github.com/asomers/mockall/blob/master/mockall/tests/mock_struct_with_static_method.rs
1054        let _i = IDENT_MTX.lock();
1055        let _e = EPC_MTX.lock();
1056
1057        // stubs
1058        let indent_ctx = MockTestRef::ident_context();
1059        indent_ctx.expect().returning(|| "TestRef".to_string());
1060
1061        let epc_ctx = MockTestRef::entry_points_caller_context();
1062        epc_ctx
1063            .expect()
1064            .returning(|h| EntryPointsCaller::new(h.clone(), vec![], |_, _| Ok(Bytes::default())));
1065
1066        // check if TestRef::new() is called exactly once
1067        let instance_ctx = MockTestRef::new_context();
1068        instance_ctx
1069            .expect()
1070            .times(1)
1071            .returning(|_, _| MockTestRef::default());
1072
1073        let mut ctx = MockHostContext::new();
1074        ctx.expect_new_contract()
1075            .returning(|_, _, _| Ok(Address::Account(AccountHash::new([0; 32]))));
1076        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1077        MockTestRef::deploy(&env, NoArgs);
1078    }
1079
1080    #[test]
1081    fn test_load_ref() {
1082        // MockTestRef::ident() and MockTestRef::entry_points_caller() are static and can't be safely used
1083        // from multiple tests at the same time. Should be to protected with a Mutex. Each function has
1084        // a separate Mutex.
1085        // https://github.com/asomers/mockall/blob/master/mockall/tests/mock_struct_with_static_method.rs
1086        let _e = EPC_MTX.lock();
1087        let _i = IDENT_MTX.lock();
1088
1089        // stubs
1090        let epc_ctx = MockTestRef::entry_points_caller_context();
1091        epc_ctx
1092            .expect()
1093            .returning(|h| EntryPointsCaller::new(h.clone(), vec![], |_, _| Ok(Bytes::default())));
1094        let indent_ctx = MockTestRef::ident_context();
1095        indent_ctx.expect().returning(|| "TestRef".to_string());
1096
1097        let mut ctx = MockHostContext::new();
1098        ctx.expect_register_contract().returning(|_, _, _| ());
1099        ctx.expect_get_events_count().returning(|_| Ok(0));
1100        ctx.expect_get_native_events_count().returning(|_| Ok(0));
1101
1102        // check if TestRef::new() is called exactly once
1103        let instance_ctx = MockTestRef::new_context();
1104        instance_ctx
1105            .expect()
1106            .times(1)
1107            .returning(|_, _| MockTestRef::default());
1108
1109        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1110        let address = Address::Account(AccountHash::new([0; 32]));
1111        MockTestRef::load(&env, address);
1112    }
1113
1114    #[test]
1115    fn test_host_env() {
1116        let mut ctx = MockHostContext::new();
1117        ctx.expect_new_contract()
1118            .returning(|_, _, _| Ok(Address::Account(AccountHash::new([0; 32]))));
1119        ctx.expect_caller()
1120            .returning(|| Address::Account(AccountHash::new([2; 32])))
1121            .times(1);
1122        ctx.expect_gas_report().returning(GasReport::new).times(1);
1123        ctx.expect_set_gas().returning(|_| ()).times(1);
1124
1125        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1126
1127        assert_eq!(env.caller(), Address::Account(AccountHash::new([2; 32])));
1128        // should call the `HostContext`
1129        env.gas_report();
1130        env.set_gas(1_000u64)
1131    }
1132
1133    #[test]
1134    fn test_successful_transfer_to_account() {
1135        // Given a host context that successfully transfers tokens.
1136        let mut ctx = MockHostContext::new();
1137        ctx.expect_transfer().returning(|_, _| Ok(()));
1138        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1139
1140        let addr = Address::Account(AccountHash::new([0; 32]));
1141        // When transfer 100 tokens to an account.
1142        let result = env.transfer(addr, 100.into());
1143        // Then the transfer should be successful.
1144        assert!(result.is_ok());
1145    }
1146
1147    #[test]
1148    fn test_failing_transfer_to_account() {
1149        // Given a host context that fails to transfer tokens.
1150        let mut ctx = MockHostContext::new();
1151        ctx.expect_transfer()
1152            .returning(|_, _| Err(OdraError::ExecutionError(ExecutionError::UnwrapError)));
1153        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1154
1155        let addr = Address::Account(AccountHash::new([0; 32]));
1156        // When transfer 100 tokens to an account.
1157        let result = env.transfer(addr, 100.into());
1158        // Then the transfer should fail.
1159        assert_eq!(
1160            result.err(),
1161            Some(OdraError::ExecutionError(ExecutionError::UnwrapError))
1162        );
1163    }
1164
1165    #[test]
1166    fn test_transfer_to_contract() {
1167        // Given a host context that successfully transfers tokens.
1168        let mut ctx = MockHostContext::new();
1169        ctx.expect_transfer().returning(|_, _| Ok(()));
1170        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1171
1172        let addr = Address::Contract(ContractPackageHash::new([0; 32]));
1173        // When transfer 100 tokens to a contract.
1174        let result = env.transfer(addr, 100.into());
1175        // Then the transfer should fail.
1176        assert_eq!(
1177            result,
1178            Err(OdraError::ExecutionError(
1179                ExecutionError::TransferToContract
1180            ))
1181        );
1182    }
1183
1184    #[test]
1185    fn test_get_event() {
1186        let addr = Address::Account(AccountHash::new([0; 32]));
1187
1188        let mut ctx = MockHostContext::new();
1189        // there are 2 events emitted by the contract
1190        ctx.expect_get_events_count().returning(|_| Ok(2));
1191        // get_event() at index 0 will return an invalid event
1192        ctx.expect_get_event()
1193            .with(predicate::always(), predicate::eq(0))
1194            .returning(|_, _| Ok(vec![1].into()));
1195        // get_event() at index 1 will return an valid event
1196        ctx.expect_get_event()
1197            .with(predicate::always(), predicate::eq(1))
1198            .returning(|_, _| Ok(TestEv {}.to_bytes().unwrap().into()));
1199
1200        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1201
1202        assert_eq!(env.get_event(&addr, 1), Ok(TestEv {}));
1203        assert_eq!(env.get_event(&addr, -1), Ok(TestEv {}));
1204        assert_eq!(
1205            env.get_event::<TestEv, _>(&addr, 0),
1206            Err(EventError::Parsing)
1207        );
1208        assert_eq!(
1209            env.get_event::<TestEv, _>(&addr, -2),
1210            Err(EventError::Parsing)
1211        );
1212        assert_eq!(
1213            env.get_event::<TestEv, _>(&addr, 2),
1214            Err(EventError::IndexOutOfBounds)
1215        );
1216        assert_eq!(
1217            env.get_event::<TestEv, _>(&addr, -3),
1218            Err(EventError::IndexOutOfBounds)
1219        );
1220    }
1221
1222    #[test]
1223    fn test_events_works() {
1224        let addr = Address::Account(AccountHash::new([0; 32]));
1225
1226        let mut ctx = MockHostContext::new();
1227        // there are 2 events emitted by the contract
1228        ctx.expect_get_events_count().returning(|_| Ok(2));
1229        // get_event() at index 0 will return an invalid event
1230        ctx.expect_get_event()
1231            .with(predicate::always(), predicate::eq(0))
1232            .returning(|_, _| Ok(vec![1].into()));
1233        // get_event() at index 1 will return an valid event
1234        ctx.expect_get_event()
1235            .with(predicate::always(), predicate::eq(1))
1236            .returning(|_, _| Ok(vec![1, 0, 1].into()));
1237
1238        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1239
1240        assert_eq!(
1241            env.events(&addr),
1242            vec![vec![1].into(), vec![1, 0, 1].into()]
1243        );
1244    }
1245
1246    #[test]
1247    #[should_panic(
1248        expected = "Couldn't get event at address Account(AccountHash(0000000000000000000000000000000000000000000000000000000000000000)) with id 0: CouldntExtractEventData"
1249    )]
1250    fn test_events_fails() {
1251        let addr = Address::Account(AccountHash::new([0; 32]));
1252
1253        let mut ctx = MockHostContext::new();
1254        // there are 2 events emitted by the contract
1255        ctx.expect_get_events_count().returning(|_| Ok(2));
1256        // get_event() at index 0 panics
1257        ctx.expect_get_event()
1258            .with(predicate::always(), predicate::eq(0))
1259            .returning(|_, _| Err(EventError::CouldntExtractEventData));
1260
1261        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1262
1263        env.events(&addr);
1264    }
1265
1266    #[test]
1267    fn test_emitted() {
1268        let addr = Address::Account(AccountHash::new([0; 32]));
1269        let mut ctx = MockHostContext::new();
1270
1271        ctx.expect_get_events_count().returning(|_| Ok(1));
1272        ctx.expect_get_event()
1273            .returning(|_, _| Ok(TestEv {}.to_bytes().unwrap().into()));
1274
1275        let env = HostEnv::new(Rc::new(RefCell::new(ctx)));
1276        assert!(env.emitted(&addr, "TestEv"));
1277        assert!(!env.emitted(&addr, "AnotherEvent"));
1278    }
1279}