Skip to content

Commit

Permalink
post-review: code structure fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Lazark0x committed Nov 5, 2024
1 parent a3e82e1 commit e825e99
Show file tree
Hide file tree
Showing 2 changed files with 388 additions and 360 deletions.
376 changes: 376 additions & 0 deletions runtime/vara/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,376 @@
// This file is part of Gear.

// Copyright (C) 2021-2024 Gear Technologies Inc.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Weight tests for the runtime.

use super::*;
use crate::Runtime;

use frame_support::dispatch::GetDispatchInfo;
use frame_system::limits::WeightsPerClass;
use gear_core::costs::LazyPagesCosts;
use pallet_gear::{InstructionWeights, MemoryWeights, SyscallWeights};
use pallet_staking::WeightInfo as _;
use sp_runtime::AccountId32;

#[cfg(feature = "dev")]
use frame_support::traits::StorageInstance;

mod utils;
use utils::*;

#[cfg(feature = "dev")]
#[test]
fn bridge_storages_have_correct_prefixes() {
// # SAFETY: Do not change storage prefixes without total bridge re-deploy.
const PALLET_PREFIX: &str = "GearEthBridge";

assert_eq!(
pallet_gear_eth_bridge::AuthoritySetHashPrefix::<Runtime>::pallet_prefix(),
PALLET_PREFIX
);
assert_eq!(
pallet_gear_eth_bridge::QueueMerkleRootPrefix::<Runtime>::pallet_prefix(),
PALLET_PREFIX
);

assert_eq!(
pallet_gear_eth_bridge::AuthoritySetHashPrefix::<Runtime>::STORAGE_PREFIX,
"AuthoritySetHash"
);
assert_eq!(
pallet_gear_eth_bridge::QueueMerkleRootPrefix::<Runtime>::STORAGE_PREFIX,
"QueueMerkleRoot"
);
}

#[cfg(feature = "dev")]
#[test]
fn bridge_session_timer_is_correct() {
assert_eq!(
<Runtime as pallet_gear_eth_bridge::Config>::SessionsPerEra::get(),
<Runtime as pallet_staking::Config>::SessionsPerEra::get()
);

// # SAFETY: Do not change staking's SessionsPerEra parameter without
// making sure of correct integration with already running network.
//
// Change of the param will require migrating `pallet-gear-eth-bridge`'s
// `ClearTimer` (or any actual time- or epoch- dependent entity) and
// corresponding constant or even total bridge re-deploy.
assert_eq!(
<Runtime as pallet_staking::Config>::SessionsPerEra::get(),
6
);
}

#[test]
fn payout_stakers_fits_in_block() {
let expected_weight =
<Runtime as pallet_staking::Config>::WeightInfo::payout_stakers_alive_staked(
<Runtime as pallet_staking::Config>::MaxExposurePageSize::get(),
);

let call: <Runtime as frame_system::Config>::RuntimeCall =
RuntimeCall::Staking(pallet_staking::Call::payout_stakers {
validator_stash: AccountId32::new(Default::default()),
era: Default::default(),
});

let dispatch_info = call.get_dispatch_info();

assert_eq!(dispatch_info.class, DispatchClass::Normal);
assert_eq!(dispatch_info.weight, expected_weight);

let block_weights = <Runtime as frame_system::Config>::BlockWeights::get();

let normal_class_weights: WeightsPerClass =
block_weights.per_class.get(DispatchClass::Normal).clone();

let normal_ref_time = normal_class_weights
.max_extrinsic
.unwrap_or(Weight::MAX)
.ref_time();
let base_weight = normal_class_weights.base_extrinsic.ref_time();

assert!(normal_ref_time - base_weight > expected_weight.ref_time());
}

#[test]
fn normal_dispatch_length_suits_minimal() {
const MB: u32 = 1024 * 1024;

let block_length = <Runtime as frame_system::Config>::BlockLength::get();

// Normal dispatch class is bigger than 2 MB.
assert!(*block_length.max.get(DispatchClass::Normal) > 2 * MB);

// Others are on maximum.
assert_eq!(*block_length.max.get(DispatchClass::Operational), 5 * MB);
assert_eq!(*block_length.max.get(DispatchClass::Mandatory), 5 * MB);
}

