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