1mod prepare;
22mod runtime;
23
24#[cfg(doc)]
25pub use crate::wasm::runtime::api_doc;
26
27#[cfg(test)]
28pub use {
29 crate::wasm::{prepare::tracker, runtime::ReturnErrorCode},
30 runtime::STABLE_API_COUNT,
31 tests::MockExt,
32};
33
34#[cfg(feature = "runtime-benchmarks")]
35pub use crate::wasm::runtime::{BenchEnv, ReturnData, TrapReason};
36
37pub use crate::wasm::{
38 prepare::{LoadedModule, LoadingMode},
39 runtime::{
40 AllowDeprecatedInterface, AllowUnstableInterface, Environment, Runtime, RuntimeCosts,
41 },
42};
43
44use crate::{
45 exec::{ExecResult, Executable, ExportedFunction, Ext},
46 gas::{GasMeter, Token},
47 weights::WeightInfo,
48 AccountIdOf, BadOrigin, BalanceOf, CodeHash, CodeInfoOf, CodeVec, Config, Error, Event,
49 HoldReason, Pallet, PristineCode, Schedule, Weight, LOG_TARGET,
50};
51use alloc::vec::Vec;
52use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
53use frame_support::{
54 dispatch::DispatchResult,
55 ensure,
56 traits::{fungible::MutateHold, tokens::Precision::BestEffort},
57};
58use sp_core::Get;
59use sp_runtime::{DispatchError, RuntimeDebug};
60use wasmi::{CompilationMode, InstancePre, Linker, Memory, MemoryType, StackLimits, Store};
61
62const BYTES_PER_PAGE: usize = 64 * 1024;
63
64#[derive(Encode, Decode, scale_info::TypeInfo)]
67#[codec(mel_bound())]
68#[scale_info(skip_type_params(T))]
69pub struct WasmBlob<T: Config> {
70 code: CodeVec<T>,
71 #[codec(skip)]
73 code_info: CodeInfo<T>,
74 #[codec(skip)]
76 code_hash: CodeHash<T>,
77}
78
79#[derive(Clone, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)]
88#[codec(mel_bound())]
89#[scale_info(skip_type_params(T))]
90pub struct CodeInfo<T: Config> {
91 owner: AccountIdOf<T>,
93 #[codec(compact)]
95 deposit: BalanceOf<T>,
96 #[codec(compact)]
98 refcount: u64,
99 determinism: Determinism,
103 code_len: u32,
105}
106
107#[derive(
109 Clone,
110 Copy,
111 Encode,
112 Decode,
113 DecodeWithMemTracking,
114 scale_info::TypeInfo,
115 MaxEncodedLen,
116 RuntimeDebug,
117 PartialEq,
118 Eq,
119)]
120pub enum Determinism {
121 Enforced,
126 Relaxed,
135}
136
137impl ExportedFunction {
138 fn identifier(&self) -> &str {
140 match self {
141 Self::Constructor => "deploy",
142 Self::Call => "call",
143 }
144 }
145}
146
147#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
149#[derive(Clone, Copy)]
150struct CodeLoadToken(u32);
151
152impl<T: Config> Token<T> for CodeLoadToken {
153 fn weight(&self) -> Weight {
154 T::WeightInfo::call_with_code_per_byte(self.0)
155 .saturating_sub(T::WeightInfo::call_with_code_per_byte(0))
156 }
157}
158
159impl<T: Config> WasmBlob<T> {
160 pub fn from_code(
162 code: Vec<u8>,
163 schedule: &Schedule<T>,
164 owner: AccountIdOf<T>,
165 determinism: Determinism,
166 ) -> Result<Self, (DispatchError, &'static str)> {
167 prepare::prepare::<runtime::Env, T>(
168 code.try_into().map_err(|_| (<Error<T>>::CodeTooLarge.into(), ""))?,
169 schedule,
170 owner,
171 determinism,
172 )
173 }
174
175 pub fn remove(origin: &T::AccountId, code_hash: CodeHash<T>) -> DispatchResult {
179 <CodeInfoOf<T>>::try_mutate_exists(&code_hash, |existing| {
180 if let Some(code_info) = existing {
181 ensure!(code_info.refcount == 0, <Error<T>>::CodeInUse);
182 ensure!(&code_info.owner == origin, BadOrigin);
183 let _ = T::Currency::release(
184 &HoldReason::CodeUploadDepositReserve.into(),
185 &code_info.owner,
186 code_info.deposit,
187 BestEffort,
188 );
189 let deposit_released = code_info.deposit;
190 let remover = code_info.owner.clone();
191
192 *existing = None;
193 <PristineCode<T>>::remove(&code_hash);
194 <Pallet<T>>::deposit_event(Event::CodeRemoved {
195 code_hash,
196 deposit_released,
197 remover,
198 });
199 Ok(())
200 } else {
201 Err(<Error<T>>::CodeNotFound.into())
202 }
203 })
204 }
205
206 pub fn instantiate<E, H>(
212 contract: LoadedModule,
213 host_state: H,
214 schedule: &Schedule<T>,
215 allow_deprecated: AllowDeprecatedInterface,
216 ) -> Result<(Store<H>, Memory, InstancePre), &'static str>
217 where
218 E: Environment<H>,
219 {
220 let mut store = Store::new(&contract.engine, host_state);
221 let mut linker = Linker::new(&contract.engine);
222 E::define(
223 &mut store,
224 &mut linker,
225 if T::UnsafeUnstableInterface::get() {
226 AllowUnstableInterface::Yes
227 } else {
228 AllowUnstableInterface::No
229 },
230 allow_deprecated,
231 )
232 .map_err(|_| "can't define host functions to Linker")?;
233
234 let memory_limits = contract.scan_imports::<T>(schedule)?;
236 let qed = "We checked the limits versus our Schedule,
239 which specifies the max amount of memory pages
240 well below u16::MAX; qed";
241 let memory = Memory::new(
242 &mut store,
243 MemoryType::new(memory_limits.0, Some(memory_limits.1)).expect(qed),
244 )
245 .expect(qed);
246
247 linker
248 .define("env", "memory", memory)
249 .expect("We just created the Linker. It has no definitions with this name; qed");
250
251 let instance = linker.instantiate(&mut store, &contract.module).map_err(|err| {
252 log::debug!(target: LOG_TARGET, "failed to instantiate module: {:?}", err);
253 "can't instantiate module with provided definitions"
254 })?;
255
256 Ok((store, memory, instance))
257 }
258
259 pub fn store_code(&mut self) -> Result<BalanceOf<T>, Error<T>> {
261 let code_hash = *self.code_hash();
262 <CodeInfoOf<T>>::mutate(code_hash, |stored_code_info| {
263 match stored_code_info {
264 Some(_) => Ok(Default::default()),
266 None => {
271 let deposit = self.code_info.deposit;
272 T::Currency::hold(
273 &HoldReason::CodeUploadDepositReserve.into(),
274 &self.code_info.owner,
275 deposit,
276 )
277 .map_err(|_| <Error<T>>::StorageDepositNotEnoughFunds)?;
278
279 self.code_info.refcount = 0;
280 <PristineCode<T>>::insert(code_hash, &self.code);
281 *stored_code_info = Some(self.code_info.clone());
282 <Pallet<T>>::deposit_event(Event::CodeStored {
283 code_hash,
284 deposit_held: deposit,
285 uploader: self.code_info.owner.clone(),
286 });
287 Ok(deposit)
288 },
289 }
290 })
291 }
292
293 #[cfg(any(test, feature = "runtime-benchmarks"))]
301 pub fn from_code_unchecked(
302 code: Vec<u8>,
303 schedule: &Schedule<T>,
304 owner: T::AccountId,
305 ) -> Result<Self, DispatchError> {
306 prepare::benchmarking::prepare(code, schedule, owner)
307 }
308}
309
310impl<T: Config> CodeInfo<T> {
311 #[cfg(test)]
312 pub fn new(owner: T::AccountId) -> Self {
313 CodeInfo {
314 owner,
315 deposit: Default::default(),
316 refcount: 0,
317 code_len: 0,
318 determinism: Determinism::Enforced,
319 }
320 }
321
322 pub fn determinism(&self) -> Determinism {
324 self.determinism
325 }
326
327 pub fn refcount(&self) -> u64 {
329 self.refcount
330 }
331
332 pub fn refcount_mut(&mut self) -> &mut u64 {
334 &mut self.refcount
335 }
336
337 pub fn deposit(&self) -> BalanceOf<T> {
339 self.deposit
340 }
341}
342
343use crate::{ExecError, ExecReturnValue};
344use wasmi::Func;
345enum InstanceOrExecReturn<'a, E: Ext> {
346 Instance((Func, Store<Runtime<'a, E>>)),
347 ExecReturn(ExecReturnValue),
348}
349
350type PreExecResult<'a, E> = Result<InstanceOrExecReturn<'a, E>, ExecError>;
351
352impl<T: Config> WasmBlob<T> {
353 pub fn process_result<E: Ext<T = T>>(
355 mut store: Store<Runtime<E>>,
356 result: Result<(), wasmi::Error>,
357 ) -> ExecResult {
358 let engine_fuel = store.get_fuel().expect("Fuel metering is enabled; qed");
359 let gas_meter = store.data_mut().ext().gas_meter_mut();
360 let _ = gas_meter.sync_from_executor(engine_fuel)?;
361 store.into_data().to_execution_result(result)
362 }
363
364 #[cfg(feature = "runtime-benchmarks")]
365 pub fn bench_prepare_call<E: Ext<T = T>>(
366 self,
367 ext: &mut E,
368 input_data: Vec<u8>,
369 ) -> (Func, Store<Runtime<E>>) {
370 use InstanceOrExecReturn::*;
371 match Self::prepare_execute(
372 self,
373 Runtime::new(ext, input_data),
374 &ExportedFunction::Call,
375 CompilationMode::Eager,
376 )
377 .expect("Benchmark should provide valid module")
378 {
379 Instance((func, store)) => (func, store),
380 ExecReturn(_) => panic!("Expected Instance"),
381 }
382 }
383
384 fn prepare_execute<'a, E: Ext<T = T>>(
385 self,
386 runtime: Runtime<'a, E>,
387 function: &'a ExportedFunction,
388 compilation_mode: CompilationMode,
389 ) -> PreExecResult<'a, E> {
390 let code = self.code.as_slice();
391 let schedule = <T>::Schedule::get();
393
394 let contract = LoadedModule::new::<T>(
395 &code,
396 self.code_info.determinism,
397 Some(StackLimits::default()),
398 LoadingMode::Unchecked,
399 compilation_mode,
400 )
401 .map_err(|err| {
402 log::debug!(target: LOG_TARGET, "failed to create wasmi module: {err:?}");
403 Error::<T>::CodeRejected
404 })?;
405
406 let (mut store, memory, instance) = Self::instantiate::<crate::wasm::runtime::Env, _>(
407 contract,
408 runtime,
409 &schedule,
410 match function {
411 ExportedFunction::Call => AllowDeprecatedInterface::Yes,
412 ExportedFunction::Constructor => AllowDeprecatedInterface::No,
413 },
414 )
415 .map_err(|msg| {
416 log::debug!(target: LOG_TARGET, "failed to instantiate code to wasmi: {}", msg);
417 Error::<T>::CodeRejected
418 })?;
419 store.data_mut().set_memory(memory);
420
421 let fuel_limit = store
424 .data_mut()
425 .ext()
426 .gas_meter_mut()
427 .gas_left()
428 .ref_time()
429 .checked_div(T::Schedule::get().ref_time_by_fuel())
430 .ok_or(Error::<T>::InvalidSchedule)?;
431 store
432 .set_fuel(fuel_limit)
433 .expect("We've set up engine to fuel consuming mode; qed");
434
435 if let &ExportedFunction::Constructor = function {
437 E::increment_refcount(self.code_hash)?;
438 }
439
440 match instance.start(&mut store) {
444 Ok(instance) => {
445 let exported_func = instance
446 .get_export(&store, function.identifier())
447 .and_then(|export| export.into_func())
448 .ok_or_else(|| {
449 log::error!(target: LOG_TARGET, "failed to find entry point");
450 Error::<T>::CodeRejected
451 })?;
452
453 Ok(InstanceOrExecReturn::Instance((exported_func, store)))
454 },
455 Err(err) => Self::process_result(store, Err(err)).map(InstanceOrExecReturn::ExecReturn),
456 }
457 }
458}
459
460impl<T: Config> Executable<T> for WasmBlob<T> {
461 fn from_storage(
462 code_hash: CodeHash<T>,
463 gas_meter: &mut GasMeter<T>,
464 ) -> Result<Self, DispatchError> {
465 let code_info = <CodeInfoOf<T>>::get(code_hash).ok_or(Error::<T>::CodeNotFound)?;
466 gas_meter.charge(CodeLoadToken(code_info.code_len))?;
467 let code = <PristineCode<T>>::get(code_hash).ok_or(Error::<T>::CodeNotFound)?;
468 Ok(Self { code, code_info, code_hash })
469 }
470
471 fn execute<E: Ext<T = T>>(
472 self,
473 ext: &mut E,
474 function: &ExportedFunction,
475 input_data: Vec<u8>,
476 ) -> ExecResult {
477 use InstanceOrExecReturn::*;
478 match Self::prepare_execute(
479 self,
480 Runtime::new(ext, input_data),
481 function,
482 CompilationMode::Lazy,
483 )? {
484 Instance((func, mut store)) => {
485 let result = func.call(&mut store, &[], &mut []);
486 Self::process_result(store, result)
487 },
488 ExecReturn(exec_return) => Ok(exec_return),
489 }
490 }
491
492 fn code_hash(&self) -> &CodeHash<T> {
493 &self.code_hash
494 }
495
496 fn code_info(&self) -> &CodeInfo<T> {
497 &self.code_info
498 }
499
500 fn is_deterministic(&self) -> bool {
501 matches!(self.code_info.determinism, Determinism::Enforced)
502 }
503}
504
505#[cfg(test)]
506mod tests {
507 use super::*;
508 use crate::{
509 exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Ext, Key, SeedOf},
510 gas::GasMeter,
511 primitives::ExecReturnValue,
512 storage::WriteOutcome,
513 tests::{RuntimeCall, Test, ALICE, BOB},
514 transient_storage::TransientStorage,
515 BalanceOf, CodeHash, Error, Origin, Pallet as Contracts,
516 };
517 use assert_matches::assert_matches;
518 use frame_support::{
519 assert_err, assert_ok, dispatch::DispatchResultWithPostInfo, weights::Weight,
520 };
521 use frame_system::pallet_prelude::BlockNumberFor;
522 use pallet_contracts_uapi::ReturnFlags;
523 use pretty_assertions::assert_eq;
524 use sp_core::H256;
525 use sp_runtime::DispatchError;
526 use std::{
527 borrow::BorrowMut,
528 cell::RefCell,
529 collections::{
530 hash_map::{Entry, HashMap},
531 HashSet,
532 },
533 };
534
535 #[derive(Debug, PartialEq, Eq)]
536 struct InstantiateEntry {
537 code_hash: H256,
538 value: u64,
539 data: Vec<u8>,
540 gas_left: u64,
541 salt: Vec<u8>,
542 }
543
544 #[derive(Debug, PartialEq, Eq)]
545 struct TerminationEntry {
546 beneficiary: AccountIdOf<Test>,
547 }
548
549 #[derive(Debug, PartialEq, Eq)]
550 struct TransferEntry {
551 to: AccountIdOf<Test>,
552 value: u64,
553 }
554
555 #[derive(Debug, PartialEq, Eq)]
556 struct CallEntry {
557 to: AccountIdOf<Test>,
558 value: u64,
559 data: Vec<u8>,
560 allows_reentry: bool,
561 read_only: bool,
562 }
563
564 #[derive(Debug, PartialEq, Eq)]
565 struct CallCodeEntry {
566 code_hash: H256,
567 data: Vec<u8>,
568 }
569
570 pub struct MockExt {
571 storage: HashMap<Vec<u8>, Vec<u8>>,
572 transient_storage: TransientStorage<Test>,
573 instantiates: Vec<InstantiateEntry>,
574 terminations: Vec<TerminationEntry>,
575 calls: Vec<CallEntry>,
576 code_calls: Vec<CallCodeEntry>,
577 transfers: Vec<TransferEntry>,
578 events: Vec<(Vec<H256>, Vec<u8>)>,
580 runtime_calls: RefCell<Vec<RuntimeCall>>,
581 schedule: Schedule<Test>,
582 gas_meter: GasMeter<Test>,
583 debug_buffer: Vec<u8>,
584 ecdsa_recover: RefCell<Vec<([u8; 65], [u8; 32])>>,
585 sr25519_verify: RefCell<Vec<([u8; 64], Vec<u8>, [u8; 32])>>,
586 code_hashes: Vec<CodeHash<Test>>,
587 caller: Origin<Test>,
588 delegate_dependencies: RefCell<HashSet<CodeHash<Test>>>,
589 }
590
591 fn call_return_data() -> Vec<u8> {
593 vec![0xDE, 0xAD, 0xBE, 0xEF]
594 }
595
596 impl Default for MockExt {
597 fn default() -> Self {
598 Self {
599 code_hashes: Default::default(),
600 storage: Default::default(),
601 transient_storage: TransientStorage::new(1024 * 1024),
602 instantiates: Default::default(),
603 terminations: Default::default(),
604 calls: Default::default(),
605 code_calls: Default::default(),
606 transfers: Default::default(),
607 events: Default::default(),
608 runtime_calls: Default::default(),
609 schedule: Default::default(),
610 gas_meter: GasMeter::new(Weight::from_parts(10_000_000_000, 10 * 1024 * 1024)),
611 debug_buffer: Default::default(),
612 ecdsa_recover: Default::default(),
613 caller: Default::default(),
614 sr25519_verify: Default::default(),
615 delegate_dependencies: Default::default(),
616 }
617 }
618 }
619
620 impl Ext for MockExt {
621 type T = Test;
622
623 fn call(
624 &mut self,
625 _gas_limit: Weight,
626 _deposit_limit: BalanceOf<Self::T>,
627 to: AccountIdOf<Self::T>,
628 value: u64,
629 data: Vec<u8>,
630 allows_reentry: bool,
631 read_only: bool,
632 ) -> Result<ExecReturnValue, ExecError> {
633 self.calls.push(CallEntry { to, value, data, allows_reentry, read_only });
634 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: call_return_data() })
635 }
636 fn delegate_call(
637 &mut self,
638 code_hash: CodeHash<Self::T>,
639 data: Vec<u8>,
640 ) -> Result<ExecReturnValue, ExecError> {
641 self.code_calls.push(CallCodeEntry { code_hash, data });
642 Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: call_return_data() })
643 }
644 fn instantiate(
645 &mut self,
646 gas_limit: Weight,
647 _deposit_limit: BalanceOf<Self::T>,
648 code_hash: CodeHash<Test>,
649 value: u64,
650 data: Vec<u8>,
651 salt: &[u8],
652 ) -> Result<(AccountIdOf<Self::T>, ExecReturnValue), ExecError> {
653 self.instantiates.push(InstantiateEntry {
654 code_hash,
655 value,
656 data: data.to_vec(),
657 gas_left: gas_limit.ref_time(),
658 salt: salt.to_vec(),
659 });
660 Ok((
661 Contracts::<Test>::contract_address(&ALICE, &code_hash, &data, salt),
662 ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() },
663 ))
664 }
665 fn set_code_hash(&mut self, hash: CodeHash<Self::T>) -> DispatchResult {
666 self.code_hashes.push(hash);
667 Ok(())
668 }
669 fn transfer(&mut self, to: &AccountIdOf<Self::T>, value: u64) -> DispatchResult {
670 self.transfers.push(TransferEntry { to: to.clone(), value });
671 Ok(())
672 }
673 fn terminate(&mut self, beneficiary: &AccountIdOf<Self::T>) -> DispatchResult {
674 self.terminations.push(TerminationEntry { beneficiary: beneficiary.clone() });
675 Ok(())
676 }
677 fn get_storage(&mut self, key: &Key<Self::T>) -> Option<Vec<u8>> {
678 self.storage.get(&key.to_vec()).cloned()
679 }
680 fn get_storage_size(&mut self, key: &Key<Self::T>) -> Option<u32> {
681 self.storage.get(&key.to_vec()).map(|val| val.len() as u32)
682 }
683 fn set_storage(
684 &mut self,
685 key: &Key<Self::T>,
686 value: Option<Vec<u8>>,
687 take_old: bool,
688 ) -> Result<WriteOutcome, DispatchError> {
689 let key = key.to_vec();
690 let entry = self.storage.entry(key.clone());
691 let result = match (entry, take_old) {
692 (Entry::Vacant(_), _) => WriteOutcome::New,
693 (Entry::Occupied(entry), false) =>
694 WriteOutcome::Overwritten(entry.remove().len() as u32),
695 (Entry::Occupied(entry), true) => WriteOutcome::Taken(entry.remove()),
696 };
697 if let Some(value) = value {
698 self.storage.insert(key, value);
699 }
700 Ok(result)
701 }
702 fn get_transient_storage(&self, key: &Key<Self::T>) -> Option<Vec<u8>> {
703 self.transient_storage.read(self.address(), key)
704 }
705 fn get_transient_storage_size(&self, key: &Key<Self::T>) -> Option<u32> {
706 self.transient_storage.read(self.address(), key).map(|value| value.len() as _)
707 }
708 fn set_transient_storage(
709 &mut self,
710 key: &Key<Self::T>,
711 value: Option<Vec<u8>>,
712 take_old: bool,
713 ) -> Result<WriteOutcome, DispatchError> {
714 let account_id = self.address().clone();
715 self.transient_storage.write(&account_id, key, value, take_old)
716 }
717 fn caller(&self) -> Origin<Self::T> {
718 self.caller.clone()
719 }
720 fn is_contract(&self, _address: &AccountIdOf<Self::T>) -> bool {
721 true
722 }
723 fn code_hash(&self, _address: &AccountIdOf<Self::T>) -> Option<CodeHash<Self::T>> {
724 Some(H256::from_slice(&[0x11; 32]))
725 }
726 fn own_code_hash(&mut self) -> &CodeHash<Self::T> {
727 const HASH: H256 = H256::repeat_byte(0x10);
728 &HASH
729 }
730 fn caller_is_origin(&self) -> bool {
731 false
732 }
733 fn caller_is_root(&self) -> bool {
734 &self.caller == &Origin::Root
735 }
736 fn address(&self) -> &AccountIdOf<Self::T> {
737 &BOB
738 }
739 fn balance(&self) -> u64 {
740 228
741 }
742 fn value_transferred(&self) -> u64 {
743 1337
744 }
745 fn now(&self) -> &u64 {
746 &1111
747 }
748 fn minimum_balance(&self) -> u64 {
749 666
750 }
751 fn random(&self, subject: &[u8]) -> (SeedOf<Self::T>, BlockNumberFor<Self::T>) {
752 (H256::from_slice(subject), 42)
753 }
754 fn deposit_event(&mut self, topics: Vec<H256>, data: Vec<u8>) {
755 self.events.push((topics, data))
756 }
757 fn block_number(&self) -> u64 {
758 121
759 }
760 fn max_value_size(&self) -> u32 {
761 16_384
762 }
763 fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
764 BalanceOf::<Self::T>::from(1312_u32)
765 .saturating_mul(weight.ref_time().into())
766 .saturating_add(
767 BalanceOf::<Self::T>::from(103_u32).saturating_mul(weight.proof_size()),
768 )
769 }
770 fn schedule(&self) -> &Schedule<Self::T> {
771 &self.schedule
772 }
773 fn gas_meter(&self) -> &GasMeter<Self::T> {
774 &self.gas_meter
775 }
776 fn gas_meter_mut(&mut self) -> &mut GasMeter<Self::T> {
777 &mut self.gas_meter
778 }
779 fn charge_storage(&mut self, _diff: &crate::storage::meter::Diff) {}
780
781 fn debug_buffer_enabled(&self) -> bool {
782 true
783 }
784 fn append_debug_buffer(&mut self, msg: &str) -> bool {
785 self.debug_buffer.extend(msg.as_bytes());
786 true
787 }
788 fn call_runtime(
789 &self,
790 call: <Self::T as Config>::RuntimeCall,
791 ) -> DispatchResultWithPostInfo {
792 self.runtime_calls.borrow_mut().push(call);
793 Ok(Default::default())
794 }
795 fn ecdsa_recover(
796 &self,
797 signature: &[u8; 65],
798 message_hash: &[u8; 32],
799 ) -> Result<[u8; 33], ()> {
800 self.ecdsa_recover.borrow_mut().push((*signature, *message_hash));
801 Ok([3; 33])
802 }
803 fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool {
804 self.sr25519_verify.borrow_mut().push((*signature, message.to_vec(), *pub_key));
805 true
806 }
807 fn contract_info(&mut self) -> &mut crate::ContractInfo<Self::T> {
808 unimplemented!()
809 }
810 #[cfg(feature = "runtime-benchmarks")]
811 fn transient_storage(&mut self) -> &mut TransientStorage<Self::T> {
812 unimplemented!()
813 }
814 fn ecdsa_to_eth_address(&self, _pk: &[u8; 33]) -> Result<[u8; 20], ()> {
815 Ok([2u8; 20])
816 }
817 fn reentrance_count(&self) -> u32 {
818 12
819 }
820 fn account_reentrance_count(&self, _account_id: &AccountIdOf<Self::T>) -> u32 {
821 12
822 }
823 fn nonce(&mut self) -> u64 {
824 995
825 }
826 fn increment_refcount(_code_hash: CodeHash<Self::T>) -> DispatchResult {
827 Ok(())
828 }
829 fn decrement_refcount(_code_hash: CodeHash<Self::T>) {}
830 fn lock_delegate_dependency(&mut self, code: CodeHash<Self::T>) -> DispatchResult {
831 self.delegate_dependencies.borrow_mut().insert(code);
832 Ok(())
833 }
834 fn unlock_delegate_dependency(&mut self, code: &CodeHash<Self::T>) -> DispatchResult {
835 self.delegate_dependencies.borrow_mut().remove(code);
836 Ok(())
837 }
838
839 fn locked_delegate_dependencies_count(&mut self) -> usize {
840 self.delegate_dependencies.borrow().len()
841 }
842
843 fn is_read_only(&self) -> bool {
844 false
845 }
846 }
847
848 fn execute_internal<E: BorrowMut<MockExt>>(
852 wat: &str,
853 input_data: Vec<u8>,
854 mut ext: E,
855 entry_point: &ExportedFunction,
856 unstable_interface: bool,
857 skip_checks: bool,
858 ) -> ExecResult {
859 type RuntimeConfig = <MockExt as Ext>::T;
860 RuntimeConfig::set_unstable_interface(unstable_interface);
861 let wasm = wat::parse_str(wat).unwrap();
862 let executable = if skip_checks {
863 WasmBlob::<RuntimeConfig>::from_code_unchecked(
864 wasm,
865 ext.borrow_mut().schedule(),
866 ALICE,
867 )?
868 } else {
869 WasmBlob::<RuntimeConfig>::from_code(
870 wasm,
871 ext.borrow_mut().schedule(),
872 ALICE,
873 Determinism::Enforced,
874 )
875 .map_err(|err| err.0)?
876 };
877 executable.execute(ext.borrow_mut(), entry_point, input_data)
878 }
879
880 fn execute<E: BorrowMut<MockExt>>(wat: &str, input_data: Vec<u8>, ext: E) -> ExecResult {
882 execute_internal(wat, input_data, ext, &ExportedFunction::Call, true, false)
883 }
884
885 fn execute_instantiate<E: BorrowMut<MockExt>>(
887 wat: &str,
888 input_data: Vec<u8>,
889 ext: E,
890 ) -> ExecResult {
891 execute_internal(wat, input_data, ext, &ExportedFunction::Constructor, true, false)
892 }
893
894 #[cfg(not(feature = "runtime-benchmarks"))]
900 fn execute_no_unstable<E: BorrowMut<MockExt>>(
901 wat: &str,
902 input_data: Vec<u8>,
903 ext: E,
904 ) -> ExecResult {
905 execute_internal(wat, input_data, ext, &ExportedFunction::Call, false, false)
906 }
907
908 fn execute_unvalidated<E: BorrowMut<MockExt>>(
913 wat: &str,
914 input_data: Vec<u8>,
915 ext: E,
916 ) -> ExecResult {
917 execute_internal(wat, input_data, ext, &ExportedFunction::Call, false, true)
918 }
919
920 #[cfg(not(feature = "runtime-benchmarks"))]
924 fn execute_instantiate_unvalidated<E: BorrowMut<MockExt>>(
925 wat: &str,
926 input_data: Vec<u8>,
927 ext: E,
928 ) -> ExecResult {
929 execute_internal(wat, input_data, ext, &ExportedFunction::Constructor, false, true)
930 }
931
932 const CODE_TRANSFER: &str = r#"
933(module
934 ;; seal_transfer(
935 ;; account_ptr: u32,
936 ;; account_len: u32,
937 ;; value_ptr: u32,
938 ;; value_len: u32,
939 ;;) -> u32
940 (import "seal0" "seal_transfer" (func $seal_transfer (param i32 i32 i32 i32) (result i32)))
941 (import "env" "memory" (memory 1 1))
942 (func (export "call")
943 (drop
944 (call $seal_transfer
945 (i32.const 4) ;; Pointer to "account" address.
946 (i32.const 32) ;; Length of "account" address.
947 (i32.const 36) ;; Pointer to the buffer with value to transfer
948 (i32.const 8) ;; Length of the buffer with value to transfer.
949 )
950 )
951 )
952 (func (export "deploy"))
953
954 ;; Destination AccountId (ALICE)
955 (data (i32.const 4)
956 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
957 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
958 )
959
960 ;; Amount of value to transfer.
961 ;; Represented by u64 (8 bytes long) in little endian.
962 (data (i32.const 36) "\99\00\00\00\00\00\00\00")
963)
964"#;
965
966 #[test]
967 fn contract_transfer() {
968 let mut mock_ext = MockExt::default();
969 assert_ok!(execute(CODE_TRANSFER, vec![], &mut mock_ext));
970
971 assert_eq!(&mock_ext.transfers, &[TransferEntry { to: ALICE, value: 153 }]);
972 }
973
974 const CODE_CALL: &str = r#"
975(module
976 ;; seal_call(
977 ;; callee_ptr: u32,
978 ;; callee_len: u32,
979 ;; gas: u64,
980 ;; value_ptr: u32,
981 ;; value_len: u32,
982 ;; input_data_ptr: u32,
983 ;; input_data_len: u32,
984 ;; output_ptr: u32,
985 ;; output_len_ptr: u32
986 ;;) -> u32
987 (import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
988 (import "env" "memory" (memory 1 1))
989 (func (export "call")
990 (drop
991 (call $seal_call
992 (i32.const 4) ;; Pointer to "callee" address.
993 (i32.const 32) ;; Length of "callee" address.
994 (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
995 (i32.const 36) ;; Pointer to the buffer with value to transfer
996 (i32.const 8) ;; Length of the buffer with value to transfer.
997 (i32.const 44) ;; Pointer to input data buffer address
998 (i32.const 4) ;; Length of input data buffer
999 (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
1000 (i32.const 0) ;; Length is ignored in this case
1001 )
1002 )
1003 )
1004 (func (export "deploy"))
1005
1006 ;; Destination AccountId (ALICE)
1007 (data (i32.const 4)
1008 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1009 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1010 )
1011
1012 ;; Amount of value to transfer.
1013 ;; Represented by u64 (8 bytes long) in little endian.
1014 (data (i32.const 36) "\06\00\00\00\00\00\00\00")
1015
1016 (data (i32.const 44) "\01\02\03\04")
1017)
1018"#;
1019
1020 #[test]
1021 fn contract_call() {
1022 let mut mock_ext = MockExt::default();
1023 assert_ok!(execute(CODE_CALL, vec![], &mut mock_ext));
1024
1025 assert_eq!(
1026 &mock_ext.calls,
1027 &[CallEntry {
1028 to: ALICE,
1029 value: 6,
1030 data: vec![1, 2, 3, 4],
1031 allows_reentry: true,
1032 read_only: false
1033 }]
1034 );
1035 }
1036
1037 #[test]
1038 fn contract_delegate_call() {
1039 const CODE: &str = r#"
1040(module
1041 ;; seal_delegate_call(
1042 ;; flags: u32,
1043 ;; code_hash_ptr: u32,
1044 ;; input_data_ptr: u32,
1045 ;; input_data_len: u32,
1046 ;; output_ptr: u32,
1047 ;; output_len_ptr: u32
1048 ;;) -> u32
1049 (import "seal0" "seal_delegate_call" (func $seal_delegate_call (param i32 i32 i32 i32 i32 i32) (result i32)))
1050 (import "env" "memory" (memory 1 1))
1051 (func (export "call")
1052 (drop
1053 (call $seal_delegate_call
1054 (i32.const 0) ;; No flags are set
1055 (i32.const 4) ;; Pointer to "callee" code_hash.
1056 (i32.const 36) ;; Pointer to input data buffer address
1057 (i32.const 4) ;; Length of input data buffer
1058 (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
1059 (i32.const 0) ;; Length is ignored in this case
1060 )
1061 )
1062 )
1063 (func (export "deploy"))
1064
1065 ;; Callee code_hash
1066 (data (i32.const 4)
1067 "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
1068 "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
1069 )
1070
1071 (data (i32.const 36) "\01\02\03\04")
1072)
1073"#;
1074 let mut mock_ext = MockExt::default();
1075 assert_ok!(execute(CODE, vec![], &mut mock_ext));
1076
1077 assert_eq!(
1078 &mock_ext.code_calls,
1079 &[CallCodeEntry { code_hash: [0x11; 32].into(), data: vec![1, 2, 3, 4] }]
1080 );
1081 }
1082
1083 #[test]
1084 fn contract_call_forward_input() {
1085 const CODE: &str = r#"
1086(module
1087 (import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32)))
1088 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
1089 (import "env" "memory" (memory 1 1))
1090 (func (export "call")
1091 (drop
1092 (call $seal_call
1093 (i32.const 1) ;; Set FORWARD_INPUT bit
1094 (i32.const 4) ;; Pointer to "callee" address.
1095 (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
1096 (i32.const 36) ;; Pointer to the buffer with value to transfer
1097 (i32.const 44) ;; Pointer to input data buffer address
1098 (i32.const 4) ;; Length of input data buffer
1099 (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
1100 (i32.const 0) ;; Length is ignored in this case
1101 )
1102 )
1103
1104 ;; triggers a trap because we already forwarded the input
1105 (call $seal_input (i32.const 1) (i32.const 44))
1106 )
1107
1108 (func (export "deploy"))
1109
1110 ;; Destination AccountId (ALICE)
1111 (data (i32.const 4)
1112 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1113 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1114 )
1115
1116 ;; Amount of value to transfer.
1117 ;; Represented by u64 (8 bytes long) in little endian.
1118 (data (i32.const 36) "\2A\00\00\00\00\00\00\00")
1119
1120 ;; The input is ignored because we forward our own input
1121 (data (i32.const 44) "\01\02\03\04")
1122)
1123"#;
1124 let mut mock_ext = MockExt::default();
1125 let input = vec![0xff, 0x2a, 0x99, 0x88];
1126 assert_err!(execute(CODE, input.clone(), &mut mock_ext), <Error<Test>>::InputForwarded,);
1127
1128 assert_eq!(
1129 &mock_ext.calls,
1130 &[CallEntry {
1131 to: ALICE,
1132 value: 0x2a,
1133 data: input,
1134 allows_reentry: false,
1135 read_only: false
1136 }]
1137 );
1138 }
1139
1140 #[test]
1141 fn contract_call_clone_input() {
1142 const CODE: &str = r#"
1143(module
1144 (import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32)))
1145 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
1146 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
1147 (import "env" "memory" (memory 1 1))
1148 (func (export "call")
1149 (drop
1150 (call $seal_call
1151 (i32.const 11) ;; Set FORWARD_INPUT | CLONE_INPUT | ALLOW_REENTRY bits
1152 (i32.const 4) ;; Pointer to "callee" address.
1153 (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
1154 (i32.const 36) ;; Pointer to the buffer with value to transfer
1155 (i32.const 44) ;; Pointer to input data buffer address
1156 (i32.const 4) ;; Length of input data buffer
1157 (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
1158 (i32.const 0) ;; Length is ignored in this case
1159 )
1160 )
1161
1162 ;; works because the input was cloned
1163 (call $seal_input (i32.const 0) (i32.const 44))
1164
1165 ;; return the input to caller for inspection
1166 (call $seal_return (i32.const 0) (i32.const 0) (i32.load (i32.const 44)))
1167 )
1168
1169 (func (export "deploy"))
1170
1171 ;; Destination AccountId (ALICE)
1172 (data (i32.const 4)
1173 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1174 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1175 )
1176
1177 ;; Amount of value to transfer.
1178 ;; Represented by u64 (8 bytes long) in little endian.
1179 (data (i32.const 36) "\2A\00\00\00\00\00\00\00")
1180
1181 ;; The input is ignored because we forward our own input
1182 (data (i32.const 44) "\01\02\03\04")
1183)
1184"#;
1185 let mut mock_ext = MockExt::default();
1186 let input = vec![0xff, 0x2a, 0x99, 0x88];
1187 let result = execute(CODE, input.clone(), &mut mock_ext).unwrap();
1188 assert_eq!(result.data, input);
1189 assert_eq!(
1190 &mock_ext.calls,
1191 &[CallEntry {
1192 to: ALICE,
1193 value: 0x2a,
1194 data: input,
1195 allows_reentry: true,
1196 read_only: false
1197 }]
1198 );
1199 }
1200
1201 #[test]
1202 fn contract_call_tail_call() {
1203 const CODE: &str = r#"
1204(module
1205 (import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32)))
1206 (import "env" "memory" (memory 1 1))
1207 (func (export "call")
1208 (drop
1209 (call $seal_call
1210 (i32.const 5) ;; Set FORWARD_INPUT | TAIL_CALL bit
1211 (i32.const 4) ;; Pointer to "callee" address.
1212 (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
1213 (i32.const 36) ;; Pointer to the buffer with value to transfer
1214 (i32.const 0) ;; Pointer to input data buffer address
1215 (i32.const 0) ;; Length of input data buffer
1216 (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
1217 (i32.const 0) ;; Length is ignored in this case
1218 )
1219 )
1220
1221 ;; a tail call never returns
1222 (unreachable)
1223 )
1224
1225 (func (export "deploy"))
1226
1227 ;; Destination AccountId (ALICE)
1228 (data (i32.const 4)
1229 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1230 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1231 )
1232
1233 ;; Amount of value to transfer.
1234 ;; Represented by u64 (8 bytes long) in little endian.
1235 (data (i32.const 36) "\2A\00\00\00\00\00\00\00")
1236)
1237"#;
1238 let mut mock_ext = MockExt::default();
1239 let input = vec![0xff, 0x2a, 0x99, 0x88];
1240 let result = execute(CODE, input.clone(), &mut mock_ext).unwrap();
1241 assert_eq!(result.data, call_return_data());
1242 assert_eq!(
1243 &mock_ext.calls,
1244 &[CallEntry {
1245 to: ALICE,
1246 value: 0x2a,
1247 data: input,
1248 allows_reentry: false,
1249 read_only: false
1250 }]
1251 );
1252 }
1253
1254 #[test]
1255 fn contains_storage_works() {
1256 const CODE: &str = r#"
1257(module
1258 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
1259 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
1260 (import "seal1" "contains_storage" (func $contains_storage (param i32 i32) (result i32)))
1261 (import "env" "memory" (memory 1 1))
1262
1263
1264 ;; size of input buffer
1265 ;; [0, 4) size of input buffer (128+32 = 160 bytes = 0xA0)
1266 (data (i32.const 0) "\A0")
1267
1268 ;; [4, 164) input buffer
1269
1270 (func (export "call")
1271 ;; Receive key
1272 (call $seal_input
1273 (i32.const 4) ;; Where we take input and store it
1274 (i32.const 0) ;; Where we take and store the length of the data
1275 )
1276 ;; Call seal_clear_storage and save what it returns at 0
1277 (i32.store (i32.const 0)
1278 (call $contains_storage
1279 (i32.const 8) ;; key_ptr
1280 (i32.load (i32.const 4)) ;; key_len
1281 )
1282 )
1283 (call $seal_return
1284 (i32.const 0) ;; flags
1285 (i32.const 0) ;; returned value
1286 (i32.const 4) ;; length of returned value
1287 )
1288 )
1289
1290 (func (export "deploy"))
1291)
1292"#;
1293
1294 let mut ext = MockExt::default();
1295 ext.set_storage(
1296 &Key::<Test>::try_from_var([1u8; 64].to_vec()).unwrap(),
1297 Some(vec![42u8]),
1298 false,
1299 )
1300 .unwrap();
1301 ext.set_storage(
1302 &Key::<Test>::try_from_var([2u8; 19].to_vec()).unwrap(),
1303 Some(vec![]),
1304 false,
1305 )
1306 .unwrap();
1307
1308 let input = (63, [1u8; 64]).encode();
1310 let result = execute(CODE, input, &mut ext).unwrap();
1311 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), crate::SENTINEL);
1313
1314 let input = (64, [1u8; 64]).encode();
1316 let result = execute(CODE, input, &mut ext).unwrap();
1317 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 1);
1319 assert_eq!(ext.storage.get(&[1u8; 64].to_vec()).unwrap(), &[42u8]);
1321
1322 let input = (19, [2u8; 19]).encode();
1324 let result = execute(CODE, input, &mut ext).unwrap();
1325 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 0);
1327 assert_eq!(ext.storage.get(&[2u8; 19].to_vec()).unwrap(), &([] as [u8; 0]));
1329 }
1330
1331 const CODE_INSTANTIATE: &str = r#"
1332(module
1333 ;; seal_instantiate(
1334 ;; code_ptr: u32,
1335 ;; code_len: u32,
1336 ;; gas: u64,
1337 ;; value_ptr: u32,
1338 ;; value_len: u32,
1339 ;; input_data_ptr: u32,
1340 ;; input_data_len: u32,
1341 ;; input_data_len: u32,
1342 ;; address_ptr: u32,
1343 ;; address_len_ptr: u32,
1344 ;; output_ptr: u32,
1345 ;; output_len_ptr: u32
1346 ;; ) -> u32
1347 (import "seal0" "seal_instantiate" (func $seal_instantiate
1348 (param i32 i32 i64 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)
1349 ))
1350 (import "env" "memory" (memory 1 1))
1351 (func (export "call")
1352 (drop
1353 (call $seal_instantiate
1354 (i32.const 16) ;; Pointer to `code_hash`
1355 (i32.const 32) ;; Length of `code_hash`
1356 (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
1357 (i32.const 4) ;; Pointer to the buffer with value to transfer
1358 (i32.const 8) ;; Length of the buffer with value to transfer
1359 (i32.const 12) ;; Pointer to input data buffer address
1360 (i32.const 4) ;; Length of input data buffer
1361 (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy address
1362 (i32.const 0) ;; Length is ignored in this case
1363 (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
1364 (i32.const 0) ;; Length is ignored in this case
1365 (i32.const 0) ;; salt_ptr
1366 (i32.const 4) ;; salt_len
1367 )
1368 )
1369 )
1370 (func (export "deploy"))
1371
1372 ;; Salt
1373 (data (i32.const 0) "\42\43\44\45")
1374 ;; Amount of value to transfer.
1375 ;; Represented by u64 (8 bytes long) in little endian.
1376 (data (i32.const 4) "\03\00\00\00\00\00\00\00")
1377 ;; Input data to pass to the contract being instantiated.
1378 (data (i32.const 12) "\01\02\03\04")
1379 ;; Hash of code.
1380 (data (i32.const 16)
1381 "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
1382 "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
1383 )
1384)
1385"#;
1386
1387 #[test]
1388 fn contract_instantiate() {
1389 let mut mock_ext = MockExt::default();
1390 assert_ok!(execute(CODE_INSTANTIATE, vec![], &mut mock_ext));
1391
1392 assert_matches!(
1393 &mock_ext.instantiates[..],
1394 [InstantiateEntry {
1395 code_hash,
1396 value: 3,
1397 data,
1398 gas_left: _,
1399 salt,
1400 }] if
1401 code_hash == &[0x11; 32].into() &&
1402 data == &vec![1, 2, 3, 4] &&
1403 salt == &vec![0x42, 0x43, 0x44, 0x45]
1404 );
1405 }
1406
1407 const CODE_TERMINATE: &str = r#"
1408(module
1409 ;; seal_terminate(
1410 ;; beneficiary_ptr: u32,
1411 ;; beneficiary_len: u32,
1412 ;; )
1413 (import "seal0" "seal_terminate" (func $seal_terminate (param i32 i32)))
1414 (import "env" "memory" (memory 1 1))
1415 (func (export "call")
1416 (call $seal_terminate
1417 (i32.const 4) ;; Pointer to "beneficiary" address.
1418 (i32.const 32) ;; Length of "beneficiary" address.
1419 )
1420 )
1421 (func (export "deploy"))
1422
1423 ;; Beneficiary AccountId to transfer the funds.
1424 (data (i32.const 4)
1425 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1426 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1427 )
1428)
1429"#;
1430
1431 #[test]
1432 fn contract_terminate() {
1433 let mut mock_ext = MockExt::default();
1434 execute(CODE_TERMINATE, vec![], &mut mock_ext).unwrap();
1435
1436 assert_eq!(&mock_ext.terminations, &[TerminationEntry { beneficiary: ALICE }]);
1437 }
1438
1439 const CODE_TRANSFER_LIMITED_GAS: &str = r#"
1440(module
1441 ;; seal_call(
1442 ;; callee_ptr: u32,
1443 ;; callee_len: u32,
1444 ;; gas: u64,
1445 ;; value_ptr: u32,
1446 ;; value_len: u32,
1447 ;; input_data_ptr: u32,
1448 ;; input_data_len: u32,
1449 ;; output_ptr: u32,
1450 ;; output_len_ptr: u32
1451 ;;) -> u32
1452 (import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
1453 (import "env" "memory" (memory 1 1))
1454 (func (export "call")
1455 (drop
1456 (call $seal_call
1457 (i32.const 4) ;; Pointer to "callee" address.
1458 (i32.const 32) ;; Length of "callee" address.
1459 (i64.const 228) ;; How much gas to devote for the execution.
1460 (i32.const 36) ;; Pointer to the buffer with value to transfer
1461 (i32.const 8) ;; Length of the buffer with value to transfer.
1462 (i32.const 44) ;; Pointer to input data buffer address
1463 (i32.const 4) ;; Length of input data buffer
1464 (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
1465 (i32.const 0) ;; Length is ignored in this cas
1466 )
1467 )
1468 )
1469 (func (export "deploy"))
1470
1471 ;; Destination AccountId to transfer the funds.
1472 (data (i32.const 4)
1473 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1474 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1475 )
1476 ;; Amount of value to transfer.
1477 ;; Represented by u64 (8 bytes long) in little endian.
1478 (data (i32.const 36) "\06\00\00\00\00\00\00\00")
1479
1480 (data (i32.const 44) "\01\02\03\04")
1481)
1482"#;
1483
1484 #[test]
1485 fn contract_call_limited_gas() {
1486 let mut mock_ext = MockExt::default();
1487 assert_ok!(execute(&CODE_TRANSFER_LIMITED_GAS, vec![], &mut mock_ext));
1488
1489 assert_eq!(
1490 &mock_ext.calls,
1491 &[CallEntry {
1492 to: ALICE,
1493 value: 6,
1494 data: vec![1, 2, 3, 4],
1495 allows_reentry: true,
1496 read_only: false
1497 }]
1498 );
1499 }
1500
1501 const CODE_ECDSA_RECOVER: &str = r#"
1502(module
1503 ;; seal_ecdsa_recover(
1504 ;; signature_ptr: u32,
1505 ;; message_hash_ptr: u32,
1506 ;; output_ptr: u32
1507 ;; ) -> u32
1508 (import "seal0" "seal_ecdsa_recover" (func $seal_ecdsa_recover (param i32 i32 i32) (result i32)))
1509 (import "env" "memory" (memory 1 1))
1510 (func (export "call")
1511 (drop
1512 (call $seal_ecdsa_recover
1513 (i32.const 36) ;; Pointer to signature.
1514 (i32.const 4) ;; Pointer to message hash.
1515 (i32.const 36) ;; Pointer for output - public key.
1516 )
1517 )
1518 )
1519 (func (export "deploy"))
1520
1521 ;; Hash of message.
1522 (data (i32.const 4)
1523 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1524 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1525 )
1526 ;; Signature
1527 (data (i32.const 36)
1528 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1529 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1530 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1531 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1532 "\01"
1533 )
1534)
1535"#;
1536
1537 #[test]
1538 fn contract_ecdsa_recover() {
1539 let mut mock_ext = MockExt::default();
1540 assert_ok!(execute(&CODE_ECDSA_RECOVER, vec![], &mut mock_ext));
1541 assert_eq!(mock_ext.ecdsa_recover.into_inner(), [([1; 65], [1; 32])]);
1542 }
1543
1544 #[test]
1545 fn contract_ecdsa_to_eth_address() {
1546 const CODE_ECDSA_TO_ETH_ADDRESS: &str = r#"
1549(module
1550 (import "seal0" "seal_ecdsa_to_eth_address" (func $seal_ecdsa_to_eth_address (param i32 i32) (result i32)))
1551 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
1552 (import "env" "memory" (memory 1 1))
1553
1554 (func (export "call")
1555 ;; fill the buffer with the eth address.
1556 (call $seal_ecdsa_to_eth_address (i32.const 0) (i32.const 0))
1557
1558 ;; Return the contents of the buffer
1559 (call $seal_return
1560 (i32.const 0)
1561 (i32.const 0)
1562 (i32.const 20)
1563 )
1564
1565 ;; seal_return doesn't return, so this is effectively unreachable.
1566 (unreachable)
1567 )
1568 (func (export "deploy"))
1569)
1570"#;
1571
1572 let output = execute(CODE_ECDSA_TO_ETH_ADDRESS, vec![], MockExt::default()).unwrap();
1573 assert_eq!(
1574 output,
1575 ExecReturnValue { flags: ReturnFlags::empty(), data: [0x02; 20].to_vec() }
1576 );
1577 }
1578
1579 #[test]
1580 fn contract_sr25519() {
1581 const CODE_SR25519: &str = r#"
1582(module
1583 (import "seal0" "sr25519_verify" (func $sr25519_verify (param i32 i32 i32 i32) (result i32)))
1584 (import "env" "memory" (memory 1 1))
1585 (func (export "call")
1586 (drop
1587 (call $sr25519_verify
1588 (i32.const 0) ;; Pointer to signature.
1589 (i32.const 64) ;; Pointer to public key.
1590 (i32.const 16) ;; message length.
1591 (i32.const 96) ;; Pointer to message.
1592 )
1593 )
1594 )
1595 (func (export "deploy"))
1596
1597 ;; Signature (64 bytes)
1598 (data (i32.const 0)
1599 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1600 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1601 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1602 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1603 )
1604
1605 ;; public key (32 bytes)
1606 (data (i32.const 64)
1607 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1608 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1609 )
1610
1611 ;; message. (16 bytes)
1612 (data (i32.const 96)
1613 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
1614 )
1615)
1616"#;
1617 let mut mock_ext = MockExt::default();
1618 assert_ok!(execute(&CODE_SR25519, vec![], &mut mock_ext));
1619 assert_eq!(mock_ext.sr25519_verify.into_inner(), [([1; 64], [1; 16].to_vec(), [1; 32])]);
1620 }
1621
1622 const CODE_GET_STORAGE: &str = r#"
1623(module
1624 (import "seal0" "seal_get_storage" (func $seal_get_storage (param i32 i32 i32) (result i32)))
1625 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
1626 (import "env" "memory" (memory 1 1))
1627
1628 ;; [0, 32) key for get storage
1629 (data (i32.const 0)
1630 "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
1631 "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
1632 )
1633
1634 ;; [32, 36) buffer size = 4k in little endian
1635 (data (i32.const 32) "\00\10")
1636
1637 ;; [36; inf) buffer where the result is copied
1638
1639 (func $assert (param i32)
1640 (block $ok
1641 (br_if $ok
1642 (local.get 0)
1643 )
1644 (unreachable)
1645 )
1646 )
1647
1648 (func (export "call")
1649 (local $buf_size i32)
1650
1651 ;; Load a storage value into contract memory.
1652 (call $assert
1653 (i32.eq
1654 (call $seal_get_storage
1655 (i32.const 0) ;; The pointer to the storage key to fetch
1656 (i32.const 36) ;; Pointer to the output buffer
1657 (i32.const 32) ;; Pointer to the size of the buffer
1658 )
1659
1660 ;; Return value 0 means that the value is found and there were
1661 ;; no errors.
1662 (i32.const 0)
1663 )
1664 )
1665
1666 ;; Find out the size of the buffer
1667 (local.set $buf_size
1668 (i32.load (i32.const 32))
1669 )
1670
1671 ;; Return the contents of the buffer
1672 (call $seal_return
1673 (i32.const 0)
1674 (i32.const 36)
1675 (local.get $buf_size)
1676 )
1677
1678 ;; env:seal_return doesn't return, so this is effectively unreachable.
1679 (unreachable)
1680 )
1681
1682 (func (export "deploy"))
1683)
1684"#;
1685
1686 #[test]
1687 fn get_storage_puts_data_into_buf() {
1688 let mut mock_ext = MockExt::default();
1689 mock_ext.storage.insert([0x11; 32].to_vec(), [0x22; 32].to_vec());
1690
1691 let output = execute(CODE_GET_STORAGE, vec![], mock_ext).unwrap();
1692
1693 assert_eq!(
1694 output,
1695 ExecReturnValue { flags: ReturnFlags::empty(), data: [0x22; 32].to_vec() }
1696 );
1697 }
1698
1699 const CODE_CALLER: &str = r#"
1701(module
1702 (import "seal0" "seal_caller" (func $seal_caller (param i32 i32)))
1703 (import "env" "memory" (memory 1 1))
1704
1705 ;; size of our buffer is 32 bytes
1706 (data (i32.const 32) "\20")
1707
1708 (func $assert (param i32)
1709 (block $ok
1710 (br_if $ok
1711 (local.get 0)
1712 )
1713 (unreachable)
1714 )
1715 )
1716
1717 (func (export "call")
1718 ;; fill the buffer with the caller.
1719 (call $seal_caller (i32.const 0) (i32.const 32))
1720
1721 ;; assert len == 32
1722 (call $assert
1723 (i32.eq
1724 (i32.load (i32.const 32))
1725 (i32.const 32)
1726 )
1727 )
1728
1729 ;; assert that the first 8 bytes are the beginning of "ALICE"
1730 (call $assert
1731 (i64.eq
1732 (i64.load (i32.const 0))
1733 (i64.const 0x0101010101010101)
1734 )
1735 )
1736 )
1737
1738 (func (export "deploy"))
1739)
1740"#;
1741
1742 #[test]
1743 fn caller() {
1744 assert_ok!(execute(CODE_CALLER, vec![], MockExt::default()));
1745 }
1746
1747 #[test]
1748 fn caller_traps_when_no_account_id() {
1749 let mut ext = MockExt::default();
1750 ext.caller = Origin::Root;
1751 assert_eq!(
1752 execute(CODE_CALLER, vec![], ext),
1753 Err(ExecError { error: DispatchError::RootNotAllowed, origin: ErrorOrigin::Caller })
1754 );
1755 }
1756
1757 const CODE_ADDRESS: &str = r#"
1759(module
1760 (import "seal0" "seal_address" (func $seal_address (param i32 i32)))
1761 (import "env" "memory" (memory 1 1))
1762
1763 ;; size of our buffer is 32 bytes
1764 (data (i32.const 32) "\20")
1765
1766 (func $assert (param i32)
1767 (block $ok
1768 (br_if $ok
1769 (local.get 0)
1770 )
1771 (unreachable)
1772 )
1773 )
1774
1775 (func (export "call")
1776 ;; fill the buffer with the self address.
1777 (call $seal_address (i32.const 0) (i32.const 32))
1778
1779 ;; assert size == 32
1780 (call $assert
1781 (i32.eq
1782 (i32.load (i32.const 32))
1783 (i32.const 32)
1784 )
1785 )
1786
1787 ;; assert that the first 8 bytes are the beginning of "BOB"
1788 (call $assert
1789 (i64.eq
1790 (i64.load (i32.const 0))
1791 (i64.const 0x0202020202020202)
1792 )
1793 )
1794 )
1795
1796 (func (export "deploy"))
1797)
1798"#;
1799
1800 #[test]
1801 fn address() {
1802 assert_ok!(execute(CODE_ADDRESS, vec![], MockExt::default()));
1803 }
1804
1805 const CODE_BALANCE: &str = r#"
1806(module
1807 (import "seal0" "seal_balance" (func $seal_balance (param i32 i32)))
1808 (import "env" "memory" (memory 1 1))
1809
1810 ;; size of our buffer is 32 bytes
1811 (data (i32.const 32) "\20")
1812
1813 (func $assert (param i32)
1814 (block $ok
1815 (br_if $ok
1816 (local.get 0)
1817 )
1818 (unreachable)
1819 )
1820 )
1821
1822 (func (export "call")
1823 ;; This stores the balance in the buffer
1824 (call $seal_balance (i32.const 0) (i32.const 32))
1825
1826 ;; assert len == 8
1827 (call $assert
1828 (i32.eq
1829 (i32.load (i32.const 32))
1830 (i32.const 8)
1831 )
1832 )
1833
1834 ;; assert that contents of the buffer is equal to the i64 value of 228.
1835 (call $assert
1836 (i64.eq
1837 (i64.load (i32.const 0))
1838 (i64.const 228)
1839 )
1840 )
1841 )
1842 (func (export "deploy"))
1843)
1844"#;
1845
1846 #[test]
1847 fn balance() {
1848 assert_ok!(execute(CODE_BALANCE, vec![], MockExt::default()));
1849 }
1850
1851 const CODE_GAS_PRICE: &str = r#"
1852(module
1853 (import "seal1" "weight_to_fee" (func $seal_weight_to_fee (param i64 i64 i32 i32)))
1854 (import "env" "memory" (memory 1 1))
1855
1856 ;; size of our buffer is 32 bytes
1857 (data (i32.const 32) "\20")
1858
1859 (func $assert (param i32)
1860 (block $ok
1861 (br_if $ok
1862 (local.get 0)
1863 )
1864 (unreachable)
1865 )
1866 )
1867
1868 (func (export "call")
1869 ;; This stores the gas price in the buffer
1870 (call $seal_weight_to_fee (i64.const 2) (i64.const 1) (i32.const 0) (i32.const 32))
1871
1872 ;; assert len == 8
1873 (call $assert
1874 (i32.eq
1875 (i32.load (i32.const 32))
1876 (i32.const 8)
1877 )
1878 )
1879
1880 ;; assert that contents of the buffer is equal to the i64 value of 2 * 1312 + 103 = 2727.
1881 (call $assert
1882 (i64.eq
1883 (i64.load (i32.const 0))
1884 (i64.const 2727)
1885 )
1886 )
1887 )
1888 (func (export "deploy"))
1889)
1890"#;
1891
1892 #[test]
1893 fn gas_price() {
1894 assert_ok!(execute(CODE_GAS_PRICE, vec![], MockExt::default()));
1895 }
1896
1897 const CODE_GAS_LEFT: &str = r#"
1898(module
1899 (import "seal1" "gas_left" (func $seal_gas_left (param i32 i32)))
1900 (import "seal0" "clear_storage" (func $clear_storage (param i32)))
1901 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
1902 (import "env" "memory" (memory 1 1))
1903
1904 ;; Make output buffer size 20 bytes
1905 (data (i32.const 20) "\14")
1906
1907 (func $assert (param i32)
1908 (block $ok
1909 (br_if $ok
1910 (local.get 0)
1911 )
1912 (unreachable)
1913 )
1914 )
1915
1916 (func (export "call")
1917 ;; Burn some PoV, clear_storage consumes some PoV as in order to clear the storage we need to we need to read its size first.
1918 (call $clear_storage (i32.const 0))
1919
1920 ;; This stores the weight left to the buffer
1921 (call $seal_gas_left (i32.const 0) (i32.const 20))
1922
1923 ;; Assert len <= 16 (max encoded Weight len)
1924 (call $assert
1925 (i32.le_u
1926 (i32.load (i32.const 20))
1927 (i32.const 16)
1928 )
1929 )
1930
1931 ;; Burn some PoV, clear_storage consumes some PoV as in order to clear the storage we need to we need to read its size first.
1932 (call $clear_storage (i32.const 0))
1933
1934 ;; Return weight left and its encoded value len
1935 (call $seal_return (i32.const 0) (i32.const 0) (i32.load (i32.const 20)))
1936
1937 (unreachable)
1938 )
1939 (func (export "deploy"))
1940)
1941"#;
1942
1943 #[test]
1944 fn gas_left() {
1945 let mut ext = MockExt::default();
1946 let gas_limit = ext.gas_meter.gas_left();
1947
1948 let output = execute(CODE_GAS_LEFT, vec![], &mut ext).unwrap();
1949
1950 let weight_left = Weight::decode(&mut &*output.data).unwrap();
1951 let actual_left = ext.gas_meter.gas_left();
1952
1953 assert!(weight_left.all_lt(gas_limit), "gas_left must be less than initial");
1954 assert!(weight_left.all_gt(actual_left), "gas_left must be greater than final");
1955 }
1956
1957 const CODE_VALUE_TRANSFERRED: &str = r#"
1958(module
1959 (import "seal0" "seal_value_transferred" (func $seal_value_transferred (param i32 i32)))
1960 (import "env" "memory" (memory 1 1))
1961
1962 ;; size of our buffer is 32 bytes
1963 (data (i32.const 32) "\20")
1964
1965 (func $assert (param i32)
1966 (block $ok
1967 (br_if $ok
1968 (local.get 0)
1969 )
1970 (unreachable)
1971 )
1972 )
1973
1974 (func (export "call")
1975 ;; This stores the value transferred in the buffer
1976 (call $seal_value_transferred (i32.const 0) (i32.const 32))
1977
1978 ;; assert len == 8
1979 (call $assert
1980 (i32.eq
1981 (i32.load (i32.const 32))
1982 (i32.const 8)
1983 )
1984 )
1985
1986 ;; assert that contents of the buffer is equal to the i64 value of 1337.
1987 (call $assert
1988 (i64.eq
1989 (i64.load (i32.const 0))
1990 (i64.const 1337)
1991 )
1992 )
1993 )
1994 (func (export "deploy"))
1995)
1996"#;
1997
1998 #[test]
1999 fn value_transferred() {
2000 assert_ok!(execute(CODE_VALUE_TRANSFERRED, vec![], MockExt::default()));
2001 }
2002
2003 const START_FN_DOES_RUN: &str = r#"
2004(module
2005 (import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32)))
2006 (import "env" "memory" (memory 1 1))
2007
2008 (start $start)
2009 (func $start
2010 (call $seal_deposit_event
2011 (i32.const 0) ;; Pointer to the start of topics buffer
2012 (i32.const 0) ;; The length of the topics buffer.
2013 (i32.const 0) ;; Pointer to the start of the data buffer
2014 (i32.const 13) ;; Length of the buffer
2015 )
2016 )
2017
2018 (func (export "call"))
2019
2020 (func (export "deploy"))
2021
2022 (data (i32.const 0) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00")
2023)
2024"#;
2025
2026 #[test]
2027 fn start_fn_does_run_on_call() {
2028 let mut ext = MockExt::default();
2029 execute(START_FN_DOES_RUN, vec![], &mut ext).unwrap();
2030 assert_eq!(
2031 ext.events[0].1,
2032 [0x00_u8, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00]
2033 );
2034 }
2035
2036 #[test]
2037 fn start_fn_does_run_on_deploy() {
2038 let mut ext = MockExt::default();
2039 execute_instantiate(START_FN_DOES_RUN, vec![], &mut ext).unwrap();
2040 assert_eq!(
2041 ext.events[0].1,
2042 [0x00_u8, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00]
2043 );
2044 }
2045
2046 const CODE_TIMESTAMP_NOW: &str = r#"
2047(module
2048 (import "seal0" "seal_now" (func $seal_now (param i32 i32)))
2049 (import "env" "memory" (memory 1 1))
2050
2051 ;; size of our buffer is 32 bytes
2052 (data (i32.const 32) "\20")
2053
2054 (func $assert (param i32)
2055 (block $ok
2056 (br_if $ok
2057 (local.get 0)
2058 )
2059 (unreachable)
2060 )
2061 )
2062
2063 (func (export "call")
2064 ;; This stores the block timestamp in the buffer
2065 (call $seal_now (i32.const 0) (i32.const 32))
2066
2067 ;; assert len == 8
2068 (call $assert
2069 (i32.eq
2070 (i32.load (i32.const 32))
2071 (i32.const 8)
2072 )
2073 )
2074
2075 ;; assert that contents of the buffer is equal to the i64 value of 1111.
2076 (call $assert
2077 (i64.eq
2078 (i64.load (i32.const 0))
2079 (i64.const 1111)
2080 )
2081 )
2082 )
2083 (func (export "deploy"))
2084)
2085"#;
2086
2087 const CODE_TIMESTAMP_NOW_UNPREFIXED: &str = r#"
2088(module
2089 (import "seal0" "now" (func $now (param i32 i32)))
2090 (import "env" "memory" (memory 1 1))
2091
2092 ;; size of our buffer is 32 bytes
2093 (data (i32.const 32) "\20")
2094
2095 (func $assert (param i32)
2096 (block $ok
2097 (br_if $ok
2098 (local.get 0)
2099 )
2100 (unreachable)
2101 )
2102 )
2103
2104 (func (export "call")
2105 ;; This stores the block timestamp in the buffer
2106 (call $now (i32.const 0) (i32.const 32))
2107
2108 ;; assert len == 8
2109 (call $assert
2110 (i32.eq
2111 (i32.load (i32.const 32))
2112 (i32.const 8)
2113 )
2114 )
2115
2116 ;; assert that contents of the buffer is equal to the i64 value of 1111.
2117 (call $assert
2118 (i64.eq
2119 (i64.load (i32.const 0))
2120 (i64.const 1111)
2121 )
2122 )
2123 )
2124 (func (export "deploy"))
2125)
2126"#;
2127
2128 #[test]
2129 fn now() {
2130 assert_ok!(execute(CODE_TIMESTAMP_NOW, vec![], MockExt::default()));
2131 assert_ok!(execute(CODE_TIMESTAMP_NOW_UNPREFIXED, vec![], MockExt::default()));
2132 }
2133
2134 const CODE_MINIMUM_BALANCE: &str = r#"
2135(module
2136 (import "seal0" "seal_minimum_balance" (func $seal_minimum_balance (param i32 i32)))
2137 (import "env" "memory" (memory 1 1))
2138
2139 ;; size of our buffer is 32 bytes
2140 (data (i32.const 32) "\20")
2141
2142 (func $assert (param i32)
2143 (block $ok
2144 (br_if $ok
2145 (local.get 0)
2146 )
2147 (unreachable)
2148 )
2149 )
2150
2151 (func (export "call")
2152 (call $seal_minimum_balance (i32.const 0) (i32.const 32))
2153
2154 ;; assert len == 8
2155 (call $assert
2156 (i32.eq
2157 (i32.load (i32.const 32))
2158 (i32.const 8)
2159 )
2160 )
2161
2162 ;; assert that contents of the buffer is equal to the i64 value of 666.
2163 (call $assert
2164 (i64.eq
2165 (i64.load (i32.const 0))
2166 (i64.const 666)
2167 )
2168 )
2169 )
2170 (func (export "deploy"))
2171)
2172"#;
2173
2174 #[test]
2175 fn minimum_balance() {
2176 assert_ok!(execute(CODE_MINIMUM_BALANCE, vec![], MockExt::default()));
2177 }
2178
2179 const CODE_RANDOM: &str = r#"
2180(module
2181 (import "seal0" "seal_random" (func $seal_random (param i32 i32 i32 i32)))
2182 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
2183 (import "env" "memory" (memory 1 1))
2184
2185 ;; [0,128) is reserved for the result of PRNG.
2186
2187 ;; the subject used for the PRNG. [128,160)
2188 (data (i32.const 128)
2189 "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F"
2190 "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F"
2191 )
2192
2193 ;; size of our buffer is 128 bytes
2194 (data (i32.const 160) "\80")
2195
2196 (func $assert (param i32)
2197 (block $ok
2198 (br_if $ok
2199 (local.get 0)
2200 )
2201 (unreachable)
2202 )
2203 )
2204
2205 (func (export "call")
2206 ;; This stores the block random seed in the buffer
2207 (call $seal_random
2208 (i32.const 128) ;; Pointer in memory to the start of the subject buffer
2209 (i32.const 32) ;; The subject buffer's length
2210 (i32.const 0) ;; Pointer to the output buffer
2211 (i32.const 160) ;; Pointer to the output buffer length
2212 )
2213
2214 ;; assert len == 32
2215 (call $assert
2216 (i32.eq
2217 (i32.load (i32.const 160))
2218 (i32.const 32)
2219 )
2220 )
2221
2222 ;; return the random data
2223 (call $seal_return
2224 (i32.const 0)
2225 (i32.const 0)
2226 (i32.const 32)
2227 )
2228 )
2229 (func (export "deploy"))
2230)
2231"#;
2232
2233 #[test]
2234 fn random() {
2235 let output = execute_unvalidated(CODE_RANDOM, vec![], MockExt::default()).unwrap();
2236
2237 assert_eq!(
2239 output,
2240 ExecReturnValue {
2241 flags: ReturnFlags::empty(),
2242 data: array_bytes::hex_into_unchecked(
2243 "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
2244 )
2245 },
2246 );
2247 }
2248
2249 const CODE_RANDOM_V1: &str = r#"
2250(module
2251 (import "seal1" "seal_random" (func $seal_random (param i32 i32 i32 i32)))
2252 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
2253 (import "env" "memory" (memory 1 1))
2254
2255 ;; [0,128) is reserved for the result of PRNG.
2256
2257 ;; the subject used for the PRNG. [128,160)
2258 (data (i32.const 128)
2259 "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F"
2260 "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F"
2261 )
2262
2263 ;; size of our buffer is 128 bytes
2264 (data (i32.const 160) "\80")
2265
2266 (func $assert (param i32)
2267 (block $ok
2268 (br_if $ok
2269 (local.get 0)
2270 )
2271 (unreachable)
2272 )
2273 )
2274
2275 (func (export "call")
2276 ;; This stores the block random seed in the buffer
2277 (call $seal_random
2278 (i32.const 128) ;; Pointer in memory to the start of the subject buffer
2279 (i32.const 32) ;; The subject buffer's length
2280 (i32.const 0) ;; Pointer to the output buffer
2281 (i32.const 160) ;; Pointer to the output buffer length
2282 )
2283
2284 ;; assert len == 32
2285 (call $assert
2286 (i32.eq
2287 (i32.load (i32.const 160))
2288 (i32.const 40)
2289 )
2290 )
2291
2292 ;; return the random data
2293 (call $seal_return
2294 (i32.const 0)
2295 (i32.const 0)
2296 (i32.const 40)
2297 )
2298 )
2299 (func (export "deploy"))
2300)
2301"#;
2302
2303 #[test]
2304 fn random_v1() {
2305 let output = execute_unvalidated(CODE_RANDOM_V1, vec![], MockExt::default()).unwrap();
2306
2307 assert_eq!(
2309 output,
2310 ExecReturnValue {
2311 flags: ReturnFlags::empty(),
2312 data: (
2313 array_bytes::hex2array_unchecked::<_, 32>(
2314 "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
2315 ),
2316 42u64,
2317 )
2318 .encode()
2319 },
2320 );
2321 }
2322
2323 const CODE_DEPOSIT_EVENT: &str = r#"
2324(module
2325 (import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32)))
2326 (import "env" "memory" (memory 1 1))
2327
2328 (func (export "call")
2329 (call $seal_deposit_event
2330 (i32.const 32) ;; Pointer to the start of topics buffer
2331 (i32.const 33) ;; The length of the topics buffer.
2332 (i32.const 8) ;; Pointer to the start of the data buffer
2333 (i32.const 13) ;; Length of the buffer
2334 )
2335 )
2336 (func (export "deploy"))
2337
2338 (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00")
2339
2340 ;; Encoded Vec<TopicOf<T>>, the buffer has length of 33 bytes.
2341 (data (i32.const 32) "\04\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33"
2342 "\33\33\33\33\33\33\33\33\33")
2343)
2344"#;
2345
2346 #[test]
2347 fn deposit_event() {
2348 let mut mock_ext = MockExt::default();
2349 assert_ok!(execute(CODE_DEPOSIT_EVENT, vec![], &mut mock_ext));
2350
2351 assert_eq!(
2352 mock_ext.events,
2353 vec![(
2354 vec![H256::repeat_byte(0x33)],
2355 vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00]
2356 )]
2357 );
2358
2359 assert!(mock_ext.gas_meter.gas_left().ref_time() > 0);
2360 }
2361
2362 const CODE_DEPOSIT_EVENT_DUPLICATES: &str = r#"
2363(module
2364 (import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32)))
2365 (import "env" "memory" (memory 1 1))
2366
2367 (func (export "call")
2368 (call $seal_deposit_event
2369 (i32.const 32) ;; Pointer to the start of topics buffer
2370 (i32.const 129) ;; The length of the topics buffer.
2371 (i32.const 8) ;; Pointer to the start of the data buffer
2372 (i32.const 13) ;; Length of the buffer
2373 )
2374 )
2375 (func (export "deploy"))
2376
2377 (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00")
2378
2379 ;; Encoded Vec<TopicOf<T>>, the buffer has length of 129 bytes.
2380 (data (i32.const 32) "\10"
2381"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
2382"\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02"
2383"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
2384"\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04")
2385)
2386"#;
2387
2388 #[test]
2390 fn deposit_event_duplicates_allowed() {
2391 let mut mock_ext = MockExt::default();
2392 assert_ok!(execute(CODE_DEPOSIT_EVENT_DUPLICATES, vec![], &mut mock_ext,));
2393
2394 assert_eq!(
2395 mock_ext.events,
2396 vec![(
2397 vec![
2398 H256::repeat_byte(0x01),
2399 H256::repeat_byte(0x02),
2400 H256::repeat_byte(0x01),
2401 H256::repeat_byte(0x04)
2402 ],
2403 vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00]
2404 )]
2405 );
2406 }
2407
2408 const CODE_DEPOSIT_EVENT_MAX_TOPICS: &str = r#"
2409(module
2410 (import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32)))
2411 (import "env" "memory" (memory 1 1))
2412
2413 (func (export "call")
2414 (call $seal_deposit_event
2415 (i32.const 32) ;; Pointer to the start of topics buffer
2416 (i32.const 161) ;; The length of the topics buffer.
2417 (i32.const 8) ;; Pointer to the start of the data buffer
2418 (i32.const 13) ;; Length of the buffer
2419 )
2420 )
2421 (func (export "deploy"))
2422
2423 (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00")
2424
2425 ;; Encoded Vec<TopicOf<T>>, the buffer has length of 161 bytes.
2426 (data (i32.const 32) "\14"
2427"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
2428"\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02"
2429"\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03"
2430"\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04"
2431"\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05")
2432)
2433"#;
2434
2435 #[test]
2437 fn deposit_event_max_topics() {
2438 assert_eq!(
2439 execute(CODE_DEPOSIT_EVENT_MAX_TOPICS, vec![], MockExt::default(),),
2440 Err(ExecError {
2441 error: Error::<Test>::TooManyTopics.into(),
2442 origin: ErrorOrigin::Caller,
2443 })
2444 );
2445 }
2446
2447 const CODE_BLOCK_NUMBER: &str = r#"
2449(module
2450 (import "seal0" "seal_block_number" (func $seal_block_number (param i32 i32)))
2451 (import "env" "memory" (memory 1 1))
2452
2453 ;; size of our buffer is 32 bytes
2454 (data (i32.const 32) "\20")
2455
2456 (func $assert (param i32)
2457 (block $ok
2458 (br_if $ok
2459 (local.get 0)
2460 )
2461 (unreachable)
2462 )
2463 )
2464
2465 (func (export "call")
2466 ;; This stores the block height in the buffer
2467 (call $seal_block_number (i32.const 0) (i32.const 32))
2468
2469 ;; assert len == 8
2470 (call $assert
2471 (i32.eq
2472 (i32.load (i32.const 32))
2473 (i32.const 8)
2474 )
2475 )
2476
2477 ;; assert that contents of the buffer is equal to the i64 value of 121.
2478 (call $assert
2479 (i64.eq
2480 (i64.load (i32.const 0))
2481 (i64.const 121)
2482 )
2483 )
2484 )
2485
2486 (func (export "deploy"))
2487)
2488"#;
2489
2490 #[test]
2491 fn block_number() {
2492 let _ = execute(CODE_BLOCK_NUMBER, vec![], MockExt::default()).unwrap();
2493 }
2494
2495 const CODE_RETURN_WITH_DATA: &str = r#"
2496(module
2497 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
2498 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
2499 (import "env" "memory" (memory 1 1))
2500
2501 (data (i32.const 32) "\20")
2502
2503 ;; Deploy routine is the same as call.
2504 (func (export "deploy")
2505 (call $call)
2506 )
2507
2508 ;; Call reads the first 4 bytes (LE) as the exit status and returns the rest as output data.
2509 (func $call (export "call")
2510 ;; Copy input data this contract memory.
2511 (call $seal_input
2512 (i32.const 0) ;; Pointer where to store input
2513 (i32.const 32) ;; Pointer to the length of the buffer
2514 )
2515
2516 ;; Copy all but the first 4 bytes of the input data as the output data.
2517 (call $seal_return
2518 (i32.load (i32.const 0))
2519 (i32.const 4)
2520 (i32.sub (i32.load (i32.const 32)) (i32.const 4))
2521 )
2522 (unreachable)
2523 )
2524)
2525"#;
2526
2527 #[test]
2528 fn seal_return_with_success_status() {
2529 let output = execute(
2530 CODE_RETURN_WITH_DATA,
2531 array_bytes::hex2bytes_unchecked("00000000445566778899"),
2532 MockExt::default(),
2533 )
2534 .unwrap();
2535
2536 assert_eq!(
2537 output,
2538 ExecReturnValue {
2539 flags: ReturnFlags::empty(),
2540 data: array_bytes::hex2bytes_unchecked("445566778899"),
2541 }
2542 );
2543 assert!(!output.did_revert());
2544 }
2545
2546 #[test]
2547 fn return_with_revert_status() {
2548 let output = execute(
2549 CODE_RETURN_WITH_DATA,
2550 array_bytes::hex2bytes_unchecked("010000005566778899"),
2551 MockExt::default(),
2552 )
2553 .unwrap();
2554
2555 assert_eq!(
2556 output,
2557 ExecReturnValue {
2558 flags: ReturnFlags::REVERT,
2559 data: array_bytes::hex2bytes_unchecked("5566778899"),
2560 }
2561 );
2562 assert!(output.did_revert());
2563 }
2564
2565 const CODE_OUT_OF_BOUNDS_ACCESS: &str = r#"
2566(module
2567 (import "seal0" "seal_terminate" (func $seal_terminate (param i32 i32)))
2568 (import "env" "memory" (memory 1 1))
2569
2570 (func (export "deploy"))
2571
2572 (func (export "call")
2573 (call $seal_terminate
2574 (i32.const 65536) ;; Pointer to "account" address (out of bound).
2575 (i32.const 8) ;; Length of "account" address.
2576 )
2577 )
2578)
2579"#;
2580
2581 #[test]
2582 fn contract_out_of_bounds_access() {
2583 let mut mock_ext = MockExt::default();
2584 let result = execute(CODE_OUT_OF_BOUNDS_ACCESS, vec![], &mut mock_ext);
2585
2586 assert_eq!(
2587 result,
2588 Err(ExecError {
2589 error: Error::<Test>::DecodingFailed.into(),
2590 origin: ErrorOrigin::Caller,
2591 })
2592 );
2593 }
2594
2595 const CODE_DECODE_FAILURE: &str = r#"
2596(module
2597 (import "seal0" "seal_terminate" (func $seal_terminate (param i32 i32)))
2598 (import "env" "memory" (memory 1 1))
2599
2600 (func (export "deploy"))
2601
2602 (func (export "call")
2603 (call $seal_terminate
2604 (i32.const 0) ;; Pointer to "account" address.
2605 (i32.const 4) ;; Length of "account" address (too small -> decode fail).
2606 )
2607 )
2608)
2609"#;
2610
2611 #[test]
2612 fn contract_decode_length_ignored() {
2613 let mut mock_ext = MockExt::default();
2614 let result = execute(CODE_DECODE_FAILURE, vec![], &mut mock_ext);
2615 assert_ok!(result);
2618 }
2619
2620 #[test]
2621 fn debug_message_works() {
2622 const CODE_DEBUG_MESSAGE: &str = r#"
2623(module
2624 (import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
2625 (import "env" "memory" (memory 1 1))
2626
2627 (data (i32.const 0) "Hello World!")
2628
2629 (func (export "call")
2630 (call $seal_debug_message
2631 (i32.const 0) ;; Pointer to the text buffer
2632 (i32.const 12) ;; The size of the buffer
2633 )
2634 drop
2635 )
2636
2637 (func (export "deploy"))
2638)
2639"#;
2640 let mut ext = MockExt::default();
2641 execute(CODE_DEBUG_MESSAGE, vec![], &mut ext).unwrap();
2642
2643 assert_eq!(std::str::from_utf8(&ext.debug_buffer).unwrap(), "Hello World!");
2644 }
2645
2646 #[test]
2647 fn debug_message_invalid_utf8_fails() {
2648 const CODE_DEBUG_MESSAGE_FAIL: &str = r#"
2649(module
2650 (import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
2651 (import "env" "memory" (memory 1 1))
2652
2653 (data (i32.const 0) "\fc")
2654
2655 (func (export "call")
2656 (call $seal_debug_message
2657 (i32.const 0) ;; Pointer to the text buffer
2658 (i32.const 1) ;; The size of the buffer
2659 )
2660 drop
2661 )
2662
2663 (func (export "deploy"))
2664)
2665"#;
2666 let mut ext = MockExt::default();
2667 let result = execute(CODE_DEBUG_MESSAGE_FAIL, vec![], &mut ext);
2668 assert_ok!(result);
2669 assert!(ext.debug_buffer.is_empty());
2670 }
2671
2672 const CODE_CALL_RUNTIME: &str = r#"
2673(module
2674 (import "seal0" "call_runtime" (func $call_runtime (param i32 i32) (result i32)))
2675 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
2676 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
2677 (import "env" "memory" (memory 1 1))
2678
2679 ;; 0x1000 = 4k in little endian
2680 ;; size of input buffer
2681 (data (i32.const 0) "\00\10")
2682
2683 (func (export "call")
2684 ;; Receive the encoded call
2685 (call $seal_input
2686 (i32.const 4) ;; Pointer to the input buffer
2687 (i32.const 0) ;; Size of the length buffer
2688 )
2689 ;; Just use the call passed as input and store result to memory
2690 (i32.store (i32.const 0)
2691 (call $call_runtime
2692 (i32.const 4) ;; Pointer where the call is stored
2693 (i32.load (i32.const 0)) ;; Size of the call
2694 )
2695 )
2696 (call $seal_return
2697 (i32.const 0) ;; flags
2698 (i32.const 0) ;; returned value
2699 (i32.const 4) ;; length of returned value
2700 )
2701 )
2702
2703 (func (export "deploy"))
2704)
2705"#;
2706
2707 #[test]
2708 fn call_runtime_works() {
2709 let call =
2710 RuntimeCall::System(frame_system::Call::remark { remark: b"Hello World".to_vec() });
2711 let mut ext = MockExt::default();
2712 let result = execute(CODE_CALL_RUNTIME, call.encode(), &mut ext).unwrap();
2713 assert_eq!(*ext.runtime_calls.borrow(), vec![call]);
2714 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 0);
2716 }
2717
2718 #[test]
2719 fn call_runtime_panics_on_invalid_call() {
2720 let mut ext = MockExt::default();
2721 let result = execute(CODE_CALL_RUNTIME, vec![0x42], &mut ext);
2722 assert_eq!(
2723 result,
2724 Err(ExecError {
2725 error: Error::<Test>::DecodingFailed.into(),
2726 origin: ErrorOrigin::Caller,
2727 })
2728 );
2729 assert_eq!(*ext.runtime_calls.borrow(), vec![]);
2730 }
2731
2732 #[test]
2733 fn set_storage_works() {
2734 const CODE: &str = r#"
2735(module
2736 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
2737 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
2738 (import "seal2" "set_storage" (func $set_storage (param i32 i32 i32 i32) (result i32)))
2739 (import "env" "memory" (memory 1 1))
2740
2741 ;; [0, 4) size of input buffer
2742 ;; 4k in little endian
2743 (data (i32.const 0) "\00\10")
2744
2745 ;; [4, 4100) input buffer
2746
2747 (func (export "call")
2748 ;; Receive (key ++ value_to_write)
2749 (call $seal_input
2750 (i32.const 4) ;; Pointer to the input buffer
2751 (i32.const 0) ;; Size of the input buffer
2752 )
2753 ;; Store the passed value to the passed key and store result to memory
2754 (i32.store (i32.const 168)
2755 (call $set_storage
2756 (i32.const 8) ;; key_ptr
2757 (i32.load (i32.const 4)) ;; key_len
2758 (i32.add ;; value_ptr = 8 + key_len
2759 (i32.const 8)
2760 (i32.load (i32.const 4)))
2761 (i32.sub ;; value_len (input_size - (key_len + key_len_len))
2762 (i32.load (i32.const 0))
2763 (i32.add
2764 (i32.load (i32.const 4))
2765 (i32.const 4)
2766 )
2767 )
2768 )
2769 )
2770 (call $seal_return
2771 (i32.const 0) ;; flags
2772 (i32.const 168) ;; ptr to returned value
2773 (i32.const 4) ;; length of returned value
2774 )
2775 )
2776
2777 (func (export "deploy"))
2778)
2779"#;
2780
2781 let mut ext = MockExt::default();
2782
2783 let input = (32, [1u8; 32], [42u8, 48]).encode();
2785 let result = execute(CODE, input, &mut ext).unwrap();
2786 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), crate::SENTINEL);
2787 assert_eq!(ext.storage.get(&[1u8; 32].to_vec()).unwrap(), &[42u8, 48]);
2788
2789 let input = (32, [1u8; 32], [0u8; 0]).encode();
2791 let result = execute(CODE, input, &mut ext).unwrap();
2792 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 2);
2793 assert_eq!(ext.storage.get(&[1u8; 32].to_vec()).unwrap(), &[0u8; 0]);
2794
2795 let input = (32, [1u8; 32], [99u8]).encode();
2797 let result = execute(CODE, input, &mut ext).unwrap();
2798 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 0);
2799 assert_eq!(ext.storage.get(&[1u8; 32].to_vec()).unwrap(), &[99u8]);
2800 }
2801
2802 #[test]
2803 fn get_storage_works() {
2804 const CODE: &str = r#"
2805(module
2806 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
2807 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
2808 (import "seal1" "get_storage" (func $get_storage (param i32 i32 i32 i32) (result i32)))
2809 (import "env" "memory" (memory 1 1))
2810
2811 ;; [0, 4) size of input buffer (160 bytes as we copy the key+len here)
2812 (data (i32.const 0) "\A0")
2813
2814 ;; [4, 8) size of output buffer
2815 ;; 4k in little endian
2816 (data (i32.const 4) "\00\10")
2817
2818 ;; [8, 168) input buffer
2819 ;; [168, 4264) output buffer
2820
2821 (func (export "call")
2822 ;; Receive (key ++ value_to_write)
2823 (call $seal_input
2824 (i32.const 8) ;; Pointer to the input buffer
2825 (i32.const 0) ;; Size of the input buffer
2826 )
2827 ;; Load a storage value and result of this call into the output buffer
2828 (i32.store (i32.const 168)
2829 (call $get_storage
2830 (i32.const 12) ;; key_ptr
2831 (i32.load (i32.const 8)) ;; key_len
2832 (i32.const 172) ;; Pointer to the output buffer
2833 (i32.const 4) ;; Pointer to the size of the buffer
2834 )
2835 )
2836 (call $seal_return
2837 (i32.const 0) ;; flags
2838 (i32.const 168) ;; output buffer ptr
2839 (i32.add ;; length: output size + 4 (retval)
2840 (i32.load (i32.const 4))
2841 (i32.const 4)
2842 )
2843 )
2844 )
2845
2846 (func (export "deploy"))
2847)
2848"#;
2849
2850 let mut ext = MockExt::default();
2851
2852 ext.set_storage(
2853 &Key::<Test>::try_from_var([1u8; 64].to_vec()).unwrap(),
2854 Some(vec![42u8]),
2855 false,
2856 )
2857 .unwrap();
2858
2859 ext.set_storage(
2860 &Key::<Test>::try_from_var([2u8; 19].to_vec()).unwrap(),
2861 Some(vec![]),
2862 false,
2863 )
2864 .unwrap();
2865
2866 let input = (63, [1u8; 64]).encode();
2868 let result = execute(CODE, input, &mut ext).unwrap();
2869 assert_eq!(
2870 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
2871 ReturnErrorCode::KeyNotFound as u32
2872 );
2873
2874 let input = (64, [1u8; 64]).encode();
2876 let result = execute(CODE, input, &mut ext).unwrap();
2877 assert_eq!(
2878 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
2879 ReturnErrorCode::Success as u32
2880 );
2881 assert_eq!(ext.storage.get(&[1u8; 64].to_vec()).unwrap(), &[42u8]);
2882 assert_eq!(&result.data[4..], &[42u8]);
2883
2884 let input = (19, [2u8; 19]).encode();
2886 let result = execute(CODE, input, &mut ext).unwrap();
2887 assert_eq!(
2888 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
2889 ReturnErrorCode::Success as u32
2890 );
2891 assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), Some(&vec![]));
2892 assert_eq!(&result.data[4..], &([] as [u8; 0]));
2893 }
2894
2895 #[test]
2896 fn clear_storage_works() {
2897 const CODE: &str = r#"
2898(module
2899 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
2900 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
2901 (import "seal1" "clear_storage" (func $clear_storage (param i32 i32) (result i32)))
2902 (import "env" "memory" (memory 1 1))
2903
2904 ;; size of input buffer
2905 ;; [0, 4) size of input buffer (128+32 = 160 bytes = 0xA0)
2906 (data (i32.const 0) "\A0")
2907
2908 ;; [4, 164) input buffer
2909
2910 (func (export "call")
2911 ;; Receive key
2912 (call $seal_input
2913 (i32.const 4) ;; Where we take input and store it
2914 (i32.const 0) ;; Where we take and store the length of thedata
2915 )
2916 ;; Call seal_clear_storage and save what it returns at 0
2917 (i32.store (i32.const 0)
2918 (call $clear_storage
2919 (i32.const 8) ;; key_ptr
2920 (i32.load (i32.const 4)) ;; key_len
2921 )
2922 )
2923 (call $seal_return
2924 (i32.const 0) ;; flags
2925 (i32.const 0) ;; returned value
2926 (i32.const 4) ;; length of returned value
2927 )
2928 )
2929
2930 (func (export "deploy"))
2931)
2932"#;
2933
2934 let mut ext = MockExt::default();
2935
2936 ext.set_storage(
2937 &Key::<Test>::try_from_var([1u8; 64].to_vec()).unwrap(),
2938 Some(vec![42u8]),
2939 false,
2940 )
2941 .unwrap();
2942 ext.set_storage(
2943 &Key::<Test>::try_from_var([2u8; 19].to_vec()).unwrap(),
2944 Some(vec![]),
2945 false,
2946 )
2947 .unwrap();
2948
2949 let input = (32, [3u8; 32]).encode();
2951 let result = execute(CODE, input, &mut ext).unwrap();
2952 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), crate::SENTINEL);
2954 assert_eq!(ext.storage.get(&[3u8; 32].to_vec()), None);
2955
2956 let input = (64, [1u8; 64]).encode();
2958 let result = execute(CODE, input, &mut ext).unwrap();
2959 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 1);
2961 assert_eq!(ext.storage.get(&[1u8; 64].to_vec()), None);
2963
2964 let input = (63, [1u8; 64]).encode();
2966 let result = execute(CODE, input, &mut ext).unwrap();
2967 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), crate::SENTINEL);
2969 assert_eq!(ext.storage.get(&[1u8; 64].to_vec()), None);
2970
2971 let input = (19, [2u8; 19]).encode();
2973 let result = execute(CODE, input, &mut ext).unwrap();
2974 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 0);
2976 assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), None);
2978 }
2979
2980 #[test]
2981 fn take_storage_works() {
2982 const CODE: &str = r#"
2983(module
2984 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
2985 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
2986 (import "seal0" "take_storage" (func $take_storage (param i32 i32 i32 i32) (result i32)))
2987 (import "env" "memory" (memory 1 1))
2988
2989 ;; [0, 4) size of input buffer (160 bytes as we copy the key+len here)
2990 (data (i32.const 0) "\A0")
2991
2992 ;; [4, 8) size of output buffer
2993 ;; 4k in little endian
2994 (data (i32.const 4) "\00\10")
2995
2996 ;; [8, 168) input buffer
2997 ;; [168, 4264) output buffer
2998
2999 (func (export "call")
3000 ;; Receive key
3001 (call $seal_input
3002 (i32.const 8) ;; Pointer to the input buffer
3003 (i32.const 0) ;; Size of the length buffer
3004 )
3005
3006 ;; Load a storage value and result of this call into the output buffer
3007 (i32.store (i32.const 168)
3008 (call $take_storage
3009 (i32.const 12) ;; key_ptr
3010 (i32.load (i32.const 8)) ;; key_len
3011 (i32.const 172) ;; Pointer to the output buffer
3012 (i32.const 4) ;; Pointer to the size of the buffer
3013 )
3014 )
3015
3016 ;; Return the contents of the buffer
3017 (call $seal_return
3018 (i32.const 0) ;; flags
3019 (i32.const 168) ;; output buffer ptr
3020 (i32.add ;; length: storage size + 4 (retval)
3021 (i32.load (i32.const 4))
3022 (i32.const 4)
3023 )
3024 )
3025 )
3026
3027 (func (export "deploy"))
3028)
3029"#;
3030
3031 let mut ext = MockExt::default();
3032
3033 ext.set_storage(
3034 &Key::<Test>::try_from_var([1u8; 64].to_vec()).unwrap(),
3035 Some(vec![42u8]),
3036 false,
3037 )
3038 .unwrap();
3039
3040 ext.set_storage(
3041 &Key::<Test>::try_from_var([2u8; 19].to_vec()).unwrap(),
3042 Some(vec![]),
3043 false,
3044 )
3045 .unwrap();
3046
3047 let input = (63, [1u8; 64]).encode();
3049 let result = execute(CODE, input, &mut ext).unwrap();
3050 assert_eq!(
3051 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
3052 ReturnErrorCode::KeyNotFound as u32
3053 );
3054
3055 let input = (64, [1u8; 64]).encode();
3057 let result = execute(CODE, input, &mut ext).unwrap();
3058 assert_eq!(
3059 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
3060 ReturnErrorCode::Success as u32
3061 );
3062 assert_eq!(ext.storage.get(&[1u8; 64].to_vec()), None);
3063 assert_eq!(&result.data[4..], &[42u8]);
3064
3065 let input = (19, [2u8; 19]).encode();
3067 let result = execute(CODE, input, &mut ext).unwrap();
3068 assert_eq!(
3069 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
3070 ReturnErrorCode::Success as u32
3071 );
3072 assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), None);
3073 assert_eq!(&result.data[4..], &[0u8; 0]);
3074 }
3075
3076 #[test]
3077 fn set_transient_storage_works() {
3078 const CODE: &str = r#"
3079(module
3080 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
3081 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
3082 (import "seal0" "set_transient_storage" (func $set_transient_storage (param i32 i32 i32 i32) (result i32)))
3083 (import "env" "memory" (memory 1 1))
3084
3085 ;; [0, 4) size of input buffer
3086 ;; 4k in little endian
3087 (data (i32.const 0) "\00\10")
3088
3089 ;; [4, 4100) input buffer
3090
3091 (func (export "call")
3092 ;; Receive (key ++ value_to_write)
3093 (call $seal_input
3094 (i32.const 4) ;; Pointer to the input buffer
3095 (i32.const 0) ;; Size of the input buffer
3096 )
3097 ;; Store the passed value to the passed key and store result to memory
3098 (i32.store (i32.const 168)
3099 (call $set_transient_storage
3100 (i32.const 8) ;; key_ptr
3101 (i32.load (i32.const 4)) ;; key_len
3102 (i32.add ;; value_ptr = 8 + key_len
3103 (i32.const 8)
3104 (i32.load (i32.const 4)))
3105 (i32.sub ;; value_len (input_size - (key_len + key_len_len))
3106 (i32.load (i32.const 0))
3107 (i32.add
3108 (i32.load (i32.const 4))
3109 (i32.const 4)
3110 )
3111 )
3112 )
3113 )
3114 (call $seal_return
3115 (i32.const 0) ;; flags
3116 (i32.const 168) ;; ptr to returned value
3117 (i32.const 4) ;; length of returned value
3118 )
3119 )
3120
3121 (func (export "deploy"))
3122)
3123"#;
3124
3125 let mut ext = MockExt::default();
3126
3127 let input = (32, [1u8; 32], [42u8, 48]).encode();
3129 let result = execute(CODE, input, &mut ext).unwrap();
3130 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), crate::SENTINEL);
3131 assert_eq!(
3132 ext.get_transient_storage(&Key::<Test>::try_from_var([1u8; 32].to_vec()).unwrap()),
3133 Some(vec![42, 48])
3134 );
3135
3136 let input = (32, [1u8; 32], [0u8; 0]).encode();
3138 let result = execute(CODE, input, &mut ext).unwrap();
3139 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 2);
3140 assert_eq!(
3141 ext.get_transient_storage(&Key::<Test>::try_from_var([1u8; 32].to_vec()).unwrap()),
3142 Some(vec![])
3143 );
3144
3145 let input = (32, [1u8; 32], [99u8]).encode();
3147 let result = execute(CODE, input, &mut ext).unwrap();
3148 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 0);
3149 assert_eq!(
3150 ext.get_transient_storage(&Key::<Test>::try_from_var([1u8; 32].to_vec()).unwrap()),
3151 Some(vec![99])
3152 );
3153 }
3154
3155 #[test]
3156 fn get_transient_storage_works() {
3157 const CODE: &str = r#"
3158(module
3159 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
3160 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
3161 (import "seal0" "get_transient_storage" (func $get_transient_storage (param i32 i32 i32 i32) (result i32)))
3162 (import "env" "memory" (memory 1 1))
3163
3164 ;; [0, 4) size of input buffer (160 bytes as we copy the key+len here)
3165 (data (i32.const 0) "\A0")
3166
3167 ;; [4, 8) size of output buffer
3168 ;; 4k in little endian
3169 (data (i32.const 4) "\00\10")
3170
3171 ;; [8, 168) input buffer
3172 ;; [168, 4264) output buffer
3173
3174 (func (export "call")
3175 ;; Receive (key ++ value_to_write)
3176 (call $seal_input
3177 (i32.const 8) ;; Pointer to the input buffer
3178 (i32.const 0) ;; Size of the input buffer
3179 )
3180 ;; Load a storage value and result of this call into the output buffer
3181 (i32.store (i32.const 168)
3182 (call $get_transient_storage
3183 (i32.const 12) ;; key_ptr
3184 (i32.load (i32.const 8)) ;; key_len
3185 (i32.const 172) ;; Pointer to the output buffer
3186 (i32.const 4) ;; Pointer to the size of the buffer
3187 )
3188 )
3189 (call $seal_return
3190 (i32.const 0) ;; flags
3191 (i32.const 168) ;; output buffer ptr
3192 (i32.add ;; length: output size + 4 (retval)
3193 (i32.load (i32.const 4))
3194 (i32.const 4)
3195 )
3196 )
3197 )
3198
3199 (func (export "deploy"))
3200)
3201"#;
3202
3203 let mut ext = MockExt::default();
3204
3205 assert_ok!(ext.set_transient_storage(
3206 &Key::<Test>::try_from_var([1u8; 64].to_vec()).unwrap(),
3207 Some(vec![42u8]),
3208 false
3209 ));
3210 assert_ok!(ext.set_transient_storage(
3211 &Key::<Test>::try_from_var([2u8; 19].to_vec()).unwrap(),
3212 Some(vec![]),
3213 false
3214 ));
3215
3216 let input = (63, [1u8; 64]).encode();
3218 let result = execute(CODE, input, &mut ext).unwrap();
3219 assert_eq!(
3220 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
3221 ReturnErrorCode::KeyNotFound as u32
3222 );
3223
3224 let input = (64, [1u8; 64]).encode();
3226 let result = execute(CODE, input, &mut ext).unwrap();
3227 assert_eq!(
3228 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
3229 ReturnErrorCode::Success as u32
3230 );
3231 assert_eq!(&result.data[4..], &[42u8]);
3232
3233 let input = (19, [2u8; 19]).encode();
3235 let result = execute(CODE, input, &mut ext).unwrap();
3236 assert_eq!(
3237 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
3238 ReturnErrorCode::Success as u32
3239 );
3240 assert_eq!(&result.data[4..], &([] as [u8; 0]));
3241 }
3242
3243 #[test]
3244 fn clear_transient_storage_works() {
3245 const CODE: &str = r#"
3246(module
3247 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
3248 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
3249 (import "seal0" "clear_transient_storage" (func $clear_transient_storage (param i32 i32) (result i32)))
3250 (import "env" "memory" (memory 1 1))
3251
3252 ;; size of input buffer
3253 ;; [0, 4) size of input buffer (128+32 = 160 bytes = 0xA0)
3254 (data (i32.const 0) "\A0")
3255
3256 ;; [4, 164) input buffer
3257
3258 (func (export "call")
3259 ;; Receive key
3260 (call $seal_input
3261 (i32.const 4) ;; Where we take input and store it
3262 (i32.const 0) ;; Where we take and store the length of thedata
3263 )
3264 ;; Call seal_clear_storage and save what it returns at 0
3265 (i32.store (i32.const 0)
3266 (call $clear_transient_storage
3267 (i32.const 8) ;; key_ptr
3268 (i32.load (i32.const 4)) ;; key_len
3269 )
3270 )
3271 (call $seal_return
3272 (i32.const 0) ;; flags
3273 (i32.const 0) ;; returned value
3274 (i32.const 4) ;; length of returned value
3275 )
3276 )
3277
3278 (func (export "deploy"))
3279)
3280"#;
3281
3282 let mut ext = MockExt::default();
3283
3284 assert_ok!(ext.set_transient_storage(
3285 &Key::<Test>::try_from_var([1u8; 64].to_vec()).unwrap(),
3286 Some(vec![42u8]),
3287 false
3288 ));
3289
3290 let input = (32, [3u8; 32]).encode();
3292 let result = execute(CODE, input, &mut ext).unwrap();
3293 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), crate::SENTINEL);
3295
3296 let input = (64, [1u8; 64]).encode();
3298 let result = execute(CODE, input, &mut ext).unwrap();
3299 assert_eq!(u32::from_le_bytes(result.data.try_into().unwrap()), 1);
3301 assert_eq!(
3303 ext.get_transient_storage(&Key::<Test>::try_from_var([1u8; 64].to_vec()).unwrap()),
3304 None
3305 );
3306 }
3307
3308 #[test]
3309 fn take_transient_storage_works() {
3310 const CODE: &str = r#"
3311(module
3312 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
3313 (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
3314 (import "seal0" "take_transient_storage" (func $take_transient_storage (param i32 i32 i32 i32) (result i32)))
3315 (import "env" "memory" (memory 1 1))
3316
3317 ;; [0, 4) size of input buffer (160 bytes as we copy the key+len here)
3318 (data (i32.const 0) "\A0")
3319
3320 ;; [4, 8) size of output buffer
3321 ;; 4k in little endian
3322 (data (i32.const 4) "\00\10")
3323
3324 ;; [8, 168) input buffer
3325 ;; [168, 4264) output buffer
3326
3327 (func (export "call")
3328 ;; Receive key
3329 (call $seal_input
3330 (i32.const 8) ;; Pointer to the input buffer
3331 (i32.const 0) ;; Size of the length buffer
3332 )
3333
3334 ;; Load a storage value and result of this call into the output buffer
3335 (i32.store (i32.const 168)
3336 (call $take_transient_storage
3337 (i32.const 12) ;; key_ptr
3338 (i32.load (i32.const 8)) ;; key_len
3339 (i32.const 172) ;; Pointer to the output buffer
3340 (i32.const 4) ;; Pointer to the size of the buffer
3341 )
3342 )
3343
3344 ;; Return the contents of the buffer
3345 (call $seal_return
3346 (i32.const 0) ;; flags
3347 (i32.const 168) ;; output buffer ptr
3348 (i32.add ;; length: storage size + 4 (retval)
3349 (i32.load (i32.const 4))
3350 (i32.const 4)
3351 )
3352 )
3353 )
3354
3355 (func (export "deploy"))
3356)
3357"#;
3358
3359 let mut ext = MockExt::default();
3360
3361 assert_ok!(ext.set_transient_storage(
3362 &Key::<Test>::try_from_var([1u8; 64].to_vec()).unwrap(),
3363 Some(vec![42u8]),
3364 false
3365 ));
3366 assert_ok!(ext.set_transient_storage(
3367 &Key::<Test>::try_from_var([2u8; 19].to_vec()).unwrap(),
3368 Some(vec![]),
3369 false
3370 ));
3371
3372 let input = (63, [1u8; 64]).encode();
3374 let result = execute(CODE, input, &mut ext).unwrap();
3375 assert_eq!(
3376 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
3377 ReturnErrorCode::KeyNotFound as u32
3378 );
3379
3380 let input = (64, [1u8; 64]).encode();
3382 let result = execute(CODE, input, &mut ext).unwrap();
3383 assert_eq!(
3384 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
3385 ReturnErrorCode::Success as u32
3386 );
3387 assert_eq!(
3388 ext.get_transient_storage(&Key::<Test>::try_from_var([1u8; 64].to_vec()).unwrap()),
3389 None
3390 );
3391 assert_eq!(&result.data[4..], &[42u8]);
3392
3393 let input = (19, [2u8; 19]).encode();
3395 let result = execute(CODE, input, &mut ext).unwrap();
3396 assert_eq!(
3397 u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
3398 ReturnErrorCode::Success as u32
3399 );
3400 assert_eq!(
3401 ext.get_transient_storage(&Key::<Test>::try_from_var([2u8; 19].to_vec()).unwrap()),
3402 None
3403 );
3404 assert_eq!(&result.data[4..], &[0u8; 0]);
3405 }
3406
3407 #[test]
3408 fn is_contract_works() {
3409 const CODE_IS_CONTRACT: &str = r#"
3410;; This runs `is_contract` check on zero account address
3411(module
3412 (import "seal0" "seal_is_contract" (func $seal_is_contract (param i32) (result i32)))
3413 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
3414 (import "env" "memory" (memory 1 1))
3415
3416 ;; [0, 32) zero-adress
3417 (data (i32.const 0)
3418 "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"
3419 "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"
3420 )
3421
3422 ;; [32, 36) here we store the return code of the `seal_is_contract`
3423
3424 (func (export "deploy"))
3425
3426 (func (export "call")
3427 (i32.store
3428 (i32.const 32)
3429 (call $seal_is_contract
3430 (i32.const 0) ;; ptr to destination address
3431 )
3432 )
3433 ;; exit with success and take `seal_is_contract` return code to the output buffer
3434 (call $seal_return (i32.const 0) (i32.const 32) (i32.const 4))
3435 )
3436)
3437"#;
3438 let output = execute(CODE_IS_CONTRACT, vec![], MockExt::default()).unwrap();
3439
3440 assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 1u32.encode() },);
3442 }
3443
3444 #[test]
3445 fn code_hash_works() {
3446 const CODE_CODE_HASH: &str = r#"
3448(module
3449 (import "seal0" "seal_code_hash" (func $seal_code_hash (param i32 i32 i32) (result i32)))
3450 (import "env" "memory" (memory 1 1))
3451
3452 ;; size of our buffer is 32 bytes
3453 (data (i32.const 32) "\20")
3454
3455 (func $assert (param i32)
3456 (block $ok
3457 (br_if $ok
3458 (local.get 0)
3459 )
3460 (unreachable)
3461 )
3462 )
3463
3464 (func (export "call")
3465 ;; fill the buffer with the code hash.
3466 (call $seal_code_hash
3467 (i32.const 0) ;; input: address_ptr (before call)
3468 (i32.const 0) ;; output: code_hash_ptr (after call)
3469 (i32.const 32) ;; same 32 bytes length for input and output
3470 )
3471
3472 ;; assert size == 32
3473 (call $assert
3474 (i32.eq
3475 (i32.load (i32.const 32))
3476 (i32.const 32)
3477 )
3478 )
3479
3480 ;; assert that the first 8 bytes are "1111111111111111"
3481 (call $assert
3482 (i64.eq
3483 (i64.load (i32.const 0))
3484 (i64.const 0x1111111111111111)
3485 )
3486 )
3487 drop
3488 )
3489
3490 (func (export "deploy"))
3491)
3492"#;
3493 assert_ok!(execute(CODE_CODE_HASH, vec![], MockExt::default()));
3494 }
3495
3496 #[test]
3497 fn own_code_hash_works() {
3498 const CODE_OWN_CODE_HASH: &str = r#"
3500(module
3501 (import "seal0" "seal_own_code_hash" (func $seal_own_code_hash (param i32 i32)))
3502 (import "env" "memory" (memory 1 1))
3503
3504 ;; size of our buffer is 32 bytes
3505 (data (i32.const 32) "\20")
3506
3507 (func $assert (param i32)
3508 (block $ok
3509 (br_if $ok
3510 (local.get 0)
3511 )
3512 (unreachable)
3513 )
3514 )
3515
3516 (func (export "call")
3517 ;; fill the buffer with the code hash
3518 (call $seal_own_code_hash
3519 (i32.const 0) ;; output: code_hash_ptr
3520 (i32.const 32) ;; 32 bytes length of code_hash output
3521 )
3522
3523 ;; assert size == 32
3524 (call $assert
3525 (i32.eq
3526 (i32.load (i32.const 32))
3527 (i32.const 32)
3528 )
3529 )
3530
3531 ;; assert that the first 8 bytes are "1010101010101010"
3532 (call $assert
3533 (i64.eq
3534 (i64.load (i32.const 0))
3535 (i64.const 0x1010101010101010)
3536 )
3537 )
3538 )
3539
3540 (func (export "deploy"))
3541)
3542"#;
3543 assert_ok!(execute(CODE_OWN_CODE_HASH, vec![], MockExt::default()));
3544 }
3545
3546 #[test]
3547 fn caller_is_origin_works() {
3548 const CODE_CALLER_IS_ORIGIN: &str = r#"
3549;; This runs `caller_is_origin` check on zero account address
3550(module
3551 (import "seal0" "seal_caller_is_origin" (func $seal_caller_is_origin (result i32)))
3552 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
3553 (import "env" "memory" (memory 1 1))
3554
3555 ;; [0, 4) here the return code of the `seal_caller_is_origin` will be stored
3556 ;; we initialize it with non-zero value to be sure that it's being overwritten below
3557 (data (i32.const 0) "\10\10\10\10")
3558
3559 (func (export "deploy"))
3560
3561 (func (export "call")
3562 (i32.store
3563 (i32.const 0)
3564 (call $seal_caller_is_origin)
3565 )
3566 ;; exit with success and take `seal_caller_is_origin` return code to the output buffer
3567 (call $seal_return (i32.const 0) (i32.const 0) (i32.const 4))
3568 )
3569)
3570"#;
3571 let output = execute(CODE_CALLER_IS_ORIGIN, vec![], MockExt::default()).unwrap();
3572
3573 assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 0u32.encode() },);
3575 }
3576
3577 #[test]
3578 fn caller_is_root_works() {
3579 const CODE_CALLER_IS_ROOT: &str = r#"
3580;; This runs `caller_is_root` check on zero account address
3581(module
3582 (import "seal0" "caller_is_root" (func $caller_is_root (result i32)))
3583 (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
3584 (import "env" "memory" (memory 1 1))
3585
3586 ;; [0, 4) here the return code of the `caller_is_root` will be stored
3587 ;; we initialize it with non-zero value to be sure that it's being overwritten below
3588 (data (i32.const 0) "\10\10\10\10")
3589
3590 (func (export "deploy"))
3591
3592 (func (export "call")
3593 (i32.store
3594 (i32.const 0)
3595 (call $caller_is_root)
3596 )
3597 ;; exit with success and take `caller_is_root` return code to the output buffer
3598 (call $seal_return (i32.const 0) (i32.const 0) (i32.const 4))
3599 )
3600)
3601"#;
3602 let output = execute(CODE_CALLER_IS_ROOT, vec![], MockExt::default()).unwrap();
3604 assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 0u32.encode() },);
3605
3606 let output = execute(
3608 CODE_CALLER_IS_ROOT,
3609 vec![],
3610 MockExt { caller: Origin::Root, ..MockExt::default() },
3611 )
3612 .unwrap();
3613 assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 1u32.encode() },);
3614 }
3615
3616 #[test]
3617 fn set_code_hash() {
3618 const CODE: &str = r#"
3619(module
3620 (import "seal0" "seal_set_code_hash" (func $seal_set_code_hash (param i32) (result i32)))
3621 (import "env" "memory" (memory 1 1))
3622 (func $assert (param i32)
3623 (block $ok
3624 (br_if $ok
3625 (local.get 0)
3626 )
3627 (unreachable)
3628 )
3629 )
3630 (func (export "call")
3631 (local $exit_code i32)
3632 (local.set $exit_code
3633 (call $seal_set_code_hash (i32.const 0))
3634 )
3635 (call $assert
3636 (i32.eq (local.get $exit_code) (i32.const 0)) ;; ReturnCode::Success
3637 )
3638 )
3639
3640 (func (export "deploy"))
3641
3642 ;; Hash of code.
3643 (data (i32.const 0)
3644 "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
3645 "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"
3646 )
3647)
3648"#;
3649
3650 let mut mock_ext = MockExt::default();
3651 execute(CODE, [0u8; 32].encode(), &mut mock_ext).unwrap();
3652
3653 assert_eq!(mock_ext.code_hashes.pop().unwrap(), H256::from_slice(&[17u8; 32]));
3654 }
3655
3656 #[test]
3657 fn reentrance_count_works() {
3658 const CODE: &str = r#"
3659(module
3660 (import "seal0" "reentrance_count" (func $reentrance_count (result i32)))
3661 (import "env" "memory" (memory 1 1))
3662 (func $assert (param i32)
3663 (block $ok
3664 (br_if $ok
3665 (local.get 0)
3666 )
3667 (unreachable)
3668 )
3669 )
3670 (func (export "call")
3671 (local $return_val i32)
3672 (local.set $return_val
3673 (call $reentrance_count)
3674 )
3675 (call $assert
3676 (i32.eq (local.get $return_val) (i32.const 12))
3677 )
3678 )
3679
3680 (func (export "deploy"))
3681)
3682"#;
3683
3684 let mut mock_ext = MockExt::default();
3685 execute(CODE, vec![], &mut mock_ext).unwrap();
3686 }
3687
3688 #[test]
3689 fn account_reentrance_count_works() {
3690 const CODE: &str = r#"
3691(module
3692 (import "seal0" "account_reentrance_count" (func $account_reentrance_count (param i32) (result i32)))
3693 (import "env" "memory" (memory 1 1))
3694 (func $assert (param i32)
3695 (block $ok
3696 (br_if $ok
3697 (local.get 0)
3698 )
3699 (unreachable)
3700 )
3701 )
3702 (func (export "call")
3703 (local $return_val i32)
3704 (local.set $return_val
3705 (call $account_reentrance_count (i32.const 0))
3706 )
3707 (call $assert
3708 (i32.eq (local.get $return_val) (i32.const 12))
3709 )
3710 )
3711
3712 (func (export "deploy"))
3713)
3714"#;
3715
3716 let mut mock_ext = MockExt::default();
3717 execute(CODE, vec![], &mut mock_ext).unwrap();
3718 }
3719
3720 #[test]
3721 fn instantiation_nonce_works() {
3722 const CODE: &str = r#"
3723(module
3724 (import "seal0" "instantiation_nonce" (func $nonce (result i64)))
3725 (import "env" "memory" (memory 1 1))
3726
3727 (func $assert (param i32)
3728 (block $ok
3729 (br_if $ok
3730 (local.get 0)
3731 )
3732 (unreachable)
3733 )
3734 )
3735 (func (export "call")
3736 (call $assert
3737 (i64.eq (call $nonce) (i64.const 995))
3738 )
3739 )
3740 (func (export "deploy"))
3741)
3742"#;
3743
3744 let mut mock_ext = MockExt::default();
3745 execute(CODE, vec![], &mut mock_ext).unwrap();
3746 }
3747
3748 #[cfg(not(feature = "runtime-benchmarks"))]
3752 #[test]
3753 fn cannot_deploy_unstable() {
3754 const CANNOT_DEPLOY_UNSTABLE: &str = r#"
3755(module
3756 (import "seal0" "reentrance_count" (func $reentrance_count (result i32)))
3757 (import "env" "memory" (memory 1 1))
3758
3759 (func (export "call"))
3760 (func (export "deploy"))
3761)
3762"#;
3763 assert_err!(
3764 execute_no_unstable(CANNOT_DEPLOY_UNSTABLE, vec![], MockExt::default()),
3765 <Error<Test>>::CodeRejected,
3766 );
3767 assert_ok!(execute(CANNOT_DEPLOY_UNSTABLE, vec![], MockExt::default()));
3768 }
3769
3770 #[cfg(not(feature = "runtime-benchmarks"))]
3774 #[test]
3775 fn cannot_deploy_deprecated() {
3776 const CODE_RANDOM_0: &str = r#"
3777(module
3778 (import "seal0" "seal_random" (func $seal_random (param i32 i32 i32 i32)))
3779 (import "env" "memory" (memory 1 1))
3780
3781 (func (export "call"))
3782 (func (export "deploy"))
3783)
3784 "#;
3785 const CODE_RANDOM_1: &str = r#"
3786(module
3787 (import "seal1" "seal_random" (func $seal_random (param i32 i32 i32 i32)))
3788 (import "env" "memory" (memory 1 1))
3789
3790 (func (export "call"))
3791 (func (export "deploy"))
3792)
3793 "#;
3794 const CODE_RANDOM_2: &str = r#"
3795(module
3796 (import "seal0" "random" (func $seal_random (param i32 i32 i32 i32)))
3797 (import "env" "memory" (memory 1 1))
3798
3799 (func (export "call"))
3800 (func (export "deploy"))
3801)
3802 "#;
3803 const CODE_RANDOM_3: &str = r#"
3804(module
3805 (import "seal1" "random" (func $seal_random (param i32 i32 i32 i32)))
3806 (import "env" "memory" (memory 1 1))
3807
3808 (func (export "call"))
3809 (func (export "deploy"))
3810)
3811 "#;
3812
3813 assert_ok!(execute_unvalidated(CODE_RANDOM_0, vec![], MockExt::default()));
3814 assert_err!(
3815 execute_instantiate_unvalidated(CODE_RANDOM_0, vec![], MockExt::default()),
3816 <Error<Test>>::CodeRejected,
3817 );
3818 assert_err!(
3819 execute(CODE_RANDOM_0, vec![], MockExt::default()),
3820 <Error<Test>>::CodeRejected,
3821 );
3822
3823 assert_ok!(execute_unvalidated(CODE_RANDOM_1, vec![], MockExt::default()));
3824 assert_err!(
3825 execute_instantiate_unvalidated(CODE_RANDOM_1, vec![], MockExt::default()),
3826 <Error<Test>>::CodeRejected,
3827 );
3828 assert_err!(
3829 execute(CODE_RANDOM_1, vec![], MockExt::default()),
3830 <Error<Test>>::CodeRejected,
3831 );
3832
3833 assert_ok!(execute_unvalidated(CODE_RANDOM_2, vec![], MockExt::default()));
3834 assert_err!(
3835 execute_instantiate_unvalidated(CODE_RANDOM_2, vec![], MockExt::default()),
3836 <Error<Test>>::CodeRejected,
3837 );
3838 assert_err!(
3839 execute(CODE_RANDOM_2, vec![], MockExt::default()),
3840 <Error<Test>>::CodeRejected,
3841 );
3842
3843 assert_ok!(execute_unvalidated(CODE_RANDOM_3, vec![], MockExt::default()));
3844 assert_err!(
3845 execute_instantiate_unvalidated(CODE_RANDOM_3, vec![], MockExt::default()),
3846 <Error<Test>>::CodeRejected,
3847 );
3848 assert_err!(
3849 execute(CODE_RANDOM_3, vec![], MockExt::default()),
3850 <Error<Test>>::CodeRejected,
3851 );
3852 }
3853
3854 #[test]
3855 fn lock_unlock_delegate_dependency() {
3856 const CODE_LOCK_UNLOCK_DELEGATE_DEPENDENCY: &str = r#"
3857(module
3858 (import "seal0" "lock_delegate_dependency" (func $lock_delegate_dependency (param i32)))
3859 (import "seal0" "unlock_delegate_dependency" (func $unlock_delegate_dependency (param i32)))
3860 (import "env" "memory" (memory 1 1))
3861 (func (export "call")
3862 (call $lock_delegate_dependency (i32.const 0))
3863 (call $lock_delegate_dependency (i32.const 32))
3864 (call $unlock_delegate_dependency (i32.const 32))
3865 )
3866 (func (export "deploy"))
3867
3868 ;; hash1 (32 bytes)
3869 (data (i32.const 0)
3870 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
3871 "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
3872 )
3873
3874 ;; hash2 (32 bytes)
3875 (data (i32.const 32)
3876 "\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02"
3877 "\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02"
3878 )
3879)
3880"#;
3881 let mut mock_ext = MockExt::default();
3882 assert_ok!(execute(&CODE_LOCK_UNLOCK_DELEGATE_DEPENDENCY, vec![], &mut mock_ext));
3883 let delegate_dependencies: Vec<_> =
3884 mock_ext.delegate_dependencies.into_inner().into_iter().collect();
3885 assert_eq!(delegate_dependencies.len(), 1);
3886 assert_eq!(delegate_dependencies[0].as_bytes(), [1; 32]);
3887 }
3888
3889 #[test]
3892 fn read_sandbox_memory_as_works_with_max_len_out_of_bounds_but_fitting_actual_data() {
3893 use frame_support::BoundedVec;
3894 use sp_core::ConstU32;
3895
3896 let mut ext = MockExt::default();
3897 let runtime = Runtime::new(&mut ext, vec![]);
3898 let data = vec![1u8, 2, 3];
3899 let memory = data.encode();
3900 let decoded: BoundedVec<u8, ConstU32<128>> =
3901 runtime.read_sandbox_memory_as(&memory, 0u32).unwrap();
3902 assert_eq!(decoded.into_inner(), data);
3903 }
3904
3905 #[test]
3906 fn run_out_of_gas_in_start_fn() {
3907 const CODE: &str = r#"
3908(module
3909 (import "env" "memory" (memory 1 1))
3910 (start $start)
3911 (func $start
3912 (loop $inf (br $inf)) ;; just run out of gas
3913 (unreachable)
3914 )
3915 (func (export "call"))
3916 (func (export "deploy"))
3917)
3918"#;
3919 let mut mock_ext = MockExt::default();
3920 assert_err!(execute(&CODE, vec![], &mut mock_ext), <Error<Test>>::OutOfGas);
3921 }
3922}