123456789101112131415161718use crate::{19 CodeHash, Event, Config, Pallet as Contracts, TrieId, BalanceOf, ContractInfo,20 gas::GasMeter,21 rent::Rent,22 storage::{self, Storage},23 Error, ContractInfoOf, Schedule, AliveContractInfo,24};25use sp_core::crypto::UncheckedFrom;26use sp_std::{prelude::*, marker::PhantomData};27use sp_runtime::{28 Perbill,29 traits::{Bounded, Zero, Convert, Saturating},30};31use frame_support::{32 dispatch::{DispatchResult, DispatchError},33 traits::{ExistenceRequirement, Currency, Time, Randomness, Get},34 weights::Weight,35 ensure,36};37use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};3839pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;40pub type MomentOf<T> = <<T as Config>::Time as Time>::Moment;41pub type SeedOf<T> = <T as frame_system::Config>::Hash;42pub type BlockNumberOf<T> = <T as frame_system::Config>::BlockNumber;43pub type StorageKey = [u8; 32];44pub type ExecResult = Result<ExecReturnValue, ExecError>;454647pub type TopicOf<T> = <T as frame_system::Config>::Hash;48495051525354#[cfg_attr(test, derive(Debug, PartialEq))]55pub enum ErrorOrigin {56 57 58 59 60 Caller,61 62 Callee,63}646566#[cfg_attr(test, derive(Debug, PartialEq))]67pub struct ExecError {68 69 pub error: DispatchError,70 71 pub origin: ErrorOrigin,72}7374impl<T: Into<DispatchError>> From<T> for ExecError {75 fn from(error: T) -> Self {76 Self {77 error: error.into(),78 origin: ErrorOrigin::Caller,79 }80 }81}828384#[derive(codec::Encode)]85#[cfg_attr(test, derive(Debug, PartialEq))]86pub struct RentParams<T: Config> {87 88 total_balance: BalanceOf<T>,89 90 free_balance: BalanceOf<T>,91 92 subsistence_threshold: BalanceOf<T>,93 94 deposit_per_contract: BalanceOf<T>,95 96 deposit_per_storage_byte: BalanceOf<T>,97 98 deposit_per_storage_item: BalanceOf<T>,99 100 rent_allowance: BalanceOf<T>,101 102 rent_fraction: Perbill,103 104 storage_size: u32,105 106 code_size: u32,107 108 code_refcount: u32,109 110 _reserved: Option<()>,111}112113impl<T> RentParams<T>114where115 T: Config,116 T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,117{118 fn new<E: Executable<T>>(119 account_id: &T::AccountId,120 contract: &AliveContractInfo<T>,121 executable: &E,122 ) -> Self {123 Self {124 total_balance: T::Currency::total_balance(account_id),125 free_balance: T::Currency::free_balance(account_id),126 subsistence_threshold: <Contracts<T>>::subsistence_threshold(),127 deposit_per_contract: T::DepositPerContract::get(),128 deposit_per_storage_byte: T::DepositPerStorageByte::get(),129 deposit_per_storage_item: T::DepositPerStorageItem::get(),130 rent_allowance: contract.rent_allowance,131 rent_fraction: T::RentFraction::get(),132 storage_size: contract.storage_size,133 code_size: executable.aggregate_code_len(),134 code_refcount: executable.refcount(),135 _reserved: None,136 }137 }138}139140141#[cfg(test)]142impl<T: Config> Default for RentParams<T> {143 fn default() -> Self {144 Self {145 total_balance: Default::default(),146 free_balance: Default::default(),147 subsistence_threshold: Default::default(),148 deposit_per_contract: Default::default(),149 deposit_per_storage_byte: Default::default(),150 deposit_per_storage_item: Default::default(),151 rent_allowance: Default::default(),152 rent_fraction: Default::default(),153 storage_size: Default::default(),154 code_size: Default::default(),155 code_refcount: Default::default(),156 _reserved: Default::default(),157 }158 }159}160161162163164165166167168169170pub trait Ext: sealing::Sealed {171 type T: Config;172173 174 175 176 177 fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>>;178179 180 181 fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> DispatchResult;182183 184 185 186 187 188 189 190 191 192 fn instantiate(193 &mut self,194 code: CodeHash<Self::T>,195 value: BalanceOf<Self::T>,196 gas_meter: &mut GasMeter<Self::T>,197 input_data: Vec<u8>,198 salt: &[u8],199 ) -> Result<(AccountIdOf<Self::T>, ExecReturnValue, u32), (ExecError, u32)>;200201 202 fn transfer(&mut self, to: &AccountIdOf<Self::T>, value: BalanceOf<Self::T>) -> DispatchResult;203204 205 206 207 208 209 210 211 212 213 214 215 216 fn terminate(217 &mut self,218 beneficiary: &AccountIdOf<Self::T>,219 ) -> Result<u32, (DispatchError, u32)>;220221 222 223 224 225 226 227 228 fn call(229 &mut self,230 to: &AccountIdOf<Self::T>,231 value: BalanceOf<Self::T>,232 gas_meter: &mut GasMeter<Self::T>,233 input_data: Vec<u8>,234 ) -> Result<(ExecReturnValue, u32), (ExecError, u32)>;235236 237 238 239 240 241 242 243 244 245 246 247 fn restore_to(248 &mut self,249 dest: AccountIdOf<Self::T>,250 code_hash: CodeHash<Self::T>,251 rent_allowance: BalanceOf<Self::T>,252 delta: Vec<StorageKey>,253 ) -> Result<(u32, u32), (DispatchError, u32, u32)>;254255 256 fn caller(&self) -> &AccountIdOf<Self::T>;257258 259 fn address(&self) -> &AccountIdOf<Self::T>;260261 262 263 264 fn balance(&self) -> BalanceOf<Self::T>;265266 267 fn value_transferred(&self) -> BalanceOf<Self::T>;268269 270 fn now(&self) -> &MomentOf<Self::T>;271272 273 fn minimum_balance(&self) -> BalanceOf<Self::T>;274275 276 fn tombstone_deposit(&self) -> BalanceOf<Self::T>;277278 279 fn random(&self, subject: &[u8]) -> (SeedOf<Self::T>, BlockNumberOf<Self::T>);280281 282 283 284 fn deposit_event(&mut self, topics: Vec<TopicOf<Self::T>>, data: Vec<u8>);285286 287 fn set_rent_allowance(&mut self, rent_allowance: BalanceOf<Self::T>);288289 290 fn rent_allowance(&self) -> BalanceOf<Self::T>;291292 293 fn block_number(&self) -> BlockNumberOf<Self::T>;294295 296 fn max_value_size(&self) -> u32;297298 299 fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T>;300301 302 fn schedule(&self) -> &Schedule<Self::T>;303304 305 fn rent_params(&self) -> &RentParams<Self::T>;306}307308309#[cfg_attr(test, derive(Clone, Copy, PartialEq))]310pub enum ExportedFunction {311 312 Constructor,313 314 Call,315}316317318319320321pub trait Executable<T: Config>: Sized {322 323 fn from_storage(324 code_hash: CodeHash<T>,325 schedule: &Schedule<T>,326 gas_meter: &mut GasMeter<T>,327 ) -> Result<Self, DispatchError>;328329 330 331 332 333 334 fn from_storage_noinstr(code_hash: CodeHash<T>) -> Result<Self, DispatchError>;335336 337 fn drop_from_storage(self);338339 340 341 342 fn add_user(code_hash: CodeHash<T>) -> Result<u32, DispatchError>;343344 345 346 347 fn remove_user(code_hash: CodeHash<T>) -> u32;348349 350 351 352 353 354 355 356 357 358 fn execute<E: Ext<T = T>>(359 self,360 ext: E,361 function: &ExportedFunction,362 input_data: Vec<u8>,363 gas_meter: &mut GasMeter<T>,364 ) -> ExecResult;365366 367 fn code_hash(&self) -> &CodeHash<T>;368369 370 371 372 373 374 375 376 377 378 379 fn occupied_storage(&self) -> u32;380381 382 fn code_len(&self) -> u32;383384 385 fn aggregate_code_len(&self) -> u32;386387 388 fn refcount(&self) -> u32;389}390391pub struct ExecutionContext<'a, T: Config + 'a, E> {392 caller: Option<&'a ExecutionContext<'a, T, E>>,393 self_account: T::AccountId,394 self_trie_id: Option<TrieId>,395 depth: usize,396 schedule: &'a Schedule<T>,397 timestamp: MomentOf<T>,398 block_number: T::BlockNumber,399 _phantom: PhantomData<E>,400}401402impl<'a, T, E> ExecutionContext<'a, T, E>403where404 T: Config,405 T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,406 E: Executable<T>,407{408 409 410 411 412 pub fn top_level(origin: T::AccountId, schedule: &'a Schedule<T>) -> Self {413 ExecutionContext {414 caller: None,415 self_trie_id: None,416 self_account: origin,417 depth: 0,418 schedule,419 timestamp: T::Time::now(),420 block_number: <frame_system::Pallet<T>>::block_number(),421 _phantom: Default::default(),422 }423 }424425 fn nested<'b, 'c: 'b>(426 &'c self,427 dest: T::AccountId,428 trie_id: TrieId,429 ) -> ExecutionContext<'b, T, E> {430 ExecutionContext {431 caller: Some(self),432 self_trie_id: Some(trie_id),433 self_account: dest,434 depth: self.depth + 1,435 schedule: self.schedule,436 timestamp: self.timestamp.clone(),437 block_number: self.block_number.clone(),438 _phantom: Default::default(),439 }440 }441442 443 444 445 446 447 pub fn call(448 &mut self,449 dest: T::AccountId,450 value: BalanceOf<T>,451 gas_meter: &mut GasMeter<T>,452 input_data: Vec<u8>,453 ) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {454 if self.depth == T::MaxDepth::get() as usize {455 return Err((Error::<T>::MaxCallDepthReached.into(), 0));456 }457458 let contract = <ContractInfoOf<T>>::get(&dest)459 .and_then(|contract| contract.get_alive())460 .ok_or((Error::<T>::NotCallable.into(), 0))?;461462 let executable = E::from_storage(contract.code_hash, &self.schedule, gas_meter)463 .map_err(|e| (e.into(), 0))?;464 let code_len = executable.code_len();465466 467 468 469 470 471 let contract = Rent::<T, E>::charge(&dest, contract, executable.occupied_storage())472 .map_err(|e| (e.into(), code_len))?473 .ok_or((Error::<T>::NotCallable.into(), code_len))?;474475 let transactor_kind = self.transactor_kind();476 let caller = self.self_account.clone();477478 let result = self479 .with_nested_context(dest.clone(), contract.trie_id.clone(), |nested| {480 if value > BalanceOf::<T>::zero() {481 transfer::<T>(TransferCause::Call, transactor_kind, &caller, &dest, value)?482 }483484 let call_context =485 nested.new_call_context(caller, &dest, value, &contract, &executable);486487 let output = executable488 .execute(call_context, &ExportedFunction::Call, input_data, gas_meter)489 .map_err(|e| ExecError {490 error: e.error,491 origin: ErrorOrigin::Callee,492 })?;493 Ok(output)494 })495 .map_err(|e| (e, code_len))?;496 Ok((result, code_len))497 }498499 pub fn instantiate(500 &mut self,501 endowment: BalanceOf<T>,502 gas_meter: &mut GasMeter<T>,503 executable: E,504 input_data: Vec<u8>,505 salt: &[u8],506 ) -> Result<(T::AccountId, ExecReturnValue), ExecError> {507 if self.depth == T::MaxDepth::get() as usize {508 Err(Error::<T>::MaxCallDepthReached)?509 }510511 let transactor_kind = self.transactor_kind();512 let caller = self.self_account.clone();513 let dest = Contracts::<T>::contract_address(&caller, executable.code_hash(), salt);514515 let output = frame_support::storage::with_transaction(|| {516 517 let dest_trie_id = Storage::<T>::generate_trie_id(&dest);518519 let output = self.with_nested_context(dest.clone(), dest_trie_id, |nested| {520 let contract = Storage::<T>::place_contract(521 &dest,522 nested523 .self_trie_id524 .clone()525 .expect("the nested context always has to have self_trie_id"),526 executable.code_hash().clone(),527 )?;528529 530 531 transfer::<T>(532 TransferCause::Instantiate,533 transactor_kind,534 &caller,535 &dest,536 endowment,537 )?;538539 540 541 542 543 let occupied_storage = executable.occupied_storage();544545 let call_context = nested.new_call_context(546 caller.clone(),547 &dest,548 endowment,549 &contract,550 &executable,551 );552553 let output = executable554 .execute(555 call_context,556 &ExportedFunction::Constructor,557 input_data,558 gas_meter,559 )560 .map_err(|e| ExecError {561 error: e.error,562 origin: ErrorOrigin::Callee,563 })?;564565 566 567 let contract = <ContractInfoOf<T>>::get(&dest)568 .and_then(|contract| contract.get_alive())569 .ok_or(Error::<T>::NotCallable)?;570571 572 573 574 575 576 Rent::<T, E>::charge(&dest, contract, occupied_storage)?577 .ok_or(Error::<T>::NewContractNotFunded)?;578579 580 deposit_event::<T>(vec![], Event::Instantiated(caller.clone(), dest.clone()));581582 Ok(output)583 });584585 use frame_support::storage::TransactionOutcome::*;586 match output {587 Ok(_) => Commit(output),588 Err(_) => Rollback(output),589 }590 })?;591592 Ok((dest, output))593 }594595 fn new_call_context<'b>(596 &'b mut self,597 caller: T::AccountId,598 dest: &T::AccountId,599 value: BalanceOf<T>,600 contract: &AliveContractInfo<T>,601 executable: &E,602 ) -> CallContext<'b, 'a, T, E> {603 let timestamp = self.timestamp.clone();604 let block_number = self.block_number.clone();605 CallContext {606 ctx: self,607 caller,608 value_transferred: value,609 timestamp,610 block_number,611 rent_params: RentParams::new(dest, contract, executable),612 _phantom: Default::default(),613 }614 }615616 617 fn with_nested_context<F>(&mut self, dest: T::AccountId, trie_id: TrieId, func: F) -> ExecResult618 where619 F: FnOnce(&mut ExecutionContext<T, E>) -> ExecResult,620 {621 use frame_support::storage::TransactionOutcome::*;622 let mut nested = self.nested(dest, trie_id);623 frame_support::storage::with_transaction(|| {624 let output = func(&mut nested);625 match output {626 Ok(ref rv) if !rv.flags.contains(ReturnFlags::REVERT) => Commit(output),627 _ => Rollback(output),628 }629 })630 }631632 633 634 fn is_live(&self, account: &T::AccountId) -> bool {635 &self.self_account == account || self.caller.map_or(false, |caller| caller.is_live(account))636 }637638 fn transactor_kind(&self) -> TransactorKind {639 if self.depth == 0 {640 debug_assert!(self.self_trie_id.is_none());641 debug_assert!(self.caller.is_none());642 debug_assert!(ContractInfoOf::<T>::get(&self.self_account).is_none());643 TransactorKind::PlainAccount644 } else {645 TransactorKind::Contract646 }647 }648}649650651enum TransactorKind {652 653 654 PlainAccount,655 656 Contract,657}658659660enum TransferCause {661 Call,662 Instantiate,663 Terminate,664}665666667668669670671672fn transfer<T: Config>(673 cause: TransferCause,674 origin: TransactorKind,675 transactor: &T::AccountId,676 dest: &T::AccountId,677 value: BalanceOf<T>,678) -> DispatchResult679where680 T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,681{682 use self::TransferCause::*;683 use self::TransactorKind::*;684685 686 687 let existence_requirement = match (cause, origin) {688 (Terminate, _) => ExistenceRequirement::AllowDeath,689 (_, Contract) => {690 ensure!(691 T::Currency::total_balance(transactor).saturating_sub(value)692 >= Contracts::<T>::subsistence_threshold(),693 Error::<T>::BelowSubsistenceThreshold,694 );695 ExistenceRequirement::KeepAlive696 }697 (_, PlainAccount) => ExistenceRequirement::KeepAlive,698 };699700 T::Currency::transfer(transactor, dest, value, existence_requirement)701 .map_err(|_| Error::<T>::TransferFailed)?;702703 Ok(())704}705706707708709710711712713714715716717struct CallContext<'a, 'b: 'a, T: Config + 'b, E> {718 ctx: &'a mut ExecutionContext<'b, T, E>,719 caller: T::AccountId,720 value_transferred: BalanceOf<T>,721 timestamp: MomentOf<T>,722 block_number: T::BlockNumber,723 rent_params: RentParams<T>,724 _phantom: PhantomData<E>,725}726727impl<'a, 'b: 'a, T, E> Ext for CallContext<'a, 'b, T, E>728where729 T: Config + 'b,730 T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,731 E: Executable<T>,732{733 type T = T;734735 fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>> {736 let trie_id = self.ctx.self_trie_id.as_ref().expect(737 "`ctx.self_trie_id` points to an alive contract within the `CallContext`;\738 it cannot be `None`;\739 expect can't fail;\740 qed",741 );742 Storage::<T>::read(trie_id, key)743 }744745 fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> DispatchResult {746 let trie_id = self.ctx.self_trie_id.as_ref().expect(747 "`ctx.self_trie_id` points to an alive contract within the `CallContext`;\748 it cannot be `None`;\749 expect can't fail;\750 qed",751 );752 753 754 755 756 757 Storage::<T>::write(&self.ctx.self_account, trie_id, &key, value)758 }759760 fn instantiate(761 &mut self,762 code_hash: CodeHash<T>,763 endowment: BalanceOf<T>,764 gas_meter: &mut GasMeter<T>,765 input_data: Vec<u8>,766 salt: &[u8],767 ) -> Result<(AccountIdOf<T>, ExecReturnValue, u32), (ExecError, u32)> {768 let executable =769 E::from_storage(code_hash, &self.ctx.schedule, gas_meter).map_err(|e| (e.into(), 0))?;770 let code_len = executable.code_len();771 self.ctx772 .instantiate(endowment, gas_meter, executable, input_data, salt)773 .map(|r| (r.0, r.1, code_len))774 .map_err(|e| (e, code_len))775 }776777 fn transfer(&mut self, to: &T::AccountId, value: BalanceOf<T>) -> DispatchResult {778 transfer::<T>(779 TransferCause::Call,780 TransactorKind::Contract,781 &self.ctx.self_account.clone(),782 to,783 value,784 )785 }786787 fn terminate(788 &mut self,789 beneficiary: &AccountIdOf<Self::T>,790 ) -> Result<u32, (DispatchError, u32)> {791 let self_id = self.ctx.self_account.clone();792 let value = T::Currency::free_balance(&self_id);793 if let Some(caller_ctx) = self.ctx.caller {794 if caller_ctx.is_live(&self_id) {795 return Err((Error::<T>::ReentranceDenied.into(), 0));796 }797 }798 transfer::<T>(799 TransferCause::Terminate,800 TransactorKind::Contract,801 &self_id,802 beneficiary,803 value,804 )805 .map_err(|e| (e, 0))?;806 if let Some(ContractInfo::Alive(info)) = ContractInfoOf::<T>::take(&self_id) {807 Storage::<T>::queue_trie_for_deletion(&info).map_err(|e| (e, 0))?;808 let code_len = E::remove_user(info.code_hash);809 Contracts::<T>::deposit_event(Event::Terminated(self_id, beneficiary.clone()));810 Ok(code_len)811 } else {812 panic!(813 "this function is only invoked by in the context of a contract;\814 this contract is therefore alive;\815 qed"816 );817 }818 }819820 fn call(821 &mut self,822 to: &T::AccountId,823 value: BalanceOf<T>,824 gas_meter: &mut GasMeter<T>,825 input_data: Vec<u8>,826 ) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {827 self.ctx.call(to.clone(), value, gas_meter, input_data)828 }829830 fn restore_to(831 &mut self,832 dest: AccountIdOf<Self::T>,833 code_hash: CodeHash<Self::T>,834 rent_allowance: BalanceOf<Self::T>,835 delta: Vec<StorageKey>,836 ) -> Result<(u32, u32), (DispatchError, u32, u32)> {837 if let Some(caller_ctx) = self.ctx.caller {838 if caller_ctx.is_live(&self.ctx.self_account) {839 return Err((Error::<T>::ReentranceDenied.into(), 0, 0));840 }841 }842843 let result = Rent::<T, E>::restore_to(844 self.ctx.self_account.clone(),845 dest.clone(),846 code_hash.clone(),847 rent_allowance,848 delta,849 );850 if let Ok(_) = result {851 deposit_event::<Self::T>(852 vec![],853 Event::Restored(854 self.ctx.self_account.clone(),855 dest,856 code_hash,857 rent_allowance,858 ),859 );860 }861 result862 }863864 fn address(&self) -> &T::AccountId {865 &self.ctx.self_account866 }867868 fn caller(&self) -> &T::AccountId {869 &self.caller870 }871872 fn balance(&self) -> BalanceOf<T> {873 T::Currency::free_balance(&self.ctx.self_account)874 }875876 fn value_transferred(&self) -> BalanceOf<T> {877 self.value_transferred878 }879880 fn random(&self, subject: &[u8]) -> (SeedOf<T>, BlockNumberOf<T>) {881 T::Randomness::random(subject)882 }883884 fn now(&self) -> &MomentOf<T> {885 &self.timestamp886 }887888 fn minimum_balance(&self) -> BalanceOf<T> {889 T::Currency::minimum_balance()890 }891892 fn tombstone_deposit(&self) -> BalanceOf<T> {893 T::TombstoneDeposit::get()894 }895896 fn deposit_event(&mut self, topics: Vec<T::Hash>, data: Vec<u8>) {897 deposit_event::<Self::T>(898 topics,899 Event::ContractEmitted(self.ctx.self_account.clone(), data),900 );901 }902903 fn set_rent_allowance(&mut self, rent_allowance: BalanceOf<T>) {904 if let Err(storage::ContractAbsentError) =905 Storage::<T>::set_rent_allowance(&self.ctx.self_account, rent_allowance)906 {907 panic!(908 "`self_account` points to an alive contract within the `CallContext`;909 set_rent_allowance cannot return `Err`; qed"910 );911 }912 }913914 fn rent_allowance(&self) -> BalanceOf<T> {915 Storage::<T>::rent_allowance(&self.ctx.self_account)916 .unwrap_or_else(|_| <BalanceOf<T>>::max_value()) 917 }918919 fn block_number(&self) -> T::BlockNumber {920 self.block_number921 }922923 fn max_value_size(&self) -> u32 {924 T::MaxValueSize::get()925 }926927 fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {928 T::WeightPrice::convert(weight)929 }930931 fn schedule(&self) -> &Schedule<Self::T> {932 &self.ctx.schedule933 }934935 fn rent_params(&self) -> &RentParams<Self::T> {936 &self.rent_params937 }938}939940fn deposit_event<T: Config>(topics: Vec<T::Hash>, event: Event<T>) {941 <frame_system::Pallet<T>>::deposit_event_indexed(942 &*topics,943 <T as Config>::Event::from(event).into(),944 )945}946947mod sealing {948 use super::*;949950 pub trait Sealed {}951952 impl<'a, 'b: 'a, T: Config, E> Sealed for CallContext<'a, 'b, T, E> {}953954 #[cfg(test)]955 impl Sealed for crate::wasm::MockExt {}956957 #[cfg(test)]958 impl Sealed for &mut crate::wasm::MockExt {}959}960961962963964965966#[cfg(test)]967mod tests {968 use super::*;969 use crate::{970 gas::GasMeter,971 tests::{ExtBuilder, Test, Event as MetaEvent},972 storage::{Storage, ContractAbsentError},973 tests::{974 ALICE, BOB, CHARLIE,975 test_utils::{place_contract, set_balance, get_balance},976 },977 exec::ExportedFunction::*,978 Error, Weight, CurrentSchedule,979 };980 use sp_core::Bytes;981 use frame_support::assert_noop;982 use sp_runtime::DispatchError;983 use assert_matches::assert_matches;984 use std::{cell::RefCell, collections::HashMap, rc::Rc};985 use pretty_assertions::{assert_eq, assert_ne};986987 type MockContext<'a> = ExecutionContext<'a, Test, MockExecutable>;988989 const GAS_LIMIT: Weight = 10_000_000_000;990991 thread_local! {992 static LOADER: RefCell<MockLoader> = RefCell::new(MockLoader::default());993 }994995 fn events() -> Vec<Event<Test>> {996 <frame_system::Pallet<Test>>::events()997 .into_iter()998 .filter_map(|meta| match meta.event {999 MetaEvent::pallet_contracts(contract_event) => Some(contract_event),1000 _ => None,1001 })1002 .collect()1003 }10041005 struct MockCtx<'a> {1006 ext: &'a mut dyn Ext<T = Test>,1007 input_data: Vec<u8>,1008 gas_meter: &'a mut GasMeter<Test>,1009 }10101011 #[derive(Clone)]1012 struct MockExecutable {1013 func: Rc<dyn Fn(MockCtx, &Self) -> ExecResult + 'static>,1014 func_type: ExportedFunction,1015 code_hash: CodeHash<Test>,1016 refcount: u64,1017 }10181019 #[derive(Default)]1020 struct MockLoader {1021 map: HashMap<CodeHash<Test>, MockExecutable>,1022 counter: u64,1023 }10241025 impl MockLoader {1026 fn insert(1027 func_type: ExportedFunction,1028 f: impl Fn(MockCtx, &MockExecutable) -> ExecResult + 'static,1029 ) -> CodeHash<Test> {1030 LOADER.with(|loader| {1031 let mut loader = loader.borrow_mut();1032 1033 let hash = <Test as frame_system::Config>::Hash::from_low_u64_be(loader.counter);1034 loader.counter += 1;1035 loader.map.insert(1036 hash,1037 MockExecutable {1038 func: Rc::new(f),1039 func_type,1040 code_hash: hash.clone(),1041 refcount: 1,1042 },1043 );1044 hash1045 })1046 }10471048 fn increment_refcount(code_hash: CodeHash<Test>) {1049 LOADER.with(|loader| {1050 let mut loader = loader.borrow_mut();1051 loader1052 .map1053 .entry(code_hash)1054 .and_modify(|executable| executable.refcount += 1)1055 .or_insert_with(|| panic!("code_hash does not exist"));1056 });1057 }10581059 fn decrement_refcount(code_hash: CodeHash<Test>) {1060 use std::collections::hash_map::Entry::Occupied;1061 LOADER.with(|loader| {1062 let mut loader = loader.borrow_mut();1063 let mut entry = match loader.map.entry(code_hash) {1064 Occupied(e) => e,1065 _ => panic!("code_hash does not exist"),1066 };1067 let refcount = &mut entry.get_mut().refcount;1068 *refcount -= 1;1069 if *refcount == 0 {1070 entry.remove();1071 }1072 });1073 }10741075 fn refcount(code_hash: &CodeHash<Test>) -> u32 {1076 LOADER.with(|loader| {1077 loader1078 .borrow()1079 .map1080 .get(code_hash)1081 .expect("code_hash does not exist")1082 .refcount()1083 })1084 }1085 }10861087 impl Executable<Test> for MockExecutable {1088 fn from_storage(1089 code_hash: CodeHash<Test>,1090 _schedule: &Schedule<Test>,1091 _gas_meter: &mut GasMeter<Test>,1092 ) -> Result<Self, DispatchError> {1093 Self::from_storage_noinstr(code_hash)1094 }10951096 fn from_storage_noinstr(code_hash: CodeHash<Test>) -> Result<Self, DispatchError> {1097 LOADER.with(|loader| {1098 loader1099 .borrow_mut()1100 .map1101 .get(&code_hash)1102 .cloned()1103 .ok_or(Error::<Test>::CodeNotFound.into())1104 })1105 }11061107 fn drop_from_storage(self) {1108 MockLoader::decrement_refcount(self.code_hash);1109 }11101111 fn add_user(code_hash: CodeHash<Test>) -> Result<u32, DispatchError> {1112 MockLoader::increment_refcount(code_hash);1113 Ok(0)1114 }11151116 fn remove_user(code_hash: CodeHash<Test>) -> u32 {1117 MockLoader::decrement_refcount(code_hash);1118 01119 }11201121 fn execute<E: Ext<T = Test>>(1122 self,1123 mut ext: E,1124 function: &ExportedFunction,1125 input_data: Vec<u8>,1126 gas_meter: &mut GasMeter<Test>,1127 ) -> ExecResult {1128 if let &Constructor = function {1129 MockLoader::increment_refcount(self.code_hash);1130 }1131 if function == &self.func_type {1132 (self.func)(1133 MockCtx {1134 ext: &mut ext,1135 input_data,1136 gas_meter,1137 },1138 &self,1139 )1140 } else {1141 exec_success()1142 }1143 }11441145 fn code_hash(&self) -> &CodeHash<Test> {1146 &self.code_hash1147 }11481149 fn occupied_storage(&self) -> u32 {1150 01151 }11521153 fn code_len(&self) -> u32 {1154 01155 }11561157 fn aggregate_code_len(&self) -> u32 {1158 01159 }11601161 fn refcount(&self) -> u32 {1162 self.refcount as u321163 }1164 }11651166 fn exec_success() -> ExecResult {1167 Ok(ExecReturnValue {1168 flags: ReturnFlags::empty(),1169 data: Bytes(Vec::new()),1170 })1171 }11721173 #[test]1174 fn it_works() {1175 thread_local! {1176 static TEST_DATA: RefCell<Vec<usize>> = RefCell::new(vec![0]);1177 }11781179 let value = Default::default();1180 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);1181 let exec_ch = MockLoader::insert(Call, |_ctx, _executable| {1182 TEST_DATA.with(|data| data.borrow_mut().push(1));1183 exec_success()1184 });11851186 ExtBuilder::default().build().execute_with(|| {1187 let schedule = <CurrentSchedule<Test>>::get();1188 let mut ctx = MockContext::top_level(ALICE, &schedule);1189 place_contract(&BOB, exec_ch);11901191 assert_matches!(ctx.call(BOB, value, &mut gas_meter, vec![]), Ok(_));1192 });11931194 TEST_DATA.with(|data| assert_eq!(*data.borrow(), vec![0, 1]));1195 }11961197 #[test]1198 fn transfer_works() {1199 1200 1201 let origin = ALICE;1202 let dest = BOB;12031204 ExtBuilder::default().build().execute_with(|| {1205 set_balance(&origin, 100);1206 set_balance(&dest, 0);12071208 super::transfer::<Test>(1209 super::TransferCause::Call,1210 super::TransactorKind::PlainAccount,1211 &origin,1212 &dest,1213 55,1214 )1215 .unwrap();12161217 assert_eq!(get_balance(&origin), 45);1218 assert_eq!(get_balance(&dest), 55);1219 });1220 }12211222 #[test]1223 fn changes_are_reverted_on_failing_call() {1224 1225 1226 let origin = ALICE;1227 let dest = BOB;12281229 let return_ch = MockLoader::insert(Call, |_, _| {1230 Ok(ExecReturnValue {1231 flags: ReturnFlags::REVERT,1232 data: Bytes(Vec::new()),1233 })1234 });12351236 ExtBuilder::default().build().execute_with(|| {1237 let schedule = <CurrentSchedule<Test>>::get();1238 let mut ctx = MockContext::top_level(origin.clone(), &schedule);1239 place_contract(&BOB, return_ch);1240 set_balance(&origin, 100);1241 let balance = get_balance(&dest);12421243 let output = ctx1244 .call(1245 dest.clone(),1246 55,1247 &mut GasMeter::<Test>::new(GAS_LIMIT),1248 vec![],1249 )1250 .unwrap();12511252 assert!(!output.0.is_success());1253 assert_eq!(get_balance(&origin), 100);12541255 1256 assert!(get_balance(&dest) < balance);1257 });1258 }12591260 #[test]1261 fn balance_too_low() {1262 1263 1264 let origin = ALICE;1265 let dest = BOB;12661267 ExtBuilder::default().build().execute_with(|| {1268 set_balance(&origin, 0);12691270 let result = super::transfer::<Test>(1271 super::TransferCause::Call,1272 super::TransactorKind::PlainAccount,1273 &origin,1274 &dest,1275 100,1276 );12771278 assert_eq!(result, Err(Error::<Test>::TransferFailed.into()));1279 assert_eq!(get_balance(&origin), 0);1280 assert_eq!(get_balance(&dest), 0);1281 });1282 }12831284 #[test]1285 fn output_is_returned_on_success() {1286 1287 1288 let origin = ALICE;1289 let dest = BOB;1290 let return_ch = MockLoader::insert(Call, |_, _| {1291 Ok(ExecReturnValue {1292 flags: ReturnFlags::empty(),1293 data: Bytes(vec![1, 2, 3, 4]),1294 })1295 });12961297 ExtBuilder::default().build().execute_with(|| {1298 let schedule = <CurrentSchedule<Test>>::get();1299 let mut ctx = MockContext::top_level(origin, &schedule);1300 place_contract(&BOB, return_ch);13011302 let result = ctx.call(dest, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);13031304 let output = result.unwrap();1305 assert!(output.0.is_success());1306 assert_eq!(output.0.data, Bytes(vec![1, 2, 3, 4]));1307 });1308 }13091310 #[test]1311 fn output_is_returned_on_failure() {1312 1313 1314 let origin = ALICE;1315 let dest = BOB;1316 let return_ch = MockLoader::insert(Call, |_, _| {1317 Ok(ExecReturnValue {1318 flags: ReturnFlags::REVERT,1319 data: Bytes(vec![1, 2, 3, 4]),1320 })1321 });13221323 ExtBuilder::default().build().execute_with(|| {1324 let schedule = <CurrentSchedule<Test>>::get();1325 let mut ctx = MockContext::top_level(origin, &schedule);1326 place_contract(&BOB, return_ch);13271328 let result = ctx.call(dest, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);13291330 let output = result.unwrap();1331 assert!(!output.0.is_success());1332 assert_eq!(output.0.data, Bytes(vec![1, 2, 3, 4]));1333 });1334 }13351336 #[test]1337 fn input_data_to_call() {1338 let input_data_ch = MockLoader::insert(Call, |ctx, _| {1339 assert_eq!(ctx.input_data, &[1, 2, 3, 4]);1340 exec_success()1341 });13421343 1344 ExtBuilder::default().build().execute_with(|| {1345 let schedule = <CurrentSchedule<Test>>::get();1346 let mut ctx = MockContext::top_level(ALICE, &schedule);1347 place_contract(&BOB, input_data_ch);13481349 let result = ctx.call(1350 BOB,1351 0,1352 &mut GasMeter::<Test>::new(GAS_LIMIT),1353 vec![1, 2, 3, 4],1354 );1355 assert_matches!(result, Ok(_));1356 });1357 }13581359 #[test]1360 fn input_data_to_instantiate() {1361 let input_data_ch = MockLoader::insert(Constructor, |ctx, _| {1362 assert_eq!(ctx.input_data, &[1, 2, 3, 4]);1363 exec_success()1364 });13651366 1367 ExtBuilder::default().build().execute_with(|| {1368 let schedule = <CurrentSchedule<Test>>::get();1369 let subsistence = Contracts::<Test>::subsistence_threshold();1370 let mut ctx = MockContext::top_level(ALICE, &schedule);1371 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);1372 let executable =1373 MockExecutable::from_storage(input_data_ch, &schedule, &mut gas_meter).unwrap();13741375 set_balance(&ALICE, subsistence * 10);13761377 let result = ctx.instantiate(1378 subsistence * 3,1379 &mut gas_meter,1380 executable,1381 vec![1, 2, 3, 4],1382 &[],1383 );1384 assert_matches!(result, Ok(_));1385 });1386 }13871388 #[test]1389 fn max_depth() {1390 1391 1392 thread_local! {1393 static REACHED_BOTTOM: RefCell<bool> = RefCell::new(false);1394 }1395 let value = Default::default();1396 let recurse_ch = MockLoader::insert(Call, |ctx, _| {1397 1398 let r = ctx.ext.call(&BOB, 0, ctx.gas_meter, vec![]);13991400 REACHED_BOTTOM.with(|reached_bottom| {1401 let mut reached_bottom = reached_bottom.borrow_mut();1402 if !*reached_bottom {1403 1404 1405 assert_eq!(r, Err((Error::<Test>::MaxCallDepthReached.into(), 0)));1406 *reached_bottom = true;1407 } else {1408 1409 assert_matches!(r, Ok(_));1410 }1411 });14121413 exec_success()1414 });14151416 ExtBuilder::default().build().execute_with(|| {1417 let schedule = <CurrentSchedule<Test>>::get();1418 let mut ctx = MockContext::top_level(ALICE, &schedule);1419 set_balance(&BOB, 1);1420 place_contract(&BOB, recurse_ch);14211422 let result = ctx.call(BOB, value, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);14231424 assert_matches!(result, Ok(_));1425 });1426 }14271428 #[test]1429 fn caller_returns_proper_values() {1430 let origin = ALICE;1431 let dest = BOB;14321433 thread_local! {1434 static WITNESSED_CALLER_BOB: RefCell<Option<AccountIdOf<Test>>> = RefCell::new(None);1435 static WITNESSED_CALLER_CHARLIE: RefCell<Option<AccountIdOf<Test>>> = RefCell::new(None);1436 }14371438 let bob_ch = MockLoader::insert(Call, |ctx, _| {1439 1440 WITNESSED_CALLER_BOB1441 .with(|caller| *caller.borrow_mut() = Some(ctx.ext.caller().clone()));14421443 1444 assert_matches!(ctx.ext.call(&CHARLIE, 0, ctx.gas_meter, vec![]), Ok(_));1445 exec_success()1446 });1447 let charlie_ch = MockLoader::insert(Call, |ctx, _| {1448 1449 WITNESSED_CALLER_CHARLIE1450 .with(|caller| *caller.borrow_mut() = Some(ctx.ext.caller().clone()));1451 exec_success()1452 });14531454 ExtBuilder::default().build().execute_with(|| {1455 let schedule = <CurrentSchedule<Test>>::get();1456 let mut ctx = MockContext::top_level(origin.clone(), &schedule);1457 place_contract(&dest, bob_ch);1458 place_contract(&CHARLIE, charlie_ch);14591460 let result = ctx.call(1461 dest.clone(),1462 0,1463 &mut GasMeter::<Test>::new(GAS_LIMIT),1464 vec![],1465 );14661467 assert_matches!(result, Ok(_));1468 });14691470 WITNESSED_CALLER_BOB.with(|caller| assert_eq!(*caller.borrow(), Some(origin)));1471 WITNESSED_CALLER_CHARLIE.with(|caller| assert_eq!(*caller.borrow(), Some(dest)));1472 }14731474 #[test]1475 fn address_returns_proper_values() {1476 let bob_ch = MockLoader::insert(Call, |ctx, _| {1477 1478 assert_eq!(*ctx.ext.address(), BOB);14791480 1481 assert_matches!(ctx.ext.call(&CHARLIE, 0, ctx.gas_meter, vec![]), Ok(_));1482 exec_success()1483 });1484 let charlie_ch = MockLoader::insert(Call, |ctx, _| {1485 assert_eq!(*ctx.ext.address(), CHARLIE);1486 exec_success()1487 });14881489 ExtBuilder::default().build().execute_with(|| {1490 let schedule = <CurrentSchedule<Test>>::get();1491 let mut ctx = MockContext::top_level(ALICE, &schedule);1492 place_contract(&BOB, bob_ch);1493 place_contract(&CHARLIE, charlie_ch);14941495 let result = ctx.call(BOB, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);14961497 assert_matches!(result, Ok(_));1498 });1499 }15001501 #[test]1502 fn refuse_instantiate_with_value_below_existential_deposit() {1503 let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success());15041505 ExtBuilder::default()1506 .existential_deposit(15)1507 .build()1508 .execute_with(|| {1509 let schedule = <CurrentSchedule<Test>>::get();1510 let mut ctx = MockContext::top_level(ALICE, &schedule);1511 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);1512 let executable =1513 MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap();15141515 assert_matches!(1516 ctx.instantiate(1517 0, 1518 &mut gas_meter,1519 executable,1520 vec![],1521 &[],1522 ),1523 Err(_)1524 );1525 });1526 }15271528 #[test]1529 fn instantiation_work_with_success_output() {1530 let dummy_ch = MockLoader::insert(Constructor, |_, _| {1531 Ok(ExecReturnValue {1532 flags: ReturnFlags::empty(),1533 data: Bytes(vec![80, 65, 83, 83]),1534 })1535 });15361537 ExtBuilder::default()1538 .existential_deposit(15)1539 .build()1540 .execute_with(|| {1541 let schedule = <CurrentSchedule<Test>>::get();1542 let mut ctx = MockContext::top_level(ALICE, &schedule);1543 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);1544 let executable =1545 MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap();1546 set_balance(&ALICE, 1000);15471548 let instantiated_contract_address = assert_matches!(1549 ctx.instantiate(1550 100,1551 &mut gas_meter,1552 executable,1553 vec![],1554 &[],1555 ),1556 Ok((address, ref output)) if output.data == Bytes(vec![80, 65, 83, 83]) => address1557 );15581559 1560 1561 assert_eq!(1562 Storage::<Test>::code_hash(&instantiated_contract_address).unwrap(),1563 dummy_ch1564 );1565 assert_eq!(1566 &events(),1567 &[Event::Instantiated(ALICE, instantiated_contract_address)]1568 );1569 });1570 }15711572 #[test]1573 fn instantiation_fails_with_failing_output() {1574 let dummy_ch = MockLoader::insert(Constructor, |_, _| {1575 Ok(ExecReturnValue {1576 flags: ReturnFlags::REVERT,1577 data: Bytes(vec![70, 65, 73, 76]),1578 })1579 });15801581 ExtBuilder::default()1582 .existential_deposit(15)1583 .build()1584 .execute_with(|| {1585 let schedule = <CurrentSchedule<Test>>::get();1586 let mut ctx = MockContext::top_level(ALICE, &schedule);1587 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);1588 let executable =1589 MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap();1590 set_balance(&ALICE, 1000);15911592 let instantiated_contract_address = assert_matches!(1593 ctx.instantiate(1594 100,1595 &mut gas_meter,1596 executable,1597 vec![],1598 &[],1599 ),1600 Ok((address, ref output)) if output.data == Bytes(vec![70, 65, 73, 76]) => address1601 );16021603 1604 assert_noop!(1605 Storage::<Test>::code_hash(&instantiated_contract_address),1606 ContractAbsentError,1607 );1608 assert!(events().is_empty());1609 });1610 }16111612 #[test]1613 fn instantiation_from_contract() {1614 let dummy_ch = MockLoader::insert(Call, |_, _| exec_success());1615 let instantiated_contract_address = Rc::new(RefCell::new(None::<AccountIdOf<Test>>));1616 let instantiator_ch = MockLoader::insert(Call, {1617 let dummy_ch = dummy_ch.clone();1618 let instantiated_contract_address = Rc::clone(&instantiated_contract_address);1619 move |ctx, _| {1620 1621 let (address, output, _) = ctx1622 .ext1623 .instantiate(1624 dummy_ch,1625 Contracts::<Test>::subsistence_threshold() * 3,1626 ctx.gas_meter,1627 vec![],1628 &[48, 49, 50],1629 )1630 .unwrap();16311632 *instantiated_contract_address.borrow_mut() = address.into();1633 Ok(output)1634 }1635 });16361637 ExtBuilder::default()1638 .existential_deposit(15)1639 .build()1640 .execute_with(|| {1641 let schedule = <CurrentSchedule<Test>>::get();1642 let mut ctx = MockContext::top_level(ALICE, &schedule);1643 set_balance(&ALICE, Contracts::<Test>::subsistence_threshold() * 100);1644 place_contract(&BOB, instantiator_ch);16451646 assert_matches!(1647 ctx.call(BOB, 20, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]),1648 Ok(_)1649 );16501651 let instantiated_contract_address = instantiated_contract_address1652 .borrow()1653 .as_ref()1654 .unwrap()1655 .clone();16561657 1658 1659 assert_eq!(1660 Storage::<Test>::code_hash(&instantiated_contract_address).unwrap(),1661 dummy_ch1662 );1663 assert_eq!(1664 &events(),1665 &[Event::Instantiated(BOB, instantiated_contract_address)]1666 );1667 });1668 }16691670 #[test]1671 fn instantiation_traps() {1672 let dummy_ch = MockLoader::insert(Constructor, |_, _| Err("It's a trap!".into()));1673 let instantiator_ch = MockLoader::insert(Call, {1674 let dummy_ch = dummy_ch.clone();1675 move |ctx, _| {1676 1677 assert_matches!(1678 ctx.ext1679 .instantiate(dummy_ch, 15u64, ctx.gas_meter, vec![], &[],),1680 Err((1681 ExecError {1682 error: DispatchError::Other("It's a trap!"),1683 origin: ErrorOrigin::Callee,1684 },1685 01686 ))1687 );16881689 exec_success()1690 }1691 });16921693 ExtBuilder::default()1694 .existential_deposit(15)1695 .build()1696 .execute_with(|| {1697 let schedule = <CurrentSchedule<Test>>::get();1698 let mut ctx = MockContext::top_level(ALICE, &schedule);1699 set_balance(&ALICE, 1000);1700 set_balance(&BOB, 100);1701 place_contract(&BOB, instantiator_ch);17021703 assert_matches!(1704 ctx.call(BOB, 20, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]),1705 Ok(_)1706 );17071708 1709 1710 assert_eq!(&events(), &[]);1711 });1712 }17131714 #[test]1715 fn termination_from_instantiate_fails() {1716 let terminate_ch = MockLoader::insert(Constructor, |ctx, _| {1717 ctx.ext.terminate(&ALICE).unwrap();1718 exec_success()1719 });17201721 ExtBuilder::default()1722 .existential_deposit(15)1723 .build()1724 .execute_with(|| {1725 let schedule = <CurrentSchedule<Test>>::get();1726 let mut ctx = MockContext::top_level(ALICE, &schedule);1727 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);1728 let executable =1729 MockExecutable::from_storage(terminate_ch, &schedule, &mut gas_meter).unwrap();1730 set_balance(&ALICE, 1000);17311732 assert_eq!(1733 ctx.instantiate(100, &mut gas_meter, executable, vec![], &[],),1734 Err(Error::<Test>::NotCallable.into())1735 );17361737 assert_eq!(&events(), &[]);1738 });1739 }17401741 #[test]1742 fn rent_allowance() {1743 let rent_allowance_ch = MockLoader::insert(Constructor, |ctx, _| {1744 let subsistence = Contracts::<Test>::subsistence_threshold();1745 let allowance = subsistence * 3;1746 assert_eq!(ctx.ext.rent_allowance(), <BalanceOf<Test>>::max_value());1747 ctx.ext.set_rent_allowance(allowance);1748 assert_eq!(ctx.ext.rent_allowance(), allowance);1749 exec_success()1750 });17511752 ExtBuilder::default().build().execute_with(|| {1753 let subsistence = Contracts::<Test>::subsistence_threshold();1754 let schedule = <CurrentSchedule<Test>>::get();1755 let mut ctx = MockContext::top_level(ALICE, &schedule);1756 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);1757 let executable =1758 MockExecutable::from_storage(rent_allowance_ch, &schedule, &mut gas_meter).unwrap();1759 set_balance(&ALICE, subsistence * 10);17601761 let result = ctx.instantiate(subsistence * 5, &mut gas_meter, executable, vec![], &[]);1762 assert_matches!(result, Ok(_));1763 });1764 }17651766 #[test]1767 fn rent_params_works() {1768 let code_hash = MockLoader::insert(Call, |ctx, executable| {1769 let address = ctx.ext.address();1770 let contract = <ContractInfoOf<Test>>::get(address)1771 .and_then(|c| c.get_alive())1772 .unwrap();1773 assert_eq!(1774 ctx.ext.rent_params(),1775 &RentParams::new(address, &contract, executable)1776 );1777 exec_success()1778 });17791780 ExtBuilder::default().build().execute_with(|| {1781 let subsistence = Contracts::<Test>::subsistence_threshold();1782 let schedule = <CurrentSchedule<Test>>::get();1783 let mut ctx = MockContext::top_level(ALICE, &schedule);1784 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);1785 set_balance(&ALICE, subsistence * 10);1786 place_contract(&BOB, code_hash);1787 ctx.call(BOB, 0, &mut gas_meter, vec![]).unwrap();1788 });1789 }17901791 #[test]1792 fn rent_params_snapshotted() {1793 let code_hash = MockLoader::insert(Call, |ctx, executable| {1794 let subsistence = Contracts::<Test>::subsistence_threshold();1795 let address = ctx.ext.address();1796 let contract = <ContractInfoOf<Test>>::get(address)1797 .and_then(|c| c.get_alive())1798 .unwrap();1799 let rent_params = RentParams::new(address, &contract, executable);18001801 1802 let allowance = 42;1803 assert_ne!(allowance, rent_params.rent_allowance);1804 ctx.ext.set_rent_allowance(allowance);1805 assert_eq!(ctx.ext.rent_params(), &rent_params);18061807 1808 1809 assert_eq!(MockLoader::refcount(&executable.code_hash), 1);1810 ctx.ext1811 .instantiate(1812 executable.code_hash,1813 subsistence * 25,1814 &mut GasMeter::<Test>::new(GAS_LIMIT),1815 vec![],1816 &[],1817 )1818 .unwrap();1819 assert_eq!(MockLoader::refcount(&executable.code_hash), 2);1820 assert_eq!(ctx.ext.rent_params(), &rent_params);18211822 exec_success()1823 });18241825 ExtBuilder::default().build().execute_with(|| {1826 let subsistence = Contracts::<Test>::subsistence_threshold();1827 let schedule = <CurrentSchedule<Test>>::get();1828 let mut ctx = MockContext::top_level(ALICE, &schedule);1829 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);1830 set_balance(&ALICE, subsistence * 100);1831 place_contract(&BOB, code_hash);1832 ctx.call(BOB, subsistence * 50, &mut gas_meter, vec![])1833 .unwrap();1834 });1835 }1836}