#![allow(unused_parens)]
use crate::{weights::WeightInfo, Config, CostsPerBlockOf, DbWeightOf};
use common::scheduler::SchedulingCostsPerBlock;
use core_processor::configs::{ExtCosts, ProcessCosts, RentCosts};
use frame_support::{traits::Get, weights::Weight};
use gear_core::{
code::MAX_WASM_PAGES_AMOUNT,
costs::SyscallCosts,
message,
pages::{GearPage, WasmPage},
};
use gear_lazy_pages_common::LazyPagesCosts;
use gear_wasm_instrument::{
gas_metering::{MemoryGrowCost, Rules},
parity_wasm::elements::{Instruction, Module, SignExtInstruction, Type},
};
use pallet_gear_proc_macro::{ScheduleDebug, WeightDebug};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_runtime::{
codec::{Decode, Encode},
RuntimeDebug,
};
use sp_std::{marker::PhantomData, vec::Vec};
pub const API_BENCHMARK_BATCH_SIZE: u32 = 80;
pub const INSTR_BENCHMARK_BATCH_SIZE: u32 = 500;
#[cfg(not(feature = "fuzz"))]
pub const STACK_HEIGHT_LIMIT: u32 = 36_743;
#[cfg(feature = "fuzz")]
pub const FUZZER_STACK_HEIGHT_LIMIT: u32 = 65_000;
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(bound(serialize = "", deserialize = "")))]
#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct Schedule<T: Config> {
pub limits: Limits,
pub instruction_weights: InstructionWeights<T>,
pub syscall_weights: SyscallWeights<T>,
pub memory_weights: MemoryWeights<T>,
pub module_instantiation_per_byte: Weight,
pub db_write_per_byte: Weight,
pub db_read_per_byte: Weight,
pub code_instrumentation_cost: Weight,
pub code_instrumentation_byte_cost: Weight,
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct Limits {
pub stack_height: Option<u32>,
pub globals: u32,
pub locals: u32,
pub parameters: u32,
pub memory_pages: u16,
pub table_size: u32,
pub br_table_size: u32,
pub subject_len: u32,
pub call_depth: u32,
pub payload_len: u32,
pub code_len: u32,
}
impl Limits {
pub fn max_memory_size(&self) -> u32 {
self.memory_pages as u32 * 64 * 1024
}
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct InstructionWeights<T: Config> {
pub version: u32,
pub i64const: u32,
pub i64load: u32,
pub i32load: u32,
pub i64store: u32,
pub i32store: u32,
pub select: u32,
pub r#if: u32,
pub br: u32,
pub br_if: u32,
pub br_table: u32,
pub br_table_per_entry: u32,
pub call: u32,
pub call_indirect: u32,
pub call_indirect_per_param: u32,
pub call_per_local: u32,
pub local_get: u32,
pub local_set: u32,
pub local_tee: u32,
pub global_get: u32,
pub global_set: u32,
pub memory_current: u32,
pub i64clz: u32,
pub i32clz: u32,
pub i64ctz: u32,
pub i32ctz: u32,
pub i64popcnt: u32,
pub i32popcnt: u32,
pub i64eqz: u32,
pub i32eqz: u32,
pub i32extend8s: u32,
pub i32extend16s: u32,
pub i64extend8s: u32,
pub i64extend16s: u32,
pub i64extend32s: u32,
pub i64extendsi32: u32,
pub i64extendui32: u32,
pub i32wrapi64: u32,
pub i64eq: u32,
pub i32eq: u32,
pub i64ne: u32,
pub i32ne: u32,
pub i64lts: u32,
pub i32lts: u32,
pub i64ltu: u32,
pub i32ltu: u32,
pub i64gts: u32,
pub i32gts: u32,
pub i64gtu: u32,
pub i32gtu: u32,
pub i64les: u32,
pub i32les: u32,
pub i64leu: u32,
pub i32leu: u32,
pub i64ges: u32,
pub i32ges: u32,
pub i64geu: u32,
pub i32geu: u32,
pub i64add: u32,
pub i32add: u32,
pub i64sub: u32,
pub i32sub: u32,
pub i64mul: u32,
pub i32mul: u32,
pub i64divs: u32,
pub i32divs: u32,
pub i64divu: u32,
pub i32divu: u32,
pub i64rems: u32,
pub i32rems: u32,
pub i64remu: u32,
pub i32remu: u32,
pub i64and: u32,
pub i32and: u32,
pub i64or: u32,
pub i32or: u32,
pub i64xor: u32,
pub i32xor: u32,
pub i64shl: u32,
pub i32shl: u32,
pub i64shrs: u32,
pub i32shrs: u32,
pub i64shru: u32,
pub i32shru: u32,
pub i64rotl: u32,
pub i32rotl: u32,
pub i64rotr: u32,
pub i32rotr: u32,
#[codec(skip)]
#[cfg_attr(feature = "std", serde(skip))]
pub _phantom: PhantomData<T>,
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct SyscallWeights<T: Config> {
pub alloc: Weight,
pub alloc_per_page: Weight,
pub free: Weight,
pub free_range: Weight,
pub free_range_per_page: Weight,
pub gr_reserve_gas: Weight,
pub gr_unreserve_gas: Weight,
pub gr_system_reserve_gas: Weight,
pub gr_gas_available: Weight,
pub gr_message_id: Weight,
pub gr_program_id: Weight,
pub gr_source: Weight,
pub gr_value: Weight,
pub gr_value_available: Weight,
pub gr_size: Weight,
pub gr_read: Weight,
pub gr_read_per_byte: Weight,
pub gr_env_vars: Weight,
pub gr_block_height: Weight,
pub gr_block_timestamp: Weight,
pub gr_random: Weight,
pub gr_reply_deposit: Weight,
pub gr_send: Weight,
pub gr_send_per_byte: Weight,
pub gr_send_wgas: Weight,
pub gr_send_wgas_per_byte: Weight,
pub gr_send_init: Weight,
pub gr_send_push: Weight,
pub gr_send_push_per_byte: Weight,
pub gr_send_commit: Weight,
pub gr_send_commit_wgas: Weight,
pub gr_reservation_send: Weight,
pub gr_reservation_send_per_byte: Weight,
pub gr_reservation_send_commit: Weight,
pub gr_reply_commit: Weight,
pub gr_reply_commit_wgas: Weight,
pub gr_reservation_reply: Weight,
pub gr_reservation_reply_per_byte: Weight,
pub gr_reservation_reply_commit: Weight,
pub gr_reply_push: Weight,
pub gr_reply: Weight,
pub gr_reply_per_byte: Weight,
pub gr_reply_wgas: Weight,
pub gr_reply_wgas_per_byte: Weight,
pub gr_reply_push_per_byte: Weight,
pub gr_reply_to: Weight,
pub gr_signal_code: Weight,
pub gr_signal_from: Weight,
pub gr_reply_input: Weight,
pub gr_reply_input_wgas: Weight,
pub gr_reply_push_input: Weight,
pub gr_reply_push_input_per_byte: Weight,
pub gr_send_input: Weight,
pub gr_send_input_wgas: Weight,
pub gr_send_push_input: Weight,
pub gr_send_push_input_per_byte: Weight,
pub gr_debug: Weight,
pub gr_debug_per_byte: Weight,
pub gr_reply_code: Weight,
pub gr_exit: Weight,
pub gr_leave: Weight,
pub gr_wait: Weight,
pub gr_wait_for: Weight,
pub gr_wait_up_to: Weight,
pub gr_wake: Weight,
pub gr_create_program: Weight,
pub gr_create_program_payload_per_byte: Weight,
pub gr_create_program_salt_per_byte: Weight,
pub gr_create_program_wgas: Weight,
pub gr_create_program_wgas_payload_per_byte: Weight,
pub gr_create_program_wgas_salt_per_byte: Weight,
#[codec(skip)]
#[cfg_attr(feature = "std", serde(skip))]
pub _phantom: PhantomData<T>,
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct MemoryWeights<T: Config> {
pub lazy_pages_signal_read: Weight,
pub lazy_pages_signal_write: Weight,
pub lazy_pages_signal_write_after_read: Weight,
pub lazy_pages_host_func_read: Weight,
pub lazy_pages_host_func_write: Weight,
pub lazy_pages_host_func_write_after_read: Weight,
pub load_page_data: Weight,
pub upload_page_data: Weight,
pub static_page: Weight,
pub mem_grow: Weight,
pub parachain_read_heuristic: Weight,
#[codec(skip)]
#[cfg_attr(feature = "std", serde(skip))]
pub _phantom: PhantomData<T>,
}
impl<T: Config> From<MemoryWeights<T>> for LazyPagesCosts {
fn from(val: MemoryWeights<T>) -> Self {
Self {
signal_read: val.lazy_pages_signal_read.ref_time().into(),
signal_write: val
.lazy_pages_signal_write
.saturating_add(val.upload_page_data)
.ref_time()
.into(),
signal_write_after_read: val
.lazy_pages_signal_write_after_read
.saturating_add(val.upload_page_data)
.ref_time()
.into(),
host_func_read: val.lazy_pages_host_func_read.ref_time().into(),
host_func_write: val
.lazy_pages_host_func_write
.saturating_add(val.upload_page_data)
.ref_time()
.into(),
host_func_write_after_read: val
.lazy_pages_host_func_write_after_read
.saturating_add(val.upload_page_data)
.ref_time()
.into(),
load_page_storage_data: val
.load_page_data
.saturating_add(val.parachain_read_heuristic)
.ref_time()
.into(),
}
}
}
macro_rules! replace_token {
($_in:tt $replacement:tt) => {
$replacement
};
}
macro_rules! call_zero {
($name:ident, $( $arg:expr ),*) => {
<T as Config>::WeightInfo::$name($( replace_token!($arg 0) ),*)
};
}
macro_rules! cost_args {
($name:ident, $( $arg: expr ),+) => {
(<T as Config>::WeightInfo::$name($( $arg ),+).saturating_sub(call_zero!($name, $( $arg ),+))).ref_time()
}
}
macro_rules! cost_batched_args {
($name:ident, $( $arg: expr ),+) => {
cost_args!($name, $( $arg ),+) / u64::from(API_BENCHMARK_BATCH_SIZE)
}
}
macro_rules! cost_instr_no_params_with_batch_size {
($name:ident, $batch_size:expr) => {
(cost_args!($name, 1) / u64::from($batch_size)) as u32
};
}
macro_rules! cost_instr_with_batch_size {
($name:ident, $num_params:expr, $batch_size:expr) => {
cost_instr_no_params_with_batch_size!($name, $batch_size).saturating_sub(
(cost_instr_no_params_with_batch_size!(instr_i64const, $batch_size) / 2)
.saturating_mul($num_params),
)
};
}
macro_rules! cost_instr {
($name:ident, $num_params:expr) => {
cost_instr_with_batch_size!($name, $num_params, INSTR_BENCHMARK_BATCH_SIZE)
};
}
macro_rules! cost_byte_args {
($name:ident, $( $arg: expr ),+) => {
cost_args!($name, $( $arg ),+) / 1024
}
}
macro_rules! cost_byte_batched_args {
($name:ident, $( $arg: expr ),+) => {
cost_batched_args!($name, $( $arg ),+) / 1024
}
}
macro_rules! cost {
($name:ident) => {
cost_args!($name, 1)
};
}
macro_rules! cost_batched {
($name:ident) => {
cost_batched_args!($name, 1)
};
}
macro_rules! cost_byte {
($name:ident) => {
cost_byte_args!($name, 1)
};
}
macro_rules! cost_byte_batched {
($name:ident) => {
cost_byte_batched_args!($name, 1)
};
}
macro_rules! to_weight {
($ref_time:expr $(, $proof_size:expr )?) => {
Weight::from_parts($ref_time, 0)$(.set_proof_size($proof_size))?
};
}
impl<T: Config> Default for Schedule<T> {
fn default() -> Self {
Self {
limits: Default::default(),
instruction_weights: Default::default(),
syscall_weights: Default::default(),
memory_weights: Default::default(),
db_write_per_byte: to_weight!(cost_byte!(db_write_per_kb)),
db_read_per_byte: to_weight!(cost_byte!(db_read_per_kb)),
module_instantiation_per_byte: to_weight!(cost_byte!(instantiate_module_per_kb)),
code_instrumentation_cost: call_zero!(reinstrument_per_kb, 0),
code_instrumentation_byte_cost: to_weight!(cost_byte!(reinstrument_per_kb)),
}
}
}
impl Default for Limits {
fn default() -> Self {
Self {
#[cfg(not(feature = "fuzz"))]
stack_height: Some(STACK_HEIGHT_LIMIT),
#[cfg(feature = "fuzz")]
stack_height: Some(FUZZER_STACK_HEIGHT_LIMIT),
globals: 256,
locals: 1024,
parameters: 128,
memory_pages: MAX_WASM_PAGES_AMOUNT,
table_size: 4096,
br_table_size: 256,
subject_len: 32,
call_depth: 32,
payload_len: message::MAX_PAYLOAD_SIZE as u32,
code_len: 512 * 1024,
}
}
}
impl<T: Config> Default for InstructionWeights<T> {
fn default() -> Self {
Self {
version: 1300,
i64const: cost_instr!(instr_i64const, 1),
i64load: cost_instr!(instr_i64load, 0),
i32load: cost_instr!(instr_i32load, 0),
i64store: cost_instr!(instr_i64store, 1),
i32store: cost_instr!(instr_i32store, 0),
select: cost_instr!(instr_select, 2),
r#if: cost_instr!(instr_if, 0),
br: cost_instr!(instr_br, 0),
br_if: cost_instr!(instr_br_if, 1),
br_table: cost_instr!(instr_br_table, 0),
br_table_per_entry: cost_instr!(instr_br_table_per_entry, 0),
call: cost_instr!(instr_call, 2),
call_indirect: cost_instr!(instr_call_indirect, 1),
call_indirect_per_param: cost_instr!(instr_call_indirect_per_param, 1),
call_per_local: cost_instr!(instr_call_per_local, 1),
local_get: cost_instr!(instr_local_get, 0),
local_set: cost_instr!(instr_local_set, 1),
local_tee: cost_instr!(instr_local_tee, 1),
global_get: cost_instr!(instr_global_get, 0),
global_set: cost_instr!(instr_global_set, 1),
memory_current: cost_instr!(instr_memory_current, 1),
i64clz: cost_instr!(instr_i64clz, 1),
i32clz: cost_instr!(instr_i32clz, 1),
i64ctz: cost_instr!(instr_i64ctz, 1),
i32ctz: cost_instr!(instr_i32ctz, 1),
i64popcnt: cost_instr!(instr_i64popcnt, 1),
i32popcnt: cost_instr!(instr_i32popcnt, 1),
i64eqz: cost_instr!(instr_i64eqz, 1),
i32eqz: cost_instr!(instr_i32eqz, 1),
i32extend8s: cost_instr!(instr_i32extend8s, 0),
i32extend16s: cost_instr!(instr_i32extend16s, 0),
i64extend8s: cost_instr!(instr_i64extend8s, 1),
i64extend16s: cost_instr!(instr_i64extend16s, 1),
i64extend32s: cost_instr!(instr_i64extend32s, 1),
i64extendsi32: cost_instr!(instr_i64extendsi32, 0),
i64extendui32: cost_instr!(instr_i64extendui32, 0),
i32wrapi64: cost_instr!(instr_i32wrapi64, 1),
i64eq: cost_instr!(instr_i64eq, 2),
i32eq: cost_instr!(instr_i32eq, 2),
i64ne: cost_instr!(instr_i64ne, 2),
i32ne: cost_instr!(instr_i32ne, 2),
i64lts: cost_instr!(instr_i64lts, 2),
i32lts: cost_instr!(instr_i32lts, 2),
i64ltu: cost_instr!(instr_i64ltu, 2),
i32ltu: cost_instr!(instr_i32ltu, 2),
i64gts: cost_instr!(instr_i64gts, 2),
i32gts: cost_instr!(instr_i32gts, 2),
i64gtu: cost_instr!(instr_i64gtu, 2),
i32gtu: cost_instr!(instr_i32gtu, 2),
i64les: cost_instr!(instr_i64les, 2),
i32les: cost_instr!(instr_i32les, 2),
i64leu: cost_instr!(instr_i64leu, 2),
i32leu: cost_instr!(instr_i32leu, 2),
i64ges: cost_instr!(instr_i64ges, 2),
i32ges: cost_instr!(instr_i32ges, 2),
i64geu: cost_instr!(instr_i64geu, 2),
i32geu: cost_instr!(instr_i32geu, 2),
i64add: cost_instr!(instr_i64add, 2),
i32add: cost_instr!(instr_i32add, 2),
i64sub: cost_instr!(instr_i64sub, 2),
i32sub: cost_instr!(instr_i32sub, 2),
i64mul: cost_instr!(instr_i64mul, 2),
i32mul: cost_instr!(instr_i32mul, 2),
i64divs: cost_instr!(instr_i64divs, 2),
i32divs: cost_instr!(instr_i32divs, 2),
i64divu: cost_instr!(instr_i64divu, 2),
i32divu: cost_instr!(instr_i32divu, 2),
i64rems: cost_instr!(instr_i64rems, 2),
i32rems: cost_instr!(instr_i32rems, 2),
i64remu: cost_instr!(instr_i64remu, 2),
i32remu: cost_instr!(instr_i32remu, 2),
i64and: cost_instr!(instr_i64and, 2),
i32and: cost_instr!(instr_i32and, 2),
i64or: cost_instr!(instr_i64or, 2),
i32or: cost_instr!(instr_i32or, 2),
i64xor: cost_instr!(instr_i64xor, 2),
i32xor: cost_instr!(instr_i32xor, 2),
i64shl: cost_instr!(instr_i64shl, 2),
i32shl: cost_instr!(instr_i32shl, 2),
i64shrs: cost_instr!(instr_i64shrs, 2),
i32shrs: cost_instr!(instr_i32shrs, 2),
i64shru: cost_instr!(instr_i64shru, 2),
i32shru: cost_instr!(instr_i32shru, 2),
i64rotl: cost_instr!(instr_i64rotl, 2),
i32rotl: cost_instr!(instr_i32rotl, 2),
i64rotr: cost_instr!(instr_i64rotr, 2),
i32rotr: cost_instr!(instr_i32rotr, 2),
_phantom: PhantomData,
}
}
}
impl<T: Config> From<SyscallWeights<T>> for SyscallCosts {
fn from(weights: SyscallWeights<T>) -> SyscallCosts {
SyscallCosts {
alloc: weights.alloc.ref_time().into(),
alloc_per_page: weights.alloc_per_page.ref_time().into(),
free: weights.free.ref_time().into(),
free_range: weights.free_range.ref_time().into(),
free_range_per_page: weights.free_range_per_page.ref_time().into(),
gr_reserve_gas: weights.gr_reserve_gas.ref_time().into(),
gr_unreserve_gas: weights.gr_unreserve_gas.ref_time().into(),
gr_system_reserve_gas: weights.gr_system_reserve_gas.ref_time().into(),
gr_gas_available: weights.gr_gas_available.ref_time().into(),
gr_message_id: weights.gr_message_id.ref_time().into(),
gr_program_id: weights.gr_program_id.ref_time().into(),
gr_source: weights.gr_source.ref_time().into(),
gr_value: weights.gr_value.ref_time().into(),
gr_value_available: weights.gr_value_available.ref_time().into(),
gr_size: weights.gr_size.ref_time().into(),
gr_read: weights.gr_read.ref_time().into(),
gr_read_per_byte: weights.gr_read_per_byte.ref_time().into(),
gr_env_vars: weights.gr_env_vars.ref_time().into(),
gr_block_height: weights.gr_block_height.ref_time().into(),
gr_block_timestamp: weights.gr_block_timestamp.ref_time().into(),
gr_random: weights.gr_random.ref_time().into(),
gr_reply_deposit: weights.gr_reply_deposit.ref_time().into(),
gr_send: weights.gr_send.ref_time().into(),
gr_send_per_byte: weights.gr_send_per_byte.ref_time().into(),
gr_send_wgas: weights.gr_send_wgas.ref_time().into(),
gr_send_wgas_per_byte: weights.gr_send_wgas_per_byte.ref_time().into(),
gr_send_init: weights.gr_send_init.ref_time().into(),
gr_send_push: weights.gr_send_push.ref_time().into(),
gr_send_push_per_byte: weights.gr_send_push_per_byte.ref_time().into(),
gr_send_commit: weights.gr_send_commit.ref_time().into(),
gr_send_commit_wgas: weights.gr_send_commit_wgas.ref_time().into(),
gr_reservation_send: weights.gr_reservation_send.ref_time().into(),
gr_reservation_send_per_byte: weights.gr_reservation_send_per_byte.ref_time().into(),
gr_reservation_send_commit: weights.gr_reservation_send_commit.ref_time().into(),
gr_send_input: weights.gr_send_input.ref_time().into(),
gr_send_input_wgas: weights.gr_send_input_wgas.ref_time().into(),
gr_send_push_input: weights.gr_send_push_input.ref_time().into(),
gr_send_push_input_per_byte: weights.gr_send_push_input_per_byte.ref_time().into(),
gr_reply: weights.gr_reply.ref_time().into(),
gr_reply_per_byte: weights.gr_reply_per_byte.ref_time().into(),
gr_reply_wgas: weights.gr_reply_wgas.ref_time().into(),
gr_reply_wgas_per_byte: weights.gr_reply_wgas_per_byte.ref_time().into(),
gr_reply_push: weights.gr_reply_push.ref_time().into(),
gr_reply_push_per_byte: weights.gr_reply_push_per_byte.ref_time().into(),
gr_reply_commit: weights.gr_reply_commit.ref_time().into(),
gr_reply_commit_wgas: weights.gr_reply_commit_wgas.ref_time().into(),
gr_reservation_reply: weights.gr_reservation_reply.ref_time().into(),
gr_reservation_reply_per_byte: weights.gr_reservation_reply_per_byte.ref_time().into(),
gr_reservation_reply_commit: weights.gr_reservation_reply_commit.ref_time().into(),
gr_reply_input: weights.gr_reply_input.ref_time().into(),
gr_reply_input_wgas: weights.gr_reply_input_wgas.ref_time().into(),
gr_reply_push_input: weights.gr_reply_push_input.ref_time().into(),
gr_reply_push_input_per_byte: weights.gr_reply_push_input_per_byte.ref_time().into(),
gr_debug: weights.gr_debug.ref_time().into(),
gr_debug_per_byte: weights.gr_debug_per_byte.ref_time().into(),
gr_reply_to: weights.gr_reply_to.ref_time().into(),
gr_signal_code: weights.gr_signal_code.ref_time().into(),
gr_signal_from: weights.gr_signal_from.ref_time().into(),
gr_reply_code: weights.gr_reply_code.ref_time().into(),
gr_exit: weights.gr_exit.ref_time().into(),
gr_leave: weights.gr_leave.ref_time().into(),
gr_wait: weights.gr_wait.ref_time().into(),
gr_wait_for: weights.gr_wait_for.ref_time().into(),
gr_wait_up_to: weights.gr_wait_up_to.ref_time().into(),
gr_wake: weights.gr_wake.ref_time().into(),
gr_create_program: weights.gr_create_program.ref_time().into(),
gr_create_program_payload_per_byte: weights
.gr_create_program_payload_per_byte
.ref_time()
.into(),
gr_create_program_salt_per_byte: weights
.gr_create_program_salt_per_byte
.ref_time()
.into(),
gr_create_program_wgas: weights.gr_create_program_wgas.ref_time().into(),
gr_create_program_wgas_payload_per_byte: weights
.gr_create_program_wgas_payload_per_byte
.ref_time()
.into(),
gr_create_program_wgas_salt_per_byte: weights
.gr_create_program_wgas_salt_per_byte
.ref_time()
.into(),
}
}
}
impl<T: Config> Default for SyscallWeights<T> {
fn default() -> Self {
Self {
gr_reply_deposit: to_weight!(cost_batched!(gr_reply_deposit))
.saturating_sub(to_weight!(cost_batched!(gr_send))),
gr_send: to_weight!(cost_batched!(gr_send)),
gr_send_per_byte: to_weight!(cost_byte_batched!(gr_send_per_kb)),
gr_send_wgas: to_weight!(cost_batched!(gr_send_wgas)),
gr_send_wgas_per_byte: to_weight!(cost_byte_batched!(gr_send_wgas_per_kb)),
gr_send_init: to_weight!(cost_batched!(gr_send_init)),
gr_send_push: to_weight!(cost_batched!(gr_send_push)),
gr_send_push_per_byte: to_weight!(cost_byte_batched!(gr_send_push_per_kb)),
gr_send_commit: to_weight!(cost_batched!(gr_send_commit)),
gr_send_commit_wgas: to_weight!(cost_batched!(gr_send_commit_wgas)),
gr_reservation_send: to_weight!(cost_batched!(gr_reservation_send)),
gr_reservation_send_per_byte: to_weight!(cost_byte_batched!(
gr_reservation_send_per_kb
)),
gr_reservation_send_commit: to_weight!(cost_batched!(gr_reservation_send_commit)),
gr_send_input: to_weight!(cost_batched!(gr_send_input)),
gr_send_input_wgas: to_weight!(cost_batched!(gr_send_input_wgas)),
gr_send_push_input: to_weight!(cost_batched!(gr_send_push_input)),
gr_send_push_input_per_byte: to_weight!(cost_byte_batched!(gr_send_push_input_per_kb)),
gr_reply: to_weight!(cost!(gr_reply)),
gr_reply_per_byte: to_weight!(cost_byte!(gr_reply_per_kb)),
gr_reply_wgas: to_weight!(cost!(gr_reply_wgas)),
gr_reply_wgas_per_byte: to_weight!(cost_byte!(gr_reply_wgas_per_kb)),
gr_reply_push: to_weight!(cost_batched!(gr_reply_push)),
gr_reply_push_per_byte: to_weight!(cost_byte!(gr_reply_push_per_kb)),
gr_reply_commit: to_weight!(cost!(gr_reply_commit)),
gr_reply_commit_wgas: to_weight!(cost!(gr_reply_commit_wgas)),
gr_reservation_reply: to_weight!(cost!(gr_reservation_reply)),
gr_reservation_reply_per_byte: to_weight!(cost!(gr_reservation_reply_per_kb)),
gr_reservation_reply_commit: to_weight!(cost!(gr_reservation_reply_commit)),
gr_reply_input: to_weight!(cost!(gr_reply_input)),
gr_reply_input_wgas: to_weight!(cost!(gr_reply_input_wgas)),
gr_reply_push_input: to_weight!(cost_batched!(gr_reply_push_input)),
gr_reply_push_input_per_byte: to_weight!(cost_byte!(gr_reply_push_input_per_kb)),
alloc: to_weight!(cost_batched!(alloc))
.saturating_sub(to_weight!(cost_batched!(alloc_per_page)))
.saturating_sub(to_weight!(cost_batched!(mem_grow))),
alloc_per_page: to_weight!(cost_batched!(alloc_per_page)),
free: to_weight!(cost_batched!(free)),
free_range: to_weight!(cost_batched!(free_range)),
free_range_per_page: to_weight!(cost_batched!(free_range_per_page)),
gr_reserve_gas: to_weight!(cost!(gr_reserve_gas)),
gr_system_reserve_gas: to_weight!(cost_batched!(gr_system_reserve_gas)),
gr_unreserve_gas: to_weight!(cost!(gr_unreserve_gas)),
gr_gas_available: to_weight!(cost_batched!(gr_gas_available)),
gr_message_id: to_weight!(cost_batched!(gr_message_id)),
gr_program_id: to_weight!(cost_batched!(gr_program_id)),
gr_source: to_weight!(cost_batched!(gr_source)),
gr_value: to_weight!(cost_batched!(gr_value)),
gr_value_available: to_weight!(cost_batched!(gr_value_available)),
gr_size: to_weight!(cost_batched!(gr_size)),
gr_read: to_weight!(cost_batched!(gr_read)),
gr_read_per_byte: to_weight!(cost_byte_batched!(gr_read_per_kb)),
gr_env_vars: to_weight!(cost_batched!(gr_env_vars)),
gr_block_height: to_weight!(cost_batched!(gr_block_height)),
gr_block_timestamp: to_weight!(cost_batched!(gr_block_timestamp)),
gr_random: to_weight!(cost_batched!(gr_random)),
gr_debug: to_weight!(cost_batched!(gr_debug)),
gr_debug_per_byte: to_weight!(cost_byte_batched!(gr_debug_per_kb)),
gr_reply_to: to_weight!(cost_batched!(gr_reply_to)),
gr_signal_code: to_weight!(cost_batched!(gr_signal_code)),
gr_signal_from: to_weight!(cost_batched!(gr_signal_from)),
gr_reply_code: to_weight!(cost_batched!(gr_reply_code)),
gr_exit: to_weight!(cost!(gr_exit)),
gr_leave: to_weight!(cost!(gr_leave)),
gr_wait: to_weight!(cost!(gr_wait)),
gr_wait_for: to_weight!(cost!(gr_wait_for)),
gr_wait_up_to: to_weight!(cost!(gr_wait_up_to)),
gr_wake: to_weight!(cost_batched!(gr_wake)),
gr_create_program: to_weight!(cost_batched!(gr_create_program)),
gr_create_program_payload_per_byte: to_weight!(cost_byte_batched_args!(
gr_create_program_per_kb,
1,
0
)),
gr_create_program_salt_per_byte: to_weight!(cost_byte_batched_args!(
gr_create_program_per_kb,
0,
1
)),
gr_create_program_wgas: to_weight!(cost_batched!(gr_create_program_wgas)),
gr_create_program_wgas_payload_per_byte: to_weight!(cost_byte_batched_args!(
gr_create_program_wgas_per_kb,
1,
0
)),
gr_create_program_wgas_salt_per_byte: to_weight!(cost_byte_batched_args!(
gr_create_program_wgas_per_kb,
0,
1
)),
_phantom: PhantomData,
}
}
}
impl<T: Config> Default for MemoryWeights<T> {
fn default() -> Self {
macro_rules! to_cost_per_gear_page {
($name:ident) => {
cost!($name) / (WasmPage::SIZE / GearPage::SIZE) as u64
};
}
const KB_SIZE: u64 = 1024;
macro_rules! host_func_access {
($name:ident, $syscall:ident) => {{
let syscall_per_kb_weight = cost_batched!($syscall);
let syscall_per_gear_page_weight =
(syscall_per_kb_weight / KB_SIZE) * GearPage::SIZE as u64;
to_cost_per_gear_page!($name).saturating_sub(syscall_per_gear_page_weight)
}};
}
const KB_AMOUNT_IN_ONE_GEAR_PAGE: u64 = GearPage::SIZE as u64 / KB_SIZE;
const _: () = assert!(KB_AMOUNT_IN_ONE_GEAR_PAGE > 0);
const _: () = assert!(GearPage::SIZE as u64 % KB_SIZE == 0);
Self {
lazy_pages_signal_read: to_weight!(to_cost_per_gear_page!(lazy_pages_signal_read)),
lazy_pages_signal_write: to_weight!(to_cost_per_gear_page!(lazy_pages_signal_write)),
lazy_pages_signal_write_after_read: to_weight!(to_cost_per_gear_page!(
lazy_pages_signal_write_after_read
)),
lazy_pages_host_func_read: to_weight!(host_func_access!(
lazy_pages_host_func_read,
gr_debug_per_kb
)),
lazy_pages_host_func_write: to_weight!(host_func_access!(
lazy_pages_host_func_write,
gr_read_per_kb
)),
lazy_pages_host_func_write_after_read: to_weight!(host_func_access!(
lazy_pages_host_func_write_after_read,
gr_read_per_kb
)),
load_page_data: to_weight!(to_cost_per_gear_page!(lazy_pages_load_page_storage_data)
.saturating_sub(to_cost_per_gear_page!(lazy_pages_signal_read))),
upload_page_data: to_weight!(cost!(db_write_per_kb)
.saturating_mul(KB_AMOUNT_IN_ONE_GEAR_PAGE)
.saturating_add(T::DbWeight::get().writes(1).ref_time())),
static_page: Weight::from_parts(100, 0),
mem_grow: to_weight!(cost_batched!(mem_grow)),
parachain_read_heuristic: Weight::zero(),
_phantom: PhantomData,
}
}
}
struct ScheduleRules<'a, T: Config> {
schedule: &'a Schedule<T>,
params: Vec<u32>,
}
impl<T: Config> Schedule<T> {
pub fn rules(&self, module: &Module) -> impl Rules + '_ {
ScheduleRules {
schedule: self,
params: module
.type_section()
.iter()
.flat_map(|section| section.types())
.map(|func| {
let Type::Function(func) = func;
func.params().len() as u32
})
.collect(),
}
}
pub fn process_costs(&self) -> ProcessCosts {
ProcessCosts {
ext: ExtCosts {
syscalls: self.syscall_weights.clone().into(),
rent: RentCosts {
waitlist: CostsPerBlockOf::<T>::waitlist().into(),
dispatch_stash: CostsPerBlockOf::<T>::dispatch_stash().into(),
reservation: CostsPerBlockOf::<T>::reservation().into(),
},
mem_grow: self.memory_weights.mem_grow.ref_time().into(),
},
lazy_pages: self.memory_weights.clone().into(),
read: DbWeightOf::<T>::get().reads(1).ref_time().into(),
read_per_byte: self.db_read_per_byte.ref_time().into(),
write: DbWeightOf::<T>::get().writes(1).ref_time().into(),
static_page: self.memory_weights.static_page.ref_time().into(),
instrumentation: self.code_instrumentation_cost.ref_time().into(),
instrumentation_per_byte: self.code_instrumentation_byte_cost.ref_time().into(),
module_instantiation_per_byte: self.module_instantiation_per_byte.ref_time().into(),
}
}
}
impl<'a, T: Config> Rules for ScheduleRules<'a, T> {
fn instruction_cost(&self, instruction: &Instruction) -> Option<u32> {
use Instruction::*;
use SignExtInstruction::*;
let w = &self.schedule.instruction_weights;
let max_params = self.schedule.limits.parameters;
let weight = match *instruction {
End | Unreachable | Return | Else | Block(_) | Loop(_) | Nop | Drop => 0,
I32Const(_) | I64Const(_) => w.i64const,
I32Load(_, _)
| I32Load8S(_, _)
| I32Load8U(_, _)
| I32Load16S(_, _)
| I32Load16U(_, _) => w.i32load,
I64Load(_, _)
| I64Load8S(_, _)
| I64Load8U(_, _)
| I64Load16S(_, _)
| I64Load16U(_, _)
| I64Load32S(_, _)
| I64Load32U(_, _) => w.i64load,
I32Store(_, _) | I32Store8(_, _) | I32Store16(_, _) => w.i32store,
I64Store(_, _) | I64Store8(_, _) | I64Store16(_, _) | I64Store32(_, _) => w.i64store,
Select => w.select,
If(_) => w.r#if,
Br(_) => w.br,
BrIf(_) => w.br_if,
Call(_) => w.call,
GetLocal(_) => w.local_get,
SetLocal(_) => w.local_set,
TeeLocal(_) => w.local_tee,
GetGlobal(_) => w.global_get,
SetGlobal(_) => w.global_set,
CurrentMemory(_) => w.memory_current,
CallIndirect(idx, _) => *self.params.get(idx as usize).unwrap_or(&max_params),
BrTable(ref data) => w
.br_table
.saturating_add(w.br_table_per_entry.saturating_mul(data.table.len() as u32)),
I32Clz => w.i32clz,
I64Clz => w.i64clz,
I32Ctz => w.i32ctz,
I64Ctz => w.i64ctz,
I32Popcnt => w.i32popcnt,
I64Popcnt => w.i64popcnt,
I32Eqz => w.i32eqz,
I64Eqz => w.i64eqz,
I64ExtendSI32 => w.i64extendsi32,
I64ExtendUI32 => w.i64extendui32,
I32WrapI64 => w.i32wrapi64,
I32Eq => w.i32eq,
I64Eq => w.i64eq,
I32Ne => w.i32ne,
I64Ne => w.i64ne,
I32LtS => w.i32lts,
I64LtS => w.i64lts,
I32LtU => w.i32ltu,
I64LtU => w.i64ltu,
I32GtS => w.i32gts,
I64GtS => w.i64gts,
I32GtU => w.i32gtu,
I64GtU => w.i64gtu,
I32LeS => w.i32les,
I64LeS => w.i64les,
I32LeU => w.i32leu,
I64LeU => w.i64leu,
I32GeS => w.i32ges,
I64GeS => w.i64ges,
I32GeU => w.i32geu,
I64GeU => w.i64geu,
I32Add => w.i32add,
I64Add => w.i64add,
I32Sub => w.i32sub,
I64Sub => w.i64sub,
I32Mul => w.i32mul,
I64Mul => w.i64mul,
I32DivS => w.i32divs,
I64DivS => w.i64divs,
I32DivU => w.i32divu,
I64DivU => w.i64divu,
I32RemS => w.i32rems,
I64RemS => w.i64rems,
I32RemU => w.i32remu,
I64RemU => w.i64remu,
I32And => w.i32and,
I64And => w.i64and,
I32Or => w.i32or,
I64Or => w.i64or,
I32Xor => w.i32xor,
I64Xor => w.i64xor,
I32Shl => w.i32shl,
I64Shl => w.i64shl,
I32ShrS => w.i32shrs,
I64ShrS => w.i64shrs,
I32ShrU => w.i32shru,
I64ShrU => w.i64shru,
I32Rotl => w.i32rotl,
I64Rotl => w.i64rotl,
I32Rotr => w.i32rotr,
I64Rotr => w.i64rotr,
SignExt(ref s) => match s {
I32Extend8S => w.i32extend8s,
I32Extend16S => w.i32extend16s,
I64Extend8S => w.i64extend8s,
I64Extend16S => w.i64extend16s,
I64Extend32S => w.i64extend32s,
},
_ => return None,
};
Some(weight)
}
fn memory_grow_cost(&self) -> MemoryGrowCost {
MemoryGrowCost::Free
}
fn call_per_local_cost(&self) -> u32 {
self.schedule.instruction_weights.call_per_local
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::mock::Test;
use gear_wasm_instrument::{
gas_metering::{CustomConstantCostRules, Rules, Schedule as WasmInstrumentSchedule},
parity_wasm::elements,
};
fn all_measured_instructions() -> Vec<Instruction> {
use elements::{BlockType, BrTableData, Instruction::*};
let default_table_data = BrTableData {
table: Default::default(),
default: 0,
};
vec![
End,
Unreachable,
Return,
Else,
I32Const(0),
I64Const(0),
Block(BlockType::NoResult),
Loop(BlockType::NoResult),
Nop,
Drop,
I32Load(0, 0),
I32Load8S(0, 0),
I32Load8U(0, 0),
I32Load16S(0, 0),
I32Load16U(0, 0),
I64Load(0, 0),
I64Load8S(0, 0),
I64Load8U(0, 0),
I64Load16S(0, 0),
I64Load16U(0, 0),
I64Load32S(0, 0),
I64Load32U(0, 0),
I32Store(0, 0),
I32Store8(0, 0),
I32Store16(0, 0),
I64Store(0, 0),
I64Store8(0, 0),
I64Store16(0, 0),
I64Store32(0, 0),
Select,
If(BlockType::NoResult),
Br(0),
BrIf(0),
Call(0),
GetLocal(0),
SetLocal(0),
TeeLocal(0),
GetGlobal(0),
SetGlobal(0),
CurrentMemory(0),
CallIndirect(0, 0),
BrTable(default_table_data.into()),
I32Clz,
I64Clz,
I32Ctz,
I64Ctz,
I32Popcnt,
I64Popcnt,
I32Eqz,
I64Eqz,
I64ExtendSI32,
I64ExtendUI32,
I32WrapI64,
I32Eq,
I64Eq,
I32Ne,
I64Ne,
I32LtS,
I64LtS,
I32LtU,
I64LtU,
I32GtS,
I64GtS,
I32GtU,
I64GtU,
I32LeS,
I64LeS,
I32LeU,
I64LeU,
I32GeS,
I64GeS,
I32GeU,
I64GeU,
I32Add,
I64Add,
I32Sub,
I64Sub,
I32Mul,
I64Mul,
I32DivS,
I64DivS,
I32DivU,
I64DivU,
I32RemS,
I64RemS,
I32RemU,
I64RemU,
I32And,
I64And,
I32Or,
I64Or,
I32Xor,
I64Xor,
I32Shl,
I64Shl,
I32ShrS,
I64ShrS,
I32ShrU,
I64ShrU,
I32Rotl,
I64Rotl,
I32Rotr,
I64Rotr,
]
}
fn default_wasm_module() -> Module {
let simple_wat = r#"
(module
(import "env" "memory" (memory 1))
(export "handle" (func $handle))
(export "init" (func $init))
(func $handle)
(func $init)
)"#;
Module::from_bytes(
wabt::Wat2Wasm::new()
.validate(false)
.convert(simple_wat)
.expect("failed to parse module"),
)
.expect("module instantiation failed")
}
#[test]
fn instructions_backward_compatibility() {
let schedule = Schedule::<Test>::default();
let wasm_instrument_schedule = WasmInstrumentSchedule::default();
let schedule_rules = schedule.rules(&default_wasm_module());
let wasm_instrument_schedule_rules = wasm_instrument_schedule.rules(&default_wasm_module());
let custom_cost_rules = CustomConstantCostRules::default();
all_measured_instructions().iter().for_each(|i| {
assert!(schedule_rules.instruction_cost(i).is_some());
assert_eq!(
schedule_rules.instruction_cost(i),
wasm_instrument_schedule_rules.instruction_cost(i)
);
assert!(custom_cost_rules.instruction_cost(i).is_some());
})
}
fn module_with_full_idx(count: usize) -> usize {
let funcs = "(func)".repeat(count);
let wat = format!("(module {funcs})");
wabt::wat2wasm(wat).expect("Failed to serialize wasm").len()
}
#[test]
fn deserialize_max_fn_idx_with_code_limit() {
use gear_wasm_instrument::parity_wasm::elements::{IndexMap, Serialize, VarUint32};
use std::io;
let code_limit = Limits::default().code_len as usize;
let empty_program_len = module_with_full_idx(1);
let empty_fn_len = module_with_full_idx(2) - empty_program_len;
let max_idx = ((code_limit - empty_program_len) / empty_fn_len + 1) as u32;
let mut buffer = vec![];
VarUint32::from(1u32).serialize(&mut buffer).unwrap();
VarUint32::from(max_idx - 1).serialize(&mut buffer).unwrap();
"foobar".to_string().serialize(&mut buffer).unwrap();
let indexmap =
IndexMap::<String>::deserialize(max_idx as usize, &mut io::Cursor::new(buffer))
.unwrap();
assert_eq!(indexmap.get(max_idx - 1), Some(&"foobar".to_string()));
assert_eq!(indexmap.len(), 1);
}
}