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

Update Wasmi to v0.32.0-beta.5 #2941

Closed
wants to merge 18 commits into from
Closed
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
51 changes: 48 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.0-beta.5", default-features = false }
impl-trait-for-tuples = "0.2"

# Only used in benchmarking to generate contract code
Expand Down
4 changes: 2 additions & 2 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 Down
3 changes: 2 additions & 1 deletion substrate/frame/contracts/src/benchmarking/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::wasm::{
AllowDeprecatedInterface, AllowUnstableInterface, Determinism, Environment, WasmBlob,
};
use sp_core::Get;
use wasmi::{errors::LinkerError, Func, Linker, StackLimits, Store};
use wasmi::{errors::LinkerError, CompilationMode, Func, Linker, StackLimits, Store};

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

Expand Down
2 changes: 1 addition & 1 deletion substrate/frame/contracts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3078,7 +3078,7 @@ fn gas_estimation_call_runtime() {
// contract encodes the result of the dispatch runtime
let outcome = u32::decode(&mut result.result.unwrap().data.as_ref()).unwrap();
assert_eq!(outcome, 0);
assert!(result.gas_required.ref_time() > result.gas_consumed.ref_time());
assert!(result.gas_required.ref_time() >= result.gas_consumed.ref_time());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is not incorrect. The comment a bit further up explains this. It is the point of the test to make sure that the pre-charging works. In this case when calling into the runtime. We use a function that has a much bigger max weight than actual weight to exercise that.

Why this broke from your changes is indeed strange.


// Make the same call using the required gas. Should succeed.
assert_ok!(
Expand Down
7 changes: 5 additions & 2 deletions substrate/frame/contracts/src/wasm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use frame_support::{
use sp_core::Get;
use sp_runtime::{DispatchError, RuntimeDebug};
use sp_std::prelude::*;
use wasmi::{InstancePre, Linker, Memory, MemoryType, StackLimits, Store};
use wasmi::{CompilationMode, InstancePre, Linker, Memory, MemoryType, StackLimits, Store};

const BYTES_PER_PAGE: usize = 64 * 1024;

Expand Down Expand Up @@ -204,11 +204,13 @@ impl<T: Config> WasmBlob<T> {
determinism: Determinism,
stack_limits: StackLimits,
allow_deprecated: AllowDeprecatedInterface,
compilation_mode: CompilationMode,
) -> Result<(Store<H>, Memory, InstancePre), &'static str>
where
E: Environment<H>,
{
let contract = LoadedModule::new::<T>(&code, determinism, Some(stack_limits))?;
let contract =
LoadedModule::new::<T>(&code, determinism, Some(stack_limits), compilation_mode)?;
let mut store = Store::new(&contract.engine, host_state);
let mut linker = Linker::new(&contract.engine);
E::define(
Expand Down Expand Up @@ -360,6 +362,7 @@ impl<T: Config> Executable<T> for WasmBlob<T> {
ExportedFunction::Call => AllowDeprecatedInterface::Yes,
ExportedFunction::Constructor => AllowDeprecatedInterface::No,
},
CompilationMode::Lazy,
)
.map_err(|msg| {
log::debug!(target: LOG_TARGET, "failed to instantiate code to wasmi: {}", msg);
Expand Down
16 changes: 10 additions & 6 deletions substrate/frame/contracts/src/wasm/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ use sp_runtime::{traits::Hash, DispatchError};
#[cfg(any(test, feature = "runtime-benchmarks"))]
use sp_std::prelude::Vec;
use wasmi::{
core::ValueType as WasmiValueType, Config as WasmiConfig, Engine, ExternType,
FuelConsumptionMode, Module, StackLimits,
core::ValueType as WasmiValueType, CompilationMode, Config as WasmiConfig, Engine, ExternType,
Module, StackLimits,
};

/// Imported memory must be located inside this module. The reason for hardcoding is that current
Expand All @@ -57,6 +57,7 @@ impl LoadedModule {
code: &[u8],
determinism: Determinism,
stack_limits: Option<StackLimits>,
compilation_mode: CompilationMode,
) -> Result<Self, &'static str> {
// NOTE: wasmi does not support unstable WebAssembly features. The module is implicitly
// checked for not having those ones when creating `wasmi::Module` below.
Expand All @@ -71,8 +72,8 @@ impl LoadedModule {
.wasm_extended_const(false)
.wasm_saturating_float_to_int(false)
.floats(matches!(determinism, Determinism::Relaxed))
.consume_fuel(true)
.fuel_consumption_mode(FuelConsumptionMode::Eager);
.compilation_mode(compilation_mode)
.consume_fuel(true);

if let Some(stack_limits) = stack_limits {
config.set_stack_limits(stack_limits);
Expand Down Expand Up @@ -229,7 +230,8 @@ where
(|| {
// We check that the module is generally valid,
// and does not have restricted WebAssembly features, here.
let contract_module = LoadedModule::new::<T>(code, determinism, None)?;
let contract_module =
LoadedModule::new::<T>(code, determinism, None, CompilationMode::Eager)?;
// The we check that module satisfies constraints the pallet puts on contracts.
contract_module.scan_exports()?;
contract_module.scan_imports::<T>(schedule)?;
Expand All @@ -255,6 +257,7 @@ where
determinism,
stack_limits,
AllowDeprecatedInterface::No,
CompilationMode::Eager,
)
.map_err(|err| {
log::debug!(target: LOG_TARGET, "{}", err);
Expand Down Expand Up @@ -312,7 +315,8 @@ pub mod benchmarking {
owner: AccountIdOf<T>,
) -> Result<WasmBlob<T>, DispatchError> {
let determinism = Determinism::Enforced;
let contract_module = LoadedModule::new::<T>(&code, determinism, None)?;
let contract_module =
LoadedModule::new::<T>(&code, determinism, None, CompilationMode::Eager)?;
let _ = contract_module.scan_imports::<T>(schedule)?;
let code: CodeVec<T> = code.try_into().map_err(|_| <Error<T>>::CodeTooLarge)?;
let code_info = CodeInfo {
Expand Down
72 changes: 38 additions & 34 deletions substrate/frame/contracts/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,49 +441,53 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> {

/// Converts the sandbox result and the runtime state into the execution outcome.
pub fn to_execution_result(self, sandbox_result: Result<(), wasmi::Error>) -> ExecResult {
use wasmi::core::TrapCode::OutOfFuel;
use wasmi::{
core::TrapCode,
errors::{ErrorKind, FuelError},
};
use TrapReason::*;

match sandbox_result {
let Err(error) = sandbox_result else {
// Contract returned from main function -> no data was returned.
Ok(_) => Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }),
return Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
};
if let ErrorKind::Fuel(FuelError::OutOfFuel) = error.kind() {
// `OutOfGas` when host asks engine to consume more than left in the _store_.
// We should never get this case, as gas meter is being charged (and hence raises error)
// first.
Err(wasmi::Error::Store(_)) => Err(Error::<E::T>::OutOfGas.into()),
// Contract either trapped or some host function aborted the execution.
Err(wasmi::Error::Trap(trap)) => {
if let Some(OutOfFuel) = trap.trap_code() {
// `OutOfGas` during engine execution.
return Err(Error::<E::T>::OutOfGas.into())
}
// If we encoded a reason then it is some abort generated by a host function.
if let Some(reason) = &trap.downcast_ref::<TrapReason>() {
match &reason {
Return(ReturnData { flags, data }) => {
let flags = ReturnFlags::from_bits(*flags)
.ok_or(Error::<E::T>::InvalidCallFlags)?;
return Ok(ExecReturnValue { flags, data: data.to_vec() })
},
Termination =>
return Ok(ExecReturnValue {
flags: ReturnFlags::empty(),
data: Vec::new(),
}),
SupervisorError(error) => return Err((*error).into()),
}
}
return Err(Error::<E::T>::OutOfGas.into())
}
match error.as_trap_code() {
Some(TrapCode::OutOfFuel) => {
// `OutOfGas` during engine execution.
return Err(Error::<E::T>::OutOfGas.into())
},
Some(_trap_code) => {
// Otherwise the trap came from the contract itself.
Err(Error::<E::T>::ContractTrapped.into())
return Err(Error::<E::T>::ContractTrapped.into())
},
// Any other error 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(_) => Err(Error::<E::T>::CodeRejected.into()),
None => {},
}
// If we encoded a reason then it is some abort generated by a host function.
if let Some(reason) = &error.downcast_ref::<TrapReason>() {
match &reason {
Return(ReturnData { flags, data }) => {
let flags =
ReturnFlags::from_bits(*flags).ok_or(Error::<E::T>::InvalidCallFlags)?;
return Ok(ExecReturnValue { flags, data: data.to_vec() })
},
Termination =>
return Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }),
SupervisorError(error) => return Err((*error).into()),
}
}
// Any other error 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(Error::<E::T>::CodeRejected.into())
}

/// Get a mutable reference to the inner `Ext`.
Expand Down
Loading
Loading