Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wasmi 32 #10

Draft
wants to merge 2 commits into
base: aleph-v1.6.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 71 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion substrate/frame/contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ serde = { version = "1", optional = true, features = ["derive"] }
smallvec = { version = "1", default-features = false, features = [
"const_generics",
] }
wasmi = { version = "0.31", default-features = false }
wasmi = { version = "0.32.3", default-features = false }
impl-trait-for-tuples = "0.2"

# Only used in benchmarking to generate contract code
Expand Down
21 changes: 12 additions & 9 deletions substrate/frame/contracts/proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ impl HostFnReturn {
Self::U64 => quote! { ::core::primitive::u64 },
};
quote! {
::core::result::Result<#ok, ::wasmi::core::Trap>
::core::result::Result<#ok, ::wasmi::Error>
}
}
}
Expand Down Expand Up @@ -660,7 +660,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2)
let into_host = if expand_blocks {
quote! {
|reason| {
::wasmi::core::Trap::from(reason)
::wasmi::Error::host(reason)
}
}
} else {
Expand All @@ -677,13 +677,13 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2)
quote! {
// Write gas from wasmi into pallet-contracts before entering the host function.
let __gas_left_before__ = {
let executor_total =
__caller__.fuel_consumed().expect("Fuel metering is enabled; qed");
let fuel =
__caller__.get_fuel().expect("Fuel metering is enabled; qed");
__caller__
.data_mut()
.ext()
.gas_meter_mut()
.sync_from_executor(executor_total)
.sync_from_executor(fuel)
.map_err(TrapReason::from)
.map_err(#into_host)?
};
Expand All @@ -694,15 +694,18 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2)
// Write gas from pallet-contracts into wasmi after leaving the host function.
let sync_gas_after = if expand_blocks {
quote! {
let fuel_consumed = __caller__
let fuel = __caller__
.data_mut()
.ext()
.gas_meter_mut()
.sync_to_executor(__gas_left_before__)
.map_err(TrapReason::from)?;
.map_err(|err| {
let error = TrapReason::from(err);
wasmi::Error::host(error)
})?;
__caller__
.consume_fuel(fuel_consumed.into())
.map_err(|_| TrapReason::from(Error::<E::T>::OutOfGas))?;
.set_fuel(fuel.into())
.expect("Fuel metering is enabled; qed");
}
} else {
quote! { }
Expand Down
7 changes: 6 additions & 1 deletion substrate/frame/contracts/src/benchmarking/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ use crate::wasm::{
};
use sp_core::Get;
use wasmi::{errors::LinkerError, Func, Linker, StackLimits, Store};
use wasmi::CompilationMode;
use crate::wasm::LoadingMode;


/// Minimal execution environment without any imported functions.
pub struct Sandbox {
Expand All @@ -50,12 +53,14 @@ impl<T: Config> From<&WasmModule<T>> for Sandbox {
StackLimits::default(),
// We are testing with an empty environment anyways
AllowDeprecatedInterface::No,
LoadingMode::Checked,
CompilationMode::Eager,
)
.expect("Failed to create benchmarking Sandbox instance");

// Set fuel for wasmi execution.
store
.add_fuel(u64::MAX)
.set_fuel(u64::MAX)
.expect("We've set up engine to fuel consuming mode; qed");

let entry_point = instance
Expand Down
68 changes: 48 additions & 20 deletions substrate/frame/contracts/src/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,45 @@ impl ChargedAmount {
}
}

// Meter for syncing the gas between the executor and the gas meter.
#[derive(DefaultNoBound)]
struct EngineMeter<T: Config> {
fuel: u64,
_phantom: PhantomData<T>,
}

impl<T: Config> EngineMeter<T> {
/// Create a meter with the given fuel limit.
fn new(limit: Weight) -> Self {
Self {
fuel: limit.ref_time().saturating_div(T::Schedule::get().ref_time_by_fuel()),
_phantom: PhantomData,
}
}

/// Set the fuel left to the given value.
/// Returns the amount of Weight consumed since the last update.
fn set_fuel(&mut self, fuel: u64) -> Weight {
let consumed = self
.fuel
.saturating_sub(fuel)
.saturating_mul(T::Schedule::get().ref_time_by_fuel());
self.fuel = fuel;
Weight::from_parts(consumed, 0)
}

/// Charge the given amount of gas.
/// Returns the amount of fuel left.
fn charge_ref_time(&mut self, ref_time: u64) -> Result<Syncable, DispatchError> {
let amount = ref_time
.checked_div(T::Schedule::get().ref_time_by_fuel())
.ok_or(Error::<T>::InvalidSchedule)?;

self.fuel.checked_sub(amount).ok_or_else(|| Error::<T>::OutOfGas)?;
Ok(Syncable(self.fuel))
}
}

/// Used to capture the gas left before entering a host function.
///
/// Has to be consumed in order to sync back the gas after leaving the host function.
Expand Down Expand Up @@ -98,12 +137,9 @@ pub struct GasMeter<T: Config> {
/// Due to `adjust_gas` and `nested` the `gas_left` can temporarily dip below its final value.
gas_left_lowest: Weight,
/// The amount of resources that was consumed by the execution engine.
///
/// This should be equivalent to `self.gas_consumed().ref_time()` but expressed in whatever
/// unit the execution engine uses to track resource consumption. We have to track it
/// separately in order to avoid the loss of precision that happens when converting from
/// ref_time to the execution engine unit.
executor_consumed: u64,
/// We have to track it separately in order to avoid the loss of precision that happens when
/// converting from ref_time to the execution engine unit.
engine_meter: EngineMeter<T>,
_phantom: PhantomData<T>,
#[cfg(test)]
tokens: Vec<ErasedToken>,
Expand All @@ -115,7 +151,7 @@ impl<T: Config> GasMeter<T> {
gas_limit,
gas_left: gas_limit,
gas_left_lowest: gas_limit,
executor_consumed: 0,
engine_meter: EngineMeter::new(gas_limit),
_phantom: PhantomData,
#[cfg(test)]
tokens: Vec::new(),
Expand Down Expand Up @@ -213,14 +249,11 @@ impl<T: Config> GasMeter<T> {
/// in order to compute the delta that needs to be charged.
pub fn sync_from_executor(
&mut self,
executor_total: u64,
engine_fuel: u64,
) -> Result<RefTimeLeft, DispatchError> {
let chargable_reftime = executor_total
.saturating_sub(self.executor_consumed)
.saturating_mul(u64::from(T::Schedule::get().instruction_weights.base));
self.executor_consumed = executor_total;
let weight_consumed = self.engine_meter.set_fuel(engine_fuel);
self.gas_left
.checked_reduce(Weight::from_parts(chargable_reftime, 0))
.checked_reduce(weight_consumed)
.ok_or_else(|| Error::<T>::OutOfGas)?;
Ok(RefTimeLeft(self.gas_left.ref_time()))
}
Expand All @@ -234,13 +267,8 @@ impl<T: Config> GasMeter<T> {
/// It is important that this does **not** actually sync with the executor. That has
/// to be done by the caller.
pub fn sync_to_executor(&mut self, before: RefTimeLeft) -> Result<Syncable, DispatchError> {
let chargable_executor_resource = before
.0
.saturating_sub(self.gas_left().ref_time())
.checked_div(u64::from(T::Schedule::get().instruction_weights.base))
.ok_or(Error::<T>::InvalidSchedule)?;
self.executor_consumed.saturating_accrue(chargable_executor_resource);
Ok(Syncable(chargable_executor_resource))
let ref_time_consumed = before.0.saturating_sub(self.gas_left().ref_time());
self.engine_meter.charge_ref_time(ref_time_consumed)
}

/// Returns the amount of gas that is required to run the same call.
Expand Down
7 changes: 7 additions & 0 deletions substrate/frame/contracts/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ pub struct Schedule<T: Config> {
pub host_fn_weights: HostFnWeights<T>,
}

impl<T: Config> Schedule<T> {
/// Returns the reference time per engine fuel.
pub fn ref_time_by_fuel(&self) -> u64 {
self.instruction_weights.base as u64
}
}

/// Describes the upper limits on various metrics.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)]
Expand Down
Loading