1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
use super::AsBudget;
use crate::{xdr::ContractCostType, Host};
use wasmi::{errors, ResourceLimiter};
/// This is a subset of `wasmi::FuelCosts` which are configurable, because it
/// doesn't derive all the traits we want. These fields (coarsely) define the
/// relative costs of different wasm instruction types and are for wasmi internal
/// fuel metering use only. Units are in "fuels".
#[derive(Clone)]
pub(crate) struct FuelConfig {
/// The base fuel costs for all instructions.
pub base: u64,
/// The fuel cost for instruction operating on Wasm entities.
///
/// # Note
///
/// A Wasm entitiy is one of `func`, `global`, `memory` or `table`.
/// Those instructions are usually a bit more costly since they need
/// multiplie indirect accesses through the Wasm instance and store.
pub entity: u64,
/// The fuel cost offset for `memory.load` instructions.
pub load: u64,
/// The fuel cost offset for `memory.store` instructions.
pub store: u64,
/// The fuel cost offset for `call` and `call_indirect` instructions.
pub call: u64,
}
// These values are calibrated and set by us.
impl Default for FuelConfig {
fn default() -> Self {
FuelConfig {
base: 1,
entity: 3,
load: 2,
store: 1,
call: 67,
}
}
}
impl FuelConfig {
// These values are the "factory default" and used for calibration.
#[cfg(any(test, feature = "testutils", feature = "bench"))]
pub(crate) fn reset(&mut self) {
self.base = 1;
self.entity = 1;
self.load = 1;
self.store = 1;
self.call = 1;
}
}
pub(crate) struct WasmiLimits {
pub table_elements: u32,
pub instances: usize,
pub tables: usize,
pub memories: usize,
}
pub(crate) const WASMI_LIMITS_CONFIG: WasmiLimits = WasmiLimits {
table_elements: 1000,
instances: 1,
tables: 1,
memories: 1,
};
impl ResourceLimiter for Host {
fn memory_growing(
&mut self,
current: usize,
desired: usize,
maximum: Option<usize>,
) -> Result<bool, errors::MemoryError> {
let host_limit = self
.as_budget()
.get_mem_bytes_remaining()
.map_err(|_| errors::MemoryError::OutOfBoundsGrowth)?;
let delta = (desired as u64).saturating_sub(current as u64);
let allow = if delta > host_limit {
false
} else {
match maximum {
Some(max) => desired <= max,
None => true,
}
};
if allow {
#[cfg(any(test, feature = "testutils", feature = "bench"))]
{
self.as_budget()
.track_wasm_mem_alloc(delta)
.map_err(|_| errors::MemoryError::OutOfBoundsGrowth)?;
}
self.as_budget()
.charge(ContractCostType::MemAlloc, Some(delta))
.map(|_| true)
.map_err(|_| errors::MemoryError::OutOfBoundsGrowth)
} else {
Err(errors::MemoryError::OutOfBoundsGrowth)
}
}
fn table_growing(
&mut self,
current: u32,
desired: u32,
maximum: Option<u32>,
) -> Result<bool, errors::TableError> {
let allow = if desired > WASMI_LIMITS_CONFIG.table_elements {
false
} else {
match maximum {
Some(max) => desired <= max,
None => true,
}
};
if allow {
Ok(allow)
} else {
Err(errors::TableError::GrowOutOfBounds {
maximum: maximum.unwrap_or(u32::MAX),
current,
delta: desired - current,
})
}
}
fn instances(&self) -> usize {
WASMI_LIMITS_CONFIG.instances
}
fn tables(&self) -> usize {
WASMI_LIMITS_CONFIG.tables
}
fn memories(&self) -> usize {
WASMI_LIMITS_CONFIG.memories
}
}