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 sp_core::RuntimeDebug,
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 _ => TransactionOutcome::Rollback(Ok((false, output))),
1070 }
1071 });
1072
1073 let (success, output) = match transaction_outcome {
1074 Ok((success, output)) => (success, output),
1076 Err(error) => (false, Err(error.into())),
1079 };
1080
1081 if success {
1082 self.transient_storage.commit_transaction();
1083 } else {
1084 self.transient_storage.rollback_transaction();
1085 }
1086
1087 self.pop_frame(success);
1088 output
1089 }
1090
1091 fn pop_frame(&mut self, persist: bool) {
1096 if !persist && self.top_frame().entry_point == ExportedFunction::Constructor {
1098 self.nonce.as_mut().map(|c| *c = c.wrapping_sub(1));
1099 }
1100
1101 let frame = self.frames.pop();
1105
1106 if let Some(mut frame) = frame {
1109 let account_id = &frame.account_id;
1110 let prev = top_frame_mut!(self);
1111
1112 prev.nested_gas.absorb_nested(frame.nested_gas);
1113
1114 if !persist {
1116 return
1117 }
1118
1119 frame.contract_info.load(account_id);
1124 let mut contract = frame.contract_info.into_contract();
1125 prev.nested_storage.absorb(frame.nested_storage, account_id, contract.as_mut());
1126
1127 if let Some(contract) = contract {
1129 if prev.account_id == *account_id {
1134 prev.contract_info = CachedContract::Cached(contract);
1135 return
1136 }
1137
1138 <ContractInfoOf<T>>::insert(account_id, contract);
1144 if let Some(c) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) {
1145 c.contract_info = CachedContract::Invalidated;
1146 }
1147 }
1148 } else {
1149 if let Some((msg, false)) = self.debug_message.as_ref().map(|m| (m, m.is_empty())) {
1150 log::debug!(
1151 target: LOG_TARGET,
1152 "Execution finished with debug buffer: {}",
1153 core::str::from_utf8(msg).unwrap_or("<Invalid UTF8>"),
1154 );
1155 }
1156 self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas));
1157 if !persist {
1158 return
1159 }
1160 let mut contract = self.first_frame.contract_info.as_contract();
1161 self.storage_meter.absorb(
1162 mem::take(&mut self.first_frame.nested_storage),
1163 &self.first_frame.account_id,
1164 contract.as_deref_mut(),
1165 );
1166 if let Some(contract) = contract {
1167 <ContractInfoOf<T>>::insert(&self.first_frame.account_id, contract);
1168 }
1169 if let Some(nonce) = self.nonce {
1170 <Nonce<T>>::set(nonce);
1171 }
1172 }
1173 }
1174
1175 fn transfer(
1177 preservation: Preservation,
1178 from: &T::AccountId,
1179 to: &T::AccountId,
1180 value: BalanceOf<T>,
1181 ) -> DispatchResult {
1182 if !value.is_zero() && from != to {
1183 T::Currency::transfer(from, to, value, preservation)
1184 .map_err(|_| Error::<T>::TransferFailed)?;
1185 }
1186 Ok(())
1187 }
1188
1189 fn initial_transfer(&self) -> DispatchResult {
1191 let frame = self.top_frame();
1192
1193 if frame.delegate_caller.is_some() {
1196 return Ok(())
1197 }
1198
1199 let value = frame.value_transferred;
1200
1201 let caller = match self.caller() {
1205 Origin::Signed(caller) => caller,
1206 Origin::Root if value.is_zero() => return Ok(()),
1207 Origin::Root => return DispatchError::RootNotAllowed.into(),
1208 };
1209 Self::transfer(Preservation::Preserve, &caller, &frame.account_id, value)
1210 }
1211
1212 fn top_frame(&self) -> &Frame<T> {
1214 top_frame!(self)
1215 }
1216
1217 fn top_frame_mut(&mut self) -> &mut Frame<T> {
1219 top_frame_mut!(self)
1220 }
1221
1222 fn frames(&self) -> impl Iterator<Item = &Frame<T>> {
1226 core::iter::once(&self.first_frame).chain(&self.frames).rev()
1227 }
1228
1229 fn frames_mut(&mut self) -> impl Iterator<Item = &mut Frame<T>> {
1231 core::iter::once(&mut self.first_frame).chain(&mut self.frames).rev()
1232 }
1233
1234 fn is_recursive(&self) -> bool {
1236 let account_id = &self.top_frame().account_id;
1237 self.frames().skip(1).any(|f| &f.account_id == account_id)
1238 }
1239
1240 fn allows_reentry(&self, id: &AccountIdOf<T>) -> bool {
1242 !self.frames().any(|f| &f.account_id == id && !f.allows_reentry)
1243 }
1244
1245 fn next_nonce(&mut self) -> u64 {
1247 let next = self.nonce().wrapping_add(1);
1248 self.nonce = Some(next);
1249 next
1250 }
1251}
1252
1253impl<'a, T, E> Ext for Stack<'a, T, E>
1254where
1255 T: Config,
1256 E: Executable<T>,
1257{
1258 type T = T;
1259
1260 fn call(
1261 &mut self,
1262 gas_limit: Weight,
1263 deposit_limit: BalanceOf<T>,
1264 to: T::AccountId,
1265 value: BalanceOf<T>,
1266 input_data: Vec<u8>,
1267 allows_reentry: bool,
1268 read_only: bool,
1269 ) -> Result<ExecReturnValue, ExecError> {
1270 self.top_frame_mut().allows_reentry = allows_reentry;
1274
1275 let try_call = || {
1276 if !self.allows_reentry(&to) {
1277 return Err(<Error<T>>::ReentranceDenied.into())
1278 }
1279
1280 let cached_info = self
1284 .frames()
1285 .find(|f| f.entry_point == ExportedFunction::Call && f.account_id == to)
1286 .and_then(|f| match &f.contract_info {
1287 CachedContract::Cached(contract) => Some(contract.clone()),
1288 _ => None,
1289 });
1290 let executable = self.push_frame(
1291 FrameArgs::Call { dest: to, cached_info, delegated_call: None },
1292 value,
1293 gas_limit,
1294 deposit_limit,
1295 read_only || self.is_read_only(),
1297 )?;
1298 self.run(executable, input_data)
1299 };
1300
1301 let result = try_call();
1303
1304 self.top_frame_mut().allows_reentry = true;
1306
1307 result
1308 }
1309
1310 fn delegate_call(
1311 &mut self,
1312 code_hash: CodeHash<Self::T>,
1313 input_data: Vec<u8>,
1314 ) -> Result<ExecReturnValue, ExecError> {
1315 let executable = E::from_storage(code_hash, self.gas_meter_mut())?;
1316 let top_frame = self.top_frame_mut();
1317 let contract_info = top_frame.contract_info().clone();
1318 let account_id = top_frame.account_id.clone();
1319 let value = top_frame.value_transferred;
1320 let executable = self.push_frame(
1321 FrameArgs::Call {
1322 dest: account_id,
1323 cached_info: Some(contract_info),
1324 delegated_call: Some(DelegatedCall { executable, caller: self.caller().clone() }),
1325 },
1326 value,
1327 Weight::zero(),
1328 BalanceOf::<T>::zero(),
1329 self.is_read_only(),
1330 )?;
1331 self.run(executable, input_data)
1332 }
1333
1334 fn instantiate(
1335 &mut self,
1336 gas_limit: Weight,
1337 deposit_limit: BalanceOf<Self::T>,
1338 code_hash: CodeHash<T>,
1339 value: BalanceOf<T>,
1340 input_data: Vec<u8>,
1341 salt: &[u8],
1342 ) -> Result<(AccountIdOf<T>, ExecReturnValue), ExecError> {
1343 let executable = E::from_storage(code_hash, self.gas_meter_mut())?;
1344 let nonce = self.next_nonce();
1345 let executable = self.push_frame(
1346 FrameArgs::Instantiate {
1347 sender: self.top_frame().account_id.clone(),
1348 nonce,
1349 executable,
1350 salt,
1351 input_data: input_data.as_ref(),
1352 },
1353 value,
1354 gas_limit,
1355 deposit_limit,
1356 self.is_read_only(),
1357 )?;
1358 let account_id = self.top_frame().account_id.clone();
1359 self.run(executable, input_data).map(|ret| (account_id, ret))
1360 }
1361
1362 fn terminate(&mut self, beneficiary: &AccountIdOf<Self::T>) -> DispatchResult {
1363 if self.is_recursive() {
1364 return Err(Error::<T>::TerminatedWhileReentrant.into())
1365 }
1366 let frame = self.top_frame_mut();
1367 let info = frame.terminate();
1368 frame.nested_storage.terminate(&info, beneficiary.clone());
1369
1370 info.queue_trie_for_deletion();
1371 ContractInfoOf::<T>::remove(&frame.account_id);
1372 Self::decrement_refcount(info.code_hash);
1373
1374 for (code_hash, deposit) in info.delegate_dependencies() {
1375 Self::decrement_refcount(*code_hash);
1376 frame
1377 .nested_storage
1378 .charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(*deposit));
1379 }
1380
1381 Contracts::<T>::deposit_event(Event::Terminated {
1382 contract: frame.account_id.clone(),
1383 beneficiary: beneficiary.clone(),
1384 });
1385 Ok(())
1386 }
1387
1388 fn transfer(&mut self, to: &T::AccountId, value: BalanceOf<T>) -> DispatchResult {
1389 Self::transfer(Preservation::Preserve, &self.top_frame().account_id, to, value)
1390 }
1391
1392 fn get_storage(&mut self, key: &Key<T>) -> Option<Vec<u8>> {
1393 self.top_frame_mut().contract_info().read(key)
1394 }
1395
1396 fn get_storage_size(&mut self, key: &Key<T>) -> Option<u32> {
1397 self.top_frame_mut().contract_info().size(key.into())
1398 }
1399
1400 fn set_storage(
1401 &mut self,
1402 key: &Key<T>,
1403 value: Option<Vec<u8>>,
1404 take_old: bool,
1405 ) -> Result<WriteOutcome, DispatchError> {
1406 let frame = self.top_frame_mut();
1407 frame.contract_info.get(&frame.account_id).write(
1408 key.into(),
1409 value,
1410 Some(&mut frame.nested_storage),
1411 take_old,
1412 )
1413 }
1414
1415 fn get_transient_storage(&self, key: &Key<T>) -> Option<Vec<u8>> {
1416 self.transient_storage.read(self.address(), key)
1417 }
1418
1419 fn get_transient_storage_size(&self, key: &Key<T>) -> Option<u32> {
1420 self.transient_storage.read(self.address(), key).map(|value| value.len() as _)
1421 }
1422
1423 fn set_transient_storage(
1424 &mut self,
1425 key: &Key<T>,
1426 value: Option<Vec<u8>>,
1427 take_old: bool,
1428 ) -> Result<WriteOutcome, DispatchError> {
1429 let account_id = self.address().clone();
1430 self.transient_storage.write(&account_id, key, value, take_old)
1431 }
1432
1433 fn address(&self) -> &T::AccountId {
1434 &self.top_frame().account_id
1435 }
1436
1437 fn caller(&self) -> Origin<T> {
1438 if let Some(caller) = &self.top_frame().delegate_caller {
1439 caller.clone()
1440 } else {
1441 self.frames()
1442 .nth(1)
1443 .map(|f| Origin::from_account_id(f.account_id.clone()))
1444 .unwrap_or(self.origin.clone())
1445 }
1446 }
1447
1448 fn is_contract(&self, address: &T::AccountId) -> bool {
1449 ContractInfoOf::<T>::contains_key(&address)
1450 }
1451
1452 fn code_hash(&self, address: &T::AccountId) -> Option<CodeHash<Self::T>> {
1453 <ContractInfoOf<T>>::get(&address).map(|contract| contract.code_hash)
1454 }
1455
1456 fn own_code_hash(&mut self) -> &CodeHash<Self::T> {
1457 &self.top_frame_mut().contract_info().code_hash
1458 }
1459
1460 fn caller_is_origin(&self) -> bool {
1461 self.origin == self.caller()
1462 }
1463
1464 fn caller_is_root(&self) -> bool {
1465 self.caller_is_origin() && self.origin == Origin::Root
1467 }
1468
1469 fn balance(&self) -> BalanceOf<T> {
1470 T::Currency::reducible_balance(
1471 &self.top_frame().account_id,
1472 Preservation::Preserve,
1473 Fortitude::Polite,
1474 )
1475 }
1476
1477 fn value_transferred(&self) -> BalanceOf<T> {
1478 self.top_frame().value_transferred
1479 }
1480
1481 fn random(&self, subject: &[u8]) -> (SeedOf<T>, BlockNumberFor<T>) {
1482 T::Randomness::random(subject)
1483 }
1484
1485 fn now(&self) -> &MomentOf<T> {
1486 &self.timestamp
1487 }
1488
1489 fn minimum_balance(&self) -> BalanceOf<T> {
1490 T::Currency::minimum_balance()
1491 }
1492
1493 fn deposit_event(&mut self, topics: Vec<T::Hash>, data: Vec<u8>) {
1494 Contracts::<Self::T>::deposit_indexed_event(
1495 topics,
1496 Event::ContractEmitted { contract: self.top_frame().account_id.clone(), data },
1497 );
1498 }
1499
1500 fn block_number(&self) -> BlockNumberFor<T> {
1501 self.block_number
1502 }
1503
1504 fn max_value_size(&self) -> u32 {
1505 self.schedule.limits.payload_len
1506 }
1507
1508 fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
1509 T::WeightPrice::convert(weight)
1510 }
1511
1512 fn schedule(&self) -> &Schedule<Self::T> {
1513 self.schedule
1514 }
1515
1516 fn gas_meter(&self) -> &GasMeter<Self::T> {
1517 &self.top_frame().nested_gas
1518 }
1519
1520 fn gas_meter_mut(&mut self) -> &mut GasMeter<Self::T> {
1521 &mut self.top_frame_mut().nested_gas
1522 }
1523
1524 fn charge_storage(&mut self, diff: &Diff) {
1525 self.top_frame_mut().nested_storage.charge(diff)
1526 }
1527
1528 fn debug_buffer_enabled(&self) -> bool {
1529 self.debug_message.is_some()
1530 }
1531
1532 fn append_debug_buffer(&mut self, msg: &str) -> bool {
1533 if let Some(buffer) = &mut self.debug_message {
1534 buffer
1535 .try_extend(&mut msg.bytes())
1536 .map_err(|_| {
1537 log::debug!(
1538 target: LOG_TARGET,
1539 "Debug buffer (of {} bytes) exhausted!",
1540 DebugBufferVec::<T>::bound(),
1541 )
1542 })
1543 .ok();
1544 true
1545 } else {
1546 false
1547 }
1548 }
1549
1550 fn call_runtime(&self, call: <Self::T as Config>::RuntimeCall) -> DispatchResultWithPostInfo {
1551 let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.address().clone()).into();
1552 origin.add_filter(T::CallFilter::contains);
1553 call.dispatch(origin)
1554 }
1555
1556 fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()> {
1557 secp256k1_ecdsa_recover_compressed(signature, message_hash).map_err(|_| ())
1558 }
1559
1560 fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool {
1561 sp_io::crypto::sr25519_verify(
1562 &SR25519Signature::from(*signature),
1563 message,
1564 &SR25519Public::from(*pub_key),
1565 )
1566 }
1567
1568 fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()> {
1569 ECDSAPublic::from(*pk).to_eth_address()
1570 }
1571
1572 #[cfg(any(test, feature = "runtime-benchmarks"))]
1573 fn contract_info(&mut self) -> &mut ContractInfo<Self::T> {
1574 self.top_frame_mut().contract_info()
1575 }
1576
1577 #[cfg(feature = "runtime-benchmarks")]
1578 fn transient_storage(&mut self) -> &mut TransientStorage<Self::T> {
1579 &mut self.transient_storage
1580 }
1581
1582 fn set_code_hash(&mut self, hash: CodeHash<Self::T>) -> DispatchResult {
1583 let frame = top_frame_mut!(self);
1584 if !E::from_storage(hash, &mut frame.nested_gas)?.is_deterministic() {
1585 return Err(<Error<T>>::Indeterministic.into())
1586 }
1587
1588 let info = frame.contract_info();
1589
1590 let prev_hash = info.code_hash;
1591 info.code_hash = hash;
1592
1593 let code_info = CodeInfoOf::<T>::get(hash).ok_or(Error::<T>::CodeNotFound)?;
1594
1595 let old_base_deposit = info.storage_base_deposit();
1596 let new_base_deposit = info.update_base_deposit(&code_info);
1597 let deposit = StorageDeposit::Charge(new_base_deposit)
1598 .saturating_sub(&StorageDeposit::Charge(old_base_deposit));
1599
1600 frame.nested_storage.charge_deposit(frame.account_id.clone(), deposit);
1601
1602 Self::increment_refcount(hash)?;
1603 Self::decrement_refcount(prev_hash);
1604 Contracts::<Self::T>::deposit_event(Event::ContractCodeUpdated {
1605 contract: frame.account_id.clone(),
1606 new_code_hash: hash,
1607 old_code_hash: prev_hash,
1608 });
1609 Ok(())
1610 }
1611
1612 fn reentrance_count(&self) -> u32 {
1613 let id: &AccountIdOf<Self::T> = &self.top_frame().account_id;
1614 self.account_reentrance_count(id).saturating_sub(1)
1615 }
1616
1617 fn account_reentrance_count(&self, account_id: &AccountIdOf<Self::T>) -> u32 {
1618 self.frames()
1619 .filter(|f| f.delegate_caller.is_none() && &f.account_id == account_id)
1620 .count() as u32
1621 }
1622
1623 fn nonce(&mut self) -> u64 {
1624 if let Some(current) = self.nonce {
1625 current
1626 } else {
1627 let current = <Nonce<T>>::get();
1628 self.nonce = Some(current);
1629 current
1630 }
1631 }
1632
1633 fn increment_refcount(code_hash: CodeHash<Self::T>) -> DispatchResult {
1634 <CodeInfoOf<Self::T>>::mutate(code_hash, |existing| -> Result<(), DispatchError> {
1635 if let Some(info) = existing {
1636 *info.refcount_mut() = info.refcount().saturating_add(1);
1637 Ok(())
1638 } else {
1639 Err(Error::<T>::CodeNotFound.into())
1640 }
1641 })
1642 }
1643
1644 fn decrement_refcount(code_hash: CodeHash<T>) {
1645 <CodeInfoOf<T>>::mutate(code_hash, |existing| {
1646 if let Some(info) = existing {
1647 *info.refcount_mut() = info.refcount().saturating_sub(1);
1648 }
1649 });
1650 }
1651
1652 fn lock_delegate_dependency(&mut self, code_hash: CodeHash<Self::T>) -> DispatchResult {
1653 let frame = self.top_frame_mut();
1654 let info = frame.contract_info.get(&frame.account_id);
1655 ensure!(code_hash != info.code_hash, Error::<T>::CannotAddSelfAsDelegateDependency);
1656
1657 let code_info = CodeInfoOf::<T>::get(code_hash).ok_or(Error::<T>::CodeNotFound)?;
1658 let deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_info.deposit());
1659
1660 info.lock_delegate_dependency(code_hash, deposit)?;
1661 Self::increment_refcount(code_hash)?;
1662 frame
1663 .nested_storage
1664 .charge_deposit(frame.account_id.clone(), StorageDeposit::Charge(deposit));
1665 Ok(())
1666 }
1667
1668 fn unlock_delegate_dependency(&mut self, code_hash: &CodeHash<Self::T>) -> DispatchResult {
1669 let frame = self.top_frame_mut();
1670 let info = frame.contract_info.get(&frame.account_id);
1671
1672 let deposit = info.unlock_delegate_dependency(code_hash)?;
1673 Self::decrement_refcount(*code_hash);
1674 frame
1675 .nested_storage
1676 .charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(deposit));
1677 Ok(())
1678 }
1679
1680 fn locked_delegate_dependencies_count(&mut self) -> usize {
1681 self.top_frame_mut().contract_info().delegate_dependencies_count()
1682 }
1683
1684 fn is_read_only(&self) -> bool {
1685 self.top_frame().read_only
1686 }
1687}
1688
1689mod sealing {
1690 use super::*;
1691
1692 pub trait Sealed {}
1693
1694 impl<'a, T: Config, E> Sealed for Stack<'a, T, E> {}
1695
1696 #[cfg(test)]
1697 impl Sealed for crate::wasm::MockExt {}
1698
1699 #[cfg(test)]
1700 impl Sealed for &mut crate::wasm::MockExt {}
1701}
1702
1703#[cfg(test)]
1709mod tests {
1710 use super::*;
1711 use crate::{
1712 exec::ExportedFunction::*,
1713 gas::GasMeter,
1714 tests::{
1715 test_utils::{get_balance, place_contract, set_balance},
1716 ExtBuilder, RuntimeCall, RuntimeEvent as MetaEvent, Test, TestFilter, ALICE, BOB,
1717 CHARLIE, GAS_LIMIT,
1718 },
1719 Error,
1720 };
1721 use assert_matches::assert_matches;
1722 use codec::{Decode, Encode};
1723 use frame_support::{assert_err, assert_ok, parameter_types};
1724 use frame_system::{EventRecord, Phase};
1725 use pallet_contracts_uapi::ReturnFlags;
1726 use pretty_assertions::assert_eq;
1727 use sp_runtime::{traits::Hash, DispatchError};
1728 use std::{cell::RefCell, collections::hash_map::HashMap, rc::Rc};
1729
1730 type System = frame_system::Pallet<Test>;
1731
1732 type MockStack<'a> = Stack<'a, Test, MockExecutable>;
1733
1734 parameter_types! {
1735 static Loader: MockLoader = MockLoader::default();
1736 }
1737
1738 fn events() -> Vec<Event<Test>> {
1739 System::events()
1740 .into_iter()
1741 .filter_map(|meta| match meta.event {
1742 MetaEvent::Contracts(contract_event) => Some(contract_event),
1743 _ => None,
1744 })
1745 .collect()
1746 }
1747
1748 struct MockCtx<'a> {
1749 ext: &'a mut MockStack<'a>,
1750 input_data: Vec<u8>,
1751 }
1752
1753 #[derive(Clone)]
1754 struct MockExecutable {
1755 func: Rc<dyn for<'a> Fn(MockCtx<'a>, &Self) -> ExecResult + 'static>,
1756 func_type: ExportedFunction,
1757 code_hash: CodeHash<Test>,
1758 code_info: CodeInfo<Test>,
1759 }
1760
1761 #[derive(Default, Clone)]
1762 pub struct MockLoader {
1763 map: HashMap<CodeHash<Test>, MockExecutable>,
1764 counter: u64,
1765 }
1766
1767 impl MockLoader {
1768 fn code_hashes() -> Vec<CodeHash<Test>> {
1769 Loader::get().map.keys().copied().collect()
1770 }
1771
1772 fn insert(
1773 func_type: ExportedFunction,
1774 f: impl Fn(MockCtx, &MockExecutable) -> ExecResult + 'static,
1775 ) -> CodeHash<Test> {
1776 Loader::mutate(|loader| {
1777 let hash = <Test as frame_system::Config>::Hash::from_low_u64_be(loader.counter);
1779 loader.counter += 1;
1780 loader.map.insert(
1781 hash,
1782 MockExecutable {
1783 func: Rc::new(f),
1784 func_type,
1785 code_hash: hash,
1786 code_info: CodeInfo::<Test>::new(ALICE),
1787 },
1788 );
1789 hash
1790 })
1791 }
1792 }
1793
1794 impl Executable<Test> for MockExecutable {
1795 fn from_storage(
1796 code_hash: CodeHash<Test>,
1797 _gas_meter: &mut GasMeter<Test>,
1798 ) -> Result<Self, DispatchError> {
1799 Loader::mutate(|loader| {
1800 loader.map.get(&code_hash).cloned().ok_or(Error::<Test>::CodeNotFound.into())
1801 })
1802 }
1803
1804 fn execute<E: Ext<T = Test>>(
1805 self,
1806 ext: &mut E,
1807 function: &ExportedFunction,
1808 input_data: Vec<u8>,
1809 ) -> ExecResult {
1810 if let &Constructor = function {
1811 E::increment_refcount(self.code_hash).unwrap();
1812 }
1813 let ext = unsafe { mem::transmute(ext) };
1823 if function == &self.func_type {
1824 (self.func)(MockCtx { ext, input_data }, &self)
1825 } else {
1826 exec_success()
1827 }
1828 }
1829
1830 fn code_hash(&self) -> &CodeHash<Test> {
1831 &self.code_hash
1832 }
1833
1834 fn code_info(&self) -> &CodeInfo<Test> {
1835 &self.code_info
1836 }
1837
1838 fn is_deterministic(&self) -> bool {
1839 true
1840 }
1841 }
1842
1843 fn exec_success() -> ExecResult {
1844 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
1845 }
1846
1847 fn exec_trapped() -> ExecResult {
1848 Err(ExecError { error: <Error<Test>>::ContractTrapped.into(), origin: ErrorOrigin::Callee })
1849 }
1850
1851 #[test]
1852 fn it_works() {
1853 parameter_types! {
1854 static TestData: Vec<usize> = vec![0];
1855 }
1856
1857 let value = Default::default();
1858 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
1859 let exec_ch = MockLoader::insert(Call, |_ctx, _executable| {
1860 TestData::mutate(|data| data.push(1));
1861 exec_success()
1862 });
1863
1864 ExtBuilder::default().build().execute_with(|| {
1865 let schedule = <Test as Config>::Schedule::get();
1866 place_contract(&BOB, exec_ch);
1867 let mut storage_meter =
1868 storage::meter::Meter::new(&Origin::from_account_id(ALICE), Some(0), value)
1869 .unwrap();
1870
1871 assert_matches!(
1872 MockStack::run_call(
1873 Origin::from_account_id(ALICE),
1874 BOB,
1875 &mut gas_meter,
1876 &mut storage_meter,
1877 &schedule,
1878 value,
1879 vec![],
1880 None,
1881 Determinism::Enforced,
1882 ),
1883 Ok(_)
1884 );
1885 });
1886
1887 assert_eq!(TestData::get(), vec![0, 1]);
1888 }
1889
1890 #[test]
1891 fn transfer_works() {
1892 let origin = ALICE;
1895 let dest = BOB;
1896
1897 ExtBuilder::default().build().execute_with(|| {
1898 set_balance(&origin, 100);
1899 set_balance(&dest, 0);
1900
1901 MockStack::transfer(Preservation::Preserve, &origin, &dest, 55).unwrap();
1902
1903 assert_eq!(get_balance(&origin), 45);
1904 assert_eq!(get_balance(&dest), 55);
1905 });
1906 }
1907
1908 #[test]
1909 fn correct_transfer_on_call() {
1910 let origin = ALICE;
1911 let dest = BOB;
1912 let value = 55;
1913
1914 let success_ch = MockLoader::insert(Call, move |ctx, _| {
1915 assert_eq!(ctx.ext.value_transferred(), value);
1916 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
1917 });
1918
1919 ExtBuilder::default().build().execute_with(|| {
1920 let schedule = <Test as Config>::Schedule::get();
1921 place_contract(&dest, success_ch);
1922 set_balance(&origin, 100);
1923 let balance = get_balance(&dest);
1924 let contract_origin = Origin::from_account_id(origin.clone());
1925 let mut storage_meter =
1926 storage::meter::Meter::new(&contract_origin, Some(0), value).unwrap();
1927
1928 let _ = MockStack::run_call(
1929 contract_origin.clone(),
1930 dest.clone(),
1931 &mut GasMeter::<Test>::new(GAS_LIMIT),
1932 &mut storage_meter,
1933 &schedule,
1934 value,
1935 vec![],
1936 None,
1937 Determinism::Enforced,
1938 )
1939 .unwrap();
1940
1941 assert_eq!(get_balance(&origin), 100 - value);
1942 assert_eq!(get_balance(&dest), balance + value);
1943 });
1944 }
1945
1946 #[test]
1947 fn correct_transfer_on_delegate_call() {
1948 let origin = ALICE;
1949 let dest = BOB;
1950 let value = 35;
1951
1952 let success_ch = MockLoader::insert(Call, move |ctx, _| {
1953 assert_eq!(ctx.ext.value_transferred(), value);
1954 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
1955 });
1956
1957 let delegate_ch = MockLoader::insert(Call, move |ctx, _| {
1958 assert_eq!(ctx.ext.value_transferred(), value);
1959 ctx.ext.delegate_call(success_ch, Vec::new())?;
1960 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
1961 });
1962
1963 ExtBuilder::default().build().execute_with(|| {
1964 let schedule = <Test as Config>::Schedule::get();
1965 place_contract(&dest, delegate_ch);
1966 set_balance(&origin, 100);
1967 let balance = get_balance(&dest);
1968 let contract_origin = Origin::from_account_id(origin.clone());
1969 let mut storage_meter =
1970 storage::meter::Meter::new(&contract_origin, Some(0), 55).unwrap();
1971
1972 let _ = MockStack::run_call(
1973 contract_origin.clone(),
1974 dest.clone(),
1975 &mut GasMeter::<Test>::new(GAS_LIMIT),
1976 &mut storage_meter,
1977 &schedule,
1978 value,
1979 vec![],
1980 None,
1981 Determinism::Enforced,
1982 )
1983 .unwrap();
1984
1985 assert_eq!(get_balance(&origin), 100 - value);
1986 assert_eq!(get_balance(&dest), balance + value);
1987 });
1988 }
1989
1990 #[test]
1991 fn changes_are_reverted_on_failing_call() {
1992 let origin = ALICE;
1995 let dest = BOB;
1996
1997 let return_ch = MockLoader::insert(Call, |_, _| {
1998 Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: Vec::new() })
1999 });
2000
2001 ExtBuilder::default().build().execute_with(|| {
2002 let schedule = <Test as Config>::Schedule::get();
2003 place_contract(&dest, return_ch);
2004 set_balance(&origin, 100);
2005 let balance = get_balance(&dest);
2006 let contract_origin = Origin::from_account_id(origin.clone());
2007 let mut storage_meter =
2008 storage::meter::Meter::new(&contract_origin, Some(0), 55).unwrap();
2009
2010 let output = MockStack::run_call(
2011 contract_origin.clone(),
2012 dest.clone(),
2013 &mut GasMeter::<Test>::new(GAS_LIMIT),
2014 &mut storage_meter,
2015 &schedule,
2016 55,
2017 vec![],
2018 None,
2019 Determinism::Enforced,
2020 )
2021 .unwrap();
2022
2023 assert!(output.did_revert());
2024 assert_eq!(get_balance(&origin), 100);
2025 assert_eq!(get_balance(&dest), balance);
2026 });
2027 }
2028
2029 #[test]
2030 fn balance_too_low() {
2031 let origin = ALICE;
2034 let dest = BOB;
2035
2036 ExtBuilder::default().build().execute_with(|| {
2037 set_balance(&origin, 0);
2038
2039 let result = MockStack::transfer(Preservation::Preserve, &origin, &dest, 100);
2040
2041 assert_eq!(result, Err(Error::<Test>::TransferFailed.into()));
2042 assert_eq!(get_balance(&origin), 0);
2043 assert_eq!(get_balance(&dest), 0);
2044 });
2045 }
2046
2047 #[test]
2048 fn output_is_returned_on_success() {
2049 let origin = ALICE;
2052 let dest = BOB;
2053 let return_ch = MockLoader::insert(Call, |_, _| {
2054 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![1, 2, 3, 4] })
2055 });
2056
2057 ExtBuilder::default().build().execute_with(|| {
2058 let schedule = <Test as Config>::Schedule::get();
2059 let contract_origin = Origin::from_account_id(origin);
2060 let mut storage_meter =
2061 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2062 place_contract(&BOB, return_ch);
2063
2064 let result = MockStack::run_call(
2065 contract_origin,
2066 dest,
2067 &mut GasMeter::<Test>::new(GAS_LIMIT),
2068 &mut storage_meter,
2069 &schedule,
2070 0,
2071 vec![],
2072 None,
2073 Determinism::Enforced,
2074 );
2075
2076 let output = result.unwrap();
2077 assert!(!output.did_revert());
2078 assert_eq!(output.data, vec![1, 2, 3, 4]);
2079 });
2080 }
2081
2082 #[test]
2083 fn output_is_returned_on_failure() {
2084 let origin = ALICE;
2087 let dest = BOB;
2088 let return_ch = MockLoader::insert(Call, |_, _| {
2089 Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![1, 2, 3, 4] })
2090 });
2091
2092 ExtBuilder::default().build().execute_with(|| {
2093 let schedule = <Test as Config>::Schedule::get();
2094 place_contract(&BOB, return_ch);
2095 let contract_origin = Origin::from_account_id(origin);
2096 let mut storage_meter =
2097 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2098
2099 let result = MockStack::run_call(
2100 contract_origin,
2101 dest,
2102 &mut GasMeter::<Test>::new(GAS_LIMIT),
2103 &mut storage_meter,
2104 &schedule,
2105 0,
2106 vec![],
2107 None,
2108 Determinism::Enforced,
2109 );
2110
2111 let output = result.unwrap();
2112 assert!(output.did_revert());
2113 assert_eq!(output.data, vec![1, 2, 3, 4]);
2114 });
2115 }
2116
2117 #[test]
2118 fn input_data_to_call() {
2119 let input_data_ch = MockLoader::insert(Call, |ctx, _| {
2120 assert_eq!(ctx.input_data, &[1, 2, 3, 4]);
2121 exec_success()
2122 });
2123
2124 ExtBuilder::default().build().execute_with(|| {
2126 let schedule = <Test as Config>::Schedule::get();
2127 place_contract(&BOB, input_data_ch);
2128 let contract_origin = Origin::from_account_id(ALICE);
2129 let mut storage_meter =
2130 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2131
2132 let result = MockStack::run_call(
2133 contract_origin,
2134 BOB,
2135 &mut GasMeter::<Test>::new(GAS_LIMIT),
2136 &mut storage_meter,
2137 &schedule,
2138 0,
2139 vec![1, 2, 3, 4],
2140 None,
2141 Determinism::Enforced,
2142 );
2143 assert_matches!(result, Ok(_));
2144 });
2145 }
2146
2147 #[test]
2148 fn input_data_to_instantiate() {
2149 let input_data_ch = MockLoader::insert(Constructor, |ctx, _| {
2150 assert_eq!(ctx.input_data, &[1, 2, 3, 4]);
2151 exec_success()
2152 });
2153
2154 ExtBuilder::default()
2156 .with_code_hashes(MockLoader::code_hashes())
2157 .build()
2158 .execute_with(|| {
2159 let schedule = <Test as Config>::Schedule::get();
2160 let min_balance = <Test as Config>::Currency::minimum_balance();
2161 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2162 let executable =
2163 MockExecutable::from_storage(input_data_ch, &mut gas_meter).unwrap();
2164 set_balance(&ALICE, min_balance * 10_000);
2165 let contract_origin = Origin::from_account_id(ALICE);
2166 let mut storage_meter =
2167 storage::meter::Meter::new(&contract_origin, None, min_balance).unwrap();
2168
2169 let result = MockStack::run_instantiate(
2170 ALICE,
2171 executable,
2172 &mut gas_meter,
2173 &mut storage_meter,
2174 &schedule,
2175 min_balance,
2176 vec![1, 2, 3, 4],
2177 &[],
2178 None,
2179 );
2180 assert_matches!(result, Ok(_));
2181 });
2182 }
2183
2184 #[test]
2185 fn max_depth() {
2186 parameter_types! {
2189 static ReachedBottom: bool = false;
2190 }
2191 let value = Default::default();
2192 let recurse_ch = MockLoader::insert(Call, |ctx, _| {
2193 let r = ctx.ext.call(
2195 Weight::zero(),
2196 BalanceOf::<Test>::zero(),
2197 BOB,
2198 0,
2199 vec![],
2200 true,
2201 false,
2202 );
2203
2204 ReachedBottom::mutate(|reached_bottom| {
2205 if !*reached_bottom {
2206 assert_eq!(r, Err(Error::<Test>::MaxCallDepthReached.into()));
2209 *reached_bottom = true;
2210 } else {
2211 assert_matches!(r, Ok(_));
2213 }
2214 });
2215
2216 exec_success()
2217 });
2218
2219 ExtBuilder::default().build().execute_with(|| {
2220 let schedule = <Test as Config>::Schedule::get();
2221 set_balance(&BOB, 1);
2222 place_contract(&BOB, recurse_ch);
2223 let contract_origin = Origin::from_account_id(ALICE);
2224 let mut storage_meter =
2225 storage::meter::Meter::new(&contract_origin, Some(0), value).unwrap();
2226
2227 let result = MockStack::run_call(
2228 contract_origin,
2229 BOB,
2230 &mut GasMeter::<Test>::new(GAS_LIMIT),
2231 &mut storage_meter,
2232 &schedule,
2233 value,
2234 vec![],
2235 None,
2236 Determinism::Enforced,
2237 );
2238
2239 assert_matches!(result, Ok(_));
2240 });
2241 }
2242
2243 #[test]
2244 fn caller_returns_proper_values() {
2245 let origin = ALICE;
2246 let dest = BOB;
2247
2248 parameter_types! {
2249 static WitnessedCallerBob: Option<AccountIdOf<Test>> = None;
2250 static WitnessedCallerCharlie: Option<AccountIdOf<Test>> = None;
2251 }
2252
2253 let bob_ch = MockLoader::insert(Call, |ctx, _| {
2254 WitnessedCallerBob::mutate(|caller| {
2256 *caller = Some(ctx.ext.caller().account_id().unwrap().clone())
2257 });
2258
2259 assert_matches!(
2261 ctx.ext.call(
2262 Weight::zero(),
2263 BalanceOf::<Test>::zero(),
2264 CHARLIE,
2265 0,
2266 vec![],
2267 true,
2268 false
2269 ),
2270 Ok(_)
2271 );
2272 exec_success()
2273 });
2274 let charlie_ch = MockLoader::insert(Call, |ctx, _| {
2275 WitnessedCallerCharlie::mutate(|caller| {
2277 *caller = Some(ctx.ext.caller().account_id().unwrap().clone())
2278 });
2279 exec_success()
2280 });
2281
2282 ExtBuilder::default().build().execute_with(|| {
2283 let schedule = <Test as Config>::Schedule::get();
2284 place_contract(&dest, bob_ch);
2285 place_contract(&CHARLIE, charlie_ch);
2286 let contract_origin = Origin::from_account_id(origin.clone());
2287 let mut storage_meter =
2288 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2289
2290 let result = MockStack::run_call(
2291 contract_origin.clone(),
2292 dest.clone(),
2293 &mut GasMeter::<Test>::new(GAS_LIMIT),
2294 &mut storage_meter,
2295 &schedule,
2296 0,
2297 vec![],
2298 None,
2299 Determinism::Enforced,
2300 );
2301
2302 assert_matches!(result, Ok(_));
2303 });
2304
2305 assert_eq!(WitnessedCallerBob::get(), Some(origin));
2306 assert_eq!(WitnessedCallerCharlie::get(), Some(dest));
2307 }
2308
2309 #[test]
2310 fn is_contract_returns_proper_values() {
2311 let bob_ch = MockLoader::insert(Call, |ctx, _| {
2312 assert!(ctx.ext.is_contract(&BOB));
2314 assert!(!ctx.ext.is_contract(&ALICE));
2316 exec_success()
2317 });
2318
2319 ExtBuilder::default().build().execute_with(|| {
2320 let schedule = <Test as Config>::Schedule::get();
2321 place_contract(&BOB, bob_ch);
2322
2323 let contract_origin = Origin::from_account_id(ALICE);
2324 let mut storage_meter =
2325 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2326 let result = MockStack::run_call(
2327 contract_origin,
2328 BOB,
2329 &mut GasMeter::<Test>::new(GAS_LIMIT),
2330 &mut storage_meter,
2331 &schedule,
2332 0,
2333 vec![],
2334 None,
2335 Determinism::Enforced,
2336 );
2337 assert_matches!(result, Ok(_));
2338 });
2339 }
2340
2341 #[test]
2342 fn code_hash_returns_proper_values() {
2343 let code_bob = MockLoader::insert(Call, |ctx, _| {
2344 assert!(ctx.ext.code_hash(&ALICE).is_none());
2346 assert!(ctx.ext.code_hash(&BOB).is_some());
2348 exec_success()
2349 });
2350
2351 ExtBuilder::default().build().execute_with(|| {
2352 let schedule = <Test as Config>::Schedule::get();
2353 place_contract(&BOB, code_bob);
2354 let contract_origin = Origin::from_account_id(ALICE);
2355 let mut storage_meter =
2356 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2357 let result = MockStack::run_call(
2359 contract_origin,
2360 BOB,
2361 &mut GasMeter::<Test>::new(GAS_LIMIT),
2362 &mut storage_meter,
2363 &schedule,
2364 0,
2365 vec![0],
2366 None,
2367 Determinism::Enforced,
2368 );
2369 assert_matches!(result, Ok(_));
2370 });
2371 }
2372
2373 #[test]
2374 fn own_code_hash_returns_proper_values() {
2375 let bob_ch = MockLoader::insert(Call, |ctx, _| {
2376 let code_hash = ctx.ext.code_hash(&BOB).unwrap();
2377 assert_eq!(*ctx.ext.own_code_hash(), code_hash);
2378 exec_success()
2379 });
2380
2381 ExtBuilder::default().build().execute_with(|| {
2382 let schedule = <Test as Config>::Schedule::get();
2383 place_contract(&BOB, bob_ch);
2384 let contract_origin = Origin::from_account_id(ALICE);
2385 let mut storage_meter =
2386 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2387 let result = MockStack::run_call(
2389 contract_origin,
2390 BOB,
2391 &mut GasMeter::<Test>::new(GAS_LIMIT),
2392 &mut storage_meter,
2393 &schedule,
2394 0,
2395 vec![0],
2396 None,
2397 Determinism::Enforced,
2398 );
2399 assert_matches!(result, Ok(_));
2400 });
2401 }
2402
2403 #[test]
2404 fn caller_is_origin_returns_proper_values() {
2405 let code_charlie = MockLoader::insert(Call, |ctx, _| {
2406 assert!(!ctx.ext.caller_is_origin());
2408 exec_success()
2409 });
2410
2411 let code_bob = MockLoader::insert(Call, |ctx, _| {
2412 assert!(ctx.ext.caller_is_origin());
2414 ctx.ext
2416 .call(Weight::zero(), BalanceOf::<Test>::zero(), CHARLIE, 0, vec![], true, false)
2417 });
2418
2419 ExtBuilder::default().build().execute_with(|| {
2420 let schedule = <Test as Config>::Schedule::get();
2421 place_contract(&BOB, code_bob);
2422 place_contract(&CHARLIE, code_charlie);
2423 let contract_origin = Origin::from_account_id(ALICE);
2424 let mut storage_meter =
2425 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2426 let result = MockStack::run_call(
2428 contract_origin,
2429 BOB,
2430 &mut GasMeter::<Test>::new(GAS_LIMIT),
2431 &mut storage_meter,
2432 &schedule,
2433 0,
2434 vec![0],
2435 None,
2436 Determinism::Enforced,
2437 );
2438 assert_matches!(result, Ok(_));
2439 });
2440 }
2441
2442 #[test]
2443 fn root_caller_succeeds() {
2444 let code_bob = MockLoader::insert(Call, |ctx, _| {
2445 assert!(ctx.ext.caller_is_root());
2447 exec_success()
2448 });
2449
2450 ExtBuilder::default().build().execute_with(|| {
2451 let schedule = <Test as Config>::Schedule::get();
2452 place_contract(&BOB, code_bob);
2453 let contract_origin = Origin::Root;
2454 let mut storage_meter =
2455 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2456 let result = MockStack::run_call(
2458 contract_origin,
2459 BOB,
2460 &mut GasMeter::<Test>::new(GAS_LIMIT),
2461 &mut storage_meter,
2462 &schedule,
2463 0,
2464 vec![0],
2465 None,
2466 Determinism::Enforced,
2467 );
2468 assert_matches!(result, Ok(_));
2469 });
2470 }
2471
2472 #[test]
2473 fn root_caller_does_not_succeed_when_value_not_zero() {
2474 let code_bob = MockLoader::insert(Call, |ctx, _| {
2475 assert!(ctx.ext.caller_is_root());
2477 exec_success()
2478 });
2479
2480 ExtBuilder::default().build().execute_with(|| {
2481 let schedule = <Test as Config>::Schedule::get();
2482 place_contract(&BOB, code_bob);
2483 let contract_origin = Origin::Root;
2484 let mut storage_meter =
2485 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2486 let result = MockStack::run_call(
2488 contract_origin,
2489 BOB,
2490 &mut GasMeter::<Test>::new(GAS_LIMIT),
2491 &mut storage_meter,
2492 &schedule,
2493 1,
2494 vec![0],
2495 None,
2496 Determinism::Enforced,
2497 );
2498 assert_matches!(result, Err(_));
2499 });
2500 }
2501
2502 #[test]
2503 fn root_caller_succeeds_with_consecutive_calls() {
2504 let code_charlie = MockLoader::insert(Call, |ctx, _| {
2505 assert!(!ctx.ext.caller_is_root());
2507 exec_success()
2508 });
2509
2510 let code_bob = MockLoader::insert(Call, |ctx, _| {
2511 assert!(ctx.ext.caller_is_root());
2513 ctx.ext
2515 .call(Weight::zero(), BalanceOf::<Test>::zero(), CHARLIE, 0, vec![], true, false)
2516 });
2517
2518 ExtBuilder::default().build().execute_with(|| {
2519 let schedule = <Test as Config>::Schedule::get();
2520 place_contract(&BOB, code_bob);
2521 place_contract(&CHARLIE, code_charlie);
2522 let contract_origin = Origin::Root;
2523 let mut storage_meter =
2524 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2525 let result = MockStack::run_call(
2527 contract_origin,
2528 BOB,
2529 &mut GasMeter::<Test>::new(GAS_LIMIT),
2530 &mut storage_meter,
2531 &schedule,
2532 0,
2533 vec![0],
2534 None,
2535 Determinism::Enforced,
2536 );
2537 assert_matches!(result, Ok(_));
2538 });
2539 }
2540
2541 #[test]
2542 fn address_returns_proper_values() {
2543 let bob_ch = MockLoader::insert(Call, |ctx, _| {
2544 assert_eq!(*ctx.ext.address(), BOB);
2546
2547 assert_matches!(
2549 ctx.ext.call(
2550 Weight::zero(),
2551 BalanceOf::<Test>::zero(),
2552 CHARLIE,
2553 0,
2554 vec![],
2555 true,
2556 false
2557 ),
2558 Ok(_)
2559 );
2560 exec_success()
2561 });
2562 let charlie_ch = MockLoader::insert(Call, |ctx, _| {
2563 assert_eq!(*ctx.ext.address(), CHARLIE);
2564 exec_success()
2565 });
2566
2567 ExtBuilder::default().build().execute_with(|| {
2568 let schedule = <Test as Config>::Schedule::get();
2569 place_contract(&BOB, bob_ch);
2570 place_contract(&CHARLIE, charlie_ch);
2571 let contract_origin = Origin::from_account_id(ALICE);
2572 let mut storage_meter =
2573 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2574
2575 let result = MockStack::run_call(
2576 contract_origin,
2577 BOB,
2578 &mut GasMeter::<Test>::new(GAS_LIMIT),
2579 &mut storage_meter,
2580 &schedule,
2581 0,
2582 vec![],
2583 None,
2584 Determinism::Enforced,
2585 );
2586
2587 assert_matches!(result, Ok(_));
2588 });
2589 }
2590
2591 #[test]
2592 fn refuse_instantiate_with_value_below_existential_deposit() {
2593 let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success());
2594
2595 ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
2596 let schedule = <Test as Config>::Schedule::get();
2597 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2598 let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap();
2599 let contract_origin = Origin::from_account_id(ALICE);
2600 let mut storage_meter =
2601 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2602
2603 assert_matches!(
2604 MockStack::run_instantiate(
2605 ALICE,
2606 executable,
2607 &mut gas_meter,
2608 &mut storage_meter,
2609 &schedule,
2610 0, vec![],
2612 &[],
2613 None,
2614 ),
2615 Err(_)
2616 );
2617 });
2618 }
2619
2620 #[test]
2621 fn instantiation_work_with_success_output() {
2622 let dummy_ch = MockLoader::insert(Constructor, |_, _| {
2623 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![80, 65, 83, 83] })
2624 });
2625
2626 ExtBuilder::default()
2627 .with_code_hashes(MockLoader::code_hashes())
2628 .existential_deposit(15)
2629 .build()
2630 .execute_with(|| {
2631 let schedule = <Test as Config>::Schedule::get();
2632 let min_balance = <Test as Config>::Currency::minimum_balance();
2633 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2634 let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap();
2635 set_balance(&ALICE, min_balance * 1000);
2636 let contract_origin = Origin::from_account_id(ALICE);
2637 let mut storage_meter = storage::meter::Meter::new(
2638 &contract_origin,
2639 Some(min_balance * 100),
2640 min_balance,
2641 )
2642 .unwrap();
2643
2644 let instantiated_contract_address = assert_matches!(
2645 MockStack::run_instantiate(
2646 ALICE,
2647 executable,
2648 &mut gas_meter,
2649 &mut storage_meter,
2650 &schedule,
2651 min_balance,
2652 vec![],
2653 &[],
2654 None,
2655 ),
2656 Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address
2657 );
2658
2659 assert_eq!(
2662 ContractInfo::<Test>::load_code_hash(&instantiated_contract_address).unwrap(),
2663 dummy_ch
2664 );
2665 assert_eq!(
2666 &events(),
2667 &[Event::Instantiated {
2668 deployer: ALICE,
2669 contract: instantiated_contract_address
2670 }]
2671 );
2672 });
2673 }
2674
2675 #[test]
2676 fn instantiation_fails_with_failing_output() {
2677 let dummy_ch = MockLoader::insert(Constructor, |_, _| {
2678 Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![70, 65, 73, 76] })
2679 });
2680
2681 ExtBuilder::default()
2682 .with_code_hashes(MockLoader::code_hashes())
2683 .existential_deposit(15)
2684 .build()
2685 .execute_with(|| {
2686 let schedule = <Test as Config>::Schedule::get();
2687 let min_balance = <Test as Config>::Currency::minimum_balance();
2688 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2689 let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap();
2690 set_balance(&ALICE, min_balance * 1000);
2691 let contract_origin = Origin::from_account_id(ALICE);
2692 let mut storage_meter = storage::meter::Meter::new(
2693 &contract_origin,
2694 Some(min_balance * 100),
2695 min_balance,
2696 )
2697 .unwrap();
2698
2699 let instantiated_contract_address = assert_matches!(
2700 MockStack::run_instantiate(
2701 ALICE,
2702 executable,
2703 &mut gas_meter,
2704 &mut storage_meter,
2705 &schedule,
2706 min_balance,
2707 vec![],
2708 &[],
2709 None,
2710 ),
2711 Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address
2712 );
2713
2714 assert!(
2716 ContractInfo::<Test>::load_code_hash(&instantiated_contract_address).is_none()
2717 );
2718 assert!(events().is_empty());
2719 });
2720 }
2721
2722 #[test]
2723 fn instantiation_from_contract() {
2724 let dummy_ch = MockLoader::insert(Call, |_, _| exec_success());
2725 let instantiated_contract_address = Rc::new(RefCell::new(None::<AccountIdOf<Test>>));
2726 let instantiator_ch = MockLoader::insert(Call, {
2727 let instantiated_contract_address = Rc::clone(&instantiated_contract_address);
2728 move |ctx, _| {
2729 let (address, output) = ctx
2731 .ext
2732 .instantiate(
2733 Weight::zero(),
2734 BalanceOf::<Test>::zero(),
2735 dummy_ch,
2736 <Test as Config>::Currency::minimum_balance(),
2737 vec![],
2738 &[48, 49, 50],
2739 )
2740 .unwrap();
2741
2742 *instantiated_contract_address.borrow_mut() = address.into();
2743 Ok(output)
2744 }
2745 });
2746
2747 ExtBuilder::default()
2748 .with_code_hashes(MockLoader::code_hashes())
2749 .existential_deposit(15)
2750 .build()
2751 .execute_with(|| {
2752 let schedule = <Test as Config>::Schedule::get();
2753 let min_balance = <Test as Config>::Currency::minimum_balance();
2754 set_balance(&ALICE, min_balance * 100);
2755 place_contract(&BOB, instantiator_ch);
2756 let contract_origin = Origin::from_account_id(ALICE);
2757 let mut storage_meter = storage::meter::Meter::new(
2758 &contract_origin,
2759 Some(min_balance * 10),
2760 min_balance * 10,
2761 )
2762 .unwrap();
2763
2764 assert_matches!(
2765 MockStack::run_call(
2766 contract_origin,
2767 BOB,
2768 &mut GasMeter::<Test>::new(GAS_LIMIT),
2769 &mut storage_meter,
2770 &schedule,
2771 min_balance * 10,
2772 vec![],
2773 None,
2774 Determinism::Enforced,
2775 ),
2776 Ok(_)
2777 );
2778
2779 let instantiated_contract_address =
2780 instantiated_contract_address.borrow().as_ref().unwrap().clone();
2781
2782 assert_eq!(
2785 ContractInfo::<Test>::load_code_hash(&instantiated_contract_address).unwrap(),
2786 dummy_ch
2787 );
2788 assert_eq!(
2789 &events(),
2790 &[
2791 Event::Instantiated {
2792 deployer: BOB,
2793 contract: instantiated_contract_address
2794 },
2795 Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },
2796 ]
2797 );
2798 });
2799 }
2800
2801 #[test]
2802 fn instantiation_traps() {
2803 let dummy_ch = MockLoader::insert(Constructor, |_, _| Err("It's a trap!".into()));
2804 let instantiator_ch = MockLoader::insert(Call, {
2805 move |ctx, _| {
2806 assert_matches!(
2808 ctx.ext.instantiate(
2809 Weight::zero(),
2810 BalanceOf::<Test>::zero(),
2811 dummy_ch,
2812 <Test as Config>::Currency::minimum_balance(),
2813 vec![],
2814 &[],
2815 ),
2816 Err(ExecError {
2817 error: DispatchError::Other("It's a trap!"),
2818 origin: ErrorOrigin::Callee,
2819 })
2820 );
2821
2822 exec_success()
2823 }
2824 });
2825
2826 ExtBuilder::default()
2827 .with_code_hashes(MockLoader::code_hashes())
2828 .existential_deposit(15)
2829 .build()
2830 .execute_with(|| {
2831 let schedule = <Test as Config>::Schedule::get();
2832 set_balance(&ALICE, 1000);
2833 set_balance(&BOB, 100);
2834 place_contract(&BOB, instantiator_ch);
2835 let contract_origin = Origin::from_account_id(ALICE);
2836 let mut storage_meter =
2837 storage::meter::Meter::new(&contract_origin, Some(200), 0).unwrap();
2838
2839 assert_matches!(
2840 MockStack::run_call(
2841 contract_origin,
2842 BOB,
2843 &mut GasMeter::<Test>::new(GAS_LIMIT),
2844 &mut storage_meter,
2845 &schedule,
2846 0,
2847 vec![],
2848 None,
2849 Determinism::Enforced,
2850 ),
2851 Ok(_)
2852 );
2853
2854 assert_eq!(
2857 &events(),
2858 &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },]
2859 );
2860 });
2861 }
2862
2863 #[test]
2864 fn termination_from_instantiate_fails() {
2865 let terminate_ch = MockLoader::insert(Constructor, |ctx, _| {
2866 ctx.ext.terminate(&ALICE).unwrap();
2867 exec_success()
2868 });
2869
2870 ExtBuilder::default()
2871 .with_code_hashes(MockLoader::code_hashes())
2872 .existential_deposit(15)
2873 .build()
2874 .execute_with(|| {
2875 let schedule = <Test as Config>::Schedule::get();
2876 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2877 let executable =
2878 MockExecutable::from_storage(terminate_ch, &mut gas_meter).unwrap();
2879 set_balance(&ALICE, 10_000);
2880 let contract_origin = Origin::from_account_id(ALICE);
2881 let mut storage_meter =
2882 storage::meter::Meter::new(&contract_origin, None, 100).unwrap();
2883
2884 assert_eq!(
2885 MockStack::run_instantiate(
2886 ALICE,
2887 executable,
2888 &mut gas_meter,
2889 &mut storage_meter,
2890 &schedule,
2891 100,
2892 vec![],
2893 &[],
2894 None,
2895 ),
2896 Err(Error::<Test>::TerminatedInConstructor.into())
2897 );
2898
2899 assert_eq!(&events(), &[]);
2900 });
2901 }
2902
2903 #[test]
2904 fn in_memory_changes_not_discarded() {
2905 let code_bob = MockLoader::insert(Call, |ctx, _| {
2914 if ctx.input_data[0] == 0 {
2915 let info = ctx.ext.contract_info();
2916 assert_eq!(info.storage_byte_deposit, 0);
2917 info.storage_byte_deposit = 42;
2918 assert_eq!(
2919 ctx.ext.call(
2920 Weight::zero(),
2921 BalanceOf::<Test>::zero(),
2922 CHARLIE,
2923 0,
2924 vec![],
2925 true,
2926 false
2927 ),
2928 exec_trapped()
2929 );
2930 assert_eq!(ctx.ext.contract_info().storage_byte_deposit, 42);
2931 }
2932 exec_success()
2933 });
2934 let code_charlie = MockLoader::insert(Call, |ctx, _| {
2935 assert!(ctx
2936 .ext
2937 .call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![99], true, false)
2938 .is_ok());
2939 exec_trapped()
2940 });
2941
2942 ExtBuilder::default().build().execute_with(|| {
2944 let schedule = <Test as Config>::Schedule::get();
2945 place_contract(&BOB, code_bob);
2946 place_contract(&CHARLIE, code_charlie);
2947 let contract_origin = Origin::from_account_id(ALICE);
2948 let mut storage_meter =
2949 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
2950
2951 let result = MockStack::run_call(
2952 contract_origin,
2953 BOB,
2954 &mut GasMeter::<Test>::new(GAS_LIMIT),
2955 &mut storage_meter,
2956 &schedule,
2957 0,
2958 vec![0],
2959 None,
2960 Determinism::Enforced,
2961 );
2962 assert_matches!(result, Ok(_));
2963 });
2964 }
2965
2966 #[test]
2967 fn recursive_call_during_constructor_fails() {
2968 let code = MockLoader::insert(Constructor, |ctx, _| {
2969 assert_matches!(
2970 ctx.ext.call(Weight::zero(), BalanceOf::<Test>::zero(), ctx.ext.address().clone(), 0, vec![], true, false),
2971 Err(ExecError{error, ..}) if error == <Error<Test>>::ContractNotFound.into()
2972 );
2973 exec_success()
2974 });
2975
2976 ExtBuilder::default()
2978 .with_code_hashes(MockLoader::code_hashes())
2979 .build()
2980 .execute_with(|| {
2981 let schedule = <Test as Config>::Schedule::get();
2982 let min_balance = <Test as Config>::Currency::minimum_balance();
2983 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
2984 let executable = MockExecutable::from_storage(code, &mut gas_meter).unwrap();
2985 set_balance(&ALICE, min_balance * 10_000);
2986 let contract_origin = Origin::from_account_id(ALICE);
2987 let mut storage_meter =
2988 storage::meter::Meter::new(&contract_origin, None, min_balance).unwrap();
2989
2990 let result = MockStack::run_instantiate(
2991 ALICE,
2992 executable,
2993 &mut gas_meter,
2994 &mut storage_meter,
2995 &schedule,
2996 min_balance,
2997 vec![],
2998 &[],
2999 None,
3000 );
3001 assert_matches!(result, Ok(_));
3002 });
3003 }
3004
3005 #[test]
3006 fn printing_works() {
3007 let code_hash = MockLoader::insert(Call, |ctx, _| {
3008 ctx.ext.append_debug_buffer("This is a test");
3009 ctx.ext.append_debug_buffer("More text");
3010 exec_success()
3011 });
3012
3013 let mut debug_buffer = DebugBufferVec::<Test>::try_from(Vec::new()).unwrap();
3014
3015 ExtBuilder::default().build().execute_with(|| {
3016 let min_balance = <Test as Config>::Currency::minimum_balance();
3017 let schedule = <Test as Config>::Schedule::get();
3018 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3019 set_balance(&ALICE, min_balance * 10);
3020 place_contract(&BOB, code_hash);
3021 let contract_origin = Origin::from_account_id(ALICE);
3022 let mut storage_meter =
3023 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3024 MockStack::run_call(
3025 contract_origin,
3026 BOB,
3027 &mut gas_meter,
3028 &mut storage_meter,
3029 &schedule,
3030 0,
3031 vec![],
3032 Some(&mut debug_buffer),
3033 Determinism::Enforced,
3034 )
3035 .unwrap();
3036 });
3037
3038 assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text");
3039 }
3040
3041 #[test]
3042 fn printing_works_on_fail() {
3043 let code_hash = MockLoader::insert(Call, |ctx, _| {
3044 ctx.ext.append_debug_buffer("This is a test");
3045 ctx.ext.append_debug_buffer("More text");
3046 exec_trapped()
3047 });
3048
3049 let mut debug_buffer = DebugBufferVec::<Test>::try_from(Vec::new()).unwrap();
3050
3051 ExtBuilder::default().build().execute_with(|| {
3052 let min_balance = <Test as Config>::Currency::minimum_balance();
3053 let schedule = <Test as Config>::Schedule::get();
3054 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3055 set_balance(&ALICE, min_balance * 10);
3056 place_contract(&BOB, code_hash);
3057 let contract_origin = Origin::from_account_id(ALICE);
3058 let mut storage_meter =
3059 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3060 let result = MockStack::run_call(
3061 contract_origin,
3062 BOB,
3063 &mut gas_meter,
3064 &mut storage_meter,
3065 &schedule,
3066 0,
3067 vec![],
3068 Some(&mut debug_buffer),
3069 Determinism::Enforced,
3070 );
3071 assert!(result.is_err());
3072 });
3073
3074 assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text");
3075 }
3076
3077 #[test]
3078 fn debug_buffer_is_limited() {
3079 let code_hash = MockLoader::insert(Call, move |ctx, _| {
3080 ctx.ext.append_debug_buffer("overflowing bytes");
3081 exec_success()
3082 });
3083
3084 let debug_buf_before =
3086 DebugBufferVec::<Test>::try_from(vec![0u8; DebugBufferVec::<Test>::bound() - 5])
3087 .unwrap();
3088 let mut debug_buf_after = debug_buf_before.clone();
3089
3090 ExtBuilder::default().build().execute_with(|| {
3091 let schedule: Schedule<Test> = <Test as Config>::Schedule::get();
3092 let min_balance = <Test as Config>::Currency::minimum_balance();
3093 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3094 set_balance(&ALICE, min_balance * 10);
3095 place_contract(&BOB, code_hash);
3096 let contract_origin = Origin::from_account_id(ALICE);
3097 let mut storage_meter =
3098 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3099 MockStack::run_call(
3100 contract_origin,
3101 BOB,
3102 &mut gas_meter,
3103 &mut storage_meter,
3104 &schedule,
3105 0,
3106 vec![],
3107 Some(&mut debug_buf_after),
3108 Determinism::Enforced,
3109 )
3110 .unwrap();
3111 assert_eq!(debug_buf_before, debug_buf_after);
3112 });
3113 }
3114
3115 #[test]
3116 fn call_reentry_direct_recursion() {
3117 let code_bob = MockLoader::insert(Call, |ctx, _| {
3119 let dest = Decode::decode(&mut ctx.input_data.as_ref()).unwrap();
3120 ctx.ext
3121 .call(Weight::zero(), BalanceOf::<Test>::zero(), dest, 0, vec![], false, false)
3122 });
3123
3124 let code_charlie = MockLoader::insert(Call, |_, _| exec_success());
3125
3126 ExtBuilder::default().build().execute_with(|| {
3127 let schedule = <Test as Config>::Schedule::get();
3128 place_contract(&BOB, code_bob);
3129 place_contract(&CHARLIE, code_charlie);
3130 let contract_origin = Origin::from_account_id(ALICE);
3131 let mut storage_meter =
3132 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3133
3134 assert_ok!(MockStack::run_call(
3136 contract_origin.clone(),
3137 BOB,
3138 &mut GasMeter::<Test>::new(GAS_LIMIT),
3139 &mut storage_meter,
3140 &schedule,
3141 0,
3142 CHARLIE.encode(),
3143 None,
3144 Determinism::Enforced
3145 ));
3146
3147 assert_err!(
3149 MockStack::run_call(
3150 contract_origin,
3151 BOB,
3152 &mut GasMeter::<Test>::new(GAS_LIMIT),
3153 &mut storage_meter,
3154 &schedule,
3155 0,
3156 BOB.encode(),
3157 None,
3158 Determinism::Enforced
3159 )
3160 .map_err(|e| e.error),
3161 <Error<Test>>::ReentranceDenied,
3162 );
3163 });
3164 }
3165
3166 #[test]
3167 fn call_deny_reentry() {
3168 let code_bob = MockLoader::insert(Call, |ctx, _| {
3169 if ctx.input_data[0] == 0 {
3170 ctx.ext.call(
3171 Weight::zero(),
3172 BalanceOf::<Test>::zero(),
3173 CHARLIE,
3174 0,
3175 vec![],
3176 false,
3177 false,
3178 )
3179 } else {
3180 exec_success()
3181 }
3182 });
3183
3184 let code_charlie = MockLoader::insert(Call, |ctx, _| {
3186 ctx.ext
3187 .call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![1], true, false)
3188 });
3189
3190 ExtBuilder::default().build().execute_with(|| {
3191 let schedule = <Test as Config>::Schedule::get();
3192 place_contract(&BOB, code_bob);
3193 place_contract(&CHARLIE, code_charlie);
3194 let contract_origin = Origin::from_account_id(ALICE);
3195 let mut storage_meter =
3196 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3197
3198 assert_err!(
3200 MockStack::run_call(
3201 contract_origin,
3202 BOB,
3203 &mut GasMeter::<Test>::new(GAS_LIMIT),
3204 &mut storage_meter,
3205 &schedule,
3206 0,
3207 vec![0],
3208 None,
3209 Determinism::Enforced
3210 )
3211 .map_err(|e| e.error),
3212 <Error<Test>>::ReentranceDenied,
3213 );
3214 });
3215 }
3216
3217 #[test]
3218 fn call_runtime_works() {
3219 let code_hash = MockLoader::insert(Call, |ctx, _| {
3220 let call = RuntimeCall::System(frame_system::Call::remark_with_event {
3221 remark: b"Hello World".to_vec(),
3222 });
3223 ctx.ext.call_runtime(call).unwrap();
3224 exec_success()
3225 });
3226
3227 ExtBuilder::default().build().execute_with(|| {
3228 let min_balance = <Test as Config>::Currency::minimum_balance();
3229 let schedule = <Test as Config>::Schedule::get();
3230 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3231 set_balance(&ALICE, min_balance * 10);
3232 place_contract(&BOB, code_hash);
3233 let contract_origin = Origin::from_account_id(ALICE);
3234 let mut storage_meter =
3235 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3236 System::reset_events();
3237 MockStack::run_call(
3238 contract_origin,
3239 BOB,
3240 &mut gas_meter,
3241 &mut storage_meter,
3242 &schedule,
3243 0,
3244 vec![],
3245 None,
3246 Determinism::Enforced,
3247 )
3248 .unwrap();
3249
3250 let remark_hash = <Test as frame_system::Config>::Hashing::hash(b"Hello World");
3251 assert_eq!(
3252 System::events(),
3253 vec![
3254 EventRecord {
3255 phase: Phase::Initialization,
3256 event: MetaEvent::System(frame_system::Event::Remarked {
3257 sender: BOB,
3258 hash: remark_hash
3259 }),
3260 topics: vec![],
3261 },
3262 EventRecord {
3263 phase: Phase::Initialization,
3264 event: MetaEvent::Contracts(crate::Event::Called {
3265 caller: Origin::from_account_id(ALICE),
3266 contract: BOB,
3267 }),
3268 topics: vec![],
3269 },
3270 ]
3271 );
3272 });
3273 }
3274
3275 #[test]
3276 fn call_runtime_filter() {
3277 let code_hash = MockLoader::insert(Call, |ctx, _| {
3278 use frame_system::Call as SysCall;
3279 use pallet_balances::Call as BalanceCall;
3280 use pallet_utility::Call as UtilCall;
3281
3282 let allowed_call =
3284 RuntimeCall::System(SysCall::remark_with_event { remark: b"Hello".to_vec() });
3285
3286 let forbidden_call = RuntimeCall::Balances(BalanceCall::transfer_allow_death {
3288 dest: CHARLIE,
3289 value: 22,
3290 });
3291
3292 assert_err!(
3294 ctx.ext.call_runtime(forbidden_call.clone()),
3295 frame_system::Error::<Test>::CallFiltered
3296 );
3297
3298 assert_ok!(ctx.ext.call_runtime(RuntimeCall::Utility(UtilCall::batch {
3300 calls: vec![allowed_call.clone(), forbidden_call, allowed_call]
3301 })),);
3302
3303 assert_eq!(get_balance(&CHARLIE), 0);
3305
3306 exec_success()
3307 });
3308
3309 TestFilter::set_filter(|call| match call {
3310 RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) => false,
3311 _ => true,
3312 });
3313
3314 ExtBuilder::default().build().execute_with(|| {
3315 let min_balance = <Test as Config>::Currency::minimum_balance();
3316 let schedule = <Test as Config>::Schedule::get();
3317 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3318 set_balance(&ALICE, min_balance * 10);
3319 place_contract(&BOB, code_hash);
3320 let contract_origin = Origin::from_account_id(ALICE);
3321 let mut storage_meter =
3322 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
3323 System::reset_events();
3324 MockStack::run_call(
3325 contract_origin,
3326 BOB,
3327 &mut gas_meter,
3328 &mut storage_meter,
3329 &schedule,
3330 0,
3331 vec![],
3332 None,
3333 Determinism::Enforced,
3334 )
3335 .unwrap();
3336
3337 let remark_hash = <Test as frame_system::Config>::Hashing::hash(b"Hello");
3338 assert_eq!(
3339 System::events(),
3340 vec![
3341 EventRecord {
3342 phase: Phase::Initialization,
3343 event: MetaEvent::System(frame_system::Event::Remarked {
3344 sender: BOB,
3345 hash: remark_hash
3346 }),
3347 topics: vec![],
3348 },
3349 EventRecord {
3350 phase: Phase::Initialization,
3351 event: MetaEvent::Utility(pallet_utility::Event::ItemCompleted),
3352 topics: vec![],
3353 },
3354 EventRecord {
3355 phase: Phase::Initialization,
3356 event: MetaEvent::Utility(pallet_utility::Event::BatchInterrupted {
3357 index: 1,
3358 error: frame_system::Error::<Test>::CallFiltered.into()
3359 },),
3360 topics: vec![],
3361 },
3362 EventRecord {
3363 phase: Phase::Initialization,
3364 event: MetaEvent::Contracts(crate::Event::Called {
3365 caller: Origin::from_account_id(ALICE),
3366 contract: BOB,
3367 }),
3368 topics: vec![],
3369 },
3370 ]
3371 );
3372 });
3373 }
3374
3375 #[test]
3376 fn nonce() {
3377 let fail_code = MockLoader::insert(Constructor, |_, _| exec_trapped());
3378 let success_code = MockLoader::insert(Constructor, |_, _| exec_success());
3379 let succ_fail_code = MockLoader::insert(Constructor, move |ctx, _| {
3380 ctx.ext
3381 .instantiate(
3382 Weight::zero(),
3383 BalanceOf::<Test>::zero(),
3384 fail_code,
3385 ctx.ext.minimum_balance() * 100,
3386 vec![],
3387 &[],
3388 )
3389 .ok();
3390 exec_success()
3391 });
3392 let succ_succ_code = MockLoader::insert(Constructor, move |ctx, _| {
3393 let (account_id, _) = ctx
3394 .ext
3395 .instantiate(
3396 Weight::zero(),
3397 BalanceOf::<Test>::zero(),
3398 success_code,
3399 ctx.ext.minimum_balance() * 100,
3400 vec![],
3401 &[],
3402 )
3403 .unwrap();
3404
3405 ctx.ext
3407 .call(
3408 Weight::zero(),
3409 BalanceOf::<Test>::zero(),
3410 account_id,
3411 0,
3412 vec![],
3413 false,
3414 false,
3415 )
3416 .unwrap();
3417
3418 exec_success()
3419 });
3420
3421 ExtBuilder::default()
3422 .with_code_hashes(MockLoader::code_hashes())
3423 .build()
3424 .execute_with(|| {
3425 let schedule = <Test as Config>::Schedule::get();
3426 let min_balance = <Test as Config>::Currency::minimum_balance();
3427 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3428 let fail_executable =
3429 MockExecutable::from_storage(fail_code, &mut gas_meter).unwrap();
3430 let success_executable =
3431 MockExecutable::from_storage(success_code, &mut gas_meter).unwrap();
3432 let succ_fail_executable =
3433 MockExecutable::from_storage(succ_fail_code, &mut gas_meter).unwrap();
3434 let succ_succ_executable =
3435 MockExecutable::from_storage(succ_succ_code, &mut gas_meter).unwrap();
3436 set_balance(&ALICE, min_balance * 10_000);
3437 let contract_origin = Origin::from_account_id(ALICE);
3438 let mut storage_meter =
3439 storage::meter::Meter::new(&contract_origin, None, min_balance * 100).unwrap();
3440
3441 MockStack::run_instantiate(
3442 ALICE,
3443 fail_executable,
3444 &mut gas_meter,
3445 &mut storage_meter,
3446 &schedule,
3447 min_balance * 100,
3448 vec![],
3449 &[],
3450 None,
3451 )
3452 .ok();
3453 assert_eq!(<Nonce<Test>>::get(), 0);
3454
3455 assert_ok!(MockStack::run_instantiate(
3456 ALICE,
3457 success_executable,
3458 &mut gas_meter,
3459 &mut storage_meter,
3460 &schedule,
3461 min_balance * 100,
3462 vec![],
3463 &[],
3464 None,
3465 ));
3466 assert_eq!(<Nonce<Test>>::get(), 1);
3467
3468 assert_ok!(MockStack::run_instantiate(
3469 ALICE,
3470 succ_fail_executable,
3471 &mut gas_meter,
3472 &mut storage_meter,
3473 &schedule,
3474 min_balance * 200,
3475 vec![],
3476 &[],
3477 None,
3478 ));
3479 assert_eq!(<Nonce<Test>>::get(), 2);
3480
3481 assert_ok!(MockStack::run_instantiate(
3482 ALICE,
3483 succ_succ_executable,
3484 &mut gas_meter,
3485 &mut storage_meter,
3486 &schedule,
3487 min_balance * 200,
3488 vec![],
3489 &[],
3490 None,
3491 ));
3492 assert_eq!(<Nonce<Test>>::get(), 4);
3493 });
3494 }
3495
3496 #[test]
3497 fn set_storage_works() {
3498 let code_hash = MockLoader::insert(Call, |ctx, _| {
3499 assert_eq!(
3501 ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
3502 Ok(WriteOutcome::New)
3503 );
3504 assert_eq!(
3505 ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true),
3506 Ok(WriteOutcome::New)
3507 );
3508 assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New));
3509 assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New));
3510 assert_eq!(
3511 ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false),
3512 Ok(WriteOutcome::New)
3513 );
3514 assert_eq!(
3515 ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true),
3516 Ok(WriteOutcome::New)
3517 );
3518
3519 assert_eq!(
3521 ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![42]), false),
3522 Ok(WriteOutcome::Overwritten(3))
3523 );
3524 assert_eq!(
3525 ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![48]), true),
3526 Ok(WriteOutcome::Taken(vec![4, 5, 6]))
3527 );
3528 assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New));
3529 assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New));
3530 assert_eq!(
3531 ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false),
3532 Ok(WriteOutcome::Overwritten(0))
3533 );
3534 assert_eq!(
3535 ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true),
3536 Ok(WriteOutcome::Taken(vec![]))
3537 );
3538
3539 exec_success()
3540 });
3541
3542 ExtBuilder::default().build().execute_with(|| {
3543 let min_balance = <Test as Config>::Currency::minimum_balance();
3544 let schedule = <Test as Config>::Schedule::get();
3545 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3546 set_balance(&ALICE, min_balance * 1000);
3547 place_contract(&BOB, code_hash);
3548 let contract_origin = Origin::from_account_id(ALICE);
3549 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3550 assert_ok!(MockStack::run_call(
3551 contract_origin,
3552 BOB,
3553 &mut gas_meter,
3554 &mut storage_meter,
3555 &schedule,
3556 0,
3557 vec![],
3558 None,
3559 Determinism::Enforced
3560 ));
3561 });
3562 }
3563
3564 #[test]
3565 fn set_storage_varsized_key_works() {
3566 let code_hash = MockLoader::insert(Call, |ctx, _| {
3567 assert_eq!(
3569 ctx.ext.set_storage(
3570 &Key::<Test>::try_from_var([1; 64].to_vec()).unwrap(),
3571 Some(vec![1, 2, 3]),
3572 false
3573 ),
3574 Ok(WriteOutcome::New)
3575 );
3576 assert_eq!(
3577 ctx.ext.set_storage(
3578 &Key::<Test>::try_from_var([2; 19].to_vec()).unwrap(),
3579 Some(vec![4, 5, 6]),
3580 true
3581 ),
3582 Ok(WriteOutcome::New)
3583 );
3584 assert_eq!(
3585 ctx.ext.set_storage(
3586 &Key::<Test>::try_from_var([3; 19].to_vec()).unwrap(),
3587 None,
3588 false
3589 ),
3590 Ok(WriteOutcome::New)
3591 );
3592 assert_eq!(
3593 ctx.ext.set_storage(
3594 &Key::<Test>::try_from_var([4; 64].to_vec()).unwrap(),
3595 None,
3596 true
3597 ),
3598 Ok(WriteOutcome::New)
3599 );
3600 assert_eq!(
3601 ctx.ext.set_storage(
3602 &Key::<Test>::try_from_var([5; 30].to_vec()).unwrap(),
3603 Some(vec![]),
3604 false
3605 ),
3606 Ok(WriteOutcome::New)
3607 );
3608 assert_eq!(
3609 ctx.ext.set_storage(
3610 &Key::<Test>::try_from_var([6; 128].to_vec()).unwrap(),
3611 Some(vec![]),
3612 true
3613 ),
3614 Ok(WriteOutcome::New)
3615 );
3616
3617 assert_eq!(
3619 ctx.ext.set_storage(
3620 &Key::<Test>::try_from_var([1; 64].to_vec()).unwrap(),
3621 Some(vec![42, 43, 44]),
3622 false
3623 ),
3624 Ok(WriteOutcome::Overwritten(3))
3625 );
3626 assert_eq!(
3627 ctx.ext.set_storage(
3628 &Key::<Test>::try_from_var([2; 19].to_vec()).unwrap(),
3629 Some(vec![48]),
3630 true
3631 ),
3632 Ok(WriteOutcome::Taken(vec![4, 5, 6]))
3633 );
3634 assert_eq!(
3635 ctx.ext.set_storage(
3636 &Key::<Test>::try_from_var([3; 19].to_vec()).unwrap(),
3637 None,
3638 false
3639 ),
3640 Ok(WriteOutcome::New)
3641 );
3642 assert_eq!(
3643 ctx.ext.set_storage(
3644 &Key::<Test>::try_from_var([4; 64].to_vec()).unwrap(),
3645 None,
3646 true
3647 ),
3648 Ok(WriteOutcome::New)
3649 );
3650 assert_eq!(
3651 ctx.ext.set_storage(
3652 &Key::<Test>::try_from_var([5; 30].to_vec()).unwrap(),
3653 Some(vec![]),
3654 false
3655 ),
3656 Ok(WriteOutcome::Overwritten(0))
3657 );
3658 assert_eq!(
3659 ctx.ext.set_storage(
3660 &Key::<Test>::try_from_var([6; 128].to_vec()).unwrap(),
3661 Some(vec![]),
3662 true
3663 ),
3664 Ok(WriteOutcome::Taken(vec![]))
3665 );
3666
3667 exec_success()
3668 });
3669
3670 ExtBuilder::default().build().execute_with(|| {
3671 let min_balance = <Test as Config>::Currency::minimum_balance();
3672 let schedule = <Test as Config>::Schedule::get();
3673 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3674 set_balance(&ALICE, min_balance * 1000);
3675 place_contract(&BOB, code_hash);
3676 let contract_origin = Origin::from_account_id(ALICE);
3677 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3678 assert_ok!(MockStack::run_call(
3679 contract_origin,
3680 BOB,
3681 &mut gas_meter,
3682 &mut storage_meter,
3683 &schedule,
3684 0,
3685 vec![],
3686 None,
3687 Determinism::Enforced
3688 ));
3689 });
3690 }
3691
3692 #[test]
3693 fn get_storage_works() {
3694 let code_hash = MockLoader::insert(Call, |ctx, _| {
3695 assert_eq!(
3696 ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
3697 Ok(WriteOutcome::New)
3698 );
3699 assert_eq!(
3700 ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false),
3701 Ok(WriteOutcome::New)
3702 );
3703 assert_eq!(ctx.ext.get_storage(&Key::Fix([1; 32])), Some(vec![1, 2, 3]));
3704 assert_eq!(ctx.ext.get_storage(&Key::Fix([2; 32])), Some(vec![]));
3705 assert_eq!(ctx.ext.get_storage(&Key::Fix([3; 32])), None);
3706
3707 exec_success()
3708 });
3709
3710 ExtBuilder::default().build().execute_with(|| {
3711 let min_balance = <Test as Config>::Currency::minimum_balance();
3712 let schedule = <Test as Config>::Schedule::get();
3713 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3714 set_balance(&ALICE, min_balance * 1000);
3715 place_contract(&BOB, code_hash);
3716 let contract_origin = Origin::from_account_id(ALICE);
3717 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3718 assert_ok!(MockStack::run_call(
3719 contract_origin,
3720 BOB,
3721 &mut gas_meter,
3722 &mut storage_meter,
3723 &schedule,
3724 0,
3725 vec![],
3726 None,
3727 Determinism::Enforced
3728 ));
3729 });
3730 }
3731
3732 #[test]
3733 fn get_storage_size_works() {
3734 let code_hash = MockLoader::insert(Call, |ctx, _| {
3735 assert_eq!(
3736 ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
3737 Ok(WriteOutcome::New)
3738 );
3739 assert_eq!(
3740 ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false),
3741 Ok(WriteOutcome::New)
3742 );
3743 assert_eq!(ctx.ext.get_storage_size(&Key::Fix([1; 32])), Some(3));
3744 assert_eq!(ctx.ext.get_storage_size(&Key::Fix([2; 32])), Some(0));
3745 assert_eq!(ctx.ext.get_storage_size(&Key::Fix([3; 32])), None);
3746
3747 exec_success()
3748 });
3749
3750 ExtBuilder::default().build().execute_with(|| {
3751 let min_balance = <Test as Config>::Currency::minimum_balance();
3752 let schedule = <Test as Config>::Schedule::get();
3753 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3754 set_balance(&ALICE, min_balance * 1000);
3755 place_contract(&BOB, code_hash);
3756 let contract_origin = Origin::from_account_id(ALICE);
3757 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3758 assert_ok!(MockStack::run_call(
3759 contract_origin,
3760 BOB,
3761 &mut gas_meter,
3762 &mut storage_meter,
3763 &schedule,
3764 0,
3765 vec![],
3766 None,
3767 Determinism::Enforced
3768 ));
3769 });
3770 }
3771
3772 #[test]
3773 fn get_storage_varsized_key_works() {
3774 let code_hash = MockLoader::insert(Call, |ctx, _| {
3775 assert_eq!(
3776 ctx.ext.set_storage(
3777 &Key::<Test>::try_from_var([1; 19].to_vec()).unwrap(),
3778 Some(vec![1, 2, 3]),
3779 false
3780 ),
3781 Ok(WriteOutcome::New)
3782 );
3783 assert_eq!(
3784 ctx.ext.set_storage(
3785 &Key::<Test>::try_from_var([2; 16].to_vec()).unwrap(),
3786 Some(vec![]),
3787 false
3788 ),
3789 Ok(WriteOutcome::New)
3790 );
3791 assert_eq!(
3792 ctx.ext.get_storage(&Key::<Test>::try_from_var([1; 19].to_vec()).unwrap()),
3793 Some(vec![1, 2, 3])
3794 );
3795 assert_eq!(
3796 ctx.ext.get_storage(&Key::<Test>::try_from_var([2; 16].to_vec()).unwrap()),
3797 Some(vec![])
3798 );
3799 assert_eq!(
3800 ctx.ext.get_storage(&Key::<Test>::try_from_var([3; 8].to_vec()).unwrap()),
3801 None
3802 );
3803
3804 exec_success()
3805 });
3806
3807 ExtBuilder::default().build().execute_with(|| {
3808 let min_balance = <Test as Config>::Currency::minimum_balance();
3809 let schedule = <Test as Config>::Schedule::get();
3810 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3811 set_balance(&ALICE, min_balance * 1000);
3812 place_contract(&BOB, code_hash);
3813 let contract_origin = Origin::from_account_id(ALICE);
3814 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3815 assert_ok!(MockStack::run_call(
3816 contract_origin,
3817 BOB,
3818 &mut gas_meter,
3819 &mut storage_meter,
3820 &schedule,
3821 0,
3822 vec![],
3823 None,
3824 Determinism::Enforced
3825 ));
3826 });
3827 }
3828
3829 #[test]
3830 fn get_storage_size_varsized_key_works() {
3831 let code_hash = MockLoader::insert(Call, |ctx, _| {
3832 assert_eq!(
3833 ctx.ext.set_storage(
3834 &Key::<Test>::try_from_var([1; 19].to_vec()).unwrap(),
3835 Some(vec![1, 2, 3]),
3836 false
3837 ),
3838 Ok(WriteOutcome::New)
3839 );
3840 assert_eq!(
3841 ctx.ext.set_storage(
3842 &Key::<Test>::try_from_var([2; 16].to_vec()).unwrap(),
3843 Some(vec![]),
3844 false
3845 ),
3846 Ok(WriteOutcome::New)
3847 );
3848 assert_eq!(
3849 ctx.ext.get_storage_size(&Key::<Test>::try_from_var([1; 19].to_vec()).unwrap()),
3850 Some(3)
3851 );
3852 assert_eq!(
3853 ctx.ext.get_storage_size(&Key::<Test>::try_from_var([2; 16].to_vec()).unwrap()),
3854 Some(0)
3855 );
3856 assert_eq!(
3857 ctx.ext.get_storage_size(&Key::<Test>::try_from_var([3; 8].to_vec()).unwrap()),
3858 None
3859 );
3860
3861 exec_success()
3862 });
3863
3864 ExtBuilder::default().build().execute_with(|| {
3865 let min_balance = <Test as Config>::Currency::minimum_balance();
3866 let schedule = <Test as Config>::Schedule::get();
3867 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
3868 set_balance(&ALICE, min_balance * 1000);
3869 place_contract(&BOB, code_hash);
3870 let contract_origin = Origin::from_account_id(ALICE);
3871 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3872 assert_ok!(MockStack::run_call(
3873 contract_origin,
3874 BOB,
3875 &mut gas_meter,
3876 &mut storage_meter,
3877 &schedule,
3878 0,
3879 vec![],
3880 None,
3881 Determinism::Enforced
3882 ));
3883 });
3884 }
3885
3886 #[test]
3887 fn set_transient_storage_works() {
3888 let code_hash = MockLoader::insert(Call, |ctx, _| {
3889 assert_eq!(
3891 ctx.ext.set_transient_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false),
3892 Ok(WriteOutcome::New)
3893 );
3894 assert_eq!(
3895 ctx.ext.set_transient_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true),
3896 Ok(WriteOutcome::New)
3897 );
3898 assert_eq!(
3899 ctx.ext.set_transient_storage(&Key::Fix([3; 32]), None, false),
3900 Ok(WriteOutcome::New)
3901 );
3902 assert_eq!(
3903 ctx.ext.set_transient_storage(&Key::Fix([4; 32]), None, true),
3904 Ok(WriteOutcome::New)
3905 );
3906 assert_eq!(
3907 ctx.ext.set_transient_storage(&Key::Fix([5; 32]), Some(vec![]), false),
3908 Ok(WriteOutcome::New)
3909 );
3910 assert_eq!(
3911 ctx.ext.set_transient_storage(&Key::Fix([6; 32]), Some(vec![]), true),
3912 Ok(WriteOutcome::New)
3913 );
3914
3915 assert_eq!(
3917 ctx.ext.set_transient_storage(&Key::Fix([1; 32]), Some(vec![42]), false),
3918 Ok(WriteOutcome::Overwritten(3))
3919 );
3920 assert_eq!(
3921 ctx.ext.set_transient_storage(&Key::Fix([2; 32]), Some(vec![48]), true),
3922 Ok(WriteOutcome::Taken(vec![4, 5, 6]))
3923 );
3924 assert_eq!(
3925 ctx.ext.set_transient_storage(&Key::Fix([3; 32]), None, false),
3926 Ok(WriteOutcome::New)
3927 );
3928 assert_eq!(
3929 ctx.ext.set_transient_storage(&Key::Fix([4; 32]), None, true),
3930 Ok(WriteOutcome::New)
3931 );
3932 assert_eq!(
3933 ctx.ext.set_transient_storage(&Key::Fix([5; 32]), Some(vec![]), false),
3934 Ok(WriteOutcome::Overwritten(0))
3935 );
3936 assert_eq!(
3937 ctx.ext.set_transient_storage(&Key::Fix([6; 32]), Some(vec![]), true),
3938 Ok(WriteOutcome::Taken(vec![]))
3939 );
3940
3941 exec_success()
3942 });
3943
3944 ExtBuilder::default().build().execute_with(|| {
3945 let schedule = <Test as Config>::Schedule::get();
3946 place_contract(&BOB, code_hash);
3947 let contract_origin = Origin::from_account_id(ALICE);
3948 let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
3949 assert_ok!(MockStack::run_call(
3950 contract_origin,
3951 BOB,
3952 &mut GasMeter::<Test>::new(GAS_LIMIT),
3953 &mut storage_meter,
3954 &schedule,
3955 0,
3956 vec![],
3957 None,
3958 Determinism::Enforced
3959 ));
3960 });
3961 }
3962
3963 #[test]
3964 fn get_transient_storage_works() {
3965 let storage_key_1 = &Key::Fix([1; 32]);
3967 let storage_key_2 = &Key::Fix([2; 32]);
3968 let storage_key_3 = &Key::Fix([3; 32]);
3969 let code_bob = MockLoader::insert(Call, |ctx, _| {
3970 if ctx.input_data[0] == 0 {
3971 assert_eq!(
3972 ctx.ext.set_transient_storage(storage_key_1, Some(vec![1, 2]), false),
3973 Ok(WriteOutcome::New)
3974 );
3975 assert_eq!(
3976 ctx.ext.call(
3977 Weight::zero(),
3978 BalanceOf::<Test>::zero(),
3979 CHARLIE,
3980 0,
3981 vec![],
3982 true,
3983 false,
3984 ),
3985 exec_success()
3986 );
3987 assert_eq!(ctx.ext.get_transient_storage(storage_key_1), Some(vec![3]));
3988 assert_eq!(ctx.ext.get_transient_storage(storage_key_2), Some(vec![]));
3989 assert_eq!(ctx.ext.get_transient_storage(storage_key_3), None);
3990 } else {
3991 assert_eq!(
3992 ctx.ext.set_transient_storage(storage_key_1, Some(vec![3]), true),
3993 Ok(WriteOutcome::Taken(vec![1, 2]))
3994 );
3995 assert_eq!(
3996 ctx.ext.set_transient_storage(storage_key_2, Some(vec![]), false),
3997 Ok(WriteOutcome::New)
3998 );
3999 }
4000 exec_success()
4001 });
4002 let code_charlie = MockLoader::insert(Call, |ctx, _| {
4003 assert!(ctx
4004 .ext
4005 .call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![99], true, false)
4006 .is_ok());
4007 assert_eq!(ctx.ext.get_transient_storage(storage_key_1), None);
4009 exec_success()
4010 });
4011
4012 ExtBuilder::default().build().execute_with(|| {
4014 let schedule = <Test as Config>::Schedule::get();
4015 place_contract(&BOB, code_bob);
4016 place_contract(&CHARLIE, code_charlie);
4017 let contract_origin = Origin::from_account_id(ALICE);
4018 let mut storage_meter =
4019 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4020
4021 let result = MockStack::run_call(
4022 contract_origin,
4023 BOB,
4024 &mut GasMeter::<Test>::new(GAS_LIMIT),
4025 &mut storage_meter,
4026 &schedule,
4027 0,
4028 vec![0],
4029 None,
4030 Determinism::Enforced,
4031 );
4032 assert_matches!(result, Ok(_));
4033 });
4034 }
4035
4036 #[test]
4037 fn get_transient_storage_size_works() {
4038 let storage_key_1 = &Key::Fix([1; 32]);
4039 let storage_key_2 = &Key::Fix([2; 32]);
4040 let storage_key_3 = &Key::Fix([3; 32]);
4041 let code_hash = MockLoader::insert(Call, |ctx, _| {
4042 assert_eq!(
4043 ctx.ext.set_transient_storage(storage_key_1, Some(vec![1, 2, 3]), false),
4044 Ok(WriteOutcome::New)
4045 );
4046 assert_eq!(
4047 ctx.ext.set_transient_storage(storage_key_2, Some(vec![]), false),
4048 Ok(WriteOutcome::New)
4049 );
4050 assert_eq!(ctx.ext.get_transient_storage_size(storage_key_1), Some(3));
4051 assert_eq!(ctx.ext.get_transient_storage_size(storage_key_2), Some(0));
4052 assert_eq!(ctx.ext.get_transient_storage_size(storage_key_3), None);
4053
4054 exec_success()
4055 });
4056
4057 ExtBuilder::default().build().execute_with(|| {
4058 let schedule = <Test as Config>::Schedule::get();
4059 place_contract(&BOB, code_hash);
4060 let contract_origin = Origin::from_account_id(ALICE);
4061 let mut storage_meter =
4062 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4063 assert_ok!(MockStack::run_call(
4064 contract_origin,
4065 BOB,
4066 &mut GasMeter::<Test>::new(GAS_LIMIT),
4067 &mut storage_meter,
4068 &schedule,
4069 0,
4070 vec![],
4071 None,
4072 Determinism::Enforced
4073 ));
4074 });
4075 }
4076
4077 #[test]
4078 fn rollback_transient_storage_works() {
4079 let storage_key = &Key::Fix([1; 32]);
4081 let code_bob = MockLoader::insert(Call, |ctx, _| {
4082 if ctx.input_data[0] == 0 {
4083 assert_eq!(
4084 ctx.ext.set_transient_storage(storage_key, Some(vec![1, 2]), false),
4085 Ok(WriteOutcome::New)
4086 );
4087 assert_eq!(
4088 ctx.ext.call(
4089 Weight::zero(),
4090 BalanceOf::<Test>::zero(),
4091 CHARLIE,
4092 0,
4093 vec![],
4094 true,
4095 false
4096 ),
4097 exec_trapped()
4098 );
4099 assert_eq!(ctx.ext.get_transient_storage(storage_key), Some(vec![1, 2]));
4100 } else {
4101 let overwritten_length = ctx.ext.get_transient_storage_size(storage_key).unwrap();
4102 assert_eq!(
4103 ctx.ext.set_transient_storage(storage_key, Some(vec![3]), false),
4104 Ok(WriteOutcome::Overwritten(overwritten_length))
4105 );
4106 assert_eq!(ctx.ext.get_transient_storage(storage_key), Some(vec![3]));
4107 }
4108 exec_success()
4109 });
4110 let code_charlie = MockLoader::insert(Call, |ctx, _| {
4111 assert!(ctx
4112 .ext
4113 .call(Weight::zero(), BalanceOf::<Test>::zero(), BOB, 0, vec![99], true, false)
4114 .is_ok());
4115 exec_trapped()
4116 });
4117
4118 ExtBuilder::default().build().execute_with(|| {
4120 let schedule = <Test as Config>::Schedule::get();
4121 place_contract(&BOB, code_bob);
4122 place_contract(&CHARLIE, code_charlie);
4123 let contract_origin = Origin::from_account_id(ALICE);
4124 let mut storage_meter =
4125 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4126
4127 let result = MockStack::run_call(
4128 contract_origin,
4129 BOB,
4130 &mut GasMeter::<Test>::new(GAS_LIMIT),
4131 &mut storage_meter,
4132 &schedule,
4133 0,
4134 vec![0],
4135 None,
4136 Determinism::Enforced,
4137 );
4138 assert_matches!(result, Ok(_));
4139 });
4140 }
4141
4142 #[test]
4143 fn ecdsa_to_eth_address_returns_proper_value() {
4144 let bob_ch = MockLoader::insert(Call, |ctx, _| {
4145 let pubkey_compressed = array_bytes::hex2array_unchecked(
4146 "028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd91",
4147 );
4148 assert_eq!(
4149 ctx.ext.ecdsa_to_eth_address(&pubkey_compressed).unwrap(),
4150 array_bytes::hex2array_unchecked::<_, 20>(
4151 "09231da7b19A016f9e576d23B16277062F4d46A8"
4152 )
4153 );
4154 exec_success()
4155 });
4156
4157 ExtBuilder::default().build().execute_with(|| {
4158 let schedule = <Test as Config>::Schedule::get();
4159 place_contract(&BOB, bob_ch);
4160
4161 let contract_origin = Origin::from_account_id(ALICE);
4162 let mut storage_meter =
4163 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4164 let result = MockStack::run_call(
4165 contract_origin,
4166 BOB,
4167 &mut GasMeter::<Test>::new(GAS_LIMIT),
4168 &mut storage_meter,
4169 &schedule,
4170 0,
4171 vec![],
4172 None,
4173 Determinism::Enforced,
4174 );
4175 assert_matches!(result, Ok(_));
4176 });
4177 }
4178
4179 #[test]
4180 fn nonce_api_works() {
4181 let fail_code = MockLoader::insert(Constructor, |_, _| exec_trapped());
4182 let success_code = MockLoader::insert(Constructor, |_, _| exec_success());
4183 let code_hash = MockLoader::insert(Call, move |ctx, _| {
4184 assert_eq!(ctx.ext.nonce(), 1);
4186 assert_eq!(ctx.ext.nonce(), 1);
4188 assert_err!(
4190 ctx.ext.instantiate(
4191 Weight::zero(),
4192 BalanceOf::<Test>::zero(),
4193 fail_code,
4194 0,
4195 vec![],
4196 &[],
4197 ),
4198 ExecError {
4199 error: <Error<Test>>::ContractTrapped.into(),
4200 origin: ErrorOrigin::Callee
4201 }
4202 );
4203 assert_eq!(ctx.ext.nonce(), 1);
4204 ctx.ext
4206 .instantiate(
4207 Weight::zero(),
4208 BalanceOf::<Test>::zero(),
4209 success_code,
4210 0,
4211 vec![],
4212 &[],
4213 )
4214 .unwrap();
4215 assert_eq!(ctx.ext.nonce(), 2);
4216 exec_success()
4217 });
4218
4219 ExtBuilder::default()
4220 .with_code_hashes(MockLoader::code_hashes())
4221 .build()
4222 .execute_with(|| {
4223 let min_balance = <Test as Config>::Currency::minimum_balance();
4224 let schedule = <Test as Config>::Schedule::get();
4225 let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
4226 set_balance(&ALICE, min_balance * 1000);
4227 place_contract(&BOB, code_hash);
4228 let contract_origin = Origin::from_account_id(ALICE);
4229 let mut storage_meter =
4230 storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
4231 assert_ok!(MockStack::run_call(
4232 contract_origin,
4233 BOB,
4234 &mut gas_meter,
4235 &mut storage_meter,
4236 &schedule,
4237 0,
4238 vec![],
4239 None,
4240 Determinism::Enforced
4241 ));
4242 });
4243 }
4244
4245 #[test]
4248 fn randomness_works() {
4249 let subject = b"nice subject".as_ref();
4250 let code_hash = MockLoader::insert(Call, move |ctx, _| {
4251 let rand = <Test as Config>::Randomness::random(subject);
4252 assert_eq!(rand, ctx.ext.random(subject));
4253 exec_success()
4254 });
4255
4256 ExtBuilder::default().build().execute_with(|| {
4257 let schedule = <Test as Config>::Schedule::get();
4258 place_contract(&BOB, code_hash);
4259
4260 let contract_origin = Origin::from_account_id(ALICE);
4261 let mut storage_meter =
4262 storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
4263 let result = MockStack::run_call(
4264 contract_origin,
4265 BOB,
4266 &mut GasMeter::<Test>::new(GAS_LIMIT),
4267 &mut storage_meter,
4268 &schedule,
4269 0,
4270 vec![],
4271 None,
4272 Determinism::Enforced,
4273 );
4274 assert_matches!(result, Ok(_));
4275 });
4276 }
4277}