1use crate::{
19 debug::{CallInterceptor, CallSpan, Tracing},
20 gas::GasMeter,
21 primitives::{ExecReturnValue, StorageDeposit},
22 storage::{self, meter::Diff, WriteOutcome},
23 transient_storage::TransientStorage,
24 BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf,
25 DebugBufferVec, Determinism, Error, Event, Nonce, Origin, Pallet as Contracts, Schedule,
26 LOG_TARGET,
27};
28use alloc::vec::Vec;
29use core::{fmt::Debug, marker::PhantomData, mem};
30use frame_support::{
31 crypto::ecdsa::ECDSAExt,
32 dispatch::{DispatchResult, DispatchResultWithPostInfo},
33 ensure,
34 storage::{with_transaction, TransactionOutcome},
35 traits::{
36 fungible::{Inspect, Mutate},
37 tokens::{Fortitude, Preservation},
38 Contains, OriginTrait, Randomness, Time,
39 },
40 weights::Weight,
41 Blake2_128Concat, BoundedVec, StorageHasher,
42};
43use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
44use smallvec::{Array, SmallVec};
45use sp_core::{
46 ecdsa::Public as ECDSAPublic,
47 sr25519::{Public as SR25519Public, Signature as SR25519Signature},
48 Get,
49};
50use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256};
51use sp_runtime::{
52 traits::{Convert, Dispatchable, Zero},
53 DispatchError,
54};
55
56pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
57pub type MomentOf<T> = <<T as Config>::Time as Time>::Moment;
58pub type SeedOf<T> = <T as frame_system::Config>::Hash;
59pub type ExecResult = Result<ExecReturnValue, ExecError>;
60
61pub type TopicOf<T> = <T as frame_system::Config>::Hash;
63
64type VarSizedKey<T> = BoundedVec<u8, <T as Config>::MaxStorageKeyLen>;
66
67pub enum Key<T: Config> {
69 Fix([u8; 32]),
71 Var(VarSizedKey<T>),
73}
74
75impl<T: Config> Key<T> {
76 pub fn to_vec(&self) -> Vec<u8> {
78 match self {
79 Key::Fix(v) => v.to_vec(),
80 Key::Var(v) => v.to_vec(),
81 }
82 }
83
84 pub fn hash(&self) -> Vec<u8> {
85 match self {
86 Key::Fix(v) => blake2_256(v.as_slice()).to_vec(),
87 Key::Var(v) => Blake2_128Concat::hash(v.as_slice()),
88 }
89 }
90
91 pub fn try_from_fix(v: Vec<u8>) -> Result<Self, Vec<u8>> {
92 <[u8; 32]>::try_from(v).map(Self::Fix)
93 }
94
95 pub fn try_from_var(v: Vec<u8>) -> Result<Self, Vec<u8>> {
96 VarSizedKey::<T>::try_from(v).map(Self::Var)
97 }
98}
99
100#[derive(Copy, Clone, PartialEq, Eq, Debug, codec::Decode, codec::Encode)]
106pub enum ErrorOrigin {
107 Caller,
112 Callee,
114}
115
116#[derive(Copy, Clone, PartialEq, Eq, Debug, codec::Decode, codec::Encode)]
118pub struct ExecError {
119 pub error: DispatchError,
121 pub origin: ErrorOrigin,
123}
124
125impl<T: Into<DispatchError>> From<T> for ExecError {
126 fn from(error: T) -> Self {
127 Self { error: error.into(), origin: ErrorOrigin::Caller }
128 }
129}
130
131pub trait Ext: sealing::Sealed {
141 type T: Config;
142
143 fn call(
147 &mut self,
148 gas_limit: Weight,
149 deposit_limit: BalanceOf<Self::T>,
150 to: AccountIdOf<Self::T>,
151 value: BalanceOf<Self::T>,
152 input_data: Vec<u8>,
153 allows_reentry: bool,
154 read_only: bool,
155 ) -> Result<ExecReturnValue, ExecError>;
156
157 fn delegate_call(
161 &mut self,
162 code: CodeHash<Self::T>,
163 input_data: Vec<u8>,
164 ) -> Result<ExecReturnValue, ExecError>;
165
166 fn instantiate(
172 &mut self,
173 gas_limit: Weight,
174 deposit_limit: BalanceOf<Self::T>,
175 code: CodeHash<Self::T>,
176 value: BalanceOf<Self::T>,
177 input_data: Vec<u8>,
178 salt: &[u8],
179 ) -> Result<(AccountIdOf<Self::T>, ExecReturnValue), ExecError>;
180
181 fn terminate(&mut self, beneficiary: &AccountIdOf<Self::T>) -> DispatchResult;
189
190 fn transfer(&mut self, to: &AccountIdOf<Self::T>, value: BalanceOf<Self::T>) -> DispatchResult;
192
193 fn get_storage(&mut self, key: &Key<Self::T>) -> Option<Vec<u8>>;
198
199 fn get_storage_size(&mut self, key: &Key<Self::T>) -> Option<u32>;
204
205 fn set_storage(
208 &mut self,
209 key: &Key<Self::T>,
210 value: Option<Vec<u8>>,
211 take_old: bool,
212 ) -> Result<WriteOutcome, DispatchError>;
213
214 fn get_transient_storage(&self, key: &Key<Self::T>) -> Option<Vec<u8>>;
219
220 fn get_transient_storage_size(&self, key: &Key<Self::T>) -> Option<u32>;
225
226 fn set_transient_storage(
229 &mut self,
230 key: &Key<Self::T>,
231 value: Option<Vec<u8>>,
232 take_old: bool,
233 ) -> Result<WriteOutcome, DispatchError>;
234
235 fn caller(&self) -> Origin<Self::T>;
237
238 fn is_contract(&self, address: &AccountIdOf<Self::T>) -> bool;
240
241 fn code_hash(&self, address: &AccountIdOf<Self::T>) -> Option<CodeHash<Self::T>>;
245
246 fn own_code_hash(&mut self) -> &CodeHash<Self::T>;
248
249 fn caller_is_origin(&self) -> bool;
254
255 fn caller_is_root(&self) -> bool;
257
258 fn address(&self) -> &AccountIdOf<Self::T>;
260
261 fn balance(&self) -> BalanceOf<Self::T>;
265
266 fn value_transferred(&self) -> BalanceOf<Self::T>;
268
269 fn now(&self) -> &MomentOf<Self::T>;
271
272 fn minimum_balance(&self) -> BalanceOf<Self::T>;
274
275 fn random(&self, subject: &[u8]) -> (SeedOf<Self::T>, BlockNumberFor<Self::T>);
277
278 fn deposit_event(&mut self, topics: Vec<TopicOf<Self::T>>, data: Vec<u8>);
282
283 fn block_number(&self) -> BlockNumberFor<Self::T>;
285
286 fn max_value_size(&self) -> u32;
288
289 fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T>;
291
292 fn schedule(&self) -> &Schedule<Self::T>;
294
295 fn gas_meter(&self) -> &GasMeter<Self::T>;
297
298 fn gas_meter_mut(&mut self) -> &mut GasMeter<Self::T>;
300
301 fn charge_storage(&mut self, diff: &Diff);
303
304 fn append_debug_buffer(&mut self, msg: &str) -> bool;
313
314 fn debug_buffer_enabled(&self) -> bool;
316
317 fn call_runtime(&self, call: <Self::T as Config>::RuntimeCall) -> DispatchResultWithPostInfo;
319
320 fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()>;
322
323 fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool;
325
326 fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()>;
328
329 #[cfg(any(test, feature = "runtime-benchmarks"))]
331 fn contract_info(&mut self) -> &mut ContractInfo<Self::T>;
332
333 #[cfg(feature = "runtime-benchmarks")]
337 fn transient_storage(&mut self) -> &mut TransientStorage<Self::T>;
338
339 fn set_code_hash(&mut self, hash: CodeHash<Self::T>) -> DispatchResult;
341
342 fn reentrance_count(&self) -> u32;
345
346 fn account_reentrance_count(&self, account_id: &AccountIdOf<Self::T>) -> u32;
350
351 fn nonce(&mut self) -> u64;
353
354 fn increment_refcount(code_hash: CodeHash<Self::T>) -> DispatchResult;
361
362 fn decrement_refcount(code_hash: CodeHash<Self::T>);
369
370 fn lock_delegate_dependency(&mut self, code_hash: CodeHash<Self::T>) -> DispatchResult;
382
383 fn unlock_delegate_dependency(&mut self, code_hash: &CodeHash<Self::T>) -> DispatchResult;
392
393 fn locked_delegate_dependencies_count(&mut self) -> usize;
397
398 fn is_read_only(&self) -> bool;
400}
401
402#[derive(
404 Copy,
405 Clone,
406 PartialEq,
407 Eq,
408 Debug,
409 codec::Decode,
410 codec::Encode,
411 codec::MaxEncodedLen,
412 scale_info::TypeInfo,
413)]
414pub enum ExportedFunction {
415 Constructor,
417 Call,
419}
420
421pub trait Executable<T: Config>: Sized {
426 fn from_storage(
431 code_hash: CodeHash<T>,
432 gas_meter: &mut GasMeter<T>,
433 ) -> Result<Self, DispatchError>;
434
435 fn execute<E: Ext<T = T>>(
445 self,
446 ext: &mut E,
447 function: &ExportedFunction,
448 input_data: Vec<u8>,
449 ) -> ExecResult;
450
451 fn code_info(&self) -> &CodeInfo<T>;
453
454 fn code_hash(&self) -> &CodeHash<T>;
456
457 fn is_deterministic(&self) -> bool;
459}
460
461pub struct Stack<'a, T: Config, E> {
467 origin: Origin<T>,
476 schedule: &'a Schedule<T>,
478 gas_meter: &'a mut GasMeter<T>,
480 storage_meter: &'a mut storage::meter::Meter<T>,
482 timestamp: MomentOf<T>,
484 block_number: BlockNumberFor<T>,
486 nonce: Option<u64>,
490 frames: SmallVec<T::CallStack>,
493 first_frame: Frame<T>,
495 debug_message: Option<&'a mut DebugBufferVec<T>>,
500 determinism: Determinism,
502 transient_storage: TransientStorage<T>,
504 _phantom: PhantomData<E>,
506}
507
508pub struct Frame<T: Config> {
518 account_id: T::AccountId,
520 contract_info: CachedContract<T>,
522 value_transferred: BalanceOf<T>,
524 entry_point: ExportedFunction,
526 nested_gas: GasMeter<T>,
528 nested_storage: storage::meter::NestedMeter<T>,
530 allows_reentry: bool,
532 read_only: bool,
534 delegate_caller: Option<Origin<T>>,
536}
537
538struct DelegatedCall<T: Config, E> {
540 executable: E,
542 caller: Origin<T>,
544}
545
546enum FrameArgs<'a, T: Config, E> {
550 Call {
551 dest: T::AccountId,
553 cached_info: Option<ContractInfo<T>>,
555 delegated_call: Option<DelegatedCall<T, E>>,
559 },
560 Instantiate {
561 sender: T::AccountId,
563 nonce: u64,
565 executable: E,
567 salt: &'a [u8],
569 input_data: &'a [u8],
571 },
572}
573
574enum CachedContract<T: Config> {
576 Cached(ContractInfo<T>),
578 Invalidated,
582 Terminated,
587}
588
589impl<T: Config> CachedContract<T> {
590 fn into_contract(self) -> Option<ContractInfo<T>> {
592 if let CachedContract::Cached(contract) = self {
593 Some(contract)
594 } else {
595 None
596 }
597 }
598
599 fn as_contract(&mut self) -> Option<&mut ContractInfo<T>> {
601 if let CachedContract::Cached(contract) = self {
602 Some(contract)
603 } else {
604 None
605 }
606 }
607}
608
609impl<T: Config> Frame<T> {
610 fn contract_info(&mut self) -> &mut ContractInfo<T> {
612 self.contract_info.get(&self.account_id)
613 }
614
615 fn terminate(&mut self) -> ContractInfo<T> {
622 self.contract_info.terminate(&self.account_id)
623 }
624}
625
626macro_rules! get_cached_or_panic_after_load {
630 ($c:expr) => {{
631 if let CachedContract::Cached(contract) = $c {
632 contract
633 } else {
634 panic!(
635 "It is impossible to remove a contract that is on the call stack;\
636 See implementations of terminate;\
637 Therefore fetching a contract will never fail while using an account id
638 that is currently active on the call stack;\
639 qed"
640 );
641 }
642 }};
643}
644
645macro_rules! top_frame {
650 ($stack:expr) => {
651 $stack.frames.last().unwrap_or(&$stack.first_frame)
652 };
653}
654
655macro_rules! top_frame_mut {
660 ($stack:expr) => {
661 $stack.frames.last_mut().unwrap_or(&mut $stack.first_frame)
662 };
663}
664
665impl<T: Config> CachedContract<T> {
666 fn load(&mut self, account_id: &T::AccountId) {
668 if let CachedContract::Invalidated = self {
669 let contract = <ContractInfoOf<T>>::get(&account_id);
670 if let Some(contract) = contract {
671 *self = CachedContract::Cached(contract);
672 }
673 }
674 }
675
676 fn get(&mut self, account_id: &T::AccountId) -> &mut ContractInfo<T> {
678 self.load(account_id);
679 get_cached_or_panic_after_load!(self)
680 }
681
682 fn terminate(&mut self, account_id: &T::AccountId) -> ContractInfo<T> {
684 self.load(account_id);
685 get_cached_or_panic_after_load!(mem::replace(self, Self::Terminated))
686 }
687}
688
689impl<'a, T, E> Stack<'a, T, E>
690where
691 T: Config,
692 E: Executable<T>,
693{
694 pub fn run_call(
705 origin: Origin<T>,
706 dest: T::AccountId,
707 gas_meter: &'a mut GasMeter<T>,
708 storage_meter: &'a mut storage::meter::Meter<T>,
709 schedule: &'a Schedule<T>,
710 value: BalanceOf<T>,
711 input_data: Vec<u8>,
712 debug_message: Option<&'a mut DebugBufferVec<T>>,
713 determinism: Determinism,
714 ) -> Result<ExecReturnValue, ExecError> {
715 let (mut stack, executable) = Self::new(
716 FrameArgs::Call { dest, cached_info: None, delegated_call: None },
717 origin,
718 gas_meter,
719 storage_meter,
720 schedule,
721 value,
722 debug_message,
723 determinism,
724 )?;
725 stack.run(executable, input_data)
726 }
727
728 pub fn run_instantiate(
739 origin: T::AccountId,
740 executable: E,
741 gas_meter: &'a mut GasMeter<T>,
742 storage_meter: &'a mut storage::meter::Meter<T>,
743 schedule: &'a Schedule<T>,
744 value: BalanceOf<T>,
745 input_data: Vec<u8>,
746 salt: &[u8],
747 debug_message: Option<&'a mut DebugBufferVec<T>>,
748 ) -> Result<(T::AccountId, ExecReturnValue), ExecError> {
749 let (mut stack, executable) = Self::new(
750 FrameArgs::Instantiate {
751 sender: origin.clone(),
752 nonce: <Nonce<T>>::get().wrapping_add(1),
753 executable,
754 salt,
755 input_data: input_data.as_ref(),
756 },
757 Origin::from_account_id(origin),
758 gas_meter,
759 storage_meter,
760 schedule,
761 value,
762 debug_message,
763 Determinism::Enforced,
764 )?;
765 let account_id = stack.top_frame().account_id.clone();
766 stack.run(executable, input_data).map(|ret| (account_id, ret))
767 }
768
769 #[cfg(feature = "runtime-benchmarks")]
770 pub fn bench_new_call(
771 dest: T::AccountId,
772 origin: Origin<T>,
773 gas_meter: &'a mut GasMeter<T>,
774 storage_meter: &'a mut storage::meter::Meter<T>,
775 schedule: &'a Schedule<T>,
776 value: BalanceOf<T>,
777 debug_message: Option<&'a mut DebugBufferVec<T>>,
778 determinism: Determinism,
779 ) -> (Self, E) {
780 Self::new(
781 FrameArgs::Call { dest, cached_info: None, delegated_call: None },
782 origin,
783 gas_meter,
784 storage_meter,
785 schedule,
786 value,
787 debug_message,
788 determinism,
789 )
790 .unwrap()
791 }
792
793 fn new(
795 args: FrameArgs<T, E>,
796 origin: Origin<T>,
797 gas_meter: &'a mut GasMeter<T>,
798 storage_meter: &'a mut storage::meter::Meter<T>,
799 schedule: &'a Schedule<T>,
800 value: BalanceOf<T>,
801 debug_message: Option<&'a mut DebugBufferVec<T>>,
802 determinism: Determinism,
803 ) -> Result<(Self, E), ExecError> {
804 let (first_frame, executable, nonce) = Self::new_frame(
805 args,
806 value,
807 gas_meter,
808 Weight::zero(),
809 storage_meter,
810 BalanceOf::<T>::zero(),
811 determinism,
812 false,
813 )?;
814
815 let stack = Self {
816 origin,
817 schedule,
818 gas_meter,
819 storage_meter,
820 timestamp: T::Time::now(),
821 block_number: <frame_system::Pallet<T>>::block_number(),
822 nonce,
823 first_frame,
824 frames: Default::default(),
825 debug_message,
826 determinism,
827 transient_storage: TransientStorage::new(T::MaxTransientStorageSize::get()),
828 _phantom: Default::default(),
829 };
830
831 Ok((stack, executable))
832 }
833
834 fn new_frame<S: storage::meter::State + Default + Debug>(
839 frame_args: FrameArgs<T, E>,
840 value_transferred: BalanceOf<T>,
841 gas_meter: &mut GasMeter<T>,
842 gas_limit: Weight,
843 storage_meter: &mut storage::meter::GenericMeter<T, S>,
844 deposit_limit: BalanceOf<T>,
845 determinism: Determinism,
846 read_only: bool,
847 ) -> Result<(Frame<T>, E, Option<u64>), ExecError> {
848 let (account_id, contract_info, executable, delegate_caller, entry_point, nonce) =
849 match frame_args {
850 FrameArgs::Call { dest, cached_info, delegated_call } => {
851 let contract = if let Some(contract) = cached_info {
852 contract
853 } else {
854 <ContractInfoOf<T>>::get(&dest).ok_or(<Error<T>>::ContractNotFound)?
855 };
856
857 let (executable, delegate_caller) =
858 if let Some(DelegatedCall { executable, caller }) = delegated_call {
859 (executable, Some(caller))
860 } else {
861 (E::from_storage(contract.code_hash, gas_meter)?, None)
862 };
863
864 (dest, contract, executable, delegate_caller, ExportedFunction::Call, None)
865 },
866 FrameArgs::Instantiate { sender, nonce, executable, salt, input_data } => {
867 let account_id = Contracts::<T>::contract_address(
868 &sender,
869 &executable.code_hash(),
870 input_data,
871 salt,
872 );
873 let contract = ContractInfo::new(&account_id, nonce, *executable.code_hash())?;
874 (
875 account_id,
876 contract,
877 executable,
878 None,
879 ExportedFunction::Constructor,
880 Some(nonce),
881 )
882 },
883 };
884
885 if !(executable.is_deterministic() ||
888 (matches!(determinism, Determinism::Relaxed) &&
889 matches!(entry_point, ExportedFunction::Call)))
890 {
891 return Err(Error::<T>::Indeterministic.into());
892 }
893
894 let frame = Frame {
895 delegate_caller,
896 value_transferred,
897 contract_info: CachedContract::Cached(contract_info),
898 account_id,
899 entry_point,
900 nested_gas: gas_meter.nested(gas_limit),
901 nested_storage: storage_meter.nested(deposit_limit),
902 allows_reentry: true,
903 read_only,
904 };
905
906 Ok((frame, executable, nonce))
907 }
908
909 fn push_frame(
911 &mut self,
912 frame_args: FrameArgs<T, E>,
913 value_transferred: BalanceOf<T>,
914 gas_limit: Weight,
915 deposit_limit: BalanceOf<T>,
916 read_only: bool,
917 ) -> Result<E, ExecError> {
918 if self.frames.len() == T::CallStack::size() {
919 return Err(Error::<T>::MaxCallDepthReached.into());
920 }
921
922 let frame = self.top_frame();
927 if let (CachedContract::Cached(contract), ExportedFunction::Call) =
928 (&frame.contract_info, frame.entry_point)
929 {
930 <ContractInfoOf<T>>::insert(frame.account_id.clone(), contract.clone());
931 }
932
933 let frame = top_frame_mut!(self);
934 let nested_gas = &mut frame.nested_gas;
935 let nested_storage = &mut frame.nested_storage;
936 let (frame, executable, _) = Self::new_frame(
937 frame_args,
938 value_transferred,
939 nested_gas,
940 gas_limit,
941 nested_storage,
942 deposit_limit,
943 self.determinism,
944 read_only,
945 )?;
946 self.frames.push(frame);
947 Ok(executable)
948 }
949
950 fn run(&mut self, executable: E, input_data: Vec<u8>) -> Result<ExecReturnValue, ExecError> {
954 let frame = self.top_frame();
955 let entry_point = frame.entry_point;
956 let delegated_code_hash =
957 if frame.delegate_caller.is_some() { Some(*executable.code_hash()) } else { None };
958
959 self.transient_storage.start_transaction();
960
961 let do_transaction = || {
962 if entry_point == ExportedFunction::Constructor {
965 let origin = &self.origin.account_id()?;
968 let frame = top_frame_mut!(self);
969 frame.nested_storage.charge_instantiate(
970 origin,
971 &frame.account_id,
972 frame.contract_info.get(&frame.account_id),
973 executable.code_info(),
974 )?;
975 }
976
977 self.initial_transfer()?;
979
980 let contract_address = &top_frame!(self).account_id;
981
982 let call_span = T::Debug::new_call_span(contract_address, entry_point, &input_data);
983
984 let output = T::Debug::intercept_call(contract_address, &entry_point, &input_data)
985 .unwrap_or_else(|| {
986 executable
987 .execute(self, &entry_point, input_data)
988 .map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })
989 })?;
990
991 call_span.after_call(&output);
992
993 if output.did_revert() {
995 return Ok(output);
996 }
997
998 if self.frames.is_empty() {
1003 let frame = &mut self.first_frame;
1004 frame.contract_info.load(&frame.account_id);
1005 let contract = frame.contract_info.as_contract();
1006 frame.nested_storage.enforce_limit(contract)?;
1007 }
1008
1009 let frame = self.top_frame();
1010 let account_id = &frame.account_id.clone();
1011 match (entry_point, delegated_code_hash) {
1012 (ExportedFunction::Constructor, _) => {
1013 if matches!(frame.contract_info, CachedContract::Terminated) {
1015 return Err(Error::<T>::TerminatedInConstructor.into());
1016 }
1017
1018 let frame = self.top_frame_mut();
1022 let contract = frame.contract_info.as_contract();
1023 frame.nested_storage.enforce_subcall_limit(contract)?;
1024
1025 let caller = self.caller().account_id()?.clone();
1026
1027 Contracts::<T>::deposit_event(Event::Instantiated {
1029 deployer: caller,
1030 contract: account_id.clone(),
1031 });
1032 },
1033 (ExportedFunction::Call, Some(code_hash)) => {
1034 Contracts::<T>::deposit_event(Event::DelegateCalled {
1035 contract: account_id.clone(),
1036 code_hash,
1037 });
1038 },
1039 (ExportedFunction::Call, None) => {
1040 let frame = self.top_frame_mut();
1043 let contract = frame.contract_info.as_contract();
1044 frame.nested_storage.enforce_subcall_limit(contract)?;
1045
1046 let caller = self.caller();
1047 Contracts::<T>::deposit_event(Event::Called {
1048 caller: caller.clone(),
1049 contract: account_id.clone(),
1050 });
1051 },
1052 }
1053
1054 Ok(output)
1055 };
1056
1057 let transaction_outcome =
1064 with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
1065 let output = do_transaction();
1066 match &output {
1067 Ok(result) if !result.did_revert() => {
1068 TransactionOutcome::Commit(Ok((true, output)))
1069 },
1070 _ => TransactionOutcome::Rollback(Ok((false, output))),
1071 }
1072 });
1073
1074 let (success, output) = match transaction_outcome {
1075 Ok((success, output)) => (success, output),
1077 Err(error) => (false, Err(error.into())),
1080 };
1081
1082 if success {
1083 self.transient_storage.commit_transaction();
1084 } else {
1085 self.transient_storage.rollback_transaction();
1086 }
1087
1088 self.pop_frame(success);
1089 output
1090 }
1091
1092 fn pop_frame(&mut self, persist: bool) {
1097 if !persist && self.top_frame().entry_point == ExportedFunction::Constructor {
1099 self.nonce.as_mut().map(|c| *c = c.wrapping_sub(1));
1100 }
1101
1102 let frame = self.frames.pop();
1106
1107 if let Some(mut frame) = frame {
1110 let account_id = &frame.account_id;
1111 let prev = top_frame_mut!(self);
1112
1113 prev.nested_gas.absorb_nested(frame.nested_gas);
1114
1115 if !persist {
1117 return;
1118 }
1119
1120 frame.contract_info.load(account_id);
1125 let mut contract = frame.contract_info.into_contract();
1126 prev.nested_storage.absorb(frame.nested_storage, account_id, contract.as_mut());
1127
1128 if let Some(contract) = contract {
1130 if prev.account_id == *account_id {
1135 prev.contract_info = CachedContract::Cached(contract);
1136 return;
1137 }
1138
1139 <ContractInfoOf<T>>::insert(account_id, contract);
1145 if let Some(c) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) {
1146 c.contract_info = CachedContract::Invalidated;
1147 }
1148 }
1149 } else {
1150 if let Some((msg, false)) = self.debug_message.as_ref().map(|m| (m, m.is_empty())) {
1151 log::debug!(
1152 target: LOG_TARGET,
1153 "Execution finished with debug buffer: {}",
1154 core::str::from_utf8(msg).unwrap_or("<Invalid UTF8>"),
1155 );
1156 }
1157 self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas));
1158 if !persist {
1159 return;
1160 }
1161 let mut contract = self.first_frame.contract_info.as_contract();
1162 self.storage_meter.absorb(
1163 mem::take(&mut self.first_frame.nested_storage),
1164 &self.first_frame.account_id,
1165 contract.as_deref_mut(),
1166 );
1167 if let Some(contract) = contract {
1168 <ContractInfoOf<T>>::insert(&self.first_frame.account_id, contract);
1169 }
1170 if let Some(nonce) = self.nonce {
1171 <Nonce<T>>::set(nonce);
1172 }
1173 }
1174 }
1175
1176 fn transfer(
1178 preservation: Preservation,
1179 from: &T::AccountId,
1180 to: &T::AccountId,
1181 value: BalanceOf<T>,
1182 ) -> DispatchResult {
1183 if !value.is_zero() && from != to {
1184 T::Currency::transfer(from, to, value, preservation)
1185 .map_err(|_| Error::<T>::TransferFailed)?;
1186 }
1187 Ok(())
1188 }
1189
1190 fn initial_transfer(&self) -> DispatchResult {
1192 let frame = self.top_frame();
1193
1194 if frame.delegate_caller.is_some() {
1197 return Ok(());
1198 }
1199
1200 let value = frame.value_transferred;
1201
1202 let caller = match self.caller() {
1206 Origin::Signed(caller) => caller,
1207 Origin::Root if value.is_zero() => return Ok(()),
1208 Origin::Root => return DispatchError::RootNotAllowed.into(),
1209 };
1210 Self::transfer(Preservation::Preserve, &caller, &frame.account_id, value)
1211 }
1212
1213 fn top_frame(&self) -> &Frame<T> {
1215 top_frame!(self)
1216 }
1217
1218 fn top_frame_mut(&mut self) -> &mut Frame<T> {
1220 top_frame_mut!(self)
1221 }
1222
1223 fn frames(&self) -> impl Iterator<Item = &Frame<T>> {
1227 core::iter::once(&self.first_frame).chain(&self.frames).rev()
1228 }
1229
1230 fn frames_mut(&mut self) -> impl Iterator<Item = &mut Frame<T>> {
1232 core::iter::once(&mut self.first_frame).chain(&mut self.frames).rev()
1233 }
1234
1235 fn is_recursive(&self) -> bool {
1237 let account_id = &self.top_frame().account_id;
1238 self.frames().skip(1).any(|f| &f.account_id == account_id)
1239 }
1240
1241 fn allows_reentry(&self, id: &AccountIdOf<T>) -> bool {
1243 !self.frames().any(|f| &f.account_id == id && !f.allows_reentry)
1244 }
1245
1246 fn next_nonce(&mut self) -> u64 {
1248 let next = self.nonce().wrapping_add(1);
1249 self.nonce = Some(next);
1250 next
1251 }
1252}
1253
1254impl<'a, T, E> Ext for Stack<'a, T, E>
1255where
1256 T: Config,
1257 E: Executable<T>,
1258{
1259 type T = T;
1260
1261 fn call(
1262 &mut self,
1263 gas_limit: Weight,
1264 deposit_limit: BalanceOf<T>,
1265 to: T::AccountId,
1266 value: BalanceOf<T>,
1267 input_data: Vec<u8>,
1268 allows_reentry: bool,
1269 read_only: bool,
1270 ) -> Result<ExecReturnValue, ExecError> {
1271 self.top_frame_mut().allows_reentry = allows_reentry;
1275
1276 let try_call = || {
1277 if !self.allows_reentry(&to) {
1278 return Err(<Error<T>>::ReentranceDenied.into());
1279 }
1280
1281 let cached_info = self
1285 .frames()
1286 .find(|f| f.entry_point == ExportedFunction::Call && f.account_id == to)
1287 .and_then(|f| match &f.contract_info {
1288 CachedContract::Cached(contract) => Some(contract.clone()),
1289 _ => None,
1290 });
1291 let executable = self.push_frame(
1292 FrameArgs::Call { dest: to, cached_info, delegated_call: None },
1293 value,
1294 gas_limit,
1295 deposit_limit,
1296 read_only || self.is_read_only(),
1298 )?;
1299 self.run(executable, input_data)
1300 };
1301
1302 let result = try_call();
1304
1305 self.top_frame_mut().allows_reentry = true;
1307
1308 result
1309 }
1310
1311 fn delegate_call(
1312 &mut self,
1313 code_hash: CodeHash<Self::T>,
1314 input_data: Vec<u8>,
1315 ) -> Result<ExecReturnValue, ExecError> {
1316 let executable = E::from_storage(code_hash, self.gas_meter_mut())?;
1317 let top_frame = self.top_frame_mut();
1318 let contract_info = top_frame.contract_info().clone();
1319 let account_id = top_frame.account_id.clone();
1320 let value = top_frame.value_transferred;
1321 let executable = self.push_frame(
1322 FrameArgs::Call {
1323 dest: account_id,
1324 cached_info: Some(contract_info),
1325 delegated_call: Some(DelegatedCall { executable, caller: self.caller().clone() }),
1326 },
1327 value,
1328 Weight::zero(),
1329 BalanceOf::<T>::zero(),
1330 self.is_read_only(),
1331 )?;
1332 self.run(executable, input_data)
1333 }
1334
1335 fn instantiate(
1336 &mut self,
1337 gas_limit: Weight,
1338 deposit_limit: BalanceOf<Self::T>,
1339 code_hash: CodeHash<T>,
1340 value: BalanceOf<T>,
1341 input_data: Vec<u8>,
1342 salt: &[u8],
1343 ) -> Result<(AccountIdOf<T>, ExecReturnValue), ExecError> {
1344 let executable = E::from_storage(code_hash, self.gas_meter_mut())?;
1345 let nonce = self.next_nonce();
1346 let executable = self.push_frame(
1347 FrameArgs::Instantiate {
1348 sender: self.top_frame().account_id.clone(),
1349 nonce,
1350 executable,
1351 salt,
1352 input_data: input_data.as_ref(),
1353 },
1354 value,
1355 gas_limit,
1356 deposit_limit,
1357 self.is_read_only(),
1358 )?;
1359 let account_id = self.top_frame().account_id.clone();
1360 self.run(executable, input_data).map(|ret| (account_id, ret))
1361 }
1362
1363 fn terminate(&mut self, beneficiary: &AccountIdOf<Self::T>) -> DispatchResult {
1364 if self.is_recursive() {
1365 return Err(Error::<T>::TerminatedWhileReentrant.into());
1366 }
1367 let frame = self.top_frame_mut();
1368 let info = frame.terminate();
1369 frame.nested_storage.terminate(&info, beneficiary.clone());
1370
1371 info.queue_trie_for_deletion();
1372 ContractInfoOf::<T>::remove(&frame.account_id);
1373 Self::decrement_refcount(info.code_hash);
1374
1375 for (code_hash, deposit) in info.delegate_dependencies() {
1376 Self::decrement_refcount(*code_hash);
1377 frame
1378 .nested_storage
1379 .charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(*deposit));
1380 }
1381
1382 Contracts::<T>::deposit_event(Event::Terminated {
1383 contract: frame.account_id.clone(),
1384 beneficiary: beneficiary.clone(),
1385 });
1386 Ok(())
1387 }
1388
1389 fn transfer(&mut self, to: &T::AccountId, value: BalanceOf<T>) -> DispatchResult {
1390 Self::transfer(Preservation::Preserve, &self.top_frame().account_id, to, value)
1391 }
1392
1393 fn get_storage(&mut self, key: &Key<T>) -> Option<Vec<u8>> {
1394 self.top_frame_mut().contract_info().read(key)
1395 }
1396
1397 fn get_storage_size(&mut self, key: &Key<T>) -> Option<u32> {
1398 self.top_frame_mut().contract_info().size(key.into())
1399 }
1400
1401 fn set_storage(
1402 &mut self,
1403 key: &Key<T>,
1404 value: Option<Vec<u8>>,
1405 take_old: bool,
1406 ) -> Result<WriteOutcome, DispatchError> {
1407 let frame = self.top_frame_mut();
1408 frame.contract_info.get(&frame.account_id).write(
1409 key.into(),
1410 value,
1411 Some(&mut frame.nested_storage),
1412 take_old,
1413 )
1414 }
1415
1416 fn get_transient_storage(&self, key: &Key<T>) -> Option<Vec<u8>> {
1417 self.transient_storage.read(self.address(), key)
1418 }
1419
1420 fn get_transient_storage_size(&self, key: &Key<T>) -> Option<u32> {
1421 self.transient_storage.read(self.address(), key).map(|value| value.len() as _)
1422 }
1423
1424 fn set_transient_storage(
1425 &mut self,
1426 key: &Key<T>,
1427 value: Option<Vec<u8>>,
1428 take_old: bool,
1429 ) -> Result<WriteOutcome, DispatchError> {
1430 let account_id = self.address().clone();
1431 self.transient_storage.write(&account_id, key, value, take_old)
1432 }
1433
1434 fn address(&self) -> &T::AccountId {
1435 &self.top_frame().account_id
1436 }
1437
1438 fn caller(&self) -> Origin<T> {
1439 if let Some(caller) = &self.top_frame().delegate_caller {
1440 caller.clone()
1441 } else {
1442 self.frames()
1443 .nth(1)
1444 .map(|f| Origin::from_account_id(f.account_id.clone()))
1445 .unwrap_or(self.origin.clone())
1446 }
1447 }
1448
1449 fn is_contract(&self, address: &T::AccountId) -> bool {
1450 ContractInfoOf::<T>::contains_key(&address)
1451 }
1452
1453 fn code_hash(&self, address: &T::AccountId) -> Option<CodeHash<Self::T>> {
1454 <ContractInfoOf<T>>::get(&address).map(|contract| contract.code_hash)
1455 }
1456
1457 fn own_code_hash(&mut self) -> &CodeHash<Self::T> {
1458 &self.top_frame_mut().contract_info().code_hash
1459 }
1460
1461 fn caller_is_origin(&self) -> bool {
1462 self.origin == self.caller()
1463 }
1464
1465 fn caller_is_root(&self) -> bool {
1466 self.caller_is_origin() && self.origin == Origin::Root
1468 }
1469
1470 fn balance(&self) -> BalanceOf<T> {
1471 T::Currency::reducible_balance(
1472 &self.top_frame().account_id,
1473 Preservation::Preserve,
1474 Fortitude::Polite,
1475 )
1476 }
1477
1478 fn value_transferred(&self) -> BalanceOf<T> {
1479 self.top_frame().value_transferred
1480 }
1481
1482 fn random(&self, subject: &[u8]) -> (SeedOf<T>, BlockNumberFor<T>) {
1483 T::Randomness::random(subject)
1484 }
1485
1486 fn now(&self) -> &MomentOf<T> {
1487 &self.timestamp
1488 }
1489
1490 fn minimum_balance(&self) -> BalanceOf<T> {
1491 T::Currency::minimum_balance()
1492 }
1493
1494 fn deposit_event(&mut self, topics: Vec<T::Hash>, data: Vec<u8>) {
1495 Contracts::<Self::T>::deposit_indexed_event(
1496 topics,
1497 Event::ContractEmitted { contract: self.top_frame().account_id.clone(), data },
1498 );
1499 }
1500
1501 fn block_number(&self) -> BlockNumberFor<T> {
1502 self.block_number
1503 }
1504
1505 fn max_value_size(&self) -> u32 {
1506 self.schedule.limits.payload_len
1507 }
1508
1509 fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
1510 T::WeightPrice::convert(weight)
1511 }
1512
1513 fn schedule(&self) -> &Schedule<Self::T> {
1514 self.schedule
1515 }
1516
1517 fn gas_meter(&self) -> &GasMeter<Self::T> {
1518 &self.top_frame().nested_gas
1519 }
1520
1521 fn gas_meter_mut(&mut self) -> &mut GasMeter<Self::T> {
1522 &mut self.top_frame_mut().nested_gas
1523 }
1524
1525 fn charge_storage(&mut self, diff: &Diff) {
1526 self.top_frame_mut().nested_storage.charge(diff)
1527 }
1528
1529 fn debug_buffer_enabled(&self) -> bool {
1530 self.debug_message.is_some()
1531 }
1532
1533 fn append_debug_buffer(&mut self, msg: &str) -> bool {
1534 if let Some(buffer) = &mut self.debug_message {
1535 buffer
1536 .try_extend(&mut msg.bytes())
1537 .map_err(|_| {
1538 log::debug!(
1539 target: LOG_TARGET,
1540 "Debug buffer (of {} bytes) exhausted!",
1541 DebugBufferVec::<T>::bound(),
1542 )
1543 })
1544 .ok();
1545 true
1546 } else {
1547 false
1548 }
1549 }
1550
1551 fn call_runtime(&self, call: <Self::T as Config>::RuntimeCall) -> DispatchResultWithPostInfo {
1552 let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.address().clone()).into();
1553 origin.add_filter(T::CallFilter::contains);
1554 call.dispatch(origin)
1555 }
1556
1557 fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()> {
1558 secp256k1_ecdsa_recover_compressed(signature, message_hash).map_err(|_| ())
1559 }
1560
1561 fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool {
1562 sp_io::crypto::sr25519_verify(
1563 &SR25519Signature::from(*signature),
1564 message,
1565 &SR25519Public::from(*pub_key),
1566 )
1567 }
1568
1569 fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()> {
1570 ECDSAPublic::from(*pk).to_eth_address()
1571 }
1572
1573 #[cfg(any(test, feature = "runtime-benchmarks"))]
1574 fn contract_info(&mut self) -> &mut ContractInfo<Self::T> {
1575 self.top_frame_mut().contract_info()
1576 }
1577
1578 #[cfg(feature = "runtime-benchmarks")]
1579 fn transient_storage(&mut self) -> &mut TransientStorage<Self::T> {
1580 &mut self.transient_storage
1581 }
1582
1583 fn set_code_hash(&mut self, hash: CodeHash<Self::T>) -> DispatchResult {
1584 let frame = top_frame_mut!(self);
1585 if !E::from_storage(hash, &mut frame.nested_gas)?.is_deterministic() {
1586 return Err(<Error<T>>::Indeterministic.into());
1587 }
1588
1589 let info = frame.contract_info();
1590
1591 let prev_hash = info.code_hash;
1592 info.code_hash = hash;
1593
1594 let code_info = CodeInfoOf::<T>::get(hash).ok_or(Error::<T>::CodeNotFound)?;
1595
1596 let old_base_deposit = info.storage_base_deposit();
1597 let new_base_deposit = info.update_base_deposit(&code_info);
1598 let deposit = StorageDeposit::Charge(new_base_deposit)
1599 .saturating_sub(&StorageDeposit::Charge(old_base_deposit));
1600
1601 frame.nested_storage.charge_deposit(frame.account_id.clone(), deposit);
1602
1603 Self::increment_refcount(hash)?;
1604 Self::decrement_refcount(prev_hash);
1605 Contracts::<Self::T>::deposit_event(Event::ContractCodeUpdated {
1606 contract: frame.account_id.clone(),
1607 new_code_hash: hash,
1608 old_code_hash: prev_hash,
1609 });
1610 Ok(())
1611 }
1612
1613 fn reentrance_count(&self) -> u32 {
1614 let id: &AccountIdOf<Self::T> = &self.top_frame().account_id;
1615 self.account_reentrance_count(id).saturating_sub(1)
1616 }
1617
1618 fn account_reentrance_count(&self, account_id: &AccountIdOf<Self::T>) -> u32 {
1619 self.frames()
1620 .filter(|f| f.delegate_caller.is_none() && &f.account_id == account_id)
1621 .count() as u32
1622 }
1623
1624 fn nonce(&mut self) -> u64 {
1625 if let Some(current) = self.nonce {
1626 current
1627 } else {
1628 let current = <Nonce<T>>::get();
1629 self.nonce = Some(current);
1630 current
1631 }
1632 }
1633
1634 fn increment_refcount(code_hash: CodeHash<Self::T>) -> DispatchResult {
1635 <CodeInfoOf<Self::T>>::mutate(code_hash, |existing| -> Result<(), DispatchError> {
1636 if let Some(info) = existing {
1637 *info.refcount_mut() = info.refcount().saturating_add(1);
1638 Ok(())
1639 } else {
1640 Err(Error::<T>::CodeNotFound.into())
1641 }
1642 })
1643 }
1644
1645 fn decrement_refcount(code_hash: CodeHash<T>) {
1646 <CodeInfoOf<T>>::mutate(code_hash, |existing| {
1647 if let Some(info) = existing {
1648 *info.refcount_mut() = info.refcount().saturating_sub(1);
1649 }
1650 });
1651 }
1652
1653 fn lock_delegate_dependency(&mut self, code_hash: CodeHash<Self::T>) -> DispatchResult {
1654 let frame = self.top_frame_mut();
1655 let info = frame.contract_info.get(&frame.account_id);
1656 ensure!(code_hash != info.code_hash, Error::<T>::CannotAddSelfAsDelegateDependency);
1657
1658 let code_info = CodeInfoOf::<T>::get(code_hash).ok_or(Error::<T>::CodeNotFound)?;
1659 let deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_info.deposit());
1660
1661 info.lock_delegate_dependency(code_hash, deposit)?;
1662 Self::increment_refcount(code_hash)?;
1663 frame
1664 .nested_storage
1665 .charge_deposit(frame.account_id.clone(), StorageDeposit::Charge(deposit));
1666 Ok(())
1667 }
1668
1669 fn unlock_delegate_dependency(&mut self, code_hash: &CodeHash<Self::T>) -> DispatchResult {
1670 let frame = self.top_frame_mut();
1671 let info = frame.contract_info.get(&frame.account_id);
1672
1673 let deposit = info.unlock_delegate_dependency(code_hash)?;
1674 Self::decrement_refcount(*code_hash);
1675 frame
1676 .nested_storage
1677 .charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(deposit));
1678 Ok(())
1679 }
1680
1681 fn locked_delegate_dependencies_count(&mut self) -> usize {
1682 self.top_frame_mut().contract_info().delegate_dependencies_count()
1683 }
1684
1685 fn is_read_only(&self) -> bool {
1686 self.top_frame().read_only
1687 }
1688}
1689
1690mod sealing {
1691 use super::*;
1692
1693 pub trait Sealed {}
1694
1695 impl<'a, T: Config, E> Sealed for Stack<'a, T, E> {}
1696
1697 #[cfg(test)]
1698 impl Sealed for crate::wasm::MockExt {}
1699
1700 #[cfg(test)]
1701 impl Sealed for &mut crate::wasm::MockExt {}
1702}
1703
1704#[cfg(test)]
1710mod tests {
1711 use super::*;
1712 use crate::{
1713 exec::ExportedFunction::*,
1714 gas::GasMeter,
1715 tests::{
1716 test_utils::{get_balance, place_contract, set_balance},
1717 ExtBuilder, RuntimeCall, RuntimeEvent as MetaEvent, Test, TestFilter, ALICE, BOB,
1718 CHARLIE, GAS_LIMIT,
1719 },
1720 Error,
1721 };
1722 use assert_matches::assert_matches;
1723 use codec::{Decode, Encode};
1724 use frame_support::{assert_err, assert_ok, parameter_types};
1725 use frame_system::{EventRecord, Phase};
1726 use pallet_contracts_uapi::ReturnFlags;
1727 use pretty_assertions::assert_eq;
1728 use sp_runtime::{traits::Hash, DispatchError};
1729 use std::{cell::RefCell, collections::hash_map::HashMap, rc::Rc};
1730
1731 type System = frame_system::Pallet<Test>;
1732
1733 type MockStack<'a> = Stack<'a, Test, MockExecutable>;
1734
1735 parameter_types! {
1736 static Loader: MockLoader = MockLoader::default();
1737 }
1738
1739 fn events() -> Vec<Event<Test>> {
1740 System::events()
1741 .into_iter()
1742 .filter_map(|meta| match meta.event {
1743 MetaEvent::Contracts(contract_event) => Some(contract_event),
1744 _ => None,
1745 })
1746 .collect()
1747 }
1748
1749 struct MockCtx<'a> {
1750 ext: &'a mut MockStack<'a>,
1751 input_data: Vec<u8>,
1752 }
1753
1754 #[derive(Clone)]
1755 struct MockExecutable {
1756 func: Rc<dyn for<'a> Fn(MockCtx<'a>, &Self) -> ExecResult + 'static>,
1757 func_type: ExportedFunction,
1758 code_hash: CodeHash<Test>,
1759 code_info: CodeInfo<Test>,
1760 }
1761
1762 #[derive(Default, Clone)]
1763 pub struct MockLoader {
1764 map: HashMap<CodeHash<Test>, MockExecutable>,
1765 counter: u64,
1766 }
1767
1768 impl MockLoader {
1769 fn code_hashes() -> Vec<CodeHash<Test>> {
1770 Loader::get().map.keys().copied().collect()
1771 }
1772
1773 fn insert(
1774 func_type: ExportedFunction,
1775 f: impl Fn(MockCtx, &MockExecutable) -> ExecResult + 'static,
1776 ) -> CodeHash<Test> {
1777 Loader::mutate(|loader| {
1778 let hash = <Test as frame_system::Config>::Hash::from_low_u64_be(loader.counter);
1780 loader.counter += 1;
1781 loader.map.insert(
1782 hash,
1783 MockExecutable {
1784 func: Rc::new(f),
1785 func_type,
1786 code_hash: hash,
1787 code_info: CodeInfo::<Test>::new(ALICE),
1788 },
1789 );
1790 hash
1791 })
1792 }
1793 }
1794
1795 impl Executable<Test> for MockExecutable {
1796 fn from_storage(
1797 code_hash: CodeHash<Test>,
1798 _gas_meter: &mut GasMeter<Test>,
1799 ) -> Result<Self, DispatchError> {
1800 Loader::mutate(|loader| {
1801 loader.map.get(&code_hash).cloned().ok_or(Error::<Test>::CodeNotFound.into())
1802 })
1803 }
1804
1805 fn execute<E: Ext<T = Test>>(
1806 self,
1807 ext: &mut E,
1808 function: &ExportedFunction,
1809 input_data: Vec<u8>,
1810 ) -> ExecResult {
1811 if let &Constructor = function {
1812 E::increment_refcount(self.code_hash).unwrap();
1813 }
1814 let ext = unsafe { mem::transmute(ext) };
1824 if function == &self.func_type {
1825 (self.func)(MockCtx { ext, input_data }, &self)
1826 } else {
1827 exec_success()
1828 }
1829 }
1830
1831 fn code_hash(&self) -> &CodeHash<Test> {
1832 &self.code_hash
1833 }
1834
1835 fn code_info(&self) -> &CodeInfo<Test> {
1836 &self.code_info
1837 }
1838
1839 fn is_deterministic(&self) -> bool {
1840 true
1841 }
1842 }
1843
1844 fn exec_success() -> ExecResult {
1845 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
1846 }
1847
1848 fn exec_trapped() -> ExecResult {
1849 Err(ExecError { error: <Error<Test>>::ContractTrapped.into(), origin: ErrorOrigin::Callee })
1850 }
1851
1852 #[test]
1853 fn it_works() {
1854 parameter_types! {
1855 static TestData: Vec<usize> = vec![0];
1856 }
1857
1858 let value = Default::default();
1859 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
1860 let exec_ch = MockLoader::insert(Call, |_ctx, _executable| {
1861 TestData::mutate(|data| data.push(1));
1862 exec_success()
1863 });
1864
1865 ExtBuilder::default().build().execute_with(|| {
1866 let schedule = <Test as Config>::Schedule::get();
1867 place_contract(&BOB, exec_ch);
1868 let mut storage_meter =
1869 storage::meter::Meter::new(&Origin::from_account_id(ALICE), Some(0), value)
1870 .unwrap();
1871
1872 assert_matches!(
1873 MockStack::run_call(
1874 Origin::from_account_id(ALICE),
1875 BOB,
1876 &mut gas_meter,
1877 &mut storage_meter,
1878 &schedule,
1879 value,
1880 vec![],
1881 None,
1882 Determinism::Enforced,
1883 ),
1884 Ok(_)
1885 );
1886 });
1887
1888 assert_eq!(TestData::get(), vec![0, 1]);
1889 }
1890
1891 #[test]
1892 fn transfer_works() {
1893 let origin = ALICE;
1896 let dest = BOB;
1897
1898 ExtBuilder::default().build().execute_with(|| {
1899 set_balance(&origin, 100);
1900 set_balance(&dest, 0);
1901
1902 MockStack::transfer(Preservation::Preserve, &origin, &dest, 55).unwrap();
1903
1904 assert_eq!(get_balance(&origin), 45);
1905 assert_eq!(get_balance(&dest), 55);
1906 });
1907 }
1908
1909 #[test]
1910 fn correct_transfer_on_call() {
1911 let origin = ALICE;
1912 let dest = BOB;
1913 let value = 55;
1914
1915 let success_ch = MockLoader::insert(Call, move |ctx, _| {
1916 assert_eq!(ctx.ext.value_transferred(), value);
1917 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
1918 });
1919
1920 ExtBuilder::default().build().execute_with(|| {
1921 let schedule = <Test as Config>::Schedule::get();
1922 place_contract(&dest, success_ch);
1923 set_balance(&origin, 100);
1924 let balance = get_balance(&dest);
1925 let contract_origin = Origin::from_account_id(origin.clone());
1926 let mut storage_meter =
1927 storage::meter::Meter::new(&contract_origin, Some(0), value).unwrap();
1928
1929 let _ = MockStack::run_call(
1930 contract_origin.clone(),
1931 dest.clone(),
1932 &mut GasMeter::<Test>::new(GAS_LIMIT),
1933 &mut storage_meter,
1934 &schedule,
1935 value,
1936 vec![],
1937 None,
1938 Determinism::Enforced,
1939 )
1940 .unwrap();
1941
1942 assert_eq!(get_balance(&origin), 100 - value);
1943 assert_eq!(get_balance(&dest), balance + value);
1944 });
1945 }
1946
1947 #[test]
1948 fn correct_transfer_on_delegate_call() {
1949 let origin = ALICE;
1950 let dest = BOB;
1951 let value = 35;
1952
1953 let success_ch = MockLoader::insert(Call, move |ctx, _| {
1954 assert_eq!(ctx.ext.value_transferred(), value);
1955 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
1956 });
1957
1958 let delegate_ch = MockLoader::insert(Call, move |ctx, _| {
1959 assert_eq!(ctx.ext.value_transferred(), value);
1960 ctx.ext.delegate_call(success_ch, Vec::new())?;
1961 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
1962 });
1963
1964 ExtBuilder::default().build().execute_with(|| {
1965 let schedule = <Test as Config>::Schedule::get();
1966 place_contract(&dest, delegate_ch);
1967 set_balance(&origin, 100);
1968 let balance = get_balance(&dest);
1969 let contract_origin = Origin::from_account_id(origin.clone());
1970 let mut storage_meter =
1971 storage::meter::Meter::new(&contract_origin, Some(0), 55).unwrap();
1972
1973 let _ = MockStack::run_call(
1974 contract_origin.clone(),
1975 dest.clone(),
1976 &mut GasMeter::<Test>::new(GAS_LIMIT),
1977 &mut storage_meter,
1978 &schedule,
1979 value,
1980 vec![],
1981 None,
1982 Determinism::Enforced,
1983 )
1984 .unwrap();
1985
1986 assert_eq!(get_balance(&origin), 100 - value);
1987 assert_eq!(get_balance(&dest), balance + value);
1988 });
1989 }
1990
1991 #[test]
1992 fn changes_are_reverted_on_failing_call() {
1993 let origin = ALICE;
1996 let dest = BOB;
1997
1998 let return_ch = MockLoader::insert(Call, |_, _| {
1999 Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: Vec::new() })
2000 });
2001
2002 ExtBuilder::default().build().execute_with(|| {
2003 let schedule = <Test as Config>::Schedule::get();
2004 place_contract(&dest, return_ch);
2005 set_balance(&origin, 100);
2006 let balance = get_balance(&dest);
2007 let contract_origin = Origin::from_account_id(origin.clone());
2008 let mut storage_meter =
2009 storage::meter::Meter::new(&contract_origin, Some(0), 55).unwrap();
2010
2011 let output = MockStack::run_call(
2012 contract_origin.clone(),
2013 dest.clone(),
2014 &mut GasMeter::<Test>::new(GAS_LIMIT),
2015 &mut storage_meter,
2016 &schedule,
2017 55,
2018 vec![],
2019 None,
2020 Determinism::Enforced,
2021 )
2022 .unwrap();
2023
2024 assert!(output.did_revert());
2025 assert_eq!(get_balance(&origin), 100);
2026 assert_eq!(get_balance(&dest), balance);
2027 });
2028 }
2029
2030 #[test]
2031 fn balance_too_low() {
2032 let origin = ALICE;
2035 let dest = BOB;
2036
2037 ExtBuilder::default().build().execute_with(|| {
2038 set_balance(&origin, 0);
2039
2040 let result = MockStack::transfer(Preservation::Preserve, &origin, &dest, 100);
2041
2042 assert_eq!(result, Err(Error::<Test>::TransferFailed.into()));
2043 assert_eq!(get_balance(&origin), 0);
2044 assert_eq!(get_balance(&dest), 0);
2045 });
2046 }
2047
2048 #[test]
2049 fn output_is_returned_on_success() {
2050 let origin = ALICE;
2053 let dest = BOB;
2054 let return_ch = MockLoader::insert(Call, |_, _| {
2055 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![1, 2, 3, 4] })
2056 });
2057
2058 ExtBuilder::default().build().execute_with(|| {
2059 let schedule = <Test as Config>::Schedule::get();
2060 let contract_origin = Origin::from_account_id(origin);
2061 let mut storage_meter =
2062 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2063 place_contract(&BOB, return_ch);
2064
2065 let result = MockStack::run_call(
2066 contract_origin,
2067 dest,
2068 &mut GasMeter::<Test>::new(GAS_LIMIT),
2069 &mut storage_meter,
2070 &schedule,
2071 0,
2072 vec![],
2073 None,
2074 Determinism::Enforced,
2075 );
2076
2077 let output = result.unwrap();
2078 assert!(!output.did_revert());
2079 assert_eq!(output.data, vec![1, 2, 3, 4]);
2080 });
2081 }
2082
2083 #[test]
2084 fn output_is_returned_on_failure() {
2085 let origin = ALICE;
2088 let dest = BOB;
2089 let return_ch = MockLoader::insert(Call, |_, _| {
2090 Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![1, 2, 3, 4] })
2091 });
2092
2093 ExtBuilder::default().build().execute_with(|| {
2094 let schedule = <Test as Config>::Schedule::get();
2095 place_contract(&BOB, return_ch);
2096 let contract_origin = Origin::from_account_id(origin);
2097 let mut storage_meter =
2098 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2099
2100 let result = MockStack::run_call(
2101 contract_origin,
2102 dest,
2103 &mut GasMeter::<Test>::new(GAS_LIMIT),
2104 &mut storage_meter,
2105 &schedule,
2106 0,
2107 vec![],
2108 None,
2109 Determinism::Enforced,
2110 );
2111
2112 let output = result.unwrap();
2113 assert!(output.did_revert());
2114 assert_eq!(output.data, vec![1, 2, 3, 4]);
2115 });
2116 }
2117
2118 #[test]
2119 fn input_data_to_call() {
2120 let input_data_ch = MockLoader::insert(Call, |ctx, _| {
2121 assert_eq!(ctx.input_data, &[1, 2, 3, 4]);
2122 exec_success()
2123 });
2124
2125 ExtBuilder::default().build().execute_with(|| {
2127 let schedule = <Test as Config>::Schedule::get();
2128 place_contract(&BOB, input_data_ch);
2129 let contract_origin = Origin::from_account_id(ALICE);
2130 let mut storage_meter =
2131 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2132
2133 let result = MockStack::run_call(
2134 contract_origin,
2135 BOB,
2136 &mut GasMeter::<Test>::new(GAS_LIMIT),
2137 &mut storage_meter,
2138 &schedule,
2139 0,
2140 vec![1, 2, 3, 4],
2141 None,
2142 Determinism::Enforced,
2143 );
2144 assert_matches!(result, Ok(_));
2145 });
2146 }
2147
2148 #[test]
2149 fn input_data_to_instantiate() {
2150 let input_data_ch = MockLoader::insert(Constructor, |ctx, _| {
2151 assert_eq!(ctx.input_data, &[1, 2, 3, 4]);
2152 exec_success()
2153 });
2154
2155 ExtBuilder::default()
2157 .with_code_hashes(MockLoader::code_hashes())
2158 .build()
2159 .execute_with(|| {
2160 let schedule = <Test as Config>::Schedule::get();
2161 let min_balance = <Test as Config>::Currency::minimum_balance();
2162 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2163 let executable =
2164 MockExecutable::from_storage(input_data_ch, &mut gas_meter).unwrap();
2165 set_balance(&ALICE, min_balance * 10_000);
2166 let contract_origin = Origin::from_account_id(ALICE);
2167 let mut storage_meter =
2168 storage::meter::Meter::new(&contract_origin, None, min_balance).unwrap();
2169
2170 let result = MockStack::run_instantiate(
2171 ALICE,
2172 executable,
2173 &mut gas_meter,
2174 &mut storage_meter,
2175 &schedule,
2176 min_balance,
2177 vec![1, 2, 3, 4],
2178 &[],
2179 None,
2180 );
2181 assert_matches!(result, Ok(_));
2182 });
2183 }
2184
2185 #[test]
2186 fn max_depth() {
2187 parameter_types! {
2190 static ReachedBottom: bool = false;
2191 }
2192 let value = Default::default();
2193 let recurse_ch = MockLoader::insert(Call, |ctx, _| {
2194 let r = ctx.ext.call(
2196 Weight::zero(),
2197 BalanceOf::<Test>::zero(),
2198 BOB,
2199 0,
2200 vec![],
2201 true,
2202 false,
2203 );
2204
2205 ReachedBottom::mutate(|reached_bottom| {
2206 if !*reached_bottom {
2207 assert_eq!(r, Err(Error::<Test>::MaxCallDepthReached.into()));
2210 *reached_bottom = true;
2211 } else {
2212 assert_matches!(r, Ok(_));
2214 }
2215 });
2216
2217 exec_success()
2218 });
2219
2220 ExtBuilder::default().build().execute_with(|| {
2221 let schedule = <Test as Config>::Schedule::get();
2222 set_balance(&BOB, 1);
2223 place_contract(&BOB, recurse_ch);
2224 let contract_origin = Origin::from_account_id(ALICE);
2225 let mut storage_meter =
2226 storage::meter::Meter::new(&contract_origin, Some(0), value).unwrap();
2227
2228 let result = MockStack::run_call(
2229 contract_origin,
2230 BOB,
2231 &mut GasMeter::<Test>::new(GAS_LIMIT),
2232 &mut storage_meter,
2233 &schedule,
2234 value,
2235 vec![],
2236 None,
2237 Determinism::Enforced,
2238 );
2239
2240 assert_matches!(result, Ok(_));
2241 });
2242 }
2243
2244 #[test]
2245 fn caller_returns_proper_values() {
2246 let origin = ALICE;
2247 let dest = BOB;
2248
2249 parameter_types! {
2250 static WitnessedCallerBob: Option<AccountIdOf<Test>> = None;
2251 static WitnessedCallerCharlie: Option<AccountIdOf<Test>> = None;
2252 }
2253
2254 let bob_ch = MockLoader::insert(Call, |ctx, _| {
2255 WitnessedCallerBob::mutate(|caller| {
2257 *caller = Some(ctx.ext.caller().account_id().unwrap().clone())
2258 });
2259
2260 assert_matches!(
2262 ctx.ext.call(
2263 Weight::zero(),
2264 BalanceOf::<Test>::zero(),
2265 CHARLIE,
2266 0,
2267 vec![],
2268 true,
2269 false
2270 ),
2271 Ok(_)
2272 );
2273 exec_success()
2274 });
2275 let charlie_ch = MockLoader::insert(Call, |ctx, _| {
2276 WitnessedCallerCharlie::mutate(|caller| {
2278 *caller = Some(ctx.ext.caller().account_id().unwrap().clone())
2279 });
2280 exec_success()
2281 });
2282
2283 ExtBuilder::default().build().execute_with(|| {
2284 let schedule = <Test as Config>::Schedule::get();
2285 place_contract(&dest, bob_ch);
2286 place_contract(&CHARLIE, charlie_ch);
2287 let contract_origin = Origin::from_account_id(origin.clone());
2288 let mut storage_meter =
2289 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2290
2291 let result = MockStack::run_call(
2292 contract_origin.clone(),
2293 dest.clone(),
2294 &mut GasMeter::<Test>::new(GAS_LIMIT),
2295 &mut storage_meter,
2296 &schedule,
2297 0,
2298 vec![],
2299 None,
2300 Determinism::Enforced,
2301 );
2302
2303 assert_matches!(result, Ok(_));
2304 });
2305
2306 assert_eq!(WitnessedCallerBob::get(), Some(origin));
2307 assert_eq!(WitnessedCallerCharlie::get(), Some(dest));
2308 }
2309
2310 #[test]
2311 fn is_contract_returns_proper_values() {
2312 let bob_ch = MockLoader::insert(Call, |ctx, _| {
2313 assert!(ctx.ext.is_contract(&BOB));
2315 assert!(!ctx.ext.is_contract(&ALICE));
2317 exec_success()
2318 });
2319
2320 ExtBuilder::default().build().execute_with(|| {
2321 let schedule = <Test as Config>::Schedule::get();
2322 place_contract(&BOB, bob_ch);
2323
2324 let contract_origin = Origin::from_account_id(ALICE);
2325 let mut storage_meter =
2326 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2327 let result = MockStack::run_call(
2328 contract_origin,
2329 BOB,
2330 &mut GasMeter::<Test>::new(GAS_LIMIT),
2331 &mut storage_meter,
2332 &schedule,
2333 0,
2334 vec![],
2335 None,
2336 Determinism::Enforced,
2337 );
2338 assert_matches!(result, Ok(_));
2339 });
2340 }
2341
2342 #[test]
2343 fn code_hash_returns_proper_values() {
2344 let code_bob = MockLoader::insert(Call, |ctx, _| {
2345 assert!(ctx.ext.code_hash(&ALICE).is_none());
2347 assert!(ctx.ext.code_hash(&BOB).is_some());
2349 exec_success()
2350 });
2351
2352 ExtBuilder::default().build().execute_with(|| {
2353 let schedule = <Test as Config>::Schedule::get();
2354 place_contract(&BOB, code_bob);
2355 let contract_origin = Origin::from_account_id(ALICE);
2356 let mut storage_meter =
2357 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2358 let result = MockStack::run_call(
2360 contract_origin,
2361 BOB,
2362 &mut GasMeter::<Test>::new(GAS_LIMIT),
2363 &mut storage_meter,
2364 &schedule,
2365 0,
2366 vec![0],
2367 None,
2368 Determinism::Enforced,
2369 );
2370 assert_matches!(result, Ok(_));
2371 });
2372 }
2373
2374 #[test]
2375 fn own_code_hash_returns_proper_values() {
2376 let bob_ch = MockLoader::insert(Call, |ctx, _| {
2377 let code_hash = ctx.ext.code_hash(&BOB).unwrap();
2378 assert_eq!(*ctx.ext.own_code_hash(), code_hash);
2379 exec_success()
2380 });
2381
2382 ExtBuilder::default().build().execute_with(|| {
2383 let schedule = <Test as Config>::Schedule::get();
2384 place_contract(&BOB, bob_ch);
2385 let contract_origin = Origin::from_account_id(ALICE);
2386 let mut storage_meter =
2387 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2388 let result = MockStack::run_call(
2390 contract_origin,
2391 BOB,
2392 &mut GasMeter::<Test>::new(GAS_LIMIT),
2393 &mut storage_meter,
2394 &schedule,
2395 0,
2396 vec![0],
2397 None,
2398 Determinism::Enforced,
2399 );
2400 assert_matches!(result, Ok(_));
2401 });
2402 }
2403
2404 #[test]
2405 fn caller_is_origin_returns_proper_values() {
2406 let code_charlie = MockLoader::insert(Call, |ctx, _| {
2407 assert!(!ctx.ext.caller_is_origin());
2409 exec_success()
2410 });
2411
2412 let code_bob = MockLoader::insert(Call, |ctx, _| {
2413 assert!(ctx.ext.caller_is_origin());
2415 ctx.ext
2417 .call(Weight::zero(), BalanceOf::<Test>::zero(), CHARLIE, 0, vec![], true, false)
2418 });
2419
2420 ExtBuilder::default().build().execute_with(|| {
2421 let schedule = <Test as Config>::Schedule::get();
2422 place_contract(&BOB, code_bob);
2423 place_contract(&CHARLIE, code_charlie);
2424 let contract_origin = Origin::from_account_id(ALICE);
2425 let mut storage_meter =
2426 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2427 let result = MockStack::run_call(
2429 contract_origin,
2430 BOB,
2431 &mut GasMeter::<Test>::new(GAS_LIMIT),
2432 &mut storage_meter,
2433 &schedule,
2434 0,
2435 vec![0],
2436 None,
2437 Determinism::Enforced,
2438 );
2439 assert_matches!(result, Ok(_));
2440 });
2441 }
2442
2443 #[test]
2444 fn root_caller_succeeds() {
2445 let code_bob = MockLoader::insert(Call, |ctx, _| {
2446 assert!(ctx.ext.caller_is_root());
2448 exec_success()
2449 });
2450
2451 ExtBuilder::default().build().execute_with(|| {
2452 let schedule = <Test as Config>::Schedule::get();
2453 place_contract(&BOB, code_bob);
2454 let contract_origin = Origin::Root;
2455 let mut storage_meter =
2456 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2457 let result = MockStack::run_call(
2459 contract_origin,
2460 BOB,
2461 &mut GasMeter::<Test>::new(GAS_LIMIT),
2462 &mut storage_meter,
2463 &schedule,
2464 0,
2465 vec![0],
2466 None,
2467 Determinism::Enforced,
2468 );
2469 assert_matches!(result, Ok(_));
2470 });
2471 }
2472
2473 #[test]
2474 fn root_caller_does_not_succeed_when_value_not_zero() {
2475 let code_bob = MockLoader::insert(Call, |ctx, _| {
2476 assert!(ctx.ext.caller_is_root());
2478 exec_success()
2479 });
2480
2481 ExtBuilder::default().build().execute_with(|| {
2482 let schedule = <Test as Config>::Schedule::get();
2483 place_contract(&BOB, code_bob);
2484 let contract_origin = Origin::Root;
2485 let mut storage_meter =
2486 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2487 let result = MockStack::run_call(
2489 contract_origin,
2490 BOB,
2491 &mut GasMeter::<Test>::new(GAS_LIMIT),
2492 &mut storage_meter,
2493 &schedule,
2494 1,
2495 vec![0],
2496 None,
2497 Determinism::Enforced,
2498 );
2499 assert_matches!(result, Err(_));
2500 });
2501 }
2502
2503 #[test]
2504 fn root_caller_succeeds_with_consecutive_calls() {
2505 let code_charlie = MockLoader::insert(Call, |ctx, _| {
2506 assert!(!ctx.ext.caller_is_root());
2508 exec_success()
2509 });
2510
2511 let code_bob = MockLoader::insert(Call, |ctx, _| {
2512 assert!(ctx.ext.caller_is_root());
2514 ctx.ext
2516 .call(Weight::zero(), BalanceOf::<Test>::zero(), CHARLIE, 0, vec![], true, false)
2517 });
2518
2519 ExtBuilder::default().build().execute_with(|| {
2520 let schedule = <Test as Config>::Schedule::get();
2521 place_contract(&BOB, code_bob);
2522 place_contract(&CHARLIE, code_charlie);
2523 let contract_origin = Origin::Root;
2524 let mut storage_meter =
2525 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2526 let result = MockStack::run_call(
2528 contract_origin,
2529 BOB,
2530 &mut GasMeter::<Test>::new(GAS_LIMIT),
2531 &mut storage_meter,
2532 &schedule,
2533 0,
2534 vec![0],
2535 None,
2536 Determinism::Enforced,
2537 );
2538 assert_matches!(result, Ok(_));
2539 });
2540 }
2541
2542 #[test]
2543 fn address_returns_proper_values() {
2544 let bob_ch = MockLoader::insert(Call, |ctx, _| {
2545 assert_eq!(*ctx.ext.address(), BOB);
2547
2548 assert_matches!(
2550 ctx.ext.call(
2551 Weight::zero(),
2552 BalanceOf::<Test>::zero(),
2553 CHARLIE,
2554 0,
2555 vec![],
2556 true,
2557 false
2558 ),
2559 Ok(_)
2560 );
2561 exec_success()
2562 });
2563 let charlie_ch = MockLoader::insert(Call, |ctx, _| {
2564 assert_eq!(*ctx.ext.address(), CHARLIE);
2565 exec_success()
2566 });
2567
2568 ExtBuilder::default().build().execute_with(|| {
2569 let schedule = <Test as Config>::Schedule::get();
2570 place_contract(&BOB, bob_ch);
2571 place_contract(&CHARLIE, charlie_ch);
2572 let contract_origin = Origin::from_account_id(ALICE);
2573 let mut storage_meter =
2574 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2575
2576 let result = MockStack::run_call(
2577 contract_origin,
2578 BOB,
2579 &mut GasMeter::<Test>::new(GAS_LIMIT),
2580 &mut storage_meter,
2581 &schedule,
2582 0,
2583 vec![],
2584 None,
2585 Determinism::Enforced,
2586 );
2587
2588 assert_matches!(result, Ok(_));
2589 });
2590 }
2591
2592 #[test]
2593 fn refuse_instantiate_with_value_below_existential_deposit() {
2594 let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success());
2595
2596 ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
2597 let schedule = <Test as Config>::Schedule::get();
2598 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2599 let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap();
2600 let contract_origin = Origin::from_account_id(ALICE);
2601 let mut storage_meter =
2602 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2603
2604 assert_matches!(
2605 MockStack::run_instantiate(
2606 ALICE,
2607 executable,
2608 &mut gas_meter,
2609 &mut storage_meter,
2610 &schedule,
2611 0, vec![],
2613 &[],
2614 None,
2615 ),
2616 Err(_)
2617 );
2618 });
2619 }
2620
2621 #[test]
2622 fn instantiation_work_with_success_output() {
2623 let dummy_ch = MockLoader::insert(Constructor, |_, _| {
2624 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![80, 65, 83, 83] })
2625 });
2626
2627 ExtBuilder::default()
2628 .with_code_hashes(MockLoader::code_hashes())
2629 .existential_deposit(15)
2630 .build()
2631 .execute_with(|| {
2632 let schedule = <Test as Config>::Schedule::get();
2633 let min_balance = <Test as Config>::Currency::minimum_balance();
2634 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2635 let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap();
2636 set_balance(&ALICE, min_balance * 1000);
2637 let contract_origin = Origin::from_account_id(ALICE);
2638 let mut storage_meter = storage::meter::Meter::new(
2639 &contract_origin,
2640 Some(min_balance * 100),
2641 min_balance,
2642 )
2643 .unwrap();
2644
2645 let instantiated_contract_address = assert_matches!(
2646 MockStack::run_instantiate(
2647 ALICE,
2648 executable,
2649 &mut gas_meter,
2650 &mut storage_meter,
2651 &schedule,
2652 min_balance,
2653 vec![],
2654 &[],
2655 None,
2656 ),
2657 Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address
2658 );
2659
2660 assert_eq!(
2663 ContractInfo::<Test>::load_code_hash(&instantiated_contract_address).unwrap(),
2664 dummy_ch
2665 );
2666 assert_eq!(
2667 &events(),
2668 &[Event::Instantiated {
2669 deployer: ALICE,
2670 contract: instantiated_contract_address
2671 }]
2672 );
2673 });
2674 }
2675
2676 #[test]
2677 fn instantiation_fails_with_failing_output() {
2678 let dummy_ch = MockLoader::insert(Constructor, |_, _| {
2679 Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![70, 65, 73, 76] })
2680 });
2681
2682 ExtBuilder::default()
2683 .with_code_hashes(MockLoader::code_hashes())
2684 .existential_deposit(15)
2685 .build()
2686 .execute_with(|| {
2687 let schedule = <Test as Config>::Schedule::get();
2688 let min_balance = <Test as Config>::Currency::minimum_balance();
2689 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2690 let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap();
2691 set_balance(&ALICE, min_balance * 1000);
2692 let contract_origin = Origin::from_account_id(ALICE);
2693 let mut storage_meter = storage::meter::Meter::new(
2694 &contract_origin,
2695 Some(min_balance * 100),
2696 min_balance,
2697 )
2698 .unwrap();
2699
2700 let instantiated_contract_address = assert_matches!(
2701 MockStack::run_instantiate(
2702 ALICE,
2703 executable,
2704 &mut gas_meter,
2705 &mut storage_meter,
2706 &schedule,
2707 min_balance,
2708 vec![],
2709 &[],
2710 None,
2711 ),
2712 Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address
2713 );
2714
2715 assert!(
2717 ContractInfo::<Test>::load_code_hash(&instantiated_contract_address).is_none()
2718 );
2719 assert!(events().is_empty());
2720 });
2721 }
2722
2723 #[test]
2724 fn instantiation_from_contract() {
2725 let dummy_ch = MockLoader::insert(Call, |_, _| exec_success());
2726 let instantiated_contract_address = Rc::new(RefCell::new(None::<AccountIdOf<Test>>));
2727 let instantiator_ch = MockLoader::insert(Call, {
2728 let instantiated_contract_address = Rc::clone(&instantiated_contract_address);
2729 move |ctx, _| {
2730 let (address, output) = ctx
2732 .ext
2733 .instantiate(
2734 Weight::zero(),
2735 BalanceOf::<Test>::zero(),
2736 dummy_ch,
2737 <Test as Config>::Currency::minimum_balance(),
2738 vec![],
2739 &[48, 49, 50],
2740 )
2741 .unwrap();
2742
2743 *instantiated_contract_address.borrow_mut() = address.into();
2744 Ok(output)
2745 }
2746 });
2747
2748 ExtBuilder::default()
2749 .with_code_hashes(MockLoader::code_hashes())
2750 .existential_deposit(15)
2751 .build()
2752 .execute_with(|| {
2753 let schedule = <Test as Config>::Schedule::get();
2754 let min_balance = <Test as Config>::Currency::minimum_balance();
2755 set_balance(&ALICE, min_balance * 100);
2756 place_contract(&BOB, instantiator_ch);
2757 let contract_origin = Origin::from_account_id(ALICE);
2758 let mut storage_meter = storage::meter::Meter::new(
2759 &contract_origin,
2760 Some(min_balance * 10),
2761 min_balance * 10,
2762 )
2763 .unwrap();
2764
2765 assert_matches!(
2766 MockStack::run_call(
2767 contract_origin,
2768 BOB,
2769 &mut GasMeter::<Test>::new(GAS_LIMIT),
2770 &mut storage_meter,
2771 &schedule,
2772 min_balance * 10,
2773 vec![],
2774 None,
2775 Determinism::Enforced,
2776 ),
2777 Ok(_)
2778 );
2779
2780 let instantiated_contract_address =
2781 instantiated_contract_address.borrow().as_ref().unwrap().clone();
2782
2783 assert_eq!(
2786 ContractInfo::<Test>::load_code_hash(&instantiated_contract_address).unwrap(),
2787 dummy_ch
2788 );
2789 assert_eq!(
2790 &events(),
2791 &[
2792 Event::Instantiated {
2793 deployer: BOB,
2794 contract: instantiated_contract_address
2795 },
2796 Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },
2797 ]
2798 );
2799 });
2800 }
2801
2802 #[test]
2803 fn instantiation_traps() {
2804 let dummy_ch = MockLoader::insert(Constructor, |_, _| Err("It's a trap!".into()));
2805 let instantiator_ch = MockLoader::insert(Call, {
2806 move |ctx, _| {
2807 assert_matches!(
2809 ctx.ext.instantiate(
2810 Weight::zero(),
2811 BalanceOf::<Test>::zero(),
2812 dummy_ch,
2813 <Test as Config>::Currency::minimum_balance(),
2814 vec![],
2815 &[],
2816 ),
2817 Err(ExecError {
2818 error: DispatchError::Other("It's a trap!"),
2819 origin: ErrorOrigin::Callee,
2820 })
2821 );
2822
2823 exec_success()
2824 }
2825 });
2826
2827 ExtBuilder::default()
2828 .with_code_hashes(MockLoader::code_hashes())
2829 .existential_deposit(15)
2830 .build()
2831 .execute_with(|| {
2832 let schedule = <Test as Config>::Schedule::get();
2833 set_balance(&ALICE, 1000);
2834 set_balance(&BOB, 100);
2835 place_contract(&BOB, instantiator_ch);
2836 let contract_origin = Origin::from_account_id(ALICE);
2837 let mut storage_meter =
2838 storage::meter::Meter::new(&contract_origin, Some(200), 0).unwrap();
2839
2840 assert_matches!(
2841 MockStack::run_call(
2842 contract_origin,
2843 BOB,
2844 &mut GasMeter::<Test>::new(GAS_LIMIT),
2845 &mut storage_meter,
2846 &schedule,
2847 0,
2848 vec![],
2849 None,
2850 Determinism::Enforced,
2851 ),
2852 Ok(_)
2853 );
2854
2855 assert_eq!(
2858 &events(),
2859 &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },]
2860 );
2861 });
2862 }
2863
2864 #[test]
2865 fn termination_from_instantiate_fails() {
2866 let terminate_ch = MockLoader::insert(Constructor, |ctx, _| {
2867 ctx.ext.terminate(&ALICE).unwrap();
2868 exec_success()
2869 });
2870
2871 ExtBuilder::default()
2872 .with_code_hashes(MockLoader::code_hashes())
2873 .existential_deposit(15)
2874 .build()
2875 .execute_with(|| {
2876 let schedule = <Test as Config>::Schedule::get();
2877 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2878 let executable =
2879 MockExecutable::from_storage(terminate_ch, &mut gas_meter).unwrap();
2880 set_balance(&ALICE, 10_000);
2881 let contract_origin = Origin::from_account_id(ALICE);
2882 let mut storage_meter =
2883 storage::meter::Meter::new(&contract_origin, None, 100).unwrap();
2884
2885 assert_eq!(
2886 MockStack::run_instantiate(
2887 ALICE,
2888 executable,
2889 &mut gas_meter,
2890 &mut storage_meter,
2891 &schedule,
2892 100,
2893 vec![],
2894 &[],
2895 None,
2896 ),
2897 Err(Error::<Test>::TerminatedInConstructor.into())
2898 );
2899
2900 assert_eq!(&events(), &[]);
2901 });
2902 }
2903
2904 #[test]
2905 fn in_memory_changes_not_discarded() {
2906 let code_bob = MockLoader::insert(Call, |ctx, _| {
2915 if ctx.input_data[0] == 0 {
2916 let info = ctx.ext.contract_info();
2917 assert_eq!(info.storage_byte_deposit, 0);
2918 info.storage_byte_deposit = 42;
2919 assert_eq!(
2920 ctx.ext.call(
2921 Weight::zero(),
2922 BalanceOf::<Test>::zero(),
2923 CHARLIE,
2924 0,
2925 vec![],
2926 true,
2927 false
2928 ),
2929 exec_trapped()
2930 );
2931 assert_eq!(ctx.ext.contract_info().storage_byte_deposit, 42);
2932 }
2933 exec_success()
2934 });
2935 let code_charlie = MockLoader::insert(Call, |ctx, _| {
2936 assert!(ctx
2937 .ext
2938 .call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![99], true, false)
2939 .is_ok());
2940 exec_trapped()
2941 });
2942
2943 ExtBuilder::default().build().execute_with(|| {
2945 let schedule = <Test as Config>::Schedule::get();
2946 place_contract(&BOB, code_bob);
2947 place_contract(&CHARLIE, code_charlie);
2948 let contract_origin = Origin::from_account_id(ALICE);
2949 let mut storage_meter =
2950 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2951
2952 let result = MockStack::run_call(
2953 contract_origin,
2954 BOB,
2955 &mut GasMeter::<Test>::new(GAS_LIMIT),
2956 &mut storage_meter,
2957 &schedule,
2958 0,
2959 vec![0],
2960 None,
2961 Determinism::Enforced,
2962 );
2963 assert_matches!(result, Ok(_));
2964 });
2965 }
2966
2967 #[test]
2968 fn recursive_call_during_constructor_fails() {
2969 let code = MockLoader::insert(Constructor, |ctx, _| {
2970 assert_matches!(
2971 ctx.ext.call(Weight::zero(), BalanceOf::<Test>::zero(), ctx.ext.address().clone(), 0, vec![], true, false),
2972 Err(ExecError{error, ..}) if error == <Error<Test>>::ContractNotFound.into()
2973 );
2974 exec_success()
2975 });
2976
2977 ExtBuilder::default()
2979 .with_code_hashes(MockLoader::code_hashes())
2980 .build()
2981 .execute_with(|| {
2982 let schedule = <Test as Config>::Schedule::get();
2983 let min_balance = <Test as Config>::Currency::minimum_balance();
2984 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2985 let executable = MockExecutable::from_storage(code, &mut gas_meter).unwrap();
2986 set_balance(&ALICE, min_balance * 10_000);
2987 let contract_origin = Origin::from_account_id(ALICE);
2988 let mut storage_meter =
2989 storage::meter::Meter::new(&contract_origin, None, min_balance).unwrap();
2990
2991 let result = MockStack::run_instantiate(
2992 ALICE,
2993 executable,
2994 &mut gas_meter,
2995 &mut storage_meter,
2996 &schedule,
2997 min_balance,
2998 vec![],
2999 &[],
3000 None,
3001 );
3002 assert_matches!(result, Ok(_));
3003 });
3004 }
3005
3006 #[test]
3007 fn printing_works() {
3008 let code_hash = MockLoader::insert(Call, |ctx, _| {
3009 ctx.ext.append_debug_buffer("This is a test");
3010 ctx.ext.append_debug_buffer("More text");
3011 exec_success()
3012 });
3013
3014 let mut debug_buffer = DebugBufferVec::<Test>::try_from(Vec::new()).unwrap();
3015
3016 ExtBuilder::default().build().execute_with(|| {
3017 let min_balance = <Test as Config>::Currency::minimum_balance();
3018 let schedule = <Test as Config>::Schedule::get();
3019 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3020 set_balance(&ALICE, min_balance * 10);
3021 place_contract(&BOB, code_hash);
3022 let contract_origin = Origin::from_account_id(ALICE);
3023 let mut storage_meter =
3024 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3025 MockStack::run_call(
3026 contract_origin,
3027 BOB,
3028 &mut gas_meter,
3029 &mut storage_meter,
3030 &schedule,
3031 0,
3032 vec![],
3033 Some(&mut debug_buffer),
3034 Determinism::Enforced,
3035 )
3036 .unwrap();
3037 });
3038
3039 assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text");
3040 }
3041
3042 #[test]
3043 fn printing_works_on_fail() {
3044 let code_hash = MockLoader::insert(Call, |ctx, _| {
3045 ctx.ext.append_debug_buffer("This is a test");
3046 ctx.ext.append_debug_buffer("More text");
3047 exec_trapped()
3048 });
3049
3050 let mut debug_buffer = DebugBufferVec::<Test>::try_from(Vec::new()).unwrap();
3051
3052 ExtBuilder::default().build().execute_with(|| {
3053 let min_balance = <Test as Config>::Currency::minimum_balance();
3054 let schedule = <Test as Config>::Schedule::get();
3055 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3056 set_balance(&ALICE, min_balance * 10);
3057 place_contract(&BOB, code_hash);
3058 let contract_origin = Origin::from_account_id(ALICE);
3059 let mut storage_meter =
3060 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3061 let result = MockStack::run_call(
3062 contract_origin,
3063 BOB,
3064 &mut gas_meter,
3065 &mut storage_meter,
3066 &schedule,
3067 0,
3068 vec![],
3069 Some(&mut debug_buffer),
3070 Determinism::Enforced,
3071 );
3072 assert!(result.is_err());
3073 });
3074
3075 assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text");
3076 }
3077
3078 #[test]
3079 fn debug_buffer_is_limited() {
3080 let code_hash = MockLoader::insert(Call, move |ctx, _| {
3081 ctx.ext.append_debug_buffer("overflowing bytes");
3082 exec_success()
3083 });
3084
3085 let debug_buf_before =
3087 DebugBufferVec::<Test>::try_from(vec![0u8; DebugBufferVec::<Test>::bound() - 5])
3088 .unwrap();
3089 let mut debug_buf_after = debug_buf_before.clone();
3090
3091 ExtBuilder::default().build().execute_with(|| {
3092 let schedule: Schedule<Test> = <Test as Config>::Schedule::get();
3093 let min_balance = <Test as Config>::Currency::minimum_balance();
3094 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3095 set_balance(&ALICE, min_balance * 10);
3096 place_contract(&BOB, code_hash);
3097 let contract_origin = Origin::from_account_id(ALICE);
3098 let mut storage_meter =
3099 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3100 MockStack::run_call(
3101 contract_origin,
3102 BOB,
3103 &mut gas_meter,
3104 &mut storage_meter,
3105 &schedule,
3106 0,
3107 vec![],
3108 Some(&mut debug_buf_after),
3109 Determinism::Enforced,
3110 )
3111 .unwrap();
3112 assert_eq!(debug_buf_before, debug_buf_after);
3113 });
3114 }
3115
3116 #[test]
3117 fn call_reentry_direct_recursion() {
3118 let code_bob = MockLoader::insert(Call, |ctx, _| {
3120 let dest = Decode::decode(&mut ctx.input_data.as_ref()).unwrap();
3121 ctx.ext
3122 .call(Weight::zero(), BalanceOf::<Test>::zero(), dest, 0, vec![], false, false)
3123 });
3124
3125 let code_charlie = MockLoader::insert(Call, |_, _| exec_success());
3126
3127 ExtBuilder::default().build().execute_with(|| {
3128 let schedule = <Test as Config>::Schedule::get();
3129 place_contract(&BOB, code_bob);
3130 place_contract(&CHARLIE, code_charlie);
3131 let contract_origin = Origin::from_account_id(ALICE);
3132 let mut storage_meter =
3133 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3134
3135 assert_ok!(MockStack::run_call(
3137 contract_origin.clone(),
3138 BOB,
3139 &mut GasMeter::<Test>::new(GAS_LIMIT),
3140 &mut storage_meter,
3141 &schedule,
3142 0,
3143 CHARLIE.encode(),
3144 None,
3145 Determinism::Enforced
3146 ));
3147
3148 assert_err!(
3150 MockStack::run_call(
3151 contract_origin,
3152 BOB,
3153 &mut GasMeter::<Test>::new(GAS_LIMIT),
3154 &mut storage_meter,
3155 &schedule,
3156 0,
3157 BOB.encode(),
3158 None,
3159 Determinism::Enforced
3160 )
3161 .map_err(|e| e.error),
3162 <Error<Test>>::ReentranceDenied,
3163 );
3164 });
3165 }
3166
3167 #[test]
3168 fn call_deny_reentry() {
3169 let code_bob = MockLoader::insert(Call, |ctx, _| {
3170 if ctx.input_data[0] == 0 {
3171 ctx.ext.call(
3172 Weight::zero(),
3173 BalanceOf::<Test>::zero(),
3174 CHARLIE,
3175 0,
3176 vec![],
3177 false,
3178 false,
3179 )
3180 } else {
3181 exec_success()
3182 }
3183 });
3184
3185 let code_charlie = MockLoader::insert(Call, |ctx, _| {
3187 ctx.ext
3188 .call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![1], true, false)
3189 });
3190
3191 ExtBuilder::default().build().execute_with(|| {
3192 let schedule = <Test as Config>::Schedule::get();
3193 place_contract(&BOB, code_bob);
3194 place_contract(&CHARLIE, code_charlie);
3195 let contract_origin = Origin::from_account_id(ALICE);
3196 let mut storage_meter =
3197 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3198
3199 assert_err!(
3201 MockStack::run_call(
3202 contract_origin,
3203 BOB,
3204 &mut GasMeter::<Test>::new(GAS_LIMIT),
3205 &mut storage_meter,
3206 &schedule,
3207 0,
3208 vec![0],
3209 None,
3210 Determinism::Enforced
3211 )
3212 .map_err(|e| e.error),
3213 <Error<Test>>::ReentranceDenied,
3214 );
3215 });
3216 }
3217
3218 #[test]
3219 fn call_runtime_works() {
3220 let code_hash = MockLoader::insert(Call, |ctx, _| {
3221 let call = RuntimeCall::System(frame_system::Call::remark_with_event {
3222 remark: b"Hello World".to_vec(),
3223 });
3224 ctx.ext.call_runtime(call).unwrap();
3225 exec_success()
3226 });
3227
3228 ExtBuilder::default().build().execute_with(|| {
3229 let min_balance = <Test as Config>::Currency::minimum_balance();
3230 let schedule = <Test as Config>::Schedule::get();
3231 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3232 set_balance(&ALICE, min_balance * 10);
3233 place_contract(&BOB, code_hash);
3234 let contract_origin = Origin::from_account_id(ALICE);
3235 let mut storage_meter =
3236 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3237 System::reset_events();
3238 MockStack::run_call(
3239 contract_origin,
3240 BOB,
3241 &mut gas_meter,
3242 &mut storage_meter,
3243 &schedule,
3244 0,
3245 vec![],
3246 None,
3247 Determinism::Enforced,
3248 )
3249 .unwrap();
3250
3251 let remark_hash = <Test as frame_system::Config>::Hashing::hash(b"Hello World");
3252 assert_eq!(
3253 System::events(),
3254 vec![
3255 EventRecord {
3256 phase: Phase::Initialization,
3257 event: MetaEvent::System(frame_system::Event::Remarked {
3258 sender: BOB,
3259 hash: remark_hash
3260 }),
3261 topics: vec![],
3262 },
3263 EventRecord {
3264 phase: Phase::Initialization,
3265 event: MetaEvent::Contracts(crate::Event::Called {
3266 caller: Origin::from_account_id(ALICE),
3267 contract: BOB,
3268 }),
3269 topics: vec![],
3270 },
3271 ]
3272 );
3273 });
3274 }
3275
3276 #[test]
3277 fn call_runtime_filter() {
3278 let code_hash = MockLoader::insert(Call, |ctx, _| {
3279 use frame_system::Call as SysCall;
3280 use pallet_balances::Call as BalanceCall;
3281 use pallet_utility::Call as UtilCall;
3282
3283 let allowed_call =
3285 RuntimeCall::System(SysCall::remark_with_event { remark: b"Hello".to_vec() });
3286
3287 let forbidden_call = RuntimeCall::Balances(BalanceCall::transfer_allow_death {
3289 dest: CHARLIE,
3290 value: 22,
3291 });
3292
3293 assert_err!(
3295 ctx.ext.call_runtime(forbidden_call.clone()),
3296 frame_system::Error::<Test>::CallFiltered
3297 );
3298
3299 assert_ok!(ctx.ext.call_runtime(RuntimeCall::Utility(UtilCall::batch {
3301 calls: vec![allowed_call.clone(), forbidden_call, allowed_call]
3302 })),);
3303
3304 assert_eq!(get_balance(&CHARLIE), 0);
3306
3307 exec_success()
3308 });
3309
3310 TestFilter::set_filter(|call| match call {
3311 RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) => false,
3312 _ => true,
3313 });
3314
3315 ExtBuilder::default().build().execute_with(|| {
3316 let min_balance = <Test as Config>::Currency::minimum_balance();
3317 let schedule = <Test as Config>::Schedule::get();
3318 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3319 set_balance(&ALICE, min_balance * 10);
3320 place_contract(&BOB, code_hash);
3321 let contract_origin = Origin::from_account_id(ALICE);
3322 let mut storage_meter =
3323 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3324 System::reset_events();
3325 MockStack::run_call(
3326 contract_origin,
3327 BOB,
3328 &mut gas_meter,
3329 &mut storage_meter,
3330 &schedule,
3331 0,
3332 vec![],
3333 None,
3334 Determinism::Enforced,
3335 )
3336 .unwrap();
3337
3338 let remark_hash = <Test as frame_system::Config>::Hashing::hash(b"Hello");
3339 assert_eq!(
3340 System::events(),
3341 vec![
3342 EventRecord {
3343 phase: Phase::Initialization,
3344 event: MetaEvent::System(frame_system::Event::Remarked {
3345 sender: BOB,
3346 hash: remark_hash
3347 }),
3348 topics: vec![],
3349 },
3350 EventRecord {
3351 phase: Phase::Initialization,
3352 event: MetaEvent::Utility(pallet_utility::Event::ItemCompleted),
3353 topics: vec![],
3354 },
3355 EventRecord {
3356 phase: Phase::Initialization,
3357 event: MetaEvent::Utility(pallet_utility::Event::BatchInterrupted {
3358 index: 1,
3359 error: frame_system::Error::<Test>::CallFiltered.into()
3360 },),
3361 topics: vec![],
3362 },
3363 EventRecord {
3364 phase: Phase::Initialization,
3365 event: MetaEvent::Contracts(crate::Event::Called {
3366 caller: Origin::from_account_id(ALICE),
3367 contract: BOB,
3368 }),
3369 topics: vec![],
3370 },
3371 ]
3372 );
3373 });
3374 }
3375
3376 #[test]
3377 fn nonce() {
3378 let fail_code = MockLoader::insert(Constructor, |_, _| exec_trapped());
3379 let success_code = MockLoader::insert(Constructor, |_, _| exec_success());
3380 let succ_fail_code = MockLoader::insert(Constructor, move |ctx, _| {
3381 ctx.ext
3382 .instantiate(
3383 Weight::zero(),
3384 BalanceOf::<Test>::zero(),
3385 fail_code,
3386 ctx.ext.minimum_balance() * 100,
3387 vec![],
3388 &[],
3389 )
3390 .ok();
3391 exec_success()
3392 });
3393 let succ_succ_code = MockLoader::insert(Constructor, move |ctx, _| {
3394 let (account_id, _) = ctx
3395 .ext
3396 .instantiate(
3397 Weight::zero(),
3398 BalanceOf::<Test>::zero(),
3399 success_code,
3400 ctx.ext.minimum_balance() * 100,
3401 vec![],
3402 &[],
3403 )
3404 .unwrap();
3405
3406 ctx.ext
3408 .call(
3409 Weight::zero(),
3410 BalanceOf::<Test>::zero(),
3411 account_id,
3412 0,
3413 vec![],
3414 false,
3415 false,
3416 )
3417 .unwrap();
3418
3419 exec_success()
3420 });
3421
3422 ExtBuilder::default()
3423 .with_code_hashes(MockLoader::code_hashes())
3424 .build()
3425 .execute_with(|| {
3426 let schedule = <Test as Config>::Schedule::get();
3427 let min_balance = <Test as Config>::Currency::minimum_balance();
3428 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3429 let fail_executable =
3430 MockExecutable::from_storage(fail_code, &mut gas_meter).unwrap();
3431 let success_executable =
3432 MockExecutable::from_storage(success_code, &mut gas_meter).unwrap();
3433 let succ_fail_executable =
3434 MockExecutable::from_storage(succ_fail_code, &mut gas_meter).unwrap();
3435 let succ_succ_executable =
3436 MockExecutable::from_storage(succ_succ_code, &mut gas_meter).unwrap();
3437 set_balance(&ALICE, min_balance * 10_000);
3438 let contract_origin = Origin::from_account_id(ALICE);
3439 let mut storage_meter =
3440 storage::meter::Meter::new(&contract_origin, None, min_balance * 100).unwrap();
3441
3442 MockStack::run_instantiate(
3443 ALICE,
3444 fail_executable,
3445 &mut gas_meter,
3446 &mut storage_meter,
3447 &schedule,
3448 min_balance * 100,
3449 vec![],
3450 &[],
3451 None,
3452 )
3453 .ok();
3454 assert_eq!(<Nonce<Test>>::get(), 0);
3455
3456 assert_ok!(MockStack::run_instantiate(
3457 ALICE,
3458 success_executable,
3459 &mut gas_meter,
3460 &mut storage_meter,
3461 &schedule,
3462 min_balance * 100,
3463 vec![],
3464 &[],
3465 None,
3466 ));
3467 assert_eq!(<Nonce<Test>>::get(), 1);
3468
3469 assert_ok!(MockStack::run_instantiate(
3470 ALICE,
3471 succ_fail_executable,
3472 &mut gas_meter,
3473 &mut storage_meter,
3474 &schedule,
3475 min_balance * 200,
3476 vec![],
3477 &[],
3478 None,
3479 ));
3480 assert_eq!(<Nonce<Test>>::get(), 2);
3481
3482 assert_ok!(MockStack::run_instantiate(
3483 ALICE,
3484 succ_succ_executable,
3485 &mut gas_meter,
3486 &mut storage_meter,
3487 &schedule,
3488 min_balance * 200,
3489 vec![],
3490 &[],
3491 None,
3492 ));
3493 assert_eq!(<Nonce<Test>>::get(), 4);
3494 });
3495 }
3496
3497 #[test]
3498 fn set_storage_works() {
3499 let code_hash = MockLoader::insert(Call, |ctx, _| {
3500 assert_eq!(
3502 ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
3503 Ok(WriteOutcome::New)
3504 );
3505 assert_eq!(
3506 ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true),
3507 Ok(WriteOutcome::New)
3508 );
3509 assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New));
3510 assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New));
3511 assert_eq!(
3512 ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false),
3513 Ok(WriteOutcome::New)
3514 );
3515 assert_eq!(
3516 ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true),
3517 Ok(WriteOutcome::New)
3518 );
3519
3520 assert_eq!(
3522 ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![42]), false),
3523 Ok(WriteOutcome::Overwritten(3))
3524 );
3525 assert_eq!(
3526 ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![48]), true),
3527 Ok(WriteOutcome::Taken(vec![4, 5, 6]))
3528 );
3529 assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New));
3530 assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New));
3531 assert_eq!(
3532 ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false),
3533 Ok(WriteOutcome::Overwritten(0))
3534 );
3535 assert_eq!(
3536 ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true),
3537 Ok(WriteOutcome::Taken(vec![]))
3538 );
3539
3540 exec_success()
3541 });
3542
3543 ExtBuilder::default().build().execute_with(|| {
3544 let min_balance = <Test as Config>::Currency::minimum_balance();
3545 let schedule = <Test as Config>::Schedule::get();
3546 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3547 set_balance(&ALICE, min_balance * 1000);
3548 place_contract(&BOB, code_hash);
3549 let contract_origin = Origin::from_account_id(ALICE);
3550 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3551 assert_ok!(MockStack::run_call(
3552 contract_origin,
3553 BOB,
3554 &mut gas_meter,
3555 &mut storage_meter,
3556 &schedule,
3557 0,
3558 vec![],
3559 None,
3560 Determinism::Enforced
3561 ));
3562 });
3563 }
3564
3565 #[test]
3566 fn set_storage_varsized_key_works() {
3567 let code_hash = MockLoader::insert(Call, |ctx, _| {
3568 assert_eq!(
3570 ctx.ext.set_storage(
3571 &Key::<Test>::try_from_var([1; 64].to_vec()).unwrap(),
3572 Some(vec![1, 2, 3]),
3573 false
3574 ),
3575 Ok(WriteOutcome::New)
3576 );
3577 assert_eq!(
3578 ctx.ext.set_storage(
3579 &Key::<Test>::try_from_var([2; 19].to_vec()).unwrap(),
3580 Some(vec![4, 5, 6]),
3581 true
3582 ),
3583 Ok(WriteOutcome::New)
3584 );
3585 assert_eq!(
3586 ctx.ext.set_storage(
3587 &Key::<Test>::try_from_var([3; 19].to_vec()).unwrap(),
3588 None,
3589 false
3590 ),
3591 Ok(WriteOutcome::New)
3592 );
3593 assert_eq!(
3594 ctx.ext.set_storage(
3595 &Key::<Test>::try_from_var([4; 64].to_vec()).unwrap(),
3596 None,
3597 true
3598 ),
3599 Ok(WriteOutcome::New)
3600 );
3601 assert_eq!(
3602 ctx.ext.set_storage(
3603 &Key::<Test>::try_from_var([5; 30].to_vec()).unwrap(),
3604 Some(vec![]),
3605 false
3606 ),
3607 Ok(WriteOutcome::New)
3608 );
3609 assert_eq!(
3610 ctx.ext.set_storage(
3611 &Key::<Test>::try_from_var([6; 128].to_vec()).unwrap(),
3612 Some(vec![]),
3613 true
3614 ),
3615 Ok(WriteOutcome::New)
3616 );
3617
3618 assert_eq!(
3620 ctx.ext.set_storage(
3621 &Key::<Test>::try_from_var([1; 64].to_vec()).unwrap(),
3622 Some(vec![42, 43, 44]),
3623 false
3624 ),
3625 Ok(WriteOutcome::Overwritten(3))
3626 );
3627 assert_eq!(
3628 ctx.ext.set_storage(
3629 &Key::<Test>::try_from_var([2; 19].to_vec()).unwrap(),
3630 Some(vec![48]),
3631 true
3632 ),
3633 Ok(WriteOutcome::Taken(vec![4, 5, 6]))
3634 );
3635 assert_eq!(
3636 ctx.ext.set_storage(
3637 &Key::<Test>::try_from_var([3; 19].to_vec()).unwrap(),
3638 None,
3639 false
3640 ),
3641 Ok(WriteOutcome::New)
3642 );
3643 assert_eq!(
3644 ctx.ext.set_storage(
3645 &Key::<Test>::try_from_var([4; 64].to_vec()).unwrap(),
3646 None,
3647 true
3648 ),
3649 Ok(WriteOutcome::New)
3650 );
3651 assert_eq!(
3652 ctx.ext.set_storage(
3653 &Key::<Test>::try_from_var([5; 30].to_vec()).unwrap(),
3654 Some(vec![]),
3655 false
3656 ),
3657 Ok(WriteOutcome::Overwritten(0))
3658 );
3659 assert_eq!(
3660 ctx.ext.set_storage(
3661 &Key::<Test>::try_from_var([6; 128].to_vec()).unwrap(),
3662 Some(vec![]),
3663 true
3664 ),
3665 Ok(WriteOutcome::Taken(vec![]))
3666 );
3667
3668 exec_success()
3669 });
3670
3671 ExtBuilder::default().build().execute_with(|| {
3672 let min_balance = <Test as Config>::Currency::minimum_balance();
3673 let schedule = <Test as Config>::Schedule::get();
3674 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3675 set_balance(&ALICE, min_balance * 1000);
3676 place_contract(&BOB, code_hash);
3677 let contract_origin = Origin::from_account_id(ALICE);
3678 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3679 assert_ok!(MockStack::run_call(
3680 contract_origin,
3681 BOB,
3682 &mut gas_meter,
3683 &mut storage_meter,
3684 &schedule,
3685 0,
3686 vec![],
3687 None,
3688 Determinism::Enforced
3689 ));
3690 });
3691 }
3692
3693 #[test]
3694 fn get_storage_works() {
3695 let code_hash = MockLoader::insert(Call, |ctx, _| {
3696 assert_eq!(
3697 ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
3698 Ok(WriteOutcome::New)
3699 );
3700 assert_eq!(
3701 ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false),
3702 Ok(WriteOutcome::New)
3703 );
3704 assert_eq!(ctx.ext.get_storage(&Key::Fix([1; 32])), Some(vec![1, 2, 3]));
3705 assert_eq!(ctx.ext.get_storage(&Key::Fix([2; 32])), Some(vec![]));
3706 assert_eq!(ctx.ext.get_storage(&Key::Fix([3; 32])), None);
3707
3708 exec_success()
3709 });
3710
3711 ExtBuilder::default().build().execute_with(|| {
3712 let min_balance = <Test as Config>::Currency::minimum_balance();
3713 let schedule = <Test as Config>::Schedule::get();
3714 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3715 set_balance(&ALICE, min_balance * 1000);
3716 place_contract(&BOB, code_hash);
3717 let contract_origin = Origin::from_account_id(ALICE);
3718 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3719 assert_ok!(MockStack::run_call(
3720 contract_origin,
3721 BOB,
3722 &mut gas_meter,
3723 &mut storage_meter,
3724 &schedule,
3725 0,
3726 vec![],
3727 None,
3728 Determinism::Enforced
3729 ));
3730 });
3731 }
3732
3733 #[test]
3734 fn get_storage_size_works() {
3735 let code_hash = MockLoader::insert(Call, |ctx, _| {
3736 assert_eq!(
3737 ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
3738 Ok(WriteOutcome::New)
3739 );
3740 assert_eq!(
3741 ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false),
3742 Ok(WriteOutcome::New)
3743 );
3744 assert_eq!(ctx.ext.get_storage_size(&Key::Fix([1; 32])), Some(3));
3745 assert_eq!(ctx.ext.get_storage_size(&Key::Fix([2; 32])), Some(0));
3746 assert_eq!(ctx.ext.get_storage_size(&Key::Fix([3; 32])), None);
3747
3748 exec_success()
3749 });
3750
3751 ExtBuilder::default().build().execute_with(|| {
3752 let min_balance = <Test as Config>::Currency::minimum_balance();
3753 let schedule = <Test as Config>::Schedule::get();
3754 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3755 set_balance(&ALICE, min_balance * 1000);
3756 place_contract(&BOB, code_hash);
3757 let contract_origin = Origin::from_account_id(ALICE);
3758 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3759 assert_ok!(MockStack::run_call(
3760 contract_origin,
3761 BOB,
3762 &mut gas_meter,
3763 &mut storage_meter,
3764 &schedule,
3765 0,
3766 vec![],
3767 None,
3768 Determinism::Enforced
3769 ));
3770 });
3771 }
3772
3773 #[test]
3774 fn get_storage_varsized_key_works() {
3775 let code_hash = MockLoader::insert(Call, |ctx, _| {
3776 assert_eq!(
3777 ctx.ext.set_storage(
3778 &Key::<Test>::try_from_var([1; 19].to_vec()).unwrap(),
3779 Some(vec![1, 2, 3]),
3780 false
3781 ),
3782 Ok(WriteOutcome::New)
3783 );
3784 assert_eq!(
3785 ctx.ext.set_storage(
3786 &Key::<Test>::try_from_var([2; 16].to_vec()).unwrap(),
3787 Some(vec![]),
3788 false
3789 ),
3790 Ok(WriteOutcome::New)
3791 );
3792 assert_eq!(
3793 ctx.ext.get_storage(&Key::<Test>::try_from_var([1; 19].to_vec()).unwrap()),
3794 Some(vec![1, 2, 3])
3795 );
3796 assert_eq!(
3797 ctx.ext.get_storage(&Key::<Test>::try_from_var([2; 16].to_vec()).unwrap()),
3798 Some(vec![])
3799 );
3800 assert_eq!(
3801 ctx.ext.get_storage(&Key::<Test>::try_from_var([3; 8].to_vec()).unwrap()),
3802 None
3803 );
3804
3805 exec_success()
3806 });
3807
3808 ExtBuilder::default().build().execute_with(|| {
3809 let min_balance = <Test as Config>::Currency::minimum_balance();
3810 let schedule = <Test as Config>::Schedule::get();
3811 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3812 set_balance(&ALICE, min_balance * 1000);
3813 place_contract(&BOB, code_hash);
3814 let contract_origin = Origin::from_account_id(ALICE);
3815 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3816 assert_ok!(MockStack::run_call(
3817 contract_origin,
3818 BOB,
3819 &mut gas_meter,
3820 &mut storage_meter,
3821 &schedule,
3822 0,
3823 vec![],
3824 None,
3825 Determinism::Enforced
3826 ));
3827 });
3828 }
3829
3830 #[test]
3831 fn get_storage_size_varsized_key_works() {
3832 let code_hash = MockLoader::insert(Call, |ctx, _| {
3833 assert_eq!(
3834 ctx.ext.set_storage(
3835 &Key::<Test>::try_from_var([1; 19].to_vec()).unwrap(),
3836 Some(vec![1, 2, 3]),
3837 false
3838 ),
3839 Ok(WriteOutcome::New)
3840 );
3841 assert_eq!(
3842 ctx.ext.set_storage(
3843 &Key::<Test>::try_from_var([2; 16].to_vec()).unwrap(),
3844 Some(vec![]),
3845 false
3846 ),
3847 Ok(WriteOutcome::New)
3848 );
3849 assert_eq!(
3850 ctx.ext.get_storage_size(&Key::<Test>::try_from_var([1; 19].to_vec()).unwrap()),
3851 Some(3)
3852 );
3853 assert_eq!(
3854 ctx.ext.get_storage_size(&Key::<Test>::try_from_var([2; 16].to_vec()).unwrap()),
3855 Some(0)
3856 );
3857 assert_eq!(
3858 ctx.ext.get_storage_size(&Key::<Test>::try_from_var([3; 8].to_vec()).unwrap()),
3859 None
3860 );
3861
3862 exec_success()
3863 });
3864
3865 ExtBuilder::default().build().execute_with(|| {
3866 let min_balance = <Test as Config>::Currency::minimum_balance();
3867 let schedule = <Test as Config>::Schedule::get();
3868 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3869 set_balance(&ALICE, min_balance * 1000);
3870 place_contract(&BOB, code_hash);
3871 let contract_origin = Origin::from_account_id(ALICE);
3872 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3873 assert_ok!(MockStack::run_call(
3874 contract_origin,
3875 BOB,
3876 &mut gas_meter,
3877 &mut storage_meter,
3878 &schedule,
3879 0,
3880 vec![],
3881 None,
3882 Determinism::Enforced
3883 ));
3884 });
3885 }
3886
3887 #[test]
3888 fn set_transient_storage_works() {
3889 let code_hash = MockLoader::insert(Call, |ctx, _| {
3890 assert_eq!(
3892 ctx.ext.set_transient_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
3893 Ok(WriteOutcome::New)
3894 );
3895 assert_eq!(
3896 ctx.ext.set_transient_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true),
3897 Ok(WriteOutcome::New)
3898 );
3899 assert_eq!(
3900 ctx.ext.set_transient_storage(&Key::Fix([3; 32]), None, false),
3901 Ok(WriteOutcome::New)
3902 );
3903 assert_eq!(
3904 ctx.ext.set_transient_storage(&Key::Fix([4; 32]), None, true),
3905 Ok(WriteOutcome::New)
3906 );
3907 assert_eq!(
3908 ctx.ext.set_transient_storage(&Key::Fix([5; 32]), Some(vec![]), false),
3909 Ok(WriteOutcome::New)
3910 );
3911 assert_eq!(
3912 ctx.ext.set_transient_storage(&Key::Fix([6; 32]), Some(vec![]), true),
3913 Ok(WriteOutcome::New)
3914 );
3915
3916 assert_eq!(
3918 ctx.ext.set_transient_storage(&Key::Fix([1; 32]), Some(vec![42]), false),
3919 Ok(WriteOutcome::Overwritten(3))
3920 );
3921 assert_eq!(
3922 ctx.ext.set_transient_storage(&Key::Fix([2; 32]), Some(vec![48]), true),
3923 Ok(WriteOutcome::Taken(vec![4, 5, 6]))
3924 );
3925 assert_eq!(
3926 ctx.ext.set_transient_storage(&Key::Fix([3; 32]), None, false),
3927 Ok(WriteOutcome::New)
3928 );
3929 assert_eq!(
3930 ctx.ext.set_transient_storage(&Key::Fix([4; 32]), None, true),
3931 Ok(WriteOutcome::New)
3932 );
3933 assert_eq!(
3934 ctx.ext.set_transient_storage(&Key::Fix([5; 32]), Some(vec![]), false),
3935 Ok(WriteOutcome::Overwritten(0))
3936 );
3937 assert_eq!(
3938 ctx.ext.set_transient_storage(&Key::Fix([6; 32]), Some(vec![]), true),
3939 Ok(WriteOutcome::Taken(vec![]))
3940 );
3941
3942 exec_success()
3943 });
3944
3945 ExtBuilder::default().build().execute_with(|| {
3946 let schedule = <Test as Config>::Schedule::get();
3947 place_contract(&BOB, code_hash);
3948 let contract_origin = Origin::from_account_id(ALICE);
3949 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3950 assert_ok!(MockStack::run_call(
3951 contract_origin,
3952 BOB,
3953 &mut GasMeter::<Test>::new(GAS_LIMIT),
3954 &mut storage_meter,
3955 &schedule,
3956 0,
3957 vec![],
3958 None,
3959 Determinism::Enforced
3960 ));
3961 });
3962 }
3963
3964 #[test]
3965 fn get_transient_storage_works() {
3966 let storage_key_1 = &Key::Fix([1; 32]);
3968 let storage_key_2 = &Key::Fix([2; 32]);
3969 let storage_key_3 = &Key::Fix([3; 32]);
3970 let code_bob = MockLoader::insert(Call, |ctx, _| {
3971 if ctx.input_data[0] == 0 {
3972 assert_eq!(
3973 ctx.ext.set_transient_storage(storage_key_1, Some(vec![1, 2]), false),
3974 Ok(WriteOutcome::New)
3975 );
3976 assert_eq!(
3977 ctx.ext.call(
3978 Weight::zero(),
3979 BalanceOf::<Test>::zero(),
3980 CHARLIE,
3981 0,
3982 vec![],
3983 true,
3984 false,
3985 ),
3986 exec_success()
3987 );
3988 assert_eq!(ctx.ext.get_transient_storage(storage_key_1), Some(vec![3]));
3989 assert_eq!(ctx.ext.get_transient_storage(storage_key_2), Some(vec![]));
3990 assert_eq!(ctx.ext.get_transient_storage(storage_key_3), None);
3991 } else {
3992 assert_eq!(
3993 ctx.ext.set_transient_storage(storage_key_1, Some(vec![3]), true),
3994 Ok(WriteOutcome::Taken(vec![1, 2]))
3995 );
3996 assert_eq!(
3997 ctx.ext.set_transient_storage(storage_key_2, Some(vec![]), false),
3998 Ok(WriteOutcome::New)
3999 );
4000 }
4001 exec_success()
4002 });
4003 let code_charlie = MockLoader::insert(Call, |ctx, _| {
4004 assert!(ctx
4005 .ext
4006 .call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![99], true, false)
4007 .is_ok());
4008 assert_eq!(ctx.ext.get_transient_storage(storage_key_1), None);
4010 exec_success()
4011 });
4012
4013 ExtBuilder::default().build().execute_with(|| {
4015 let schedule = <Test as Config>::Schedule::get();
4016 place_contract(&BOB, code_bob);
4017 place_contract(&CHARLIE, code_charlie);
4018 let contract_origin = Origin::from_account_id(ALICE);
4019 let mut storage_meter =
4020 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4021
4022 let result = MockStack::run_call(
4023 contract_origin,
4024 BOB,
4025 &mut GasMeter::<Test>::new(GAS_LIMIT),
4026 &mut storage_meter,
4027 &schedule,
4028 0,
4029 vec![0],
4030 None,
4031 Determinism::Enforced,
4032 );
4033 assert_matches!(result, Ok(_));
4034 });
4035 }
4036
4037 #[test]
4038 fn get_transient_storage_size_works() {
4039 let storage_key_1 = &Key::Fix([1; 32]);
4040 let storage_key_2 = &Key::Fix([2; 32]);
4041 let storage_key_3 = &Key::Fix([3; 32]);
4042 let code_hash = MockLoader::insert(Call, |ctx, _| {
4043 assert_eq!(
4044 ctx.ext.set_transient_storage(storage_key_1, Some(vec![1, 2, 3]), false),
4045 Ok(WriteOutcome::New)
4046 );
4047 assert_eq!(
4048 ctx.ext.set_transient_storage(storage_key_2, Some(vec![]), false),
4049 Ok(WriteOutcome::New)
4050 );
4051 assert_eq!(ctx.ext.get_transient_storage_size(storage_key_1), Some(3));
4052 assert_eq!(ctx.ext.get_transient_storage_size(storage_key_2), Some(0));
4053 assert_eq!(ctx.ext.get_transient_storage_size(storage_key_3), None);
4054
4055 exec_success()
4056 });
4057
4058 ExtBuilder::default().build().execute_with(|| {
4059 let schedule = <Test as Config>::Schedule::get();
4060 place_contract(&BOB, code_hash);
4061 let contract_origin = Origin::from_account_id(ALICE);
4062 let mut storage_meter =
4063 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4064 assert_ok!(MockStack::run_call(
4065 contract_origin,
4066 BOB,
4067 &mut GasMeter::<Test>::new(GAS_LIMIT),
4068 &mut storage_meter,
4069 &schedule,
4070 0,
4071 vec![],
4072 None,
4073 Determinism::Enforced
4074 ));
4075 });
4076 }
4077
4078 #[test]
4079 fn rollback_transient_storage_works() {
4080 let storage_key = &Key::Fix([1; 32]);
4082 let code_bob = MockLoader::insert(Call, |ctx, _| {
4083 if ctx.input_data[0] == 0 {
4084 assert_eq!(
4085 ctx.ext.set_transient_storage(storage_key, Some(vec![1, 2]), false),
4086 Ok(WriteOutcome::New)
4087 );
4088 assert_eq!(
4089 ctx.ext.call(
4090 Weight::zero(),
4091 BalanceOf::<Test>::zero(),
4092 CHARLIE,
4093 0,
4094 vec![],
4095 true,
4096 false
4097 ),
4098 exec_trapped()
4099 );
4100 assert_eq!(ctx.ext.get_transient_storage(storage_key), Some(vec![1, 2]));
4101 } else {
4102 let overwritten_length = ctx.ext.get_transient_storage_size(storage_key).unwrap();
4103 assert_eq!(
4104 ctx.ext.set_transient_storage(storage_key, Some(vec![3]), false),
4105 Ok(WriteOutcome::Overwritten(overwritten_length))
4106 );
4107 assert_eq!(ctx.ext.get_transient_storage(storage_key), Some(vec![3]));
4108 }
4109 exec_success()
4110 });
4111 let code_charlie = MockLoader::insert(Call, |ctx, _| {
4112 assert!(ctx
4113 .ext
4114 .call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![99], true, false)
4115 .is_ok());
4116 exec_trapped()
4117 });
4118
4119 ExtBuilder::default().build().execute_with(|| {
4121 let schedule = <Test as Config>::Schedule::get();
4122 place_contract(&BOB, code_bob);
4123 place_contract(&CHARLIE, code_charlie);
4124 let contract_origin = Origin::from_account_id(ALICE);
4125 let mut storage_meter =
4126 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4127
4128 let result = MockStack::run_call(
4129 contract_origin,
4130 BOB,
4131 &mut GasMeter::<Test>::new(GAS_LIMIT),
4132 &mut storage_meter,
4133 &schedule,
4134 0,
4135 vec![0],
4136 None,
4137 Determinism::Enforced,
4138 );
4139 assert_matches!(result, Ok(_));
4140 });
4141 }
4142
4143 #[test]
4144 fn ecdsa_to_eth_address_returns_proper_value() {
4145 let bob_ch = MockLoader::insert(Call, |ctx, _| {
4146 let pubkey_compressed = array_bytes::hex2array_unchecked(
4147 "028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd91",
4148 );
4149 assert_eq!(
4150 ctx.ext.ecdsa_to_eth_address(&pubkey_compressed).unwrap(),
4151 array_bytes::hex2array_unchecked::<_, 20>(
4152 "09231da7b19A016f9e576d23B16277062F4d46A8"
4153 )
4154 );
4155 exec_success()
4156 });
4157
4158 ExtBuilder::default().build().execute_with(|| {
4159 let schedule = <Test as Config>::Schedule::get();
4160 place_contract(&BOB, bob_ch);
4161
4162 let contract_origin = Origin::from_account_id(ALICE);
4163 let mut storage_meter =
4164 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4165 let result = MockStack::run_call(
4166 contract_origin,
4167 BOB,
4168 &mut GasMeter::<Test>::new(GAS_LIMIT),
4169 &mut storage_meter,
4170 &schedule,
4171 0,
4172 vec![],
4173 None,
4174 Determinism::Enforced,
4175 );
4176 assert_matches!(result, Ok(_));
4177 });
4178 }
4179
4180 #[test]
4181 fn nonce_api_works() {
4182 let fail_code = MockLoader::insert(Constructor, |_, _| exec_trapped());
4183 let success_code = MockLoader::insert(Constructor, |_, _| exec_success());
4184 let code_hash = MockLoader::insert(Call, move |ctx, _| {
4185 assert_eq!(ctx.ext.nonce(), 1);
4187 assert_eq!(ctx.ext.nonce(), 1);
4189 assert_err!(
4191 ctx.ext.instantiate(
4192 Weight::zero(),
4193 BalanceOf::<Test>::zero(),
4194 fail_code,
4195 0,
4196 vec![],
4197 &[],
4198 ),
4199 ExecError {
4200 error: <Error<Test>>::ContractTrapped.into(),
4201 origin: ErrorOrigin::Callee
4202 }
4203 );
4204 assert_eq!(ctx.ext.nonce(), 1);
4205 ctx.ext
4207 .instantiate(
4208 Weight::zero(),
4209 BalanceOf::<Test>::zero(),
4210 success_code,
4211 0,
4212 vec![],
4213 &[],
4214 )
4215 .unwrap();
4216 assert_eq!(ctx.ext.nonce(), 2);
4217 exec_success()
4218 });
4219
4220 ExtBuilder::default()
4221 .with_code_hashes(MockLoader::code_hashes())
4222 .build()
4223 .execute_with(|| {
4224 let min_balance = <Test as Config>::Currency::minimum_balance();
4225 let schedule = <Test as Config>::Schedule::get();
4226 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
4227 set_balance(&ALICE, min_balance * 1000);
4228 place_contract(&BOB, code_hash);
4229 let contract_origin = Origin::from_account_id(ALICE);
4230 let mut storage_meter =
4231 storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
4232 assert_ok!(MockStack::run_call(
4233 contract_origin,
4234 BOB,
4235 &mut gas_meter,
4236 &mut storage_meter,
4237 &schedule,
4238 0,
4239 vec![],
4240 None,
4241 Determinism::Enforced
4242 ));
4243 });
4244 }
4245
4246 #[test]
4249 fn randomness_works() {
4250 let subject = b"nice subject".as_ref();
4251 let code_hash = MockLoader::insert(Call, move |ctx, _| {
4252 let rand = <Test as Config>::Randomness::random(subject);
4253 assert_eq!(rand, ctx.ext.random(subject));
4254 exec_success()
4255 });
4256
4257 ExtBuilder::default().build().execute_with(|| {
4258 let schedule = <Test as Config>::Schedule::get();
4259 place_contract(&BOB, code_hash);
4260
4261 let contract_origin = Origin::from_account_id(ALICE);
4262 let mut storage_meter =
4263 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4264 let result = MockStack::run_call(
4265 contract_origin,
4266 BOB,
4267 &mut GasMeter::<Test>::new(GAS_LIMIT),
4268 &mut storage_meter,
4269 &schedule,
4270 0,
4271 vec![],
4272 None,
4273 Determinism::Enforced,
4274 );
4275 assert_matches!(result, Ok(_));
4276 });
4277 }
4278}