use std::collections::BTreeMap;
use pwasm_utils::rules::{InstructionType, Metering, Set};
use types::bytesrepr::{self, FromBytes, ToBytes, U32_SERIALIZED_LENGTH};
const NUM_FIELDS: usize = 10;
pub const WASM_COSTS_SERIALIZED_LENGTH: usize = NUM_FIELDS * U32_SERIALIZED_LENGTH;
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub struct WasmCosts {
pub regular: u32,
pub div: u32,
pub mul: u32,
pub mem: u32,
pub initial_mem: u32,
pub grow_mem: u32,
pub memcpy: u32,
pub max_stack_height: u32,
pub opcodes_mul: u32,
pub opcodes_div: u32,
}
impl WasmCosts {
pub(crate) fn to_set(&self) -> Set {
let meterings = {
let mut tmp = BTreeMap::new();
tmp.insert(InstructionType::Load, Metering::Fixed(self.mem));
tmp.insert(InstructionType::Store, Metering::Fixed(self.mem));
tmp.insert(InstructionType::Div, Metering::Fixed(self.div));
tmp.insert(InstructionType::Mul, Metering::Fixed(self.mul));
tmp
};
Set::new(self.regular, meterings)
.with_grow_cost(self.grow_mem)
.with_forbidden_floats()
}
}
impl ToBytes for WasmCosts {
fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
let mut ret = bytesrepr::unchecked_allocate_buffer(self);
ret.append(&mut self.regular.to_bytes()?);
ret.append(&mut self.div.to_bytes()?);
ret.append(&mut self.mul.to_bytes()?);
ret.append(&mut self.mem.to_bytes()?);
ret.append(&mut self.initial_mem.to_bytes()?);
ret.append(&mut self.grow_mem.to_bytes()?);
ret.append(&mut self.memcpy.to_bytes()?);
ret.append(&mut self.max_stack_height.to_bytes()?);
ret.append(&mut self.opcodes_mul.to_bytes()?);
ret.append(&mut self.opcodes_div.to_bytes()?);
Ok(ret)
}
fn serialized_length(&self) -> usize {
WASM_COSTS_SERIALIZED_LENGTH
}
}
impl FromBytes for WasmCosts {
fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
let (regular, rem): (u32, &[u8]) = FromBytes::from_bytes(bytes)?;
let (div, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
let (mul, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
let (mem, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
let (initial_mem, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
let (grow_mem, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
let (memcpy, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
let (max_stack_height, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
let (opcodes_mul, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
let (opcodes_div, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
let wasm_costs = WasmCosts {
regular,
div,
mul,
mem,
initial_mem,
grow_mem,
memcpy,
max_stack_height,
opcodes_mul,
opcodes_div,
};
Ok((wasm_costs, rem))
}
}
pub mod gens {
use proptest::{num, prop_compose};
use crate::wasm_costs::WasmCosts;
prop_compose! {
pub fn wasm_costs_arb()(
regular in num::u32::ANY,
div in num::u32::ANY,
mul in num::u32::ANY,
mem in num::u32::ANY,
initial_mem in num::u32::ANY,
grow_mem in num::u32::ANY,
memcpy in num::u32::ANY,
max_stack_height in num::u32::ANY,
opcodes_mul in num::u32::ANY,
opcodes_div in num::u32::ANY,
) -> WasmCosts {
WasmCosts {
regular,
div,
mul,
mem,
initial_mem,
grow_mem,
memcpy,
max_stack_height,
opcodes_mul,
opcodes_div,
}
}
}
}
#[cfg(test)]
mod tests {
use proptest::proptest;
use types::bytesrepr;
use super::gens;
use crate::wasm_costs::WasmCosts;
fn wasm_costs_mock() -> WasmCosts {
WasmCosts {
regular: 1,
div: 16,
mul: 4,
mem: 2,
initial_mem: 4096,
grow_mem: 8192,
memcpy: 1,
max_stack_height: 64 * 1024,
opcodes_mul: 3,
opcodes_div: 8,
}
}
fn wasm_costs_free() -> WasmCosts {
WasmCosts {
regular: 0,
div: 0,
mul: 0,
mem: 0,
initial_mem: 4096,
grow_mem: 8192,
memcpy: 0,
max_stack_height: 64 * 1024,
opcodes_mul: 1,
opcodes_div: 1,
}
}
#[test]
fn should_serialize_and_deserialize() {
let mock = wasm_costs_mock();
let free = wasm_costs_free();
bytesrepr::test_serialization_roundtrip(&mock);
bytesrepr::test_serialization_roundtrip(&free);
}
proptest! {
#[test]
fn should_serialize_and_deserialize_with_arbitrary_values(
wasm_costs in gens::wasm_costs_arb()
) {
bytesrepr::test_serialization_roundtrip(&wasm_costs);
}
}
}