diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index ac8259048d4..4acb3464385 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -20,7 +20,6 @@ mod apis; pub mod constants; -pub mod weights; use sp_runtime::traits::Get; diff --git a/runtime/common/src/weights.rs b/runtime/common/src/weights.rs deleted file mode 100644 index 74e167b6ad8..00000000000 --- a/runtime/common/src/weights.rs +++ /dev/null @@ -1,308 +0,0 @@ -use gear_core::{ - costs::{CostOf, LazyPagesCosts}, - pages::GearPagesAmount, -}; -use pallet_gear::{InstructionWeights, MemoryWeights, SyscallWeights}; - -const INSTRUCTIONS_SPREAD: u8 = 50; -const SYSCALL_SPREAD: u8 = 10; -const PAGES_SPREAD: u8 = 10; - -#[track_caller] -fn check_spreading(weight: u64, expected: u64, spread: u8) { - let left = expected - expected * spread as u64 / 100; - let right = expected + expected * spread as u64 / 100; - - assert!( - left <= weight && weight <= right, - "Weight is {weight} ps. Expected weight is {expected} ps. {spread}% spread interval: [{left} ps, {right} ps]" - ); -} - -#[track_caller] -fn check_instruction_weight(weight: u32, expected: u32) { - check_spreading(weight.into(), expected.into(), INSTRUCTIONS_SPREAD); -} - -#[track_caller] -fn check_syscall_weight(weight: u64, expected: u64) { - check_spreading(weight, expected, SYSCALL_SPREAD); -} - -#[track_caller] -fn check_pages_weight(weight: u64, expected: u64) { - check_spreading(weight, expected, PAGES_SPREAD); -} - -/// Check that the weights of instructions are within the expected range -pub fn check_instructions_weights( - weights: InstructionWeights, - expected: InstructionWeights, -) { - check_instruction_weight(weights.i64const, expected.i64const); - check_instruction_weight(weights.i64load, expected.i64load); - check_instruction_weight(weights.i32load, expected.i32load); - check_instruction_weight(weights.i64store, expected.i64store); - check_instruction_weight(weights.i32store, expected.i32store); - check_instruction_weight(weights.select, expected.select); - check_instruction_weight(weights.r#if, expected.r#if); - check_instruction_weight(weights.br, expected.br); - check_instruction_weight(weights.br_if, expected.br_if); - check_instruction_weight(weights.br_table, expected.br_table); - check_instruction_weight(weights.br_table_per_entry, expected.br_table_per_entry); - check_instruction_weight(weights.call, expected.call); - check_instruction_weight(weights.call_indirect, expected.call_indirect); - check_instruction_weight( - weights.call_indirect_per_param, - expected.call_indirect_per_param, - ); - check_instruction_weight(weights.call_per_local, expected.call_per_local); - check_instruction_weight(weights.local_get, expected.local_get); - check_instruction_weight(weights.local_set, expected.local_set); - check_instruction_weight(weights.local_tee, expected.local_tee); - check_instruction_weight(weights.global_get, expected.global_get); - check_instruction_weight(weights.global_set, expected.global_set); - check_instruction_weight(weights.memory_current, expected.memory_current); - check_instruction_weight(weights.i64clz, expected.i64clz); - check_instruction_weight(weights.i32clz, expected.i32clz); - check_instruction_weight(weights.i64ctz, expected.i64ctz); - check_instruction_weight(weights.i32ctz, expected.i32ctz); - check_instruction_weight(weights.i64popcnt, expected.i64popcnt); - check_instruction_weight(weights.i32popcnt, expected.i32popcnt); - check_instruction_weight(weights.i64eqz, expected.i64eqz); - check_instruction_weight(weights.i32eqz, expected.i32eqz); - check_instruction_weight(weights.i32extend8s, expected.i32extend8s); - check_instruction_weight(weights.i32extend16s, expected.i32extend16s); - check_instruction_weight(weights.i64extend8s, expected.i64extend8s); - check_instruction_weight(weights.i64extend16s, expected.i64extend16s); - check_instruction_weight(weights.i64extend32s, expected.i64extend32s); - check_instruction_weight(weights.i64extendsi32, expected.i64extendsi32); - check_instruction_weight(weights.i64extendui32, expected.i64extendui32); - check_instruction_weight(weights.i32wrapi64, expected.i32wrapi64); - check_instruction_weight(weights.i64eq, expected.i64eq); - check_instruction_weight(weights.i32eq, expected.i32eq); - check_instruction_weight(weights.i64ne, expected.i64ne); - check_instruction_weight(weights.i32ne, expected.i32ne); - check_instruction_weight(weights.i64lts, expected.i64lts); - check_instruction_weight(weights.i32lts, expected.i32lts); - check_instruction_weight(weights.i64ltu, expected.i64ltu); - check_instruction_weight(weights.i32ltu, expected.i32ltu); - check_instruction_weight(weights.i64gts, expected.i64gts); - check_instruction_weight(weights.i32gts, expected.i32gts); - check_instruction_weight(weights.i64gtu, expected.i64gtu); - check_instruction_weight(weights.i32gtu, expected.i32gtu); - check_instruction_weight(weights.i64les, expected.i64les); - check_instruction_weight(weights.i32les, expected.i32les); - check_instruction_weight(weights.i64leu, expected.i64leu); - check_instruction_weight(weights.i32leu, expected.i32leu); - check_instruction_weight(weights.i64ges, expected.i64ges); - check_instruction_weight(weights.i32ges, expected.i32ges); - check_instruction_weight(weights.i64geu, expected.i64geu); - check_instruction_weight(weights.i32geu, expected.i32geu); - check_instruction_weight(weights.i64add, expected.i64add); - check_instruction_weight(weights.i32add, expected.i32add); - check_instruction_weight(weights.i64sub, expected.i64sub); - check_instruction_weight(weights.i32sub, expected.i32sub); - check_instruction_weight(weights.i64mul, expected.i64mul); - check_instruction_weight(weights.i32mul, expected.i32mul); - check_instruction_weight(weights.i64divs, expected.i64divs); - check_instruction_weight(weights.i32divs, expected.i32divs); - check_instruction_weight(weights.i64divu, expected.i64divu); - check_instruction_weight(weights.i32divu, expected.i32divu); - check_instruction_weight(weights.i64rems, expected.i64rems); - check_instruction_weight(weights.i32rems, expected.i32rems); - check_instruction_weight(weights.i64remu, expected.i64remu); - check_instruction_weight(weights.i32remu, expected.i32remu); - check_instruction_weight(weights.i64and, expected.i64and); - check_instruction_weight(weights.i32and, expected.i32and); - check_instruction_weight(weights.i64or, expected.i64or); - check_instruction_weight(weights.i32or, expected.i32or); - check_instruction_weight(weights.i64xor, expected.i64xor); - check_instruction_weight(weights.i32xor, expected.i32xor); - check_instruction_weight(weights.i64shl, expected.i64shl); - check_instruction_weight(weights.i32shl, expected.i32shl); - check_instruction_weight(weights.i64shrs, expected.i64shrs); - check_instruction_weight(weights.i32shrs, expected.i32shrs); - check_instruction_weight(weights.i64shru, expected.i64shru); - check_instruction_weight(weights.i32shru, expected.i32shru); - check_instruction_weight(weights.i64rotl, expected.i64rotl); - check_instruction_weight(weights.i32rotl, expected.i32rotl); - check_instruction_weight(weights.i64rotr, expected.i64rotr); - check_instruction_weight(weights.i32rotr, expected.i32rotr); -} - -/// Check that the weights of syscalls are within the expected range -pub fn check_syscall_weights( - weights: SyscallWeights, - expected: SyscallWeights, -) { - macro_rules! check { - ($inst_name:ident) => { - check_syscall_weight( - weights.$inst_name.ref_time(), - expected.$inst_name.ref_time(), - ); - }; - } - - check!(alloc); - check!(free); - check!(free_range); - check!(free_range_per_page); - check!(gr_reserve_gas); - check!(gr_unreserve_gas); - check!(gr_system_reserve_gas); - check!(gr_gas_available); - check!(gr_message_id); - check!(gr_program_id); - check!(gr_source); - check!(gr_value); - check!(gr_value_available); - check!(gr_size); - check!(gr_read); - check!(gr_read_per_byte); - check!(gr_env_vars); - check!(gr_block_height); - check!(gr_block_timestamp); - check!(gr_random); - check!(gr_reply_deposit); - check!(gr_send); - check!(gr_send_per_byte); - check!(gr_send_wgas); - check!(gr_send_wgas_per_byte); - check!(gr_send_init); - check!(gr_send_push); - check!(gr_send_push_per_byte); - check!(gr_send_commit); - check!(gr_send_commit_wgas); - check!(gr_reservation_send); - check!(gr_reservation_send_per_byte); - check!(gr_reservation_send_commit); - check!(gr_reply_commit); - check!(gr_reply_commit_wgas); - check!(gr_reservation_reply); - check!(gr_reservation_reply_per_byte); - check!(gr_reservation_reply_commit); - check!(gr_reply_push); - check!(gr_reply); - check!(gr_reply_per_byte); - check!(gr_reply_wgas); - check!(gr_reply_wgas_per_byte); - check!(gr_reply_push_per_byte); - check!(gr_reply_to); - check!(gr_signal_code); - check!(gr_signal_from); - check!(gr_reply_input); - check!(gr_reply_input_wgas); - check!(gr_reply_push_input); - check!(gr_reply_push_input_per_byte); - check!(gr_send_input); - check!(gr_send_input_wgas); - check!(gr_send_push_input); - check!(gr_send_push_input_per_byte); - check!(gr_debug); - check!(gr_debug_per_byte); - check!(gr_reply_code); - check!(gr_exit); - check!(gr_leave); - check!(gr_wait); - check!(gr_wait_for); - check!(gr_wait_up_to); - check!(gr_wake); - check!(gr_create_program); - check!(gr_create_program_payload_per_byte); - check!(gr_create_program_salt_per_byte); - check!(gr_create_program_wgas); - check!(gr_create_program_wgas_payload_per_byte); - check!(gr_create_program_wgas_salt_per_byte); -} - -/// Check that the lazy-pages costs are within the expected range -pub fn check_lazy_pages_costs( - lazy_pages_costs: LazyPagesCosts, - expected_lazy_pages_costs: LazyPagesCosts, -) { - check_pages_weight( - lazy_pages_costs.signal_read.cost_for_one(), - expected_lazy_pages_costs.signal_read.cost_for_one(), - ); - check_pages_weight( - lazy_pages_costs.signal_write.cost_for_one(), - expected_lazy_pages_costs.signal_write.cost_for_one(), - ); - check_pages_weight( - lazy_pages_costs.signal_write_after_read.cost_for_one(), - expected_lazy_pages_costs - .signal_write_after_read - .cost_for_one(), - ); - check_pages_weight( - lazy_pages_costs.host_func_read.cost_for_one(), - expected_lazy_pages_costs.host_func_read.cost_for_one(), - ); - check_pages_weight( - lazy_pages_costs.host_func_write.cost_for_one(), - expected_lazy_pages_costs.host_func_write.cost_for_one(), - ); - check_pages_weight( - lazy_pages_costs.host_func_write_after_read.cost_for_one(), - expected_lazy_pages_costs - .host_func_write_after_read - .cost_for_one(), - ); - check_pages_weight( - lazy_pages_costs.load_page_storage_data.cost_for_one(), - expected_lazy_pages_costs - .load_page_storage_data - .cost_for_one(), - ); -} - -/// Memory pages access costs. -pub struct PagesCosts { - pub load_page_data: CostOf, - pub upload_page_data: CostOf, - pub mem_grow: CostOf, - pub mem_grow_per_page: CostOf, - pub parachain_read_heuristic: CostOf, -} - -impl From> for PagesCosts { - fn from(val: MemoryWeights) -> Self { - Self { - load_page_data: val.load_page_data.ref_time().into(), - upload_page_data: val.upload_page_data.ref_time().into(), - mem_grow: val.mem_grow.ref_time().into(), - mem_grow_per_page: val.mem_grow_per_page.ref_time().into(), - parachain_read_heuristic: val.parachain_read_heuristic.ref_time().into(), - } - } -} - -/// Check that the pages costs are within the expected range -pub fn check_pages_costs(page_costs: PagesCosts, expected_page_costs: PagesCosts) { - check_pages_weight( - page_costs.load_page_data.cost_for_one(), - expected_page_costs.load_page_data.cost_for_one(), - ); - - check_pages_weight( - page_costs.upload_page_data.cost_for_one(), - expected_page_costs.upload_page_data.cost_for_one(), - ); - - check_pages_weight( - page_costs.mem_grow.cost_for_one(), - expected_page_costs.mem_grow.cost_for_one(), - ); - - check_pages_weight( - page_costs.mem_grow_per_page.cost_for_one(), - expected_page_costs.mem_grow_per_page.cost_for_one(), - ); - - check_pages_weight( - page_costs.parachain_read_heuristic.cost_for_one(), - expected_page_costs.parachain_read_heuristic.cost_for_one(), - ); -} diff --git a/runtime/vara/src/tests.rs b/runtime/vara/src/tests.rs index 41289598654..061e03fa809 100644 --- a/runtime/vara/src/tests.rs +++ b/runtime/vara/src/tests.rs @@ -20,15 +20,345 @@ use super::*; use crate::Runtime; use frame_support::{dispatch::GetDispatchInfo, traits::StorageInstance}; use frame_system::limits::WeightsPerClass; -use gear_core::costs::LazyPagesCosts; +use gear_core::{ + costs::{CostOf, LazyPagesCosts}, + pages::GearPagesAmount, +}; use pallet_gear::{InstructionWeights, MemoryWeights, SyscallWeights}; use pallet_staking::WeightInfo as _; -use runtime_common::weights::{ - check_instructions_weights, check_lazy_pages_costs, check_pages_costs, check_syscall_weights, - PagesCosts, -}; use sp_runtime::AccountId32; +const INSTRUCTIONS_SPREAD: u8 = 50; +const SYSCALL_SPREAD: u8 = 10; +const PAGES_SPREAD: u8 = 10; + +/// Structure to hold weight expectation +struct WeightExpectation { + weight: u64, + expected: u64, + spread: u8, + name: &'static str, +} + +impl WeightExpectation { + fn new(weight: u64, expected: u64, spread: u8, name: &'static str) -> Self { + Self { + weight, + expected, + spread, + name, + } + } + + fn check(&self) -> Result<(), String> { + let left = self.expected - self.expected * self.spread as u64 / 100; + let right = self.expected + self.expected * self.spread as u64 / 100; + + if left > self.weight || self.weight > right { + return Err(format!("Instruction [{}]. Weight is {} ps. Expected weight is {} ps. {}% spread interval: [{left} ps, {right} ps]", self.name, self.weight, self.expected, self.spread)); + } + + Ok(()) + } +} + +fn check_expectations(expectations: &[WeightExpectation]) -> Result<(), Vec> { + let errors = expectations + .iter() + .filter_map(|expectation| { + if let Err(err) = expectation.check() { + Some(err) + } else { + None + } + }) + .collect::>(); + + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } +} + +/// Memory pages access costs. +struct PagesCosts { + pub load_page_data: CostOf, + pub upload_page_data: CostOf, + pub mem_grow: CostOf, + pub mem_grow_per_page: CostOf, + pub parachain_read_heuristic: CostOf, +} + +impl From> for PagesCosts { + fn from(val: MemoryWeights) -> Self { + Self { + load_page_data: val.load_page_data.ref_time().into(), + upload_page_data: val.upload_page_data.ref_time().into(), + mem_grow: val.mem_grow.ref_time().into(), + mem_grow_per_page: val.mem_grow_per_page.ref_time().into(), + parachain_read_heuristic: val.parachain_read_heuristic.ref_time().into(), + } + } +} + +/// Check that the weights of instructions are within the expected range +fn check_instructions_weights( + weights: InstructionWeights, + expected: InstructionWeights, +) -> Result<(), Vec> { + macro_rules! expectation { + ($inst_name:ident) => { + WeightExpectation::new( + weights.$inst_name.into(), + expected.$inst_name.into(), + INSTRUCTIONS_SPREAD, + stringify!($inst_name), + ) + }; + } + + let expectations = vec![ + expectation!(i64const), + expectation!(i64load), + expectation!(i32load), + expectation!(i64store), + expectation!(i32store), + expectation!(select), + expectation!(r#if), + expectation!(br), + expectation!(br_if), + expectation!(br_table), + expectation!(br_table_per_entry), + expectation!(call), + expectation!(call_indirect), + expectation!(call_indirect_per_param), + expectation!(call_per_local), + expectation!(local_get), + expectation!(local_set), + expectation!(local_tee), + expectation!(global_get), + expectation!(global_set), + expectation!(memory_current), + expectation!(i64clz), + expectation!(i32clz), + expectation!(i64ctz), + expectation!(i32ctz), + expectation!(i64popcnt), + expectation!(i32popcnt), + expectation!(i64eqz), + expectation!(i32eqz), + expectation!(i32extend8s), + expectation!(i32extend16s), + expectation!(i64extend8s), + expectation!(i64extend16s), + expectation!(i64extend32s), + expectation!(i64extendsi32), + expectation!(i64extendui32), + expectation!(i32wrapi64), + expectation!(i64eq), + expectation!(i32eq), + expectation!(i64ne), + expectation!(i32ne), + expectation!(i64lts), + expectation!(i32lts), + expectation!(i64ltu), + expectation!(i32ltu), + expectation!(i64gts), + expectation!(i32gts), + expectation!(i64gtu), + expectation!(i32gtu), + expectation!(i64les), + expectation!(i32les), + expectation!(i64leu), + expectation!(i32leu), + expectation!(i64ges), + expectation!(i32ges), + expectation!(i64geu), + expectation!(i32geu), + expectation!(i64add), + expectation!(i32add), + expectation!(i64sub), + expectation!(i32sub), + expectation!(i64mul), + expectation!(i32mul), + expectation!(i64divs), + expectation!(i32divs), + expectation!(i64divu), + expectation!(i32divu), + expectation!(i64rems), + expectation!(i32rems), + expectation!(i64remu), + expectation!(i32remu), + expectation!(i64and), + expectation!(i32and), + expectation!(i64or), + expectation!(i32or), + expectation!(i64xor), + expectation!(i32xor), + expectation!(i64shl), + expectation!(i32shl), + expectation!(i64shrs), + expectation!(i32shrs), + expectation!(i64shru), + expectation!(i32shru), + expectation!(i64rotl), + expectation!(i32rotl), + expectation!(i64rotr), + expectation!(i32rotr), + ]; + + check_expectations(&expectations) +} + +/// Check that the weights of syscalls are within the expected range +fn check_syscall_weights( + weights: SyscallWeights, + expected: SyscallWeights, +) -> Result<(), Vec> { + macro_rules! expectation { + ($inst_name:ident) => { + WeightExpectation::new( + weights.$inst_name.ref_time(), + expected.$inst_name.ref_time(), + SYSCALL_SPREAD, + stringify!($inst_name), + ) + }; + } + + let expectations = vec![ + expectation!(alloc), + expectation!(free), + expectation!(free_range), + expectation!(free_range_per_page), + expectation!(gr_reserve_gas), + expectation!(gr_unreserve_gas), + expectation!(gr_system_reserve_gas), + expectation!(gr_gas_available), + expectation!(gr_message_id), + expectation!(gr_program_id), + expectation!(gr_source), + expectation!(gr_value), + expectation!(gr_value_available), + expectation!(gr_size), + expectation!(gr_read), + expectation!(gr_read_per_byte), + expectation!(gr_env_vars), + expectation!(gr_block_height), + expectation!(gr_block_timestamp), + expectation!(gr_random), + expectation!(gr_reply_deposit), + expectation!(gr_send), + expectation!(gr_send_per_byte), + expectation!(gr_send_wgas), + expectation!(gr_send_wgas_per_byte), + expectation!(gr_send_init), + expectation!(gr_send_push), + expectation!(gr_send_push_per_byte), + expectation!(gr_send_commit), + expectation!(gr_send_commit_wgas), + expectation!(gr_reservation_send), + expectation!(gr_reservation_send_per_byte), + expectation!(gr_reservation_send_commit), + expectation!(gr_reply_commit), + expectation!(gr_reply_commit_wgas), + expectation!(gr_reservation_reply), + expectation!(gr_reservation_reply_per_byte), + expectation!(gr_reservation_reply_commit), + expectation!(gr_reply_push), + expectation!(gr_reply), + expectation!(gr_reply_per_byte), + expectation!(gr_reply_wgas), + expectation!(gr_reply_wgas_per_byte), + expectation!(gr_reply_push_per_byte), + expectation!(gr_reply_to), + expectation!(gr_signal_code), + expectation!(gr_signal_from), + expectation!(gr_reply_input), + expectation!(gr_reply_input_wgas), + expectation!(gr_reply_push_input), + expectation!(gr_reply_push_input_per_byte), + expectation!(gr_send_input), + expectation!(gr_send_input_wgas), + expectation!(gr_send_push_input), + expectation!(gr_send_push_input_per_byte), + expectation!(gr_debug), + expectation!(gr_debug_per_byte), + expectation!(gr_reply_code), + expectation!(gr_exit), + expectation!(gr_leave), + expectation!(gr_wait), + expectation!(gr_wait_for), + expectation!(gr_wait_up_to), + expectation!(gr_wake), + expectation!(gr_create_program), + expectation!(gr_create_program_payload_per_byte), + expectation!(gr_create_program_salt_per_byte), + expectation!(gr_create_program_wgas), + expectation!(gr_create_program_wgas_payload_per_byte), + expectation!(gr_create_program_wgas_salt_per_byte), + ]; + + check_expectations(&expectations) +} + +/// Check that the lazy-pages costs are within the expected range +fn check_lazy_pages_costs( + lazy_pages_costs: LazyPagesCosts, + expected_lazy_pages_costs: LazyPagesCosts, +) -> Result<(), Vec> { + macro_rules! expectation { + ($inst_name:ident) => { + WeightExpectation::new( + lazy_pages_costs.$inst_name.cost_for_one(), + expected_lazy_pages_costs.$inst_name.cost_for_one(), + PAGES_SPREAD, + stringify!($inst_name), + ) + }; + } + + let expectations = vec![ + expectation!(signal_read), + expectation!(signal_write), + expectation!(signal_write_after_read), + expectation!(host_func_read), + expectation!(host_func_write), + expectation!(host_func_write_after_read), + expectation!(load_page_storage_data), + ]; + + check_expectations(&expectations) +} + +/// Check that the pages costs are within the expected range +fn check_pages_costs( + page_costs: PagesCosts, + expected_page_costs: PagesCosts, +) -> Result<(), Vec> { + macro_rules! expectation { + ($inst_name:ident) => { + WeightExpectation::new( + page_costs.$inst_name.cost_for_one(), + expected_page_costs.$inst_name.cost_for_one(), + PAGES_SPREAD, + stringify!($inst_name), + ) + }; + } + + let expectations = vec![ + expectation!(load_page_data), + expectation!(upload_page_data), + expectation!(mem_grow), + expectation!(mem_grow_per_page), + expectation!(parachain_read_heuristic), + ]; + + check_expectations(&expectations) +} + #[cfg(feature = "dev")] #[test] fn bridge_storages_have_correct_prefixes() { @@ -224,7 +554,9 @@ fn instruction_weights_heuristics_test() { i32rotr: 300, }; - check_instructions_weights(weights, expected_weights); + let result = check_instructions_weights(weights, expected_weights); + + assert!(result.is_ok(), "{:#?}", result.err().unwrap()); } #[test] @@ -305,7 +637,9 @@ fn syscall_weights_test() { _phantom: Default::default(), }; - check_syscall_weights(weights, expected); + let result = check_syscall_weights(weights, expected); + + assert!(result.is_ok(), "{:#?}", result.err().unwrap()); } #[test] @@ -320,7 +654,9 @@ fn page_costs_heuristic_test() { parachain_read_heuristic: 0.into(), }; - check_pages_costs(page_costs, expected_page_costs); + let result = check_pages_costs(page_costs, expected_page_costs); + + assert!(result.is_ok(), "{:#?}", result.err().unwrap()); } #[test] @@ -337,7 +673,9 @@ fn lazy_page_costs_heuristic_test() { load_page_storage_data: 9_000_000.into(), }; - check_lazy_pages_costs(lazy_pages_costs, expected_lazy_pages_costs); + let result = check_lazy_pages_costs(lazy_pages_costs, expected_lazy_pages_costs); + + assert!(result.is_ok(), "{:#?}", result.err().unwrap()); } /// Check that it is not possible to write/change memory pages too cheaply,