// Copyright 2018-2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . //! This module provides a means for executing contracts //! represented in wasm. use crate::{CodeHash, Schedule, Trait}; use crate::wasm::env_def::FunctionImplProvider; use crate::exec::{Ext, ExecResult}; use crate::gas::GasMeter; use sp_std::prelude::*; use codec::{Encode, Decode}; use sp_sandbox; #[macro_use] mod env_def; mod code_cache; mod prepare; mod runtime; use self::runtime::{to_execution_result, Runtime}; use self::code_cache::load as load_code; pub use self::code_cache::save as save_code; /// A prepared wasm module ready for execution. #[derive(Clone, Encode, Decode)] pub struct PrefabWasmModule { /// Version of the schedule with which the code was instrumented. #[codec(compact)] schedule_version: u32, #[codec(compact)] initial: u32, #[codec(compact)] maximum: u32, /// This field is reserved for future evolution of format. /// /// Basically, for now this field will be serialized as `None`. In the future /// we would be able to extend this structure with. _reserved: Option<()>, /// Code instrumented with the latest schedule. code: Vec, } /// Wasm executable loaded by `WasmLoader` and executed by `WasmVm`. pub struct WasmExecutable { entrypoint_name: &'static str, prefab_module: PrefabWasmModule, } /// Loader which fetches `WasmExecutable` from the code cache. pub struct WasmLoader<'a> { schedule: &'a Schedule, } impl<'a> WasmLoader<'a> { pub fn new(schedule: &'a Schedule) -> Self { WasmLoader { schedule } } } impl<'a, T: Trait> crate::exec::Loader for WasmLoader<'a> { type Executable = WasmExecutable; fn load_init(&self, code_hash: &CodeHash) -> Result { let prefab_module = load_code::(code_hash, self.schedule)?; Ok(WasmExecutable { entrypoint_name: "deploy", prefab_module, }) } fn load_main(&self, code_hash: &CodeHash) -> Result { let prefab_module = load_code::(code_hash, self.schedule)?; Ok(WasmExecutable { entrypoint_name: "call", prefab_module, }) } } /// Implementation of `Vm` that takes `WasmExecutable` and executes it. pub struct WasmVm<'a> { schedule: &'a Schedule, } impl<'a> WasmVm<'a> { pub fn new(schedule: &'a Schedule) -> Self { WasmVm { schedule } } } impl<'a, T: Trait> crate::exec::Vm for WasmVm<'a> { type Executable = WasmExecutable; fn execute>( &self, exec: &WasmExecutable, mut ext: E, input_data: Vec, gas_meter: &mut GasMeter, ) -> ExecResult { let memory = sp_sandbox::Memory::new(exec.prefab_module.initial, Some(exec.prefab_module.maximum)) .unwrap_or_else(|_| { // unlike `.expect`, explicit panic preserves the source location. // Needed as we can't use `RUST_BACKTRACE` in here. panic!( "exec.prefab_module.initial can't be greater than exec.prefab_module.maximum; thus Memory::new must not fail; qed" ) }); let mut imports = sp_sandbox::EnvironmentDefinitionBuilder::new(); imports.add_memory("env", "memory", memory.clone()); runtime::Env::impls(&mut |name, func_ptr| { imports.add_host_func("env", name, func_ptr); }); let mut runtime = Runtime::new( &mut ext, input_data, &self.schedule, memory, gas_meter, ); // Instantiate the instance from the instrumented module code and invoke the contract // entrypoint. let result = sp_sandbox::Instance::new(&exec.prefab_module.code, &imports, &mut runtime) .and_then(|mut instance| instance.invoke(exec.entrypoint_name, &[], &mut runtime)); to_execution_result(runtime, result) } } #[cfg(test)] mod tests { use super::*; use std::collections::HashMap; use std::cell::RefCell; use sp_core::H256; use crate::exec::{Ext, StorageKey, ExecError, ExecReturnValue, STATUS_SUCCESS}; use crate::gas::{Gas, GasMeter}; use crate::tests::{Test, Call}; use crate::wasm::prepare::prepare_contract; use crate::CodeHash; use wabt; use hex_literal::hex; use assert_matches::assert_matches; use sp_runtime::DispatchError; #[derive(Debug, PartialEq, Eq)] struct DispatchEntry(Call); #[derive(Debug, PartialEq, Eq)] struct RestoreEntry { dest: u64, code_hash: H256, rent_allowance: u64, delta: Vec, } #[derive(Debug, PartialEq, Eq)] struct InstantiateEntry { code_hash: H256, endowment: u64, data: Vec, gas_left: u64, } #[derive(Debug, PartialEq, Eq)] struct TerminationEntry { beneficiary: u64, gas_left: u64, } #[derive(Debug, PartialEq, Eq)] struct TransferEntry { to: u64, value: u64, data: Vec, gas_left: u64, } #[derive(Default)] pub struct MockExt { storage: HashMap>, rent_allowance: u64, instantiates: Vec, terminations: Vec, transfers: Vec, dispatches: Vec, restores: Vec, // (topics, data) events: Vec<(Vec, Vec)>, next_account_id: u64, /// Runtime storage keys works the following way. /// /// - If the test code requests a value and it doesn't exist in this storage map then a /// panic happens. /// - If the value does exist it is returned and then removed from the map. So a panic /// happens if the same value is requested for the second time. /// /// This behavior is used to prevent mixing up an access to unexpected location and empty /// cell. runtime_storage_keys: RefCell, Option>>>, } impl Ext for MockExt { type T = Test; fn get_storage(&self, key: &StorageKey) -> Option> { self.storage.get(key).cloned() } fn set_storage(&mut self, key: StorageKey, value: Option>) -> Result<(), &'static str> { *self.storage.entry(key).or_insert(Vec::new()) = value.unwrap_or(Vec::new()); Ok(()) } fn instantiate( &mut self, code_hash: &CodeHash, endowment: u64, gas_meter: &mut GasMeter, data: Vec, ) -> Result<(u64, ExecReturnValue), ExecError> { self.instantiates.push(InstantiateEntry { code_hash: code_hash.clone(), endowment, data: data.to_vec(), gas_left: gas_meter.gas_left(), }); let address = self.next_account_id; self.next_account_id += 1; Ok(( address, ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new(), }, )) } fn transfer( &mut self, to: &u64, value: u64, gas_meter: &mut GasMeter, ) -> Result<(), DispatchError> { self.transfers.push(TransferEntry { to: *to, value, data: Vec::new(), gas_left: gas_meter.gas_left(), }); Ok(()) } fn call( &mut self, to: &u64, value: u64, gas_meter: &mut GasMeter, data: Vec, ) -> ExecResult { self.transfers.push(TransferEntry { to: *to, value, data: data, gas_left: gas_meter.gas_left(), }); // Assume for now that it was just a plain transfer. // TODO: Add tests for different call outcomes. Ok(ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new() }) } fn terminate( &mut self, beneficiary: &u64, gas_meter: &mut GasMeter, ) -> Result<(), DispatchError> { self.terminations.push(TerminationEntry { beneficiary: *beneficiary, gas_left: gas_meter.gas_left(), }); Ok(()) } fn note_dispatch_call(&mut self, call: Call) { self.dispatches.push(DispatchEntry(call)); } fn note_restore_to( &mut self, dest: u64, code_hash: H256, rent_allowance: u64, delta: Vec, ) { self.restores.push(RestoreEntry { dest, code_hash, rent_allowance, delta, }); } fn caller(&self) -> &u64 { &42 } fn address(&self) -> &u64 { &69 } fn balance(&self) -> u64 { 228 } fn value_transferred(&self) -> u64 { 1337 } fn now(&self) -> &u64 { &1111 } fn minimum_balance(&self) -> u64 { 666 } fn tombstone_deposit(&self) -> u64 { 16 } fn random(&self, subject: &[u8]) -> H256 { H256::from_slice(subject) } fn deposit_event(&mut self, topics: Vec, data: Vec) { self.events.push((topics, data)) } fn set_rent_allowance(&mut self, rent_allowance: u64) { self.rent_allowance = rent_allowance; } fn rent_allowance(&self) -> u64 { self.rent_allowance } fn block_number(&self) -> u64 { 121 } fn max_value_size(&self) -> u32 { 16_384 } fn get_runtime_storage(&self, key: &[u8]) -> Option> { let opt_value = self.runtime_storage_keys .borrow_mut() .remove(key); opt_value.unwrap_or_else(|| panic!( "{:?} doesn't exist. values that do exist {:?}", key, self.runtime_storage_keys ) ) } } impl Ext for &mut MockExt { type T = ::T; fn get_storage(&self, key: &[u8; 32]) -> Option> { (**self).get_storage(key) } fn set_storage(&mut self, key: [u8; 32], value: Option>) -> Result<(), &'static str> { (**self).set_storage(key, value) } fn instantiate( &mut self, code: &CodeHash, value: u64, gas_meter: &mut GasMeter, input_data: Vec, ) -> Result<(u64, ExecReturnValue), ExecError> { (**self).instantiate(code, value, gas_meter, input_data) } fn transfer( &mut self, to: &u64, value: u64, gas_meter: &mut GasMeter, ) -> Result<(), DispatchError> { (**self).transfer(to, value, gas_meter) } fn terminate( &mut self, beneficiary: &u64, gas_meter: &mut GasMeter, ) -> Result<(), DispatchError> { (**self).terminate(beneficiary, gas_meter) } fn call( &mut self, to: &u64, value: u64, gas_meter: &mut GasMeter, input_data: Vec, ) -> ExecResult { (**self).call(to, value, gas_meter, input_data) } fn note_dispatch_call(&mut self, call: Call) { (**self).note_dispatch_call(call) } fn note_restore_to( &mut self, dest: u64, code_hash: H256, rent_allowance: u64, delta: Vec, ) { (**self).note_restore_to( dest, code_hash, rent_allowance, delta, ) } fn caller(&self) -> &u64 { (**self).caller() } fn address(&self) -> &u64 { (**self).address() } fn balance(&self) -> u64 { (**self).balance() } fn value_transferred(&self) -> u64 { (**self).value_transferred() } fn now(&self) -> &u64 { (**self).now() } fn minimum_balance(&self) -> u64 { (**self).minimum_balance() } fn tombstone_deposit(&self) -> u64 { (**self).tombstone_deposit() } fn random(&self, subject: &[u8]) -> H256 { (**self).random(subject) } fn deposit_event(&mut self, topics: Vec, data: Vec) { (**self).deposit_event(topics, data) } fn set_rent_allowance(&mut self, rent_allowance: u64) { (**self).set_rent_allowance(rent_allowance) } fn rent_allowance(&self) -> u64 { (**self).rent_allowance() } fn block_number(&self) -> u64 { (**self).block_number() } fn max_value_size(&self) -> u32 { (**self).max_value_size() } fn get_runtime_storage(&self, key: &[u8]) -> Option> { (**self).get_runtime_storage(key) } } fn execute( wat: &str, input_data: Vec, ext: E, gas_meter: &mut GasMeter, ) -> ExecResult { use crate::exec::Vm; let wasm = wabt::wat2wasm(wat).unwrap(); let schedule = crate::Schedule::default(); let prefab_module = prepare_contract::(&wasm, &schedule).unwrap(); let exec = WasmExecutable { // Use a "call" convention. entrypoint_name: "call", prefab_module, }; let cfg = Default::default(); let vm = WasmVm::new(&cfg); vm.execute(&exec, ext, input_data, gas_meter) } const CODE_TRANSFER: &str = r#" (module ;; ext_transfer( ;; account_ptr: u32, ;; account_len: u32, ;; value_ptr: u32, ;; value_len: u32, ;;) -> u32 (import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32 i32) (result i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (drop (call $ext_transfer (i32.const 4) ;; Pointer to "account" address. (i32.const 8) ;; Length of "account" address. (i32.const 12) ;; Pointer to the buffer with value to transfer (i32.const 8) ;; Length of the buffer with value to transfer. ) ) ) (func (export "deploy")) ;; Destination AccountId to transfer the funds. ;; Represented by u64 (8 bytes long) in little endian. (data (i32.const 4) "\07\00\00\00\00\00\00\00") ;; Amount of value to transfer. ;; Represented by u64 (8 bytes long) in little endian. (data (i32.const 12) "\99\00\00\00\00\00\00\00") ) "#; #[test] fn contract_transfer() { let mut mock_ext = MockExt::default(); let _ = execute( CODE_TRANSFER, vec![], &mut mock_ext, &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!( &mock_ext.transfers, &[TransferEntry { to: 7, value: 153, data: Vec::new(), gas_left: 49978, }] ); } const CODE_CALL: &str = r#" (module ;; ext_call( ;; callee_ptr: u32, ;; callee_len: u32, ;; gas: u64, ;; value_ptr: u32, ;; value_len: u32, ;; input_data_ptr: u32, ;; input_data_len: u32 ;;) -> u32 (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (drop (call $ext_call (i32.const 4) ;; Pointer to "callee" address. (i32.const 8) ;; Length of "callee" address. (i64.const 0) ;; How much gas to devote for the execution. 0 = all. (i32.const 12) ;; Pointer to the buffer with value to transfer (i32.const 8) ;; Length of the buffer with value to transfer. (i32.const 20) ;; Pointer to input data buffer address (i32.const 4) ;; Length of input data buffer ) ) ) (func (export "deploy")) ;; Destination AccountId to transfer the funds. ;; Represented by u64 (8 bytes long) in little endian. (data (i32.const 4) "\09\00\00\00\00\00\00\00") ;; Amount of value to transfer. ;; Represented by u64 (8 bytes long) in little endian. (data (i32.const 12) "\06\00\00\00\00\00\00\00") (data (i32.const 20) "\01\02\03\04") ) "#; #[test] fn contract_call() { let mut mock_ext = MockExt::default(); let _ = execute( CODE_CALL, vec![], &mut mock_ext, &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!( &mock_ext.transfers, &[TransferEntry { to: 9, value: 6, data: vec![1, 2, 3, 4], gas_left: 49971, }] ); } const CODE_INSTANTIATE: &str = r#" (module ;; ext_instantiate( ;; code_ptr: u32, ;; code_len: u32, ;; gas: u64, ;; value_ptr: u32, ;; value_len: u32, ;; input_data_ptr: u32, ;; input_data_len: u32, ;; ) -> u32 (import "env" "ext_instantiate" (func $ext_instantiate (param i32 i32 i64 i32 i32 i32 i32) (result i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (drop (call $ext_instantiate (i32.const 16) ;; Pointer to `code_hash` (i32.const 32) ;; Length of `code_hash` (i64.const 0) ;; How much gas to devote for the execution. 0 = all. (i32.const 4) ;; Pointer to the buffer with value to transfer (i32.const 8) ;; Length of the buffer with value to transfer (i32.const 12) ;; Pointer to input data buffer address (i32.const 4) ;; Length of input data buffer ) ) ) (func (export "deploy")) ;; Amount of value to transfer. ;; Represented by u64 (8 bytes long) in little endian. (data (i32.const 4) "\03\00\00\00\00\00\00\00") ;; Input data to pass to the contract being instantiated. (data (i32.const 12) "\01\02\03\04") ;; Hash of code. (data (i32.const 16) "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11" "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11" ) ) "#; #[test] fn contract_instantiate() { let mut mock_ext = MockExt::default(); let _ = execute( CODE_INSTANTIATE, vec![], &mut mock_ext, &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!( &mock_ext.instantiates, &[InstantiateEntry { code_hash: [0x11; 32].into(), endowment: 3, data: vec![1, 2, 3, 4], gas_left: 49947, }] ); } const CODE_TERMINATE: &str = r#" (module ;; ext_terminate( ;; beneficiary_ptr: u32, ;; beneficiary_len: u32, ;; ) (import "env" "ext_terminate" (func $ext_terminate (param i32 i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (call $ext_terminate (i32.const 4) ;; Pointer to "beneficiary" address. (i32.const 8) ;; Length of "beneficiary" address. ) ) (func (export "deploy")) ;; Beneficiary AccountId to transfer the funds. ;; Represented by u64 (8 bytes long) in little endian. (data (i32.const 4) "\09\00\00\00\00\00\00\00") ) "#; #[test] fn contract_terminate() { let mut mock_ext = MockExt::default(); execute( CODE_TERMINATE, vec![], &mut mock_ext, &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!( &mock_ext.terminations, &[TerminationEntry { beneficiary: 0x09, gas_left: 49989, }] ); } const CODE_TRANSFER_LIMITED_GAS: &str = r#" (module ;; ext_call( ;; callee_ptr: u32, ;; callee_len: u32, ;; gas: u64, ;; value_ptr: u32, ;; value_len: u32, ;; input_data_ptr: u32, ;; input_data_len: u32 ;;) -> u32 (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (drop (call $ext_call (i32.const 4) ;; Pointer to "callee" address. (i32.const 8) ;; Length of "callee" address. (i64.const 228) ;; How much gas to devote for the execution. (i32.const 12) ;; Pointer to the buffer with value to transfer (i32.const 8) ;; Length of the buffer with value to transfer. (i32.const 20) ;; Pointer to input data buffer address (i32.const 4) ;; Length of input data buffer ) ) ) (func (export "deploy")) ;; Destination AccountId to transfer the funds. ;; Represented by u64 (8 bytes long) in little endian. (data (i32.const 4) "\09\00\00\00\00\00\00\00") ;; Amount of value to transfer. ;; Represented by u64 (8 bytes long) in little endian. (data (i32.const 12) "\06\00\00\00\00\00\00\00") (data (i32.const 20) "\01\02\03\04") ) "#; #[test] fn contract_call_limited_gas() { let mut mock_ext = MockExt::default(); let _ = execute( &CODE_TRANSFER_LIMITED_GAS, vec![], &mut mock_ext, &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!( &mock_ext.transfers, &[TransferEntry { to: 9, value: 6, data: vec![1, 2, 3, 4], gas_left: 228, }] ); } const CODE_GET_STORAGE: &str = r#" (module (import "env" "ext_get_storage" (func $ext_get_storage (param i32) (result i32))) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "ext_return" (func $ext_return (param i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") (local $buf_size i32) ;; Load a storage value into the scratch buf. (call $assert (i32.eq (call $ext_get_storage (i32.const 4) ;; The pointer to the storage key to fetch ) ;; Return value 0 means that the value is found and there were ;; no errors. (i32.const 0) ) ) ;; Find out the size of the scratch buffer (set_local $buf_size (call $ext_scratch_size) ) ;; Copy scratch buffer into this contract memory. (call $ext_scratch_read (i32.const 36) ;; The pointer where to store the scratch buffer contents, ;; 36 = 4 + 32 (i32.const 0) ;; Offset from the start of the scratch buffer. (get_local ;; Count of bytes to copy. $buf_size ) ) ;; Return the contents of the buffer (call $ext_return (i32.const 36) (get_local $buf_size) ) ;; env:ext_return doesn't return, so this is effectively unreachable. (unreachable) ) (func (export "deploy")) (data (i32.const 4) "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11" "\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11" ) ) "#; #[test] fn get_storage_puts_data_into_scratch_buf() { let mut mock_ext = MockExt::default(); mock_ext .storage .insert([0x11; 32], [0x22; 32].to_vec()); let output = execute( CODE_GET_STORAGE, vec![], mock_ext, &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: [0x22; 32].to_vec() }); } /// calls `ext_caller`, loads the address from the scratch buffer and /// compares it with the constant 42. const CODE_CALLER: &str = r#" (module (import "env" "ext_caller" (func $ext_caller)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") ;; fill the scratch buffer with the caller. (call $ext_caller) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i64 value of 42. (call $assert (i64.eq (i64.load (i32.const 8) ) (i64.const 42) ) ) ) (func (export "deploy")) ) "#; #[test] fn caller() { let _ = execute( CODE_CALLER, vec![], MockExt::default(), &mut GasMeter::with_limit(50_000, 1), ).unwrap(); } /// calls `ext_address`, loads the address from the scratch buffer and /// compares it with the constant 69. const CODE_ADDRESS: &str = r#" (module (import "env" "ext_address" (func $ext_address)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") ;; fill the scratch buffer with the self address. (call $ext_address) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i64 value of 69. (call $assert (i64.eq (i64.load (i32.const 8) ) (i64.const 69) ) ) ) (func (export "deploy")) ) "#; #[test] fn address() { let _ = execute( CODE_ADDRESS, vec![], MockExt::default(), &mut GasMeter::with_limit(50_000, 1), ).unwrap(); } const CODE_BALANCE: &str = r#" (module (import "env" "ext_balance" (func $ext_balance)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") ;; This stores the balance in the scratch buffer (call $ext_balance) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i64 value of 228. (call $assert (i64.eq (i64.load (i32.const 8) ) (i64.const 228) ) ) ) (func (export "deploy")) ) "#; #[test] fn balance() { let mut gas_meter = GasMeter::with_limit(50_000, 1); let _ = execute( CODE_BALANCE, vec![], MockExt::default(), &mut gas_meter, ).unwrap(); } const CODE_GAS_PRICE: &str = r#" (module (import "env" "ext_gas_price" (func $ext_gas_price)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") ;; This stores the gas price in the scratch buffer (call $ext_gas_price) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i64 value of 1312. (call $assert (i64.eq (i64.load (i32.const 8) ) (i64.const 1312) ) ) ) (func (export "deploy")) ) "#; #[test] fn gas_price() { let mut gas_meter = GasMeter::with_limit(50_000, 1312); let _ = execute( CODE_GAS_PRICE, vec![], MockExt::default(), &mut gas_meter, ).unwrap(); } const CODE_GAS_LEFT: &str = r#" (module (import "env" "ext_gas_left" (func $ext_gas_left)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "ext_return" (func $ext_return (param i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") ;; This stores the gas left in the scratch buffer (call $ext_gas_left) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) (call $ext_return (i32.const 8) (i32.const 8) ) (unreachable) ) (func (export "deploy")) ) "#; #[test] fn gas_left() { let mut gas_meter = GasMeter::with_limit(50_000, 1312); let output = execute( CODE_GAS_LEFT, vec![], MockExt::default(), &mut gas_meter, ).unwrap(); let gas_left = Gas::decode(&mut output.data.as_slice()).unwrap(); assert!(gas_left < 50_000, "gas_left must be less than initial"); assert!(gas_left > gas_meter.gas_left(), "gas_left must be greater than final"); } const CODE_VALUE_TRANSFERRED: &str = r#" (module (import "env" "ext_value_transferred" (func $ext_value_transferred)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") ;; This stores the value transferred in the scratch buffer (call $ext_value_transferred) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i64 value of 1337. (call $assert (i64.eq (i64.load (i32.const 8) ) (i64.const 1337) ) ) ) (func (export "deploy")) ) "#; #[test] fn value_transferred() { let mut gas_meter = GasMeter::with_limit(50_000, 1); let _ = execute( CODE_VALUE_TRANSFERRED, vec![], MockExt::default(), &mut gas_meter, ).unwrap(); } const CODE_DISPATCH_CALL: &str = r#" (module (import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (call $ext_dispatch_call (i32.const 8) ;; Pointer to the start of encoded call buffer (i32.const 13) ;; Length of the buffer ) ) (func (export "deploy")) (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00") ) "#; #[test] fn dispatch_call() { // This test can fail due to the encoding changes. In case it becomes too annoying // let's rewrite so as we use this module controlled call or we serialize it in runtime. let mut mock_ext = MockExt::default(); let _ = execute( CODE_DISPATCH_CALL, vec![], &mut mock_ext, &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!( &mock_ext.dispatches, &[DispatchEntry( Call::Balances(pallet_balances::Call::set_balance(42, 1337, 0)), )] ); } const CODE_RETURN_FROM_START_FN: &str = r#" (module (import "env" "ext_return" (func $ext_return (param i32 i32))) (import "env" "memory" (memory 1 1)) (start $start) (func $start (call $ext_return (i32.const 8) (i32.const 4) ) (unreachable) ) (func (export "call") (unreachable) ) (func (export "deploy")) (data (i32.const 8) "\01\02\03\04") ) "#; #[test] fn return_from_start_fn() { let output = execute( CODE_RETURN_FROM_START_FN, vec![], MockExt::default(), &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: vec![1, 2, 3, 4] }); } const CODE_TIMESTAMP_NOW: &str = r#" (module (import "env" "ext_now" (func $ext_now)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") ;; This stores the block timestamp in the scratch buffer (call $ext_now) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i64 value of 1111. (call $assert (i64.eq (i64.load (i32.const 8) ) (i64.const 1111) ) ) ) (func (export "deploy")) ) "#; #[test] fn now() { let mut gas_meter = GasMeter::with_limit(50_000, 1); let _ = execute( CODE_TIMESTAMP_NOW, vec![], MockExt::default(), &mut gas_meter, ).unwrap(); } const CODE_MINIMUM_BALANCE: &str = r#" (module (import "env" "ext_minimum_balance" (func $ext_minimum_balance)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") (call $ext_minimum_balance) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i64 value of 666. (call $assert (i64.eq (i64.load (i32.const 8) ) (i64.const 666) ) ) ) (func (export "deploy")) ) "#; #[test] fn minimum_balance() { let mut gas_meter = GasMeter::with_limit(50_000, 1); let _ = execute( CODE_MINIMUM_BALANCE, vec![], MockExt::default(), &mut gas_meter, ).unwrap(); } const CODE_TOMBSTONE_DEPOSIT: &str = r#" (module (import "env" "ext_tombstone_deposit" (func $ext_tombstone_deposit)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") (call $ext_tombstone_deposit) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i64 value of 16. (call $assert (i64.eq (i64.load (i32.const 8) ) (i64.const 16) ) ) ) (func (export "deploy")) ) "#; #[test] fn tombstone_deposit() { let mut gas_meter = GasMeter::with_limit(50_000, 1); let _ = execute( CODE_TOMBSTONE_DEPOSIT, vec![], MockExt::default(), &mut gas_meter, ).unwrap(); } const CODE_RANDOM: &str = r#" (module (import "env" "ext_random" (func $ext_random (param i32 i32))) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "ext_return" (func $ext_return (param i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") ;; This stores the block random seed in the scratch buffer (call $ext_random (i32.const 40) ;; Pointer in memory to the start of the subject buffer (i32.const 32) ;; The subject buffer's length ) ;; assert $ext_scratch_size == 32 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 32) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 32) ;; Count of bytes to copy. ) ;; return the data from the contract (call $ext_return (i32.const 8) (i32.const 32) ) ) (func (export "deploy")) ;; [8,40) is reserved for the result of PRNG. ;; the subject used for the PRNG. [40,72) (data (i32.const 40) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F" "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F" ) ) "#; #[test] fn random() { let mut gas_meter = GasMeter::with_limit(50_000, 1); let output = execute( CODE_RANDOM, vec![], MockExt::default(), &mut gas_meter, ).unwrap(); // The mock ext just returns the same data that was passed as the subject. assert_eq!( output, ExecReturnValue { status: STATUS_SUCCESS, data: hex!("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F").to_vec(), }, ); } const CODE_DEPOSIT_EVENT: &str = r#" (module (import "env" "ext_deposit_event" (func $ext_deposit_event (param i32 i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (call $ext_deposit_event (i32.const 32) ;; Pointer to the start of topics buffer (i32.const 33) ;; The length of the topics buffer. (i32.const 8) ;; Pointer to the start of the data buffer (i32.const 13) ;; Length of the buffer ) ) (func (export "deploy")) (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00") ;; Encoded Vec>, the buffer has length of 33 bytes. (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" "\33\33\33\33\33\33\33\33\33") ) "#; #[test] fn deposit_event() { let mut mock_ext = MockExt::default(); let mut gas_meter = GasMeter::with_limit(50_000, 1); let _ = execute( CODE_DEPOSIT_EVENT, vec![], &mut mock_ext, &mut gas_meter ).unwrap(); assert_eq!(mock_ext.events, vec![ (vec![H256::repeat_byte(0x33)], vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00]) ]); assert_eq!(gas_meter.gas_left(), 49934); } const CODE_DEPOSIT_EVENT_MAX_TOPICS: &str = r#" (module (import "env" "ext_deposit_event" (func $ext_deposit_event (param i32 i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (call $ext_deposit_event (i32.const 32) ;; Pointer to the start of topics buffer (i32.const 161) ;; The length of the topics buffer. (i32.const 8) ;; Pointer to the start of the data buffer (i32.const 13) ;; Length of the buffer ) ) (func (export "deploy")) (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00") ;; Encoded Vec>, the buffer has length of 161 bytes. (data (i32.const 32) "\14" "\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" "\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" "\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" "\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" "\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") ) "#; #[test] fn deposit_event_max_topics() { // Checks that the runtime traps if there are more than `max_topic_events` topics. let mut gas_meter = GasMeter::with_limit(50_000, 1); assert_matches!( execute( CODE_DEPOSIT_EVENT_MAX_TOPICS, vec![], MockExt::default(), &mut gas_meter ), Err(ExecError { reason: DispatchError::Other("contract trapped during execution"), buffer: _ }) ); } const CODE_DEPOSIT_EVENT_DUPLICATES: &str = r#" (module (import "env" "ext_deposit_event" (func $ext_deposit_event (param i32 i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (call $ext_deposit_event (i32.const 32) ;; Pointer to the start of topics buffer (i32.const 129) ;; The length of the topics buffer. (i32.const 8) ;; Pointer to the start of the data buffer (i32.const 13) ;; Length of the buffer ) ) (func (export "deploy")) (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00") ;; Encoded Vec>, the buffer has length of 129 bytes. (data (i32.const 32) "\10" "\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" "\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" "\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" "\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") ) "#; #[test] fn deposit_event_duplicates() { // Checks that the runtime traps if there are duplicates. let mut gas_meter = GasMeter::with_limit(50_000, 1); assert_matches!( execute( CODE_DEPOSIT_EVENT_DUPLICATES, vec![], MockExt::default(), &mut gas_meter ), Err(ExecError { reason: DispatchError::Other("contract trapped during execution"), buffer: _ }) ); } /// calls `ext_block_number`, loads the current block number from the scratch buffer and /// compares it with the constant 121. const CODE_BLOCK_NUMBER: &str = r#" (module (import "env" "ext_block_number" (func $ext_block_number)) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "call") ;; This stores the block height in the scratch buffer (call $ext_block_number) ;; assert $ext_scratch_size == 8 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 8) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 8) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 8) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i64 value of 121. (call $assert (i64.eq (i64.load (i32.const 8) ) (i64.const 121) ) ) ) (func (export "deploy")) ) "#; #[test] fn block_number() { let _ = execute( CODE_BLOCK_NUMBER, vec![], MockExt::default(), &mut GasMeter::with_limit(50_000, 1), ).unwrap(); } // asserts that the size of the input data is 4. const CODE_SIMPLE_ASSERT: &str = r#" (module (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func (export "deploy")) (func (export "call") (call $assert (i32.eq (call $ext_scratch_size) (i32.const 4) ) ) ) ) "#; #[test] fn output_buffer_capacity_preserved_on_success() { let mut input_data = Vec::with_capacity(1_234); input_data.extend_from_slice(&[1, 2, 3, 4][..]); let output = execute( CODE_SIMPLE_ASSERT, input_data, MockExt::default(), &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!(output.data.len(), 0); assert_eq!(output.data.capacity(), 1_234); } #[test] fn output_buffer_capacity_preserved_on_failure() { let mut input_data = Vec::with_capacity(1_234); input_data.extend_from_slice(&[1, 2, 3, 4, 5][..]); let error = execute( CODE_SIMPLE_ASSERT, input_data, MockExt::default(), &mut GasMeter::with_limit(50_000, 1), ).err().unwrap(); assert_eq!(error.buffer.capacity(), 1_234); } const CODE_RETURN_WITH_DATA: &str = r#" (module (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "ext_scratch_write" (func $ext_scratch_write (param i32 i32))) (import "env" "memory" (memory 1 1)) ;; Deploy routine is the same as call. (func (export "deploy") (result i32) (call $call) ) ;; Call reads the first 4 bytes (LE) as the exit status and returns the rest as output data. (func $call (export "call") (result i32) (local $buf_size i32) (local $exit_status i32) ;; Find out the size of the scratch buffer (set_local $buf_size (call $ext_scratch_size)) ;; Copy scratch buffer into this contract memory. (call $ext_scratch_read (i32.const 0) ;; The pointer where to store the scratch buffer contents, (i32.const 0) ;; Offset from the start of the scratch buffer. (get_local $buf_size) ;; Count of bytes to copy. ) ;; Copy all but the first 4 bytes of the input data as the output data. (call $ext_scratch_write (i32.const 4) ;; Offset from the start of the scratch buffer. (i32.sub ;; Count of bytes to copy. (get_local $buf_size) (i32.const 4) ) ) ;; Return the first 4 bytes of the input data as the exit status. (i32.load (i32.const 0)) ) ) "#; #[test] fn return_with_success_status() { let output = execute( CODE_RETURN_WITH_DATA, hex!("00112233445566778899").to_vec(), MockExt::default(), &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!(output, ExecReturnValue { status: 0, data: hex!("445566778899").to_vec() }); assert!(output.is_success()); } #[test] fn return_with_failure_status() { let output = execute( CODE_RETURN_WITH_DATA, hex!("112233445566778899").to_vec(), MockExt::default(), &mut GasMeter::with_limit(50_000, 1), ).unwrap(); assert_eq!(output, ExecReturnValue { status: 17, data: hex!("5566778899").to_vec() }); assert!(!output.is_success()); } const CODE_GET_RUNTIME_STORAGE: &str = r#" (module (import "env" "ext_get_runtime_storage" (func $ext_get_runtime_storage (param i32 i32) (result i32)) ) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) (import "env" "ext_scratch_write" (func $ext_scratch_write (param i32 i32))) (import "env" "memory" (memory 1 1)) (func (export "deploy")) (func $assert (param i32) (block $ok (br_if $ok (get_local 0) ) (unreachable) ) ) (func $call (export "call") ;; Load runtime storage for the first key and assert that it exists. (call $assert (i32.eq (call $ext_get_runtime_storage (i32.const 16) (i32.const 4) ) (i32.const 0) ) ) ;; assert $ext_scratch_size == 4 (call $assert (i32.eq (call $ext_scratch_size) (i32.const 4) ) ) ;; copy contents of the scratch buffer into the contract's memory. (call $ext_scratch_read (i32.const 4) ;; Pointer in memory to the place where to copy. (i32.const 0) ;; Offset from the start of the scratch buffer. (i32.const 4) ;; Count of bytes to copy. ) ;; assert that contents of the buffer is equal to the i32 value of 0x14144020. (call $assert (i32.eq (i32.load (i32.const 4) ) (i32.const 0x14144020) ) ) ;; Load the second key and assert that it doesn't exist. (call $assert (i32.eq (call $ext_get_runtime_storage (i32.const 20) (i32.const 4) ) (i32.const 1) ) ) ) ;; The first key, 4 bytes long. (data (i32.const 16) "\01\02\03\04") ;; The second key, 4 bytes long. (data (i32.const 20) "\02\03\04\05") ) "#; #[test] fn get_runtime_storage() { let mut gas_meter = GasMeter::with_limit(50_000, 1); let mock_ext = MockExt::default(); // "\01\02\03\04" - Some(0x14144020) // "\02\03\04\05" - None *mock_ext.runtime_storage_keys.borrow_mut() = [ ([1, 2, 3, 4].to_vec(), Some(0x14144020u32.to_le_bytes().to_vec())), ([2, 3, 4, 5].to_vec().to_vec(), None), ] .iter() .cloned() .collect(); let _ = execute( CODE_GET_RUNTIME_STORAGE, vec![], mock_ext, &mut gas_meter, ).unwrap(); } }