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