#[test]
fn instruction_weights_heuristics_test() {
let weights = InstructionWeights::<Runtime>::default();

let expected_weights = InstructionWeights {
version: 0,
_phantom: core::marker::PhantomData,

i64const: 160,
i64load: 5_800,
i32load: 8_000,
i64store: 10_000,
i32store: 20_000,
select: 7_100,
r#if: 8_000,
br: 3_300,
br_if: 6_000,
br_table: 10_900,
br_table_per_entry: 150,

call: 4_900,
call_per_local: 0,
call_indirect: 22_100,
call_indirect_per_param: 1_000,

local_get: 900,
local_set: 1_900,
local_tee: 2_500,
global_get: 700,
global_set: 1_000,
memory_current: 14_200,

i64clz: 400,
i32clz: 300,
i64ctz: 400,
i32ctz: 250,
i64popcnt: 450,
i32popcnt: 350,
i64eqz: 1_300,
i32eqz: 1_200,
i32extend8s: 200,
i32extend16s: 200,
i64extend8s: 400,
i64extend16s: 400,
i64extend32s: 400,
i64extendsi32: 200,
i64extendui32: 200,
i32wrapi64: 200,
i64eq: 1_800,
i32eq: 1_100,
i64ne: 1_700,
i32ne: 1_000,

i64lts: 1_200,
i32lts: 1_000,
i64ltu: 1_200,
i32ltu: 1_000,
i64gts: 1_200,
i32gts: 1_000,
i64gtu: 1_900,
i32gtu: 1_000,
i64les: 1_900,
i32les: 1_000,
i64leu: 1_200,
i32leu: 1_000,

i64ges: 1_300,
i32ges: 1_000,
i64geu: 1_300,
i32geu: 1_000,
i64add: 1_300,
i32add: 500,
i64sub: 1_300,
i32sub: 500,
i64mul: 2_000,
i32mul: 1_000,
i64divs: 3_500,
i32divs: 3_500,

i64divu: 3_500,
i32divu: 3_500,
i64rems: 18_000,
i32rems: 15_000,
i64remu: 3_500,
i32remu: 3_500,
i64and: 1_000,
i32and: 500,
i64or: 1_000,
i32or: 500,
i64xor: 1_000,
i32xor: 500,

i64shl: 1_000,
i32shl: 400,
i64shrs: 1_000,
i32shrs: 250,
i64shru: 1_000,
i32shru: 400,
i64rotl: 750,
i32rotl: 400,
i64rotr: 1_000,
i32rotr: 300,
};

let result = check_instructions_weights(weights, expected_weights);

assert!(result.is_ok(), "{:#?}", result.err().unwrap());
assert_eq!(result.unwrap(), expected_instructions_weights_count());
}

#[test]
fn syscall_weights_test() {
let weights = SyscallWeights::<Runtime>::default();

let expected = SyscallWeights {
alloc: 1_500_000.into(),
free: 900_000.into(),
free_range: 900_000.into(),
free_range_per_page: 40_000.into(),
gr_reserve_gas: 2_300_000.into(),
gr_unreserve_gas: 2_200_000.into(),
gr_system_reserve_gas: 1_200_000.into(),
gr_gas_available: 1_000_000.into(),
gr_message_id: 1_000_000.into(),
gr_program_id: 1_000_000.into(),
gr_source: 1_000_000.into(),
gr_value: 1_000_000.into(),
gr_value_available: 1_000_000.into(),
gr_size: 1_000_000.into(),
gr_read: 1_900_000.into(),
gr_read_per_byte: 200.into(),
gr_env_vars: 1_200_000.into(),
gr_block_height: 1_000_000.into(),
gr_block_timestamp: 1_000_000.into(),
gr_random: 1_900_000.into(),
gr_reply_deposit: 4_900_000.into(),
gr_send: 3_200_000.into(),
gr_send_per_byte: 500.into(),
gr_send_wgas: 3_300_000.into(),
gr_send_wgas_per_byte: 500.into(),
gr_send_init: 1_200_000.into(),
gr_send_push: 2_000_000.into(),
gr_send_push_per_byte: 500.into(),
gr_send_commit: 2_700_000.into(),
gr_send_commit_wgas: 2_700_000.into(),
gr_reservation_send: 3_400_000.into(),
gr_reservation_send_per_byte: 500.into(),
gr_reservation_send_commit: 2_900_000.into(),
gr_reply_commit: 12_000_000.into(),
gr_reply_commit_wgas: 12_000_000.into(),
gr_reservation_reply: 8_500_000.into(),
gr_reservation_reply_per_byte: 675_000.into(),
gr_reservation_reply_commit: 8_000_000.into(),
gr_reply_push: 1_700_000.into(),
gr_reply: 12_500_000.into(),
gr_reply_per_byte: 650.into(),
gr_reply_wgas: 12_500_000.into(),
gr_reply_wgas_per_byte: 650.into(),
gr_reply_push_per_byte: 640.into(),
gr_reply_to: 1_000_000.into(),
gr_signal_code: 1_000_000.into(),
gr_signal_from: 1_000_000.into(),
gr_reply_input: 20_000_000.into(),
gr_reply_input_wgas: 30_000_000.into(),
gr_reply_push_input: 1_200_000.into(),
gr_reply_push_input_per_byte: 110.into(),
gr_send_input: 3_100_000.into(),
gr_send_input_wgas: 3_100_000.into(),
gr_send_push_input: 1_500_000.into(),
gr_send_push_input_per_byte: 150.into(),
gr_debug: 1_200_000.into(),
gr_debug_per_byte: 500.into(),
gr_reply_code: 1_000_000.into(),
gr_exit: 18_000_000.into(),
gr_leave: 14_000_000.into(),
gr_wait: 14_000_000.into(),
gr_wait_for: 14_000_000.into(),
gr_wait_up_to: 14_500_000.into(),
gr_wake: 3_300_000.into(),
gr_create_program: 4_100_000.into(),
gr_create_program_payload_per_byte: 120.into(),
gr_create_program_salt_per_byte: 1_400.into(),
gr_create_program_wgas: 4_100_000.into(),
gr_create_program_wgas_payload_per_byte: 120.into(),
gr_create_program_wgas_salt_per_byte: 1_400.into(),
_phantom: Default::default(),
};

let result = check_syscall_weights(weights, expected);

assert!(result.is_ok(), "{:#?}", result.err().unwrap());
assert_eq!(result.unwrap(), expected_syscall_weights_count());
}

#[test]
fn page_costs_heuristic_test() {
let page_costs: LazyPagesCosts = MemoryWeights::<Runtime>::default().into();

let expected_page_costs = LazyPagesCosts {
load_page_data: 9_000_000.into(),
upload_page_data: 105_000_000.into(),
mem_grow: 800_000.into(),
mem_grow_per_page: 0.into(),
parachain_read_heuristic: 0.into(),
..Default::default()
};

let result = check_pages_costs(page_costs, expected_page_costs);

assert!(result.is_ok(), "{:#?}", result.err().unwrap());
assert_eq!(result.unwrap(), expected_pages_costs_count());
}

#[test]
fn lazy_page_costs_heuristic_test() {
let lazy_pages_costs: LazyPagesCosts = MemoryWeights::<Runtime>::default().into();

let expected_lazy_pages_costs = LazyPagesCosts {
signal_read: 28_000_000.into(),
signal_write: 138_000_000.into(),
signal_write_after_read: 112_000_000.into(),
host_func_read: 29_000_000.into(),
host_func_write: 137_000_000.into(),
host_func_write_after_read: 112_000_000.into(),
load_page_storage_data: 9_000_000.into(),
..Default::default()
};

let result = check_lazy_pages_costs(lazy_pages_costs, expected_lazy_pages_costs);

assert!(result.is_ok(), "{:#?}", result.err().unwrap());
assert_eq!(result.unwrap(), expected_lazy_pages_costs_count());
}

/// Check that it is not possible to write/change memory pages too cheaply,
/// because this may cause runtime heap memory overflow.
#[test]
fn write_is_not_too_cheap() {
let costs: LazyPagesCosts = MemoryWeights::<Runtime>::default().into();
let cheapest_write = u64::MAX
.min(costs.signal_write.cost_for_one())
.min(costs.signal_read.cost_for_one() + costs.signal_write_after_read.cost_for_one())
.min(costs.host_func_write.cost_for_one())
.min(costs.host_func_read.cost_for_one() + costs.host_func_write_after_read.cost_for_one());

let block_max_gas = 3 * 10 ^ 12; // 3 seconds
let runtime_heap_size_in_wasm_pages = 0x4000; // 1GB
assert!((block_max_gas / cheapest_write) < runtime_heap_size_in_wasm_pages);
}
Loading

0 comments on commit e825e99

Please sign in to comment.