One node two runtimes (#191)

* One node two runtimes

This enables the rococo-collator to run the normal and the contracts runtime.

* Fix tests
This commit is contained in:
Bastian Köcher
2020-08-11 11:35:54 +02:00
committed by GitHub
parent 3ed6030110
commit 3b71c2a6e2
33 changed files with 1958 additions and 1466 deletions
@@ -20,9 +20,9 @@
use std::sync::Arc;
use codec::Codec;
use cumulus_pallet_contracts_primitives::RentProjection;
use jsonrpc_core::{Error, ErrorCode, Result};
use jsonrpc_derive::rpc;
use cumulus_pallet_contracts_primitives::RentProjection;
use serde::{Deserialize, Serialize};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
@@ -106,7 +106,11 @@ pub enum RpcContractExecResult {
impl From<ContractExecResult> for RpcContractExecResult {
fn from(r: ContractExecResult) -> Self {
match r {
ContractExecResult::Success { flags, data, gas_consumed } => RpcContractExecResult::Success {
ContractExecResult::Success {
flags,
data,
gas_consumed,
} => RpcContractExecResult::Success {
flags,
data: data.into(),
gas_consumed,
@@ -293,7 +297,8 @@ mod tests {
#[test]
fn call_request_should_serialize_deserialize_properly() {
type Req = CallRequest<String, u128>;
let req: Req = serde_json::from_str(r#"
let req: Req = serde_json::from_str(
r#"
{
"origin": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
"dest": "5DRakbLVnjVrW6niwLfHGW24EeCEvDAFGEXrtaYS5M4ynoom",
@@ -301,7 +306,9 @@ mod tests {
"gasLimit": 1000000000000,
"inputData": "0x8c97db39"
}
"#).unwrap();
"#,
)
.unwrap();
assert_eq!(req.gas_limit.into_u256(), U256::from(0xe8d4a51000u64));
}
@@ -21,14 +21,19 @@ use super::{
TrieIdGenerator,
};
use crate::exec::StorageKey;
use sp_std::cell::RefCell;
use sp_std::collections::btree_map::{BTreeMap, Entry};
use sp_std::prelude::*;
use frame_support::{
storage::unhashed as storage,
traits::{Currency, Imbalance, SignedImbalance},
StorageMap,
};
use frame_system;
use sp_io::hashing::blake2_256;
use sp_runtime::traits::{Bounded, Zero};
use frame_support::traits::{Currency, Imbalance, SignedImbalance};
use frame_support::{storage::unhashed as storage, StorageMap};
use frame_system;
use sp_std::{
cell::RefCell,
collections::btree_map::{BTreeMap, Entry},
prelude::*,
};
// Note: we don't provide Option<Contract> because we can't create
// the trie_id in the overlay, thus we provide an overlay on the fields
@@ -133,8 +138,7 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
trie_id: Option<&TrieId>,
location: &StorageKey,
) -> Option<Vec<u8>> {
trie_id
.and_then(|id| storage::get_raw(&crate::prefixed_key(id, &blake2_256(location))))
trie_id.and_then(|id| storage::get_raw(&crate::prefixed_key(id, &blake2_256(location))))
}
fn get_code_hash(&self, account: &T::AccountId) -> Option<CodeHash<T>> {
<ContractInfoOf<T>>::get(account).and_then(|i| i.as_alive().map(|i| i.code_hash))
@@ -241,13 +245,13 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
if prev_value.is_empty() {
new_info.empty_pair_count -= 1;
}
},
}
(None, Some(new_value)) => {
new_info.total_pair_count += 1;
if new_value.is_empty() {
new_info.empty_pair_count += 1;
}
},
}
(Some(prev_value), Some(new_value)) => {
if prev_value.is_empty() {
new_info.empty_pair_count -= 1;
File diff suppressed because it is too large Load Diff
@@ -15,11 +15,11 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use crate::Trait;
use sp_std::marker::PhantomData;
use sp_runtime::traits::Zero;
use frame_support::dispatch::{
DispatchError, DispatchResultWithPostInfo, PostDispatchInfo, DispatchErrorWithPostInfo,
DispatchError, DispatchErrorWithPostInfo, DispatchResultWithPostInfo, PostDispatchInfo,
};
use sp_runtime::traits::Zero;
use sp_std::marker::PhantomData;
#[cfg(test)]
use std::{any::Any, fmt::Debug};
@@ -189,7 +189,8 @@ impl<T: Trait> GasMeter<T> {
}
/// Turn this GasMeter into a DispatchResult that contains the actually used gas.
pub fn into_dispatch_result<R, E>(self, result: Result<R, E>) -> DispatchResultWithPostInfo where
pub fn into_dispatch_result<R, E>(self, result: Result<R, E>) -> DispatchResultWithPostInfo
where
E: Into<DispatchError>,
{
let post_info = PostDispatchInfo {
@@ -199,7 +200,10 @@ impl<T: Trait> GasMeter<T> {
result
.map(|_| post_info)
.map_err(|e| DispatchErrorWithPostInfo { post_info, error: e.into() })
.map_err(|e| DispatchErrorWithPostInfo {
post_info,
error: e.into(),
})
}
#[cfg(test)]
@@ -256,7 +260,9 @@ mod tests {
struct SimpleToken(u64);
impl Token<Test> for SimpleToken {
type Metadata = ();
fn calculate_amount(&self, _metadata: &()) -> u64 { self.0 }
fn calculate_amount(&self, _metadata: &()) -> u64 {
self.0
}
}
struct MultiplierTokenMetadata {
@@ -285,8 +291,10 @@ mod tests {
fn simple() {
let mut gas_meter = GasMeter::<Test>::new(50000);
let result = gas_meter
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10));
let result = gas_meter.charge(
&MultiplierTokenMetadata { multiplier: 3 },
MultiplierToken(10),
);
assert!(!result.is_out_of_gas());
assert_eq!(gas_meter.gas_left(), 49_970);
@@ -297,7 +305,10 @@ mod tests {
let mut gas_meter = GasMeter::<Test>::new(50000);
assert!(!gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas());
assert!(!gas_meter
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10))
.charge(
&MultiplierTokenMetadata { multiplier: 3 },
MultiplierToken(10)
)
.is_out_of_gas());
let mut tokens = gas_meter.tokens()[0..2].iter();
@@ -327,7 +338,6 @@ mod tests {
assert!(gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas());
}
// Charging the exact amount that the user paid for should be
// possible.
#[test]
@@ -84,42 +84,42 @@ mod gas;
mod account_db;
mod exec;
mod wasm;
mod rent;
mod wasm;
#[cfg(test)]
mod tests;
use crate::exec::ExecutionContext;
use crate::account_db::{AccountDb, DirectAccountDb};
use crate::wasm::{WasmLoader, WasmVm};
use crate::{
account_db::{AccountDb, DirectAccountDb},
exec::ExecutionContext,
wasm::{WasmLoader, WasmVm},
};
pub use crate::gas::{Gas, GasMeter};
pub use crate::exec::{ExecResult, ExecReturnValue, ExecError, StatusCode};
pub use crate::{
exec::{ExecError, ExecResult, ExecReturnValue, StatusCode},
gas::{Gas, GasMeter},
};
use codec::{Codec, Decode, Encode};
use cumulus_pallet_contracts_primitives::{ContractAccessError, RentProjection};
use frame_support::{
decl_error, decl_event, decl_module, decl_storage,
dispatch::{DispatchResult, DispatchResultWithPostInfo, Dispatchable, PostDispatchInfo},
parameter_types,
traits::{Currency, Get, OnUnbalanced, Randomness, Time},
weights::{GetDispatchInfo, Weight},
Parameter,
};
use frame_system::{ensure_root, ensure_signed, RawOrigin};
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use sp_core::crypto::UncheckedFrom;
use sp_std::{prelude::*, marker::PhantomData, fmt::Debug};
use codec::{Codec, Encode, Decode};
use sp_runtime::{
traits::{
Hash, StaticLookup, Zero, MaybeSerializeDeserialize, Member, Convert,
},
traits::{Convert, Hash, MaybeSerializeDeserialize, Member, StaticLookup, Zero},
RuntimeDebug,
};
use frame_support::dispatch::{
PostDispatchInfo, DispatchResult, Dispatchable, DispatchResultWithPostInfo
};
use frame_support::{
Parameter, decl_module, decl_event, decl_storage, decl_error,
parameter_types,
};
use frame_support::traits::{OnUnbalanced, Currency, Get, Time, Randomness};
use frame_support::weights::GetDispatchInfo;
use frame_system::{ensure_signed, RawOrigin, ensure_root};
use cumulus_pallet_contracts_primitives::{RentProjection, ContractAccessError};
use frame_support::weights::Weight;
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
pub type CodeHash<T> = <T as frame_system::Trait>::Hash;
pub type TrieId = Vec<u8>;
@@ -240,10 +240,16 @@ pub struct RawTombstoneContractInfo<H, Hasher>(H, PhantomData<Hasher>);
impl<H, Hasher> RawTombstoneContractInfo<H, Hasher>
where
H: Member + MaybeSerializeDeserialize+ Debug
+ AsRef<[u8]> + AsMut<[u8]> + Copy + Default
+ sp_std::hash::Hash + Codec,
Hasher: Hash<Output=H>,
H: Member
+ MaybeSerializeDeserialize
+ Debug
+ AsRef<[u8]>
+ AsMut<[u8]>
+ Copy
+ Default
+ sp_std::hash::Hash
+ Codec,
Hasher: Hash<Output = H>,
{
fn new(storage_root: &[u8], code_hash: H) -> Self {
let mut buf = Vec::new();
@@ -272,7 +278,7 @@ pub struct TrieIdFromParentCounter<T: Trait>(PhantomData<T>);
/// accountid_counter`.
impl<T: Trait> TrieIdGenerator<T::AccountId> for TrieIdFromParentCounter<T>
where
T::AccountId: AsRef<[u8]>
T::AccountId: AsRef<[u8]>,
{
fn trie_id(account_id: &T::AccountId) -> TrieId {
// Note that skipping a value due to error is not an issue here.
@@ -321,10 +327,9 @@ pub trait Trait: frame_system::Trait {
type Currency: Currency<Self::AccountId>;
/// The outer call dispatch type.
type Call:
Parameter +
Dispatchable<PostInfo=PostDispatchInfo, Origin=<Self as frame_system::Trait>::Origin> +
GetDispatchInfo;
type Call: Parameter
+ Dispatchable<PostInfo = PostDispatchInfo, Origin = <Self as frame_system::Trait>::Origin>
+ GetDispatchInfo;
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
@@ -390,9 +395,13 @@ pub trait Trait: frame_system::Trait {
pub struct SimpleAddressDeterminer<T: Trait>(PhantomData<T>);
impl<T: Trait> ContractAddressFor<CodeHash<T>, T::AccountId> for SimpleAddressDeterminer<T>
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
fn contract_address_for(code_hash: &CodeHash<T>, data: &[u8], origin: &T::AccountId) -> T::AccountId {
fn contract_address_for(
code_hash: &CodeHash<T>,
data: &[u8],
origin: &T::AccountId,
) -> T::AccountId {
let data_hash = T::Hashing::hash(data);
let mut buf = Vec::new();
@@ -641,13 +650,15 @@ impl<T: Trait> Module<T> {
impl<T: Trait> Module<T> {
fn calc_code_put_costs(code: &Vec<u8>) -> Gas {
<Module<T>>::current_schedule().put_code_per_byte_cost.saturating_mul(code.len() as Gas)
<Module<T>>::current_schedule()
.put_code_per_byte_cost
.saturating_mul(code.len() as Gas)
}
fn execute_wasm(
origin: T::AccountId,
gas_meter: &mut GasMeter<T>,
func: impl FnOnce(&mut ExecutionContext<T, WasmVm, WasmLoader>, &mut GasMeter<T>) -> ExecResult
func: impl FnOnce(&mut ExecutionContext<T, WasmVm, WasmLoader>, &mut GasMeter<T>) -> ExecResult,
) -> ExecResult {
let cfg = Config::preload();
let vm = WasmVm::new(&cfg.schedule);
@@ -656,7 +667,11 @@ impl<T: Trait> Module<T> {
let result = func(&mut ctx, gas_meter);
if result.as_ref().map(|output| output.is_success()).unwrap_or(false) {
if result
.as_ref()
.map(|output| output.is_success())
.unwrap_or(false)
{
// Commit all changes that made it thus far into the persistent storage.
DirectAccountDb.commit(ctx.overlay.into_change_set());
}
@@ -665,17 +680,11 @@ impl<T: Trait> Module<T> {
ctx.deferred.into_iter().for_each(|deferred| {
use self::exec::DeferredAction::*;
match deferred {
DepositEvent {
topics,
event,
} => <frame_system::Module<T>>::deposit_event_indexed(
DepositEvent { topics, event } => <frame_system::Module<T>>::deposit_event_indexed(
&*topics,
<T as Trait>::Event::from(event).into(),
),
DispatchRuntimeCall {
origin: who,
call,
} => {
DispatchRuntimeCall { origin: who, call } => {
let info = call.get_dispatch_info();
let result = call.dispatch(RawOrigin::Signed(who.clone()).into());
let post_info = match result {
@@ -693,11 +702,19 @@ impl<T: Trait> Module<T> {
delta,
} => {
let result = Self::restore_to(
donor.clone(), dest.clone(), code_hash.clone(), rent_allowance.clone(), delta
);
Self::deposit_event(
RawEvent::Restored(donor, dest, code_hash, rent_allowance, result.is_ok())
donor.clone(),
dest.clone(),
code_hash.clone(),
rent_allowance.clone(),
delta,
);
Self::deposit_event(RawEvent::Restored(
donor,
dest,
code_hash,
rent_allowance,
result.is_ok(),
));
}
}
});
@@ -20,10 +20,14 @@ use crate::{
AliveContractInfo, BalanceOf, ContractInfo, ContractInfoOf, Module, RawEvent,
TombstoneContractInfo, Trait,
};
use frame_support::storage::unhashed as storage;
use frame_support::traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReason};
use frame_support::StorageMap;
use cumulus_pallet_contracts_primitives::{ContractAccessError, RentProjection, RentProjectionResult};
use cumulus_pallet_contracts_primitives::{
ContractAccessError, RentProjection, RentProjectionResult,
};
use frame_support::{
storage::unhashed as storage,
traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReason},
StorageMap,
};
use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, SaturatedConversion, Saturating, Zero};
/// The amount to charge.
@@ -238,10 +242,8 @@ fn enact_verdict<T: Trait>(
// Use a dummy storage root because restoration is currentlyy unsupported
// for parachains anyways.
let tombstone = <TombstoneContractInfo<T>>::new(
&[0u8; 32],
alive_contract_info.code_hash,
);
let tombstone =
<TombstoneContractInfo<T>>::new(&[0u8; 32], alive_contract_info.code_hash);
let tombstone_info = ContractInfo::Tombstone(tombstone);
<ContractInfoOf<T>>::insert(account, &tombstone_info);
File diff suppressed because it is too large Load Diff
@@ -26,11 +26,13 @@
//! this guarantees that every instrumented contract code in cache cannot have the version equal to the current one.
//! Thus, before executing a contract it should be reinstrument with new schedule.
use crate::wasm::{prepare, runtime::Env, PrefabWasmModule};
use crate::{CodeHash, CodeStorage, PristineCode, Schedule, Trait};
use sp_std::prelude::*;
use sp_runtime::traits::Hash;
use crate::{
wasm::{prepare, runtime::Env, PrefabWasmModule},
CodeHash, CodeStorage, PristineCode, Schedule, Trait,
};
use frame_support::StorageMap;
use sp_runtime::traits::Hash;
use sp_std::prelude::*;
/// Put code in the storage. The hash of code is used as a key and is returned
/// as a result of this function.
@@ -58,8 +60,7 @@ pub fn load<T: Trait>(
code_hash: &CodeHash<T>,
schedule: &Schedule,
) -> Result<PrefabWasmModule, &'static str> {
let mut prefab_module =
<CodeStorage<T>>::get(code_hash).ok_or_else(|| "code is not found")?;
let mut prefab_module = <CodeStorage<T>>::get(code_hash).ok_or_else(|| "code is not found")?;
if prefab_module.schedule_version < schedule.version {
// The current schedule version is greater than the version of the one cached
@@ -193,14 +193,14 @@ macro_rules! define_env {
#[cfg(test)]
mod tests {
use parity_wasm::elements::FunctionType;
use parity_wasm::elements::ValueType;
use crate::{
exec::Ext,
gas::Gas,
wasm::{tests::MockExt, Runtime},
};
use parity_wasm::elements::{FunctionType, ValueType};
use sp_runtime::traits::Zero;
use sp_sandbox::{ReturnValue, Value};
use crate::wasm::tests::MockExt;
use crate::wasm::Runtime;
use crate::exec::Ext;
use crate::gas::Gas;
#[test]
fn macro_unmarshall_then_body_then_marshall_value_or_trap() {
@@ -263,8 +263,10 @@ mod tests {
Err(sp_sandbox::HostError)
}
});
let _f: fn(&mut Runtime<MockExt>, &[sp_sandbox::Value])
-> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> = ext_gas::<MockExt>;
let _f: fn(
&mut Runtime<MockExt>,
&[sp_sandbox::Value],
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> = ext_gas::<MockExt>;
}
#[test]
@@ -317,7 +319,13 @@ mod tests {
},
);
assert!(Env::can_satisfy(b"ext_gas", &FunctionType::new(vec![ValueType::I32], None)));
assert!(!Env::can_satisfy(b"not_exists", &FunctionType::new(vec![], None)));
assert!(Env::can_satisfy(
b"ext_gas",
&FunctionType::new(vec![ValueType::I32], None)
));
assert!(!Env::can_satisfy(
b"not_exists",
&FunctionType::new(vec![], None)
));
}
}
@@ -17,8 +17,8 @@
use super::Runtime;
use crate::exec::Ext;
use sp_sandbox::Value;
use parity_wasm::elements::{FunctionType, ValueType};
use sp_sandbox::Value;
#[macro_use]
pub(crate) mod macros;
@@ -66,11 +66,10 @@ impl ConvertibleToWasm for u64 {
}
}
pub(crate) type HostFunc<E> =
fn(
&mut Runtime<E>,
&[sp_sandbox::Value]
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError>;
pub(crate) type HostFunc<E> = fn(
&mut Runtime<E>,
&[sp_sandbox::Value],
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError>;
pub(crate) trait FunctionImplProvider<E: Ext> {
fn impls<F: FnMut(&[u8], HostFunc<E>)>(f: &mut F);
@@ -17,14 +17,16 @@
//! 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 crate::{
exec::{ExecResult, Ext},
gas::GasMeter,
wasm::env_def::FunctionImplProvider,
CodeHash, Schedule, Trait,
};
use sp_std::prelude::*;
use codec::{Encode, Decode};
use codec::{Decode, Encode};
use sp_sandbox;
use sp_std::prelude::*;
#[macro_use]
mod env_def;
@@ -32,8 +34,10 @@ mod code_cache;
mod prepare;
mod runtime;
use self::runtime::{to_execution_result, Runtime};
use self::code_cache::load as load_code;
use self::{
code_cache::load as load_code,
runtime::{to_execution_result, Runtime},
};
pub use self::code_cache::save as save_code;
@@ -116,8 +120,8 @@ impl<'a, T: Trait> crate::exec::Vm<T> for WasmVm<'a> {
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.
// 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;
@@ -131,13 +135,7 @@ impl<'a, T: Trait> crate::exec::Vm<T> for WasmVm<'a> {
imports.add_host_func("env", name, func_ptr);
});
let mut runtime = Runtime::new(
&mut ext,
input_data,
&self.schedule,
memory,
gas_meter,
);
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.
@@ -150,19 +148,20 @@ impl<'a, T: Trait> crate::exec::Vm<T> for WasmVm<'a> {
#[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, BalanceOf};
use wabt;
use hex_literal::hex;
use crate::{
exec::{ExecError, ExecReturnValue, Ext, StorageKey, STATUS_SUCCESS},
gas::{Gas, GasMeter},
tests::{Call, Test},
wasm::prepare::prepare_contract,
BalanceOf, CodeHash,
};
use assert_matches::assert_matches;
use sp_runtime::DispatchError;
use frame_support::weights::Weight;
use hex_literal::hex;
use sp_core::H256;
use sp_runtime::DispatchError;
use std::{cell::RefCell, collections::HashMap};
use wabt;
const GAS_LIMIT: Gas = 10_000_000_000;
@@ -230,9 +229,11 @@ mod tests {
fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>> {
self.storage.get(key).cloned()
}
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>)
-> Result<(), &'static str>
{
fn set_storage(
&mut self,
key: StorageKey,
value: Option<Vec<u8>>,
) -> Result<(), &'static str> {
*self.storage.entry(key).or_insert(Vec::new()) = value.unwrap_or(Vec::new());
Ok(())
}
@@ -289,7 +290,10 @@ mod tests {
});
// 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() })
Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: Vec::new(),
})
}
fn terminate(
&mut self,
@@ -360,21 +364,22 @@ mod tests {
self.rent_allowance
}
fn block_number(&self) -> u64 { 121 }
fn block_number(&self) -> u64 {
121
}
fn max_value_size(&self) -> u32 { 16_384 }
fn max_value_size(&self) -> u32 {
16_384
}
fn get_runtime_storage(&self, key: &[u8]) -> Option<Vec<u8>> {
let opt_value = self.runtime_storage_keys
.borrow_mut()
.remove(key);
opt_value.unwrap_or_else(||
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
key, self.runtime_storage_keys
)
)
})
}
fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
BalanceOf::<Self::T>::from(1312_u32).saturating_mul(weight.into())
@@ -387,9 +392,11 @@ mod tests {
fn get_storage(&self, key: &[u8; 32]) -> Option<Vec<u8>> {
(**self).get_storage(key)
}
fn set_storage(&mut self, key: [u8; 32], value: Option<Vec<u8>>)
-> Result<(), &'static str>
{
fn set_storage(
&mut self,
key: [u8; 32],
value: Option<Vec<u8>>,
) -> Result<(), &'static str> {
(**self).set_storage(key, value)
}
fn instantiate(
@@ -435,12 +442,7 @@ mod tests {
rent_allowance: u64,
delta: Vec<StorageKey>,
) {
(**self).note_restore_to(
dest,
code_hash,
rent_allowance,
delta,
)
(**self).note_restore_to(dest, code_hash, rent_allowance, delta)
}
fn caller(&self) -> &u64 {
(**self).caller()
@@ -499,8 +501,7 @@ mod tests {
let wasm = wabt::wat2wasm(wat).unwrap();
let schedule = crate::Schedule::default();
let prefab_module =
prepare_contract::<super::runtime::Env>(&wasm, &schedule).unwrap();
let prefab_module = prepare_contract::<super::runtime::Env>(&wasm, &schedule).unwrap();
let exec = WasmExecutable {
// Use a "call" convention.
@@ -554,7 +555,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.transfers,
@@ -614,7 +616,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.transfers,
@@ -676,7 +679,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.instantiates,
@@ -719,7 +723,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.terminations,
@@ -777,7 +782,8 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.transfers,
@@ -861,18 +867,23 @@ mod tests {
#[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());
mock_ext.storage.insert([0x11; 32], [0x22; 32].to_vec());
let output = execute(
CODE_GET_STORAGE,
vec![],
mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: [0x22; 32].to_vec() });
assert_eq!(
output,
ExecReturnValue {
status: STATUS_SUCCESS,
data: [0x22; 32].to_vec()
}
);
}
/// calls `ext_caller`, loads the address from the scratch buffer and
@@ -934,7 +945,8 @@ mod tests {
vec![],
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
}
/// calls `ext_address`, loads the address from the scratch buffer and
@@ -996,7 +1008,8 @@ mod tests {
vec![],
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
}
const CODE_BALANCE: &str = r#"
@@ -1051,12 +1064,7 @@ mod tests {
#[test]
fn balance() {
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_BALANCE,
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
let _ = execute(CODE_BALANCE, vec![], MockExt::default(), &mut gas_meter).unwrap();
}
const CODE_GAS_PRICE: &str = r#"
@@ -1111,12 +1119,7 @@ mod tests {
#[test]
fn gas_price() {
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_GAS_PRICE,
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
let _ = execute(CODE_GAS_PRICE, vec![], MockExt::default(), &mut gas_meter).unwrap();
}
const CODE_GAS_LEFT: &str = r#"
@@ -1170,16 +1173,14 @@ mod tests {
fn gas_left() {
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let output = execute(
CODE_GAS_LEFT,
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
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 < GAS_LIMIT, "gas_left must be less than initial");
assert!(gas_left > gas_meter.gas_left(), "gas_left must be greater than final");
assert!(
gas_left > gas_meter.gas_left(),
"gas_left must be greater than final"
);
}
const CODE_VALUE_TRANSFERRED: &str = r#"
@@ -1239,7 +1240,8 @@ mod tests {
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
)
.unwrap();
}
const CODE_DISPATCH_CALL: &str = r#"
@@ -1270,13 +1272,14 @@ mod tests {
vec![],
&mut mock_ext,
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(
&mock_ext.dispatches,
&[DispatchEntry(
Call::Balances(pallet_balances::Call::set_balance(42, 1337, 0)),
)]
&[DispatchEntry(Call::Balances(
pallet_balances::Call::set_balance(42, 1337, 0)
),)]
);
}
@@ -1310,9 +1313,16 @@ mod tests {
vec![],
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: vec![1, 2, 3, 4] });
assert_eq!(
output,
ExecReturnValue {
status: STATUS_SUCCESS,
data: vec![1, 2, 3, 4]
}
);
}
const CODE_TIMESTAMP_NOW: &str = r#"
@@ -1372,7 +1382,8 @@ mod tests {
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
)
.unwrap();
}
const CODE_MINIMUM_BALANCE: &str = r#"
@@ -1431,7 +1442,8 @@ mod tests {
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
)
.unwrap();
}
const CODE_TOMBSTONE_DEPOSIT: &str = r#"
@@ -1490,7 +1502,8 @@ mod tests {
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
)
.unwrap();
}
const CODE_RANDOM: &str = r#"
@@ -1554,19 +1567,15 @@ mod tests {
fn random() {
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let output = execute(
CODE_RANDOM,
vec![],
MockExt::default(),
&mut gas_meter,
).unwrap();
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(),
data: hex!("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F")
.to_vec(),
},
);
}
@@ -1598,17 +1607,15 @@ mod tests {
fn deposit_event() {
let mut mock_ext = MockExt::default();
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_DEPOSIT_EVENT,
vec![],
&mut mock_ext,
&mut gas_meter
).unwrap();
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!(
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(), 9967000000);
}
@@ -1653,7 +1660,8 @@ mod tests {
&mut gas_meter
),
Err(ExecError {
reason: DispatchError::Other("contract trapped during execution"), buffer: _
reason: DispatchError::Other("contract trapped during execution"),
buffer: _,
})
);
}
@@ -1696,7 +1704,10 @@ mod tests {
MockExt::default(),
&mut gas_meter
),
Err(ExecError { reason: DispatchError::Other("contract trapped during execution"), buffer: _ })
Err(ExecError {
reason: DispatchError::Other("contract trapped during execution"),
buffer: _,
})
);
}
@@ -1759,7 +1770,8 @@ mod tests {
vec![],
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
}
// asserts that the size of the input data is 4.
@@ -1799,7 +1811,8 @@ mod tests {
input_data,
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output.data.len(), 0);
assert_eq!(output.data.capacity(), 1_234);
@@ -1815,7 +1828,9 @@ mod tests {
input_data,
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).err().unwrap();
)
.err()
.unwrap();
assert_eq!(error.buffer.capacity(), 1_234);
}
@@ -1869,9 +1884,16 @@ mod tests {
hex!("00112233445566778899").to_vec(),
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output, ExecReturnValue { status: 0, data: hex!("445566778899").to_vec() });
assert_eq!(
output,
ExecReturnValue {
status: 0,
data: hex!("445566778899").to_vec()
}
);
assert!(output.is_success());
}
@@ -1882,9 +1904,16 @@ mod tests {
hex!("112233445566778899").to_vec(),
MockExt::default(),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
)
.unwrap();
assert_eq!(output, ExecReturnValue { status: 17, data: hex!("5566778899").to_vec() });
assert_eq!(
output,
ExecReturnValue {
status: 17,
data: hex!("5566778899").to_vec()
}
);
assert!(!output.is_success());
}
@@ -1973,17 +2002,15 @@ mod tests {
// "\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())),
(
[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();
let _ = execute(CODE_GET_RUNTIME_STORAGE, vec![], mock_ext, &mut gas_meter).unwrap();
}
}
@@ -18,15 +18,15 @@
//! wasm module before execution. It also extracts some essential information
//! from a module.
use crate::wasm::env_def::ImportSatisfyCheck;
use crate::wasm::PrefabWasmModule;
use crate::Schedule;
use crate::{
wasm::{env_def::ImportSatisfyCheck, PrefabWasmModule},
Schedule,
};
use parity_wasm::elements::{self, Internal, External, MemoryType, Type, ValueType};
use pwasm_utils;
use pwasm_utils::rules;
use parity_wasm::elements::{self, External, Internal, MemoryType, Type, ValueType};
use pwasm_utils::{self, rules};
use sp_runtime::traits::SaturatedConversion;
use sp_std::prelude::*;
use sp_runtime::traits::{SaturatedConversion};
struct ContractModule<'a> {
/// A deserialized module. The module is valid (this is Guaranteed by `new` method).
@@ -39,10 +39,7 @@ impl<'a> ContractModule<'a> {
///
/// Returns `Err` if the `original_code` couldn't be decoded or
/// if it contains an invalid module.
fn new(
original_code: &[u8],
schedule: &'a Schedule,
) -> Result<Self, &'static str> {
fn new(original_code: &[u8], schedule: &'a Schedule) -> Result<Self, &'static str> {
use wasmi_validation::{validate_module, PlainValidator};
let module =
@@ -53,10 +50,7 @@ impl<'a> ContractModule<'a> {
// Return a `ContractModule` instance with
// __valid__ module.
Ok(ContractModule {
module,
schedule,
})
Ok(ContractModule { module, schedule })
}
/// Ensures that module doesn't declare internal memories.
@@ -65,7 +59,8 @@ impl<'a> ContractModule<'a> {
/// Memory section contains declarations of internal linear memories, so if we find one
/// we reject such a module.
fn ensure_no_internal_memory(&self) -> Result<(), &'static str> {
if self.module
if self
.module
.memory_section()
.map_or(false, |ms| ms.entries().len() > 0)
{
@@ -86,7 +81,7 @@ impl<'a> ContractModule<'a> {
// Check the table's initial size as there is no instruction or environment function
// capable of growing the table.
if table_type.limits().initial() > limit {
return Err("table exceeds maximum size allowed")
return Err("table exceeds maximum size allowed");
}
}
}
@@ -98,8 +93,9 @@ impl<'a> ContractModule<'a> {
if let Some(global_section) = self.module.global_section() {
for global in global_section.entries() {
match global.global_type().content_type() {
ValueType::F32 | ValueType::F64 =>
return Err("use of floating point type in globals is forbidden"),
ValueType::F32 | ValueType::F64 => {
return Err("use of floating point type in globals is forbidden")
}
_ => {}
}
}
@@ -109,8 +105,9 @@ impl<'a> ContractModule<'a> {
for func_body in code_section.bodies() {
for local in func_body.locals() {
match local.value_type() {
ValueType::F32 | ValueType::F64 =>
return Err("use of floating point type in locals is forbidden"),
ValueType::F32 | ValueType::F64 => {
return Err("use of floating point type in locals is forbidden")
}
_ => {}
}
}
@@ -124,8 +121,11 @@ impl<'a> ContractModule<'a> {
let return_type = func_type.return_type();
for value_type in func_type.params().iter().chain(return_type.iter()) {
match value_type {
ValueType::F32 | ValueType::F64 =>
return Err("use of floating point type in function types is forbidden"),
ValueType::F32 | ValueType::F64 => {
return Err(
"use of floating point type in function types is forbidden",
)
}
_ => {}
}
}
@@ -138,13 +138,12 @@ impl<'a> ContractModule<'a> {
}
fn inject_gas_metering(self) -> Result<Self, &'static str> {
let gas_rules =
rules::Set::new(
self.schedule.regular_op_cost.clone().saturated_into(),
Default::default(),
)
.with_grow_cost(self.schedule.grow_mem_cost.clone().saturated_into())
.with_forbidden_floats();
let gas_rules = rules::Set::new(
self.schedule.regular_op_cost.clone().saturated_into(),
Default::default(),
)
.with_grow_cost(self.schedule.grow_mem_cost.clone().saturated_into())
.with_forbidden_floats();
let contract_module = pwasm_utils::inject_gas_counter(self.module, &gas_rules)
.map_err(|_| "gas instrumentation failed")?;
@@ -195,11 +194,9 @@ impl<'a> ContractModule<'a> {
.map(|is| is.entries())
.unwrap_or(&[])
.iter()
.filter(|entry| {
match *entry.external() {
External::Function(_) => true,
_ => false,
}
.filter(|entry| match *entry.external() {
External::Function(_) => true,
_ => false,
})
.count();
@@ -232,15 +229,17 @@ impl<'a> ContractModule<'a> {
// The [] -> [] signature predates the [] -> [i32] signature and is supported for
// backwards compatibility. This will likely be removed once ink! is updated to
// generate modules with the new function signatures.
let func_ty_idx = func_entries.get(fn_idx as usize)
let func_ty_idx = func_entries
.get(fn_idx as usize)
.ok_or_else(|| "export refers to non-existent function")?
.type_ref();
let Type::Function(ref func_ty) = types
.get(func_ty_idx as usize)
.ok_or_else(|| "function has a non-existent type")?;
if !func_ty.params().is_empty() ||
!(func_ty.return_type().is_none() ||
func_ty.return_type() == Some(ValueType::I32)) {
if !func_ty.params().is_empty()
|| !(func_ty.return_type().is_none()
|| func_ty.return_type() == Some(ValueType::I32))
{
return Err("entry point has wrong signature");
}
}
@@ -286,10 +285,10 @@ impl<'a> ContractModule<'a> {
&External::Function(ref type_idx) => type_idx,
&External::Memory(ref memory_type) => {
if import.field() != "memory" {
return Err("Memory import must have the field name 'memory'")
return Err("Memory import must have the field name 'memory'");
}
if imported_mem_type.is_some() {
return Err("Multiple memory imports defined")
return Err("Multiple memory imports defined");
}
imported_mem_type = Some(memory_type);
continue;
@@ -317,8 +316,7 @@ impl<'a> ContractModule<'a> {
}
fn into_wasm_code(self) -> Result<Vec<u8>, &'static str> {
elements::serialize(self.module)
.map_err(|_| "error serializing instrumented module")
elements::serialize(self.module).map_err(|_| "error serializing instrumented module")
}
}
@@ -394,9 +392,9 @@ pub fn prepare_contract<C: ImportSatisfyCheck>(
mod tests {
use super::*;
use crate::exec::Ext;
use assert_matches::assert_matches;
use std::fmt;
use wabt;
use assert_matches::assert_matches;
impl fmt::Debug for PrefabWasmModule {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -429,7 +427,8 @@ mod tests {
};
}
prepare_test!(no_floats,
prepare_test!(
no_floats,
r#"
(module
(func (export "call")
@@ -454,7 +453,8 @@ mod tests {
assert_eq!(Schedule::default().max_memory_pages, 16);
}
prepare_test!(memory_with_one_page,
prepare_test!(
memory_with_one_page,
r#"
(module
(import "env" "memory" (memory 1 1))
@@ -466,7 +466,8 @@ mod tests {
Ok(_)
);
prepare_test!(internal_memory_declaration,
prepare_test!(
internal_memory_declaration,
r#"
(module
(memory 1 1)
@@ -478,7 +479,8 @@ mod tests {
Err("module declares internal memory")
);
prepare_test!(no_memory_import,
prepare_test!(
no_memory_import,
r#"
(module
;; no memory imported
@@ -489,7 +491,8 @@ mod tests {
Ok(_)
);
prepare_test!(initial_exceeds_maximum,
prepare_test!(
initial_exceeds_maximum,
r#"
(module
(import "env" "memory" (memory 16 1))
@@ -501,7 +504,8 @@ mod tests {
Err("Module is not valid")
);
prepare_test!(no_maximum,
prepare_test!(
no_maximum,
r#"
(module
(import "env" "memory" (memory 1))
@@ -513,7 +517,8 @@ mod tests {
Err("Maximum number of pages should be always declared.")
);
prepare_test!(requested_maximum_exceeds_configured_maximum,
prepare_test!(
requested_maximum_exceeds_configured_maximum,
r#"
(module
(import "env" "memory" (memory 1 17))
@@ -525,7 +530,8 @@ mod tests {
Err("Maximum number of pages should not exceed the configured maximum.")
);
prepare_test!(field_name_not_memory,
prepare_test!(
field_name_not_memory,
r#"
(module
(import "env" "forgetit" (memory 1 1))
@@ -537,7 +543,8 @@ mod tests {
Err("Memory import must have the field name 'memory'")
);
prepare_test!(multiple_memory_imports,
prepare_test!(
multiple_memory_imports,
r#"
(module
(import "env" "memory" (memory 1 1))
@@ -550,7 +557,8 @@ mod tests {
Err("Module is not valid")
);
prepare_test!(table_import,
prepare_test!(
table_import,
r#"
(module
(import "env" "table" (table 1 anyfunc))
@@ -562,7 +570,8 @@ mod tests {
Err("Cannot import tables")
);
prepare_test!(global_import,
prepare_test!(
global_import,
r#"
(module
(global $g (import "env" "global") i32)
@@ -583,7 +592,8 @@ mod tests {
assert_eq!(Schedule::default().max_table_size, 16384);
}
prepare_test!(no_tables,
prepare_test!(
no_tables,
r#"
(module
(func (export "call"))
@@ -593,7 +603,8 @@ mod tests {
Ok(_)
);
prepare_test!(table_valid_size,
prepare_test!(
table_valid_size,
r#"
(module
(table 10000 funcref)
@@ -605,7 +616,8 @@ mod tests {
Ok(_)
);
prepare_test!(table_too_big,
prepare_test!(
table_too_big,
r#"
(module
(table 20000 funcref)
@@ -620,7 +632,8 @@ mod tests {
mod imports {
use super::*;
prepare_test!(can_import_legit_function,
prepare_test!(
can_import_legit_function,
r#"
(module
(import "env" "nop" (func (param i64)))
@@ -634,7 +647,8 @@ mod tests {
// even though gas is defined the contract can't import it since
// it is an implementation defined.
prepare_test!(can_not_import_gas_function,
prepare_test!(
can_not_import_gas_function,
r#"
(module
(import "env" "gas" (func (param i32)))
@@ -647,7 +661,8 @@ mod tests {
);
// nothing can be imported from non-"env" module for now.
prepare_test!(non_env_import,
prepare_test!(
non_env_import,
r#"
(module
(import "another_module" "memory" (memory 1 1))
@@ -660,7 +675,8 @@ mod tests {
);
// wrong signature
prepare_test!(wrong_signature,
prepare_test!(
wrong_signature,
r#"
(module
(import "env" "gas" (func (param i64)))
@@ -672,7 +688,8 @@ mod tests {
Err("module imports a non-existent function")
);
prepare_test!(unknown_func_name,
prepare_test!(
unknown_func_name,
r#"
(module
(import "env" "unknown_func" (func))
@@ -684,7 +701,8 @@ mod tests {
Err("module imports a non-existent function")
);
prepare_test!(ext_println_debug_disabled,
prepare_test!(
ext_println_debug_disabled,
r#"
(module
(import "env" "ext_println" (func $ext_println (param i32 i32)))
@@ -698,16 +716,19 @@ mod tests {
#[test]
fn ext_println_debug_enabled() {
let wasm = wabt::Wat2Wasm::new().validate(false).convert(
r#"
let wasm = wabt::Wat2Wasm::new()
.validate(false)
.convert(
r#"
(module
(import "env" "ext_println" (func $ext_println (param i32 i32)))
(func (export "call"))
(func (export "deploy"))
)
"#
).unwrap();
"#,
)
.unwrap();
let mut schedule = Schedule::default();
schedule.enable_println = true;
let r = prepare_contract::<TestEnv>(wasm.as_ref(), &schedule);
@@ -718,7 +739,8 @@ mod tests {
mod entrypoints {
use super::*;
prepare_test!(it_works,
prepare_test!(
it_works,
r#"
(module
(func (export "call"))
@@ -728,7 +750,8 @@ mod tests {
Ok(_)
);
prepare_test!(omit_deploy,
prepare_test!(
omit_deploy,
r#"
(module
(func (export "call"))
@@ -737,7 +760,8 @@ mod tests {
Err("deploy function isn't exported")
);
prepare_test!(omit_call,
prepare_test!(
omit_call,
r#"
(module
(func (export "deploy"))
@@ -747,7 +771,8 @@ mod tests {
);
// Try to use imported function as an entry point.
prepare_test!(try_sneak_export_as_entrypoint,
prepare_test!(
try_sneak_export_as_entrypoint,
r#"
(module
(import "env" "panic" (func))
@@ -761,7 +786,8 @@ mod tests {
);
// Try to use imported function as an entry point.
prepare_test!(try_sneak_export_as_global,
prepare_test!(
try_sneak_export_as_global,
r#"
(module
(func (export "deploy"))
@@ -771,7 +797,8 @@ mod tests {
Err("expected a function")
);
prepare_test!(wrong_signature,
prepare_test!(
wrong_signature,
r#"
(module
(func (export "deploy"))
@@ -781,7 +808,8 @@ mod tests {
Err("entry point has wrong signature")
);
prepare_test!(unknown_exports,
prepare_test!(
unknown_exports,
r#"
(module
(func (export "call"))
@@ -792,7 +820,8 @@ mod tests {
Err("unknown export: expecting only deploy and call functions")
);
prepare_test!(global_float,
prepare_test!(
global_float,
r#"
(module
(global $x f32 (f32.const 0))
@@ -803,7 +832,8 @@ mod tests {
Err("use of floating point type in globals is forbidden")
);
prepare_test!(local_float,
prepare_test!(
local_float,
r#"
(module
(func $foo (local f32))
@@ -814,7 +844,8 @@ mod tests {
Err("use of floating point type in locals is forbidden")
);
prepare_test!(param_float,
prepare_test!(
param_float,
r#"
(module
(func $foo (param f32))
@@ -825,7 +856,8 @@ mod tests {
Err("use of floating point type in function types is forbidden")
);
prepare_test!(result_float,
prepare_test!(
result_float,
r#"
(module
(func $foo (result f32) (f32.const 0))
@@ -16,23 +16,18 @@
//! Environment definition of the wasm smart-contract runtime.
use crate::{Schedule, Trait, CodeHash, BalanceOf};
use crate::exec::{
Ext, ExecResult, ExecError, ExecReturnValue, StorageKey, TopicOf, STATUS_SUCCESS,
use crate::{
exec::{ExecError, ExecResult, ExecReturnValue, Ext, StorageKey, TopicOf, STATUS_SUCCESS},
gas::{Gas, GasMeter, GasMeterResult, Token},
BalanceOf, CodeHash, Schedule, Trait,
};
use crate::gas::{Gas, GasMeter, Token, GasMeterResult};
use sp_sandbox;
use frame_system;
use sp_std::{prelude::*, mem, convert::TryInto};
use codec::{Decode, Encode};
use sp_runtime::traits::{Bounded, SaturatedConversion};
use sp_io::hashing::{
keccak_256,
blake2_256,
blake2_128,
sha2_256,
};
use frame_support::weights::GetDispatchInfo;
use frame_system;
use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256};
use sp_runtime::traits::{Bounded, SaturatedConversion};
use sp_sandbox;
use sp_std::{convert::TryInto, mem, prelude::*};
/// The value returned from ext_call and ext_instantiate contract external functions if the call or
/// instantiation traps. This value is chosen as if the execution does not trap, the return value
@@ -93,19 +88,19 @@ pub(crate) fn to_execution_result<E: Ext>(
status: STATUS_SUCCESS,
data,
})
},
}
Some(SpecialTrap::Termination) => {
return Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: Vec::new(),
})
},
}
Some(SpecialTrap::OutOfGas) => {
return Err(ExecError {
reason: "ran out of gas during contract execution".into(),
buffer: runtime.scratch_buf,
})
},
}
None => (),
}
@@ -115,29 +110,43 @@ pub(crate) fn to_execution_result<E: Ext>(
Ok(sp_sandbox::ReturnValue::Unit) => {
let mut buffer = runtime.scratch_buf;
buffer.clear();
Ok(ExecReturnValue { status: STATUS_SUCCESS, data: buffer })
Ok(ExecReturnValue {
status: STATUS_SUCCESS,
data: buffer,
})
}
Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::Value::I32(exit_code))) => {
let status = (exit_code & 0xFF).try_into()
let status = (exit_code & 0xFF)
.try_into()
.expect("exit_code is masked into the range of a u8; qed");
Ok(ExecReturnValue { status, data: runtime.scratch_buf })
Ok(ExecReturnValue {
status,
data: runtime.scratch_buf,
})
}
// This should never happen as the return type of exported functions should have been
// validated by the code preparation process. However, because panics are really
// undesirable in the runtime code, we treat this as a trap for now. Eventually, we might
// want to revisit this.
Ok(_) => Err(ExecError { reason: "return type error".into(), buffer: runtime.scratch_buf }),
Ok(_) => Err(ExecError {
reason: "return type error".into(),
buffer: runtime.scratch_buf,
}),
// `Error::Module` is returned only if instantiation or linking failed (i.e.
// wasm binary tried to import a function that is not provided by the host).
// This shouldn't happen because validation process ought to reject such binaries.
//
// Because panics are really undesirable in the runtime code, we treat this as
// a trap for now. Eventually, we might want to revisit this.
Err(sp_sandbox::Error::Module) =>
Err(ExecError { reason: "validation error".into(), buffer: runtime.scratch_buf }),
Err(sp_sandbox::Error::Module) => Err(ExecError {
reason: "validation error".into(),
buffer: runtime.scratch_buf,
}),
// Any other kind of a trap should result in a failure.
Err(sp_sandbox::Error::Execution) | Err(sp_sandbox::Error::OutOfBounds) =>
Err(ExecError { reason: "contract trapped during execution".into(), buffer: runtime.scratch_buf }),
Err(sp_sandbox::Error::Execution) | Err(sp_sandbox::Error::OutOfBounds) => Err(ExecError {
reason: "contract trapped during execution".into(),
buffer: runtime.scratch_buf,
}),
}
}
@@ -188,14 +197,12 @@ impl<T: Trait> Token<T> for RuntimeToken {
data_cost
.and_then(|data_cost| {
topics_cost.and_then(|topics_cost| {
data_cost.checked_add(topics_cost)
})
topics_cost.and_then(|topics_cost| data_cost.checked_add(topics_cost))
})
.and_then(|data_and_topics_cost|
.and_then(|data_and_topics_cost| {
data_and_topics_cost.checked_add(metadata.event_base_cost)
)
},
})
}
DispatchWithWeight(gas) => gas.checked_add(metadata.dispatch_base_cost),
};
@@ -214,10 +221,10 @@ fn charge_gas<T: Trait, Tok: Token<T>>(
) -> Result<(), sp_sandbox::HostError> {
match gas_meter.charge(metadata, token) {
GasMeterResult::Proceed => Ok(()),
GasMeterResult::OutOfGas => {
GasMeterResult::OutOfGas => {
*special_trap = Some(SpecialTrap::OutOfGas);
Err(sp_sandbox::HostError)
},
}
}
}
@@ -242,7 +249,9 @@ fn read_sandbox_memory<E: Ext>(
)?;
let mut buf = vec![0u8; len as usize];
ctx.memory.get(ptr, buf.as_mut_slice()).map_err(|_| sp_sandbox::HostError)?;
ctx.memory
.get(ptr, buf.as_mut_slice())
.map_err(|_| sp_sandbox::HostError)?;
Ok(buf)
}
@@ -267,7 +276,9 @@ fn read_sandbox_memory_into_scratch<E: Ext>(
)?;
ctx.scratch_buf.resize(len as usize, 0);
ctx.memory.get(ptr, ctx.scratch_buf.as_mut_slice()).map_err(|_| sp_sandbox::HostError)?;
ctx.memory
.get(ptr, ctx.scratch_buf.as_mut_slice())
.map_err(|_| sp_sandbox::HostError)?;
Ok(())
}
@@ -1165,14 +1176,10 @@ where
/// the order of items is not preserved.
fn has_duplicates<T: PartialEq + AsRef<[u8]>>(items: &mut Vec<T>) -> bool {
// Sort the vector
items.sort_unstable_by(|a, b| {
Ord::cmp(a.as_ref(), b.as_ref())
});
items.sort_unstable_by(|a, b| Ord::cmp(a.as_ref(), b.as_ref()));
// And then find any two consecutive equal elements.
items.windows(2).any(|w| {
match w {
&[ref a, ref b] => a == b,
_ => false,
}
items.windows(2).any(|w| match w {
&[ref a, ref b] => a == b,
_ => false,
})
}