use cosmwasm_vm_derive::hash_function;
use std::sync::Arc;
use wasmer::NativeEngineExt;
use wasmer::{
sys::BaseTunables, wasmparser::Operator, CompilerConfig, Engine, Pages, Target, WASM_PAGE_SIZE,
};
use crate::size::Size;
use super::gatekeeper::Gatekeeper;
use super::limiting_tunables::LimitingTunables;
use super::metering::{is_accounting, Metering};
const MAX_WASM_PAGES: u32 = 65536;
#[hash_function(const_name = "COST_FUNCTION_HASH")]
fn cost(operator: &Operator) -> u64 {
const GAS_PER_OPERATION: u64 = 115;
if is_accounting(operator) {
GAS_PER_OPERATION * 14
} else {
GAS_PER_OPERATION
}
}
pub fn make_compiler_config() -> impl CompilerConfig + Into<Engine> {
wasmer::Singlepass::new()
}
pub fn make_runtime_engine(memory_limit: Option<Size>) -> Engine {
let mut engine = Engine::headless();
if let Some(limit) = memory_limit {
let base = BaseTunables::for_target(&Target::default());
let tunables = LimitingTunables::new(base, limit_to_pages(limit));
engine.set_tunables(tunables);
}
engine
}
pub fn make_compiling_engine(memory_limit: Option<Size>) -> Engine {
let gas_limit = 0;
let deterministic = Arc::new(Gatekeeper::default());
let metering = Arc::new(Metering::new(gas_limit, cost));
let mut compiler = make_compiler_config();
compiler.canonicalize_nans(true);
compiler.push_middleware(deterministic);
compiler.push_middleware(metering);
let mut engine: Engine = compiler.into();
if let Some(limit) = memory_limit {
let base = BaseTunables::for_target(&Target::default());
let tunables = LimitingTunables::new(base, limit_to_pages(limit));
engine.set_tunables(tunables);
}
engine
}
fn limit_to_pages(limit: Size) -> Pages {
let limit_in_pages: usize = limit.0 / WASM_PAGE_SIZE;
let capped = match u32::try_from(limit_in_pages) {
Ok(x) => std::cmp::min(x, MAX_WASM_PAGES),
Err(_too_large) => MAX_WASM_PAGES,
};
Pages(capped)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cost_works() {
assert_eq!(cost(&Operator::Br { relative_depth: 3 }), 1610);
assert_eq!(cost(&Operator::Return {}), 1610);
assert_eq!(cost(&Operator::I64Const { value: 7 }), 115);
assert_eq!(cost(&Operator::I64Extend8S {}), 115);
}
#[test]
fn limit_to_pages_works() {
assert_eq!(limit_to_pages(Size::new(0)), Pages(0));
assert_eq!(limit_to_pages(Size::new(1)), Pages(0));
assert_eq!(limit_to_pages(Size::kibi(63)), Pages(0));
assert_eq!(limit_to_pages(Size::kibi(64)), Pages(1));
assert_eq!(limit_to_pages(Size::kibi(65)), Pages(1));
assert_eq!(limit_to_pages(Size::new(u32::MAX as usize)), Pages(65535));
assert_eq!(limit_to_pages(Size::gibi(3)), Pages(49152));
assert_eq!(limit_to_pages(Size::gibi(4)), Pages(65536));
assert_eq!(limit_to_pages(Size::gibi(5)), Pages(65536));
assert_eq!(limit_to_pages(Size::new(usize::MAX)), Pages(65536));
}
}