mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 17:31:03 +00:00
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:
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user