mod deployed_contracts;
use crate::address::Addressable;
use crate::gas_report::GasReport;
use crate::host::deployed_contracts::DeployedContract;
use crate::{
call_result::CallResult, entry_point_callback::EntryPointsCaller, CallDef, ContractCallResult,
ContractEnv, EventError, VmError
};
#[cfg(not(target_arch = "wasm32"))]
use crate::{consts, contract::OdraContract, contract_def::HasIdent};
use crate::{prelude::*, utils};
use casper_event_standard::EventInstance;
use casper_types::{
bytesrepr::{Bytes, FromBytes, ToBytes},
CLTyped, PublicKey, RuntimeArgs, U512
};
pub trait HostRef {
fn new(address: Address, env: HostEnv) -> Self;
fn with_tokens(&self, tokens: U512) -> Self;
fn contract_address(&self) -> Address;
fn env(&self) -> &HostEnv;
fn get_event<T>(&self, index: i32) -> Result<T, EventError>
where
T: FromBytes + EventInstance + 'static;
fn last_call(&self) -> ContractCallResult;
}
impl<T: HostRef> Addressable for T {
fn address(&self) -> Address {
HostRef::contract_address(self)
}
}
pub trait HostRefLoader<T: HostRef> {
fn load(env: &HostEnv, address: Address) -> T;
}
pub trait EntryPointsCallerProvider {
fn entry_points_caller(env: &HostEnv) -> EntryPointsCaller;
}
#[cfg(not(target_arch = "wasm32"))]
pub trait Deployer<R: OdraContract>: Sized {
fn deploy(env: &HostEnv, init_args: R::InitArgs) -> R::HostRef;
fn try_deploy(env: &HostEnv, init_args: R::InitArgs) -> OdraResult<R::HostRef>;
fn deploy_with_cfg(env: &HostEnv, init_args: R::InitArgs, cfg: InstallConfig) -> R::HostRef;
fn try_deploy_with_cfg(
env: &HostEnv,
init_args: R::InitArgs,
cfg: InstallConfig
) -> OdraResult<R::HostRef>;
fn try_upgrade(
env: &HostEnv,
address: Address,
init_args: R::UpgradeArgs
) -> OdraResult<R::HostRef>;
fn try_upgrade_with_cfg(
env: &HostEnv,
address: Address,
upgrade_args: R::UpgradeArgs,
cfg: UpgradeConfig
) -> OdraResult<R::HostRef>;
}
pub trait InitArgs: Into<RuntimeArgs> {}
pub trait UpgradeArgs: Into<RuntimeArgs> {}
pub struct NoArgs;
impl InitArgs for NoArgs {}
impl UpgradeArgs for NoArgs {}
impl From<NoArgs> for RuntimeArgs {
fn from(_: NoArgs) -> Self {
RuntimeArgs::new()
}
}
#[cfg(not(target_arch = "wasm32"))]
pub struct InstallConfig {
pub package_named_key: String,
pub is_upgradable: bool,
pub allow_key_override: bool
}
#[cfg(not(target_arch = "wasm32"))]
pub struct UpgradeConfig {
pub package_named_key: String,
pub force_create_upgrade_group: bool,
pub allow_key_override: bool
}
#[cfg(not(target_arch = "wasm32"))]
impl InstallConfig {
pub fn new<T: HasIdent>(is_upgradable: bool, allow_key_override: bool) -> Self {
InstallConfig {
package_named_key: T::ident(),
is_upgradable,
allow_key_override
}
}
pub fn upgradable<T: HasIdent>() -> Self {
InstallConfig::new::<T>(true, true)
}
}
#[cfg(not(target_arch = "wasm32"))]
impl UpgradeConfig {
pub fn new<T: HasIdent>() -> Self {
UpgradeConfig {
package_named_key: T::ident(),
force_create_upgrade_group: false,
allow_key_override: true
}
}
}
#[cfg(not(target_arch = "wasm32"))]
impl<R: OdraContract> Deployer<R> for R {
fn deploy(
env: &HostEnv,
init_args: <R as OdraContract>::InitArgs
) -> <R as OdraContract>::HostRef {
let contract_ident = R::HostRef::ident();
match Self::try_deploy(env, init_args) {
Ok(contract) => contract,
Err(OdraError::ExecutionError(ExecutionError::MissingArg)) => {
core::panic!("Invalid init args for contract {}.", contract_ident)
}
Err(e) => core::panic!("Contract init failed {:?}", e)
}
}
fn try_deploy(
env: &HostEnv,
init_args: <R as OdraContract>::InitArgs
) -> OdraResult<<R as OdraContract>::HostRef> {
Self::try_deploy_with_cfg(
env,
init_args,
InstallConfig::new::<<R as OdraContract>::HostRef>(false, true)
)
}
fn deploy_with_cfg(
env: &HostEnv,
init_args: <R as OdraContract>::InitArgs,
cfg: InstallConfig
) -> <R as OdraContract>::HostRef {
let contract_ident = R::HostRef::ident();
match Self::try_deploy_with_cfg(env, init_args, cfg) {
Ok(contract) => contract,
Err(OdraError::ExecutionError(ExecutionError::MissingArg)) => {
core::panic!("Invalid init args for contract {}.", contract_ident)
}
Err(e) => core::panic!("Contract init failed {:?}", e)
}
}
fn try_deploy_with_cfg(
env: &HostEnv,
init_args: <R as OdraContract>::InitArgs,
cfg: InstallConfig
) -> OdraResult<<R as OdraContract>::HostRef> {
let contract_ident = R::HostRef::ident();
let caller = R::HostRef::entry_points_caller(env);
let mut init_args = init_args.into();
init_args.insert(consts::IS_UPGRADABLE_ARG, cfg.is_upgradable)?;
init_args.insert(consts::IS_UPGRADE_ARG, false)?;
init_args.insert(consts::ALLOW_KEY_OVERRIDE_ARG, cfg.allow_key_override)?;
init_args.insert(
consts::PACKAGE_HASH_KEY_NAME_ARG,
format!("{}_package_hash", cfg.package_named_key)
)?;
let address = env.new_contract(&contract_ident, init_args, caller)?;
Ok(R::HostRef::new(address, env.clone()))
}
fn try_upgrade(
env: &HostEnv,
contract_to_upgrade: Address,
upgrade_args: <R as OdraContract>::UpgradeArgs
) -> OdraResult<<R as OdraContract>::HostRef> {
Self::try_upgrade_with_cfg(
env,
contract_to_upgrade,
upgrade_args,
UpgradeConfig::new::<<R as OdraContract>::HostRef>()
)
}
fn try_upgrade_with_cfg(
env: &HostEnv,
contract_to_upgrade: Address,
upgrade_args: <R as OdraContract>::UpgradeArgs,
cfg: UpgradeConfig
) -> OdraResult<<R as OdraContract>::HostRef> {
let mut upgrade_args = upgrade_args.into();
upgrade_args.insert(consts::IS_UPGRADE_ARG, true)?;
upgrade_args.insert(
consts::PACKAGE_HASH_TO_UPGRADE_ARG,
contract_to_upgrade.value()
)?;
upgrade_args.insert(consts::ALLOW_KEY_OVERRIDE_ARG, cfg.allow_key_override)?;
upgrade_args.insert(
consts::PACKAGE_HASH_KEY_NAME_ARG,
format!("{}_package_hash", cfg.package_named_key)
)?;
upgrade_args.insert(consts::CREATE_UPGRADE_GROUP, cfg.force_create_upgrade_group)?;
let contract_ident = R::HostRef::ident();
let entry_points_caller = R::HostRef::entry_points_caller(env);
let address = env.upgrade_contract(
&contract_ident,
contract_to_upgrade,
upgrade_args,
entry_points_caller
)?;
Ok(HostRef::new(address, env.clone()))
}
}
#[cfg(not(target_arch = "wasm32"))]
impl<T: OdraContract> HostRefLoader<T::HostRef> for T {
fn load(env: &HostEnv, address: Address) -> T::HostRef {
let caller = T::HostRef::entry_points_caller(env);
let contract_name = T::HostRef::ident();
env.register_contract(address, contract_name, caller);
T::HostRef::new(address, env.clone())
}
}
#[cfg_attr(test, mockall::automock)]
pub trait HostContext {
fn set_caller(&self, caller: Address);
fn set_gas(&self, gas: u64);
fn caller(&self) -> Address;
fn get_account(&self, index: usize) -> Address;
fn get_validator(&self, index: usize) -> PublicKey;
fn remove_validator(&self, index: usize);
fn balance_of(&self, address: &Address) -> U512;
fn advance_block_time(&self, time_diff: u64);
fn advance_with_auctions(&self, time_diff: u64);
fn auction_delay(&self) -> u64;
fn unbonding_delay(&self) -> u64;
fn delegated_amount(&self, delegator: Address, validator: PublicKey) -> U512;
fn block_time(&self) -> u64;
fn get_event(&self, contract_address: &Address, index: u32) -> Result<Bytes, EventError>;
fn get_native_event(&self, contract_address: &Address, index: u32)
-> Result<Bytes, EventError>;
fn get_events_count(&self, contract_address: &Address) -> Result<u32, EventError>;
fn get_native_events_count(&self, contract_address: &Address) -> Result<u32, EventError>;
fn call_contract(
&self,
address: &Address,
call_def: CallDef,
use_proxy: bool
) -> OdraResult<Bytes>;
fn new_contract(
&self,
name: &str,
init_args: RuntimeArgs,
entry_points_caller: EntryPointsCaller
) -> OdraResult<Address>;
fn upgrade_contract(
&self,
name: &str,
contract_to_upgrade: Address,
upgrade_args: RuntimeArgs,
entry_points_caller: EntryPointsCaller
) -> OdraResult<Address>;
fn register_contract(
&self,
address: Address,
contract_name: String,
entry_points_caller: EntryPointsCaller
);
fn contract_env(&self) -> ContractEnv;
fn gas_report(&self) -> GasReport;
fn last_call_gas_cost(&self) -> u64;
fn sign_message(&self, message: &Bytes, address: &Address) -> Bytes;
fn public_key(&self, address: &Address) -> PublicKey;
fn transfer(&self, to: Address, amount: U512) -> OdraResult<()>;
}
#[derive(Clone)]
pub struct HostEnv {
backend: Rc<dyn HostContext>,
last_call_result: Rc<RefCell<Option<CallResult>>>,
deployed_contracts: Rc<RefCell<BTreeMap<Address, DeployedContract>>>,
captures_events: Rc<RefCell<bool>>
}
impl HostEnv {
pub fn new(backend: Rc<dyn HostContext>) -> HostEnv {
HostEnv {
backend,
last_call_result: RefCell::new(None).into(),
deployed_contracts: RefCell::new(Default::default()).into(),
captures_events: Rc::new(RefCell::new(true))
}
}
pub fn set_captures_events(&self, captures: bool) {
*self.captures_events.borrow_mut() = captures;
if captures {
let contract_addresses: Vec<Address> =
self.deployed_contracts.borrow().keys().copied().collect();
for contract_address in contract_addresses {
self.init_events(&contract_address);
}
}
}
pub fn get_account(&self, index: usize) -> Address {
let backend = self.backend.as_ref();
backend.get_account(index)
}
pub fn get_validator(&self, index: usize) -> PublicKey {
let backend = self.backend.as_ref();
backend.get_validator(index)
}
pub fn set_caller(&self, address: Address) {
if address.is_contract() {
panic!("Caller cannot be a contract: {:?}", address)
}
let backend = self.backend.as_ref();
backend.set_caller(address)
}
pub fn advance_block_time(&self, time_diff: u64) {
let backend = self.backend.as_ref();
backend.advance_block_time(time_diff)
}
pub fn advance_with_auctions(&self, time_diff: u64) {
let backend = self.backend.as_ref();
backend.advance_with_auctions(time_diff);
}
pub fn auction_delay(&self) -> u64 {
let backend = self.backend.as_ref();
backend.auction_delay()
}
pub fn unbonding_delay(&self) -> u64 {
let backend = self.backend.as_ref();
backend.unbonding_delay()
}
pub fn delegated_amount(&self, delegator: Address, validator: PublicKey) -> U512 {
let backend = self.backend.as_ref();
backend.delegated_amount(delegator, validator)
}
pub fn remove_validator(&self, index: usize) {
let backend = self.backend.as_ref();
backend.remove_validator(index);
}
pub fn block_time(&self) -> u64 {
let backend = self.backend.as_ref();
backend.block_time()
}
pub fn block_time_millis(&self) -> u64 {
let backend = self.backend.as_ref();
backend.block_time()
}
pub fn block_time_secs(&self) -> u64 {
let backend = self.backend.as_ref();
backend.block_time().checked_div(1000).unwrap()
}
pub fn new_contract(
&self,
name: &str,
init_args: RuntimeArgs,
entry_points_caller: EntryPointsCaller
) -> OdraResult<Address> {
let mut entry_points_caller = entry_points_caller.clone();
entry_points_caller.remove_entry_point("upgrade");
let backend = self.backend.as_ref();
let contract_address = backend.new_contract(name, init_args, entry_points_caller)?;
self.deployed_contracts
.borrow_mut()
.insert(contract_address, DeployedContract::new(contract_address));
Ok(contract_address)
}
pub fn upgrade_contract(
&self,
name: &str,
contract_to_upgrade: Address,
upgrade_args: RuntimeArgs,
entry_points_caller: EntryPointsCaller
) -> OdraResult<Address> {
let mut entry_points_caller = entry_points_caller.clone();
entry_points_caller.remove_entry_point("init");
let backend = self.backend.as_ref();
let upgraded_contract = backend.upgrade_contract(
name,
contract_to_upgrade,
upgrade_args,
entry_points_caller
)?;
let mut contracts = self.deployed_contracts.borrow_mut();
let contract = contracts.get_mut(&upgraded_contract).unwrap();
contract.current_version += 1;
contract.native_events_count = 0;
Ok(upgraded_contract)
}
pub fn register_contract(
&self,
address: Address,
contract_name: String,
entry_points_caller: EntryPointsCaller
) {
let backend = self.backend.as_ref();
backend.register_contract(address, contract_name, entry_points_caller);
self.deployed_contracts
.borrow_mut()
.insert(address, DeployedContract::new(address));
}
pub fn call_contract<T: FromBytes + CLTyped>(
&self,
address: Address,
call_def: CallDef
) -> OdraResult<T> {
let use_proxy = T::cl_type() != <()>::cl_type() || !call_def.amount().is_zero();
let call_result = self.raw_call_contract(address, call_def, use_proxy);
call_result.map(|bytes| {
T::from_bytes(&bytes)
.map(|(obj, _)| obj)
.map_err(|_| OdraError::VmError(VmError::Deserialization))
})?
}
pub fn raw_call_contract(
&self,
address: Address,
call_def: CallDef,
use_proxy: bool
) -> OdraResult<Bytes> {
let call_result = {
let backend = self.backend.as_ref();
backend.call_contract(&address, call_def, use_proxy)
};
let mut events_map: BTreeMap<Address, Vec<Bytes>> = BTreeMap::new();
let mut native_events_map: BTreeMap<Address, Vec<Bytes>> = BTreeMap::new();
let captures_events = *self.captures_events.borrow();
if captures_events {
self.deployed_contracts.borrow_mut().iter_mut().for_each(
|(contract_address, contract)| {
let events = self.last_events(contract);
let native_events = self.last_native_events(contract);
events_map.insert(*contract_address, events);
native_events_map.insert(*contract_address, native_events);
}
);
}
let backend = self.backend.as_ref();
let last_call_gas_cost = backend.last_call_gas_cost();
self.last_call_result.replace(Some(CallResult::new(
address,
backend.caller(),
last_call_gas_cost,
call_result.clone(),
events_map,
native_events_map
)));
call_result
}
pub fn contract_env(&self) -> ContractEnv {
self.backend.contract_env()
}
pub fn gas_report(&self) -> GasReport {
self.backend.gas_report().clone()
}
pub fn balance_of<T: Addressable>(&self, addr: &T) -> U512 {
let backend = self.backend.as_ref();
backend.balance_of(&addr.address())
}
pub fn get_event<T: FromBytes + EventInstance, R: Addressable>(
&self,
addr: &R,
index: i32
) -> Result<T, EventError> {
let contract_address = addr.address();
let backend = self.backend.as_ref();
let events_count = self.events_count(&contract_address);
let event_absolute_position = crate::utils::event_absolute_position(events_count, index)
.ok_or(EventError::IndexOutOfBounds)?;
let bytes = backend.get_event(&contract_address, event_absolute_position)?;
let (event, remainder) = T::from_bytes(&bytes).map_err(|_| EventError::Parsing)?;
if remainder.is_empty() {
Ok(event)
} else {
Err(EventError::Formatting)
}
}
pub fn get_native_event<T: FromBytes + EventInstance, R: Addressable>(
&self,
addr: &R,
index: i32
) -> Result<T, EventError> {
let contract_address = addr.address();
let backend = self.backend.as_ref();
let events_count = self.native_events_count(&contract_address);
let event_absolute_position = crate::utils::event_absolute_position(events_count, index)
.ok_or(EventError::IndexOutOfBounds)?;
let bytes = backend.get_native_event(&contract_address, event_absolute_position)?;
T::from_bytes(&bytes)
.map_err(|_| EventError::Parsing)
.map(|r| r.0)
}
pub fn get_event_bytes<T: Addressable>(
&self,
addr: &T,
index: u32
) -> Result<Bytes, EventError> {
let backend = self.backend.as_ref();
backend.get_event(&addr.address(), index)
}
pub fn get_native_event_bytes<T: Addressable>(
&self,
addr: &T,
index: u32
) -> Result<Bytes, EventError> {
let backend = self.backend.as_ref();
backend.get_native_event(&addr.address(), index)
}
pub fn event_names<T: Addressable>(&self, addr: &T) -> Vec<String> {
let events_count = self.events_count(addr);
let backend = self.backend.as_ref();
(0..events_count)
.map(|event_id| {
backend
.get_event(&addr.address(), event_id)
.and_then(|bytes| utils::extract_event_name(&bytes))
.unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
})
.collect()
}
pub fn events<T: Addressable>(&self, addr: &T) -> Vec<Bytes> {
let backend = self.backend.as_ref();
let contract_address = addr.address();
let events_count = backend
.get_events_count(&contract_address)
.unwrap_or_default();
(0..events_count)
.map(|event_id| {
backend
.get_event(&contract_address, event_id)
.unwrap_or_else(|e| {
panic!(
"Couldn't get event at address {:?} with id {}: {:?}",
&contract_address, event_id, e
)
})
})
.collect()
}
pub fn events_count<T: Addressable>(&self, addr: &T) -> u32 {
let backend = self.backend.as_ref();
backend
.get_events_count(&addr.address())
.unwrap_or_default()
}
pub fn native_events_count<T: Addressable>(&self, addr: &T) -> u32 {
let backend = self.backend.as_ref();
backend
.get_native_events_count(&addr.address())
.unwrap_or_default()
}
pub fn emitted_event<T: ToBytes + EventInstance, R: Addressable>(
&self,
addr: &R,
event: T
) -> bool {
let contract_address = addr.address();
let events_count = self.events_count(addr);
let event_bytes = Bytes::from(
event
.to_bytes()
.unwrap_or_else(|_| panic!("Couldn't serialize event"))
);
(0..events_count)
.map(|event_id| {
self.get_event_bytes(&contract_address, event_id)
.unwrap_or_else(|e| {
panic!(
"Couldn't get event at address {:?} with id {}: {:?}",
&contract_address, event_id, e
)
})
})
.any(|bytes| bytes == event_bytes)
}
pub fn emitted_native_event<T: ToBytes + EventInstance, R: Addressable>(
&self,
addr: &R,
event: T
) -> bool {
let contract_address = addr.address();
let events_count = self.native_events_count(addr);
if events_count > 0 {
let event_bytes = Bytes::from(
event
.to_bytes()
.unwrap_or_else(|_| panic!("Couldn't serialize event"))
);
(0..events_count)
.map(|event_id| {
self.get_native_event_bytes(&contract_address, event_id)
.unwrap_or_else(|e| {
panic!(
"Couldn't get event at address {:?} with id {}: {:?}",
&contract_address, event_id, e
)
})
})
.any(|bytes| bytes == event_bytes)
} else {
false
}
}
pub fn emitted<T: AsRef<str>, R: Addressable>(&self, addr: &R, event_name: T) -> bool {
let events_count = self.events_count(addr);
(0..events_count)
.map(|event_id| {
self.get_event_bytes(addr, event_id).unwrap_or_else(|e| {
panic!(
"Couldn't get event at address {:?} with id {}: {:?}",
addr.address(),
event_id,
e
)
})
})
.any(|bytes| {
utils::extract_event_name(&bytes)
.unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
.as_str()
== event_name.as_ref()
})
}
pub fn emitted_native<T: AsRef<str>, R: Addressable>(&self, addr: &R, event_name: T) -> bool {
let events_count = self.native_events_count(addr);
(0..events_count)
.map(|event_id| {
self.get_native_event_bytes(addr, event_id)
.unwrap_or_else(|e| {
panic!(
"Couldn't get event at address {:?} with id {}: {:?}",
addr.address(),
event_id,
e
)
})
})
.any(|bytes| {
utils::extract_event_name(&bytes)
.unwrap_or_else(|e| panic!("Couldn't extract event name: {:?}", e))
.as_str()
== event_name.as_ref()
})
}
pub fn last_call_result(&self, contract_address: Address) -> ContractCallResult {
self.last_call_result
.borrow()
.clone()
.unwrap()
.contract_last_call(contract_address)
}
pub fn sign_message(&self, message: &Bytes, address: &Address) -> Bytes {
let backend = self.backend.as_ref();
backend.sign_message(message, address)
}
pub fn public_key(&self, address: &Address) -> PublicKey {
let backend = self.backend.as_ref();
backend.public_key(address)
}
pub fn caller(&self) -> Address {
let backend = self.backend.as_ref();
backend.caller()
}
pub fn set_gas(&self, gas: u64) {
let backend = self.backend.as_ref();
backend.set_gas(gas)
}
pub fn transfer(&self, to: Address, amount: U512) -> OdraResult<()> {
if to.is_contract() {
return Err(OdraError::ExecutionError(
ExecutionError::TransferToContract
));
}
let backend = self.backend.as_ref();
backend.transfer(to, amount)
}
fn last_events(&self, contract: &mut DeployedContract) -> Vec<Bytes> {
let old_count = contract.events_count;
let new_count = self.events_count(&contract.address);
let mut events = vec![];
for count in old_count..new_count {
let event = self.get_event_bytes(&contract.address, count).unwrap();
events.push(event);
}
contract.events_count = new_count;
events
}
fn last_native_events(&self, contract: &mut DeployedContract) -> Vec<Bytes> {
let old_count = contract.native_events_count;
let new_count = self.native_events_count(&contract.address);
let mut events = vec![];
for count in old_count..new_count {
let event = self
.get_native_event_bytes(&contract.address, count)
.unwrap();
events.push(event);
}
contract.native_events_count = new_count;
events
}
fn init_events(&self, contract_address: &Address) {
let needs_init = {
let contracts = self.deployed_contracts.borrow();
contracts
.get(contract_address)
.map(|contract| !contract.events_initialized)
.unwrap_or(false)
};
if needs_init {
let events_count = self.events_count(contract_address);
let native_events_count = self.native_events_count(contract_address);
let mut contracts = self.deployed_contracts.borrow_mut();
let contract = contracts.get_mut(contract_address).unwrap();
contract.events_count = events_count;
contract.native_events_count = native_events_count;
contract.events_initialized = true;
}
}
}
#[cfg(test)]
mod test {
use core::fmt::Debug;
use super::*;
use casper_event_standard::Event;
use casper_types::account::AccountHash;
use casper_types::contracts::ContractPackageHash;
use mockall::{mock, predicate};
use std::sync::Mutex;
static IDENT_MTX: Mutex<()> = Mutex::new(());
static EPC_MTX: Mutex<()> = Mutex::new(());
#[derive(Debug, Event, PartialEq)]
struct TestEv {}
mock! {
TestRef {}
impl HasIdent for TestRef {
fn ident() -> String;
}
impl EntryPointsCallerProvider for TestRef {
fn entry_points_caller(env: &HostEnv) -> EntryPointsCaller;
}
impl HostRef for TestRef {
fn new(address: Address, env: HostEnv) -> Self;
fn with_tokens(&self, tokens: U512) -> Self;
fn contract_address(&self) -> Address;
fn env(&self) -> &HostEnv;
fn get_event<T>(&self, index: i32) -> Result<T, EventError> where T: FromBytes + EventInstance + 'static;
fn last_call(&self) -> ContractCallResult;
}
}
impl crate::ContractRef for MockTestRef {
fn new(_env: Rc<ContractEnv>, _address: Address) -> Self {
unimplemented!()
}
fn address(&self) -> &Address {
unimplemented!()
}
fn with_tokens(&self, _tokens: U512) -> Self {
unimplemented!()
}
}
impl OdraContract for MockTestRef {
type HostRef = MockTestRef;
type ContractRef = MockTestRef;
type InitArgs = NoArgs;
type UpgradeArgs = NoArgs;
}
mock! {
Ev {}
impl Into<RuntimeArgs> for Ev {
fn into(self) -> RuntimeArgs;
}
}
#[test]
fn test_deploy_with_default_args() {
let _i = IDENT_MTX.lock();
let _e = EPC_MTX.lock();
let indent_ctx = MockTestRef::ident_context();
indent_ctx.expect().returning(|| "TestRef".to_string());
let epc_ctx = MockTestRef::entry_points_caller_context();
epc_ctx
.expect()
.returning(|h| EntryPointsCaller::new(h.clone(), vec![], |_, _| Ok(Bytes::default())));
let instance_ctx = MockTestRef::new_context();
instance_ctx
.expect()
.times(1)
.returning(|_, _| MockTestRef::default());
let mut ctx = MockHostContext::new();
ctx.expect_new_contract()
.returning(|_, _, _| Ok(Address::Account(AccountHash::new([0; 32]))));
let env = HostEnv::new(Rc::new(ctx));
MockTestRef::deploy(&env, NoArgs);
}
#[test]
fn test_load_ref() {
let _e = EPC_MTX.lock();
let _i = IDENT_MTX.lock();
let epc_ctx = MockTestRef::entry_points_caller_context();
epc_ctx
.expect()
.returning(|h| EntryPointsCaller::new(h.clone(), vec![], |_, _| Ok(Bytes::default())));
let indent_ctx = MockTestRef::ident_context();
indent_ctx.expect().returning(|| "TestRef".to_string());
let mut ctx = MockHostContext::new();
ctx.expect_register_contract().returning(|_, _, _| ());
ctx.expect_get_events_count().returning(|_| Ok(0));
ctx.expect_get_native_events_count().returning(|_| Ok(0));
let instance_ctx = MockTestRef::new_context();
instance_ctx
.expect()
.times(1)
.returning(|_, _| MockTestRef::default());
let env = HostEnv::new(Rc::new(ctx));
let address = Address::Account(AccountHash::new([0; 32]));
MockTestRef::load(&env, address);
}
#[test]
fn test_host_env() {
let mut ctx = MockHostContext::new();
ctx.expect_new_contract()
.returning(|_, _, _| Ok(Address::Account(AccountHash::new([0; 32]))));
ctx.expect_caller()
.returning(|| Address::Account(AccountHash::new([2; 32])))
.times(1);
ctx.expect_gas_report().returning(GasReport::new).times(1);
ctx.expect_set_gas().returning(|_| ()).times(1);
let env = HostEnv::new(Rc::new(ctx));
assert_eq!(env.caller(), Address::Account(AccountHash::new([2; 32])));
env.gas_report();
env.set_gas(1_000u64)
}
#[test]
fn test_successful_transfer_to_account() {
let mut ctx = MockHostContext::new();
ctx.expect_transfer().returning(|_, _| Ok(()));
let env = HostEnv::new(Rc::new(ctx));
let addr = Address::Account(AccountHash::new([0; 32]));
let result = env.transfer(addr, 100.into());
assert!(result.is_ok());
}
#[test]
fn test_failing_transfer_to_account() {
let mut ctx = MockHostContext::new();
ctx.expect_transfer()
.returning(|_, _| Err(OdraError::ExecutionError(ExecutionError::UnwrapError)));
let env = HostEnv::new(Rc::new(ctx));
let addr = Address::Account(AccountHash::new([0; 32]));
let result = env.transfer(addr, 100.into());
assert_eq!(
result.err(),
Some(OdraError::ExecutionError(ExecutionError::UnwrapError))
);
}
#[test]
fn test_transfer_to_contract() {
let mut ctx = MockHostContext::new();
ctx.expect_transfer().returning(|_, _| Ok(()));
let env = HostEnv::new(Rc::new(ctx));
let addr = Address::Contract(ContractPackageHash::new([0; 32]));
let result = env.transfer(addr, 100.into());
assert_eq!(
result,
Err(OdraError::ExecutionError(
ExecutionError::TransferToContract
))
);
}
#[test]
fn test_get_event() {
let addr = Address::Account(AccountHash::new([0; 32]));
let mut ctx = MockHostContext::new();
ctx.expect_get_events_count().returning(|_| Ok(2));
ctx.expect_get_event()
.with(predicate::always(), predicate::eq(0))
.returning(|_, _| Ok(vec![1].into()));
ctx.expect_get_event()
.with(predicate::always(), predicate::eq(1))
.returning(|_, _| Ok(TestEv {}.to_bytes().unwrap().into()));
let env = HostEnv::new(Rc::new(ctx));
assert_eq!(env.get_event(&addr, 1), Ok(TestEv {}));
assert_eq!(env.get_event(&addr, -1), Ok(TestEv {}));
assert_eq!(
env.get_event::<TestEv, _>(&addr, 0),
Err(EventError::Parsing)
);
assert_eq!(
env.get_event::<TestEv, _>(&addr, -2),
Err(EventError::Parsing)
);
assert_eq!(
env.get_event::<TestEv, _>(&addr, 2),
Err(EventError::IndexOutOfBounds)
);
assert_eq!(
env.get_event::<TestEv, _>(&addr, -3),
Err(EventError::IndexOutOfBounds)
);
}
#[test]
fn test_events_works() {
let addr = Address::Account(AccountHash::new([0; 32]));
let mut ctx = MockHostContext::new();
ctx.expect_get_events_count().returning(|_| Ok(2));
ctx.expect_get_event()
.with(predicate::always(), predicate::eq(0))
.returning(|_, _| Ok(vec![1].into()));
ctx.expect_get_event()
.with(predicate::always(), predicate::eq(1))
.returning(|_, _| Ok(vec![1, 0, 1].into()));
let env = HostEnv::new(Rc::new(ctx));
assert_eq!(
env.events(&addr),
vec![vec![1].into(), vec![1, 0, 1].into()]
);
}
#[test]
#[should_panic(
expected = "Couldn't get event at address Account(AccountHash(0000000000000000000000000000000000000000000000000000000000000000)) with id 0: CouldntExtractEventData"
)]
fn test_events_fails() {
let addr = Address::Account(AccountHash::new([0; 32]));
let mut ctx = MockHostContext::new();
ctx.expect_get_events_count().returning(|_| Ok(2));
ctx.expect_get_event()
.with(predicate::always(), predicate::eq(0))
.returning(|_, _| Err(EventError::CouldntExtractEventData));
let env = HostEnv::new(Rc::new(ctx));
env.events(&addr);
}
#[test]
fn test_emitted() {
let addr = Address::Account(AccountHash::new([0; 32]));
let mut ctx = MockHostContext::new();
ctx.expect_get_events_count().returning(|_| Ok(1));
ctx.expect_get_event()
.returning(|_, _| Ok(TestEv {}.to_bytes().unwrap().into()));
let env = HostEnv::new(Rc::new(ctx));
assert!(env.emitted(&addr, "TestEv"));
assert!(!env.emitted(&addr, "AnotherEvent"));
}
}