// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see .
//! This module contains the cost schedule and supporting code that constructs a
//! sane default schedule from a `WeightInfo` implementation.
use crate::{Trait, WeightInfo};
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use frame_support::weights::Weight;
use sp_std::{marker::PhantomData, fmt};
use codec::{Encode, Decode};
/// How many API calls are executed in a single batch. The reason for increasing the amount
/// of API calls in batches (per benchmark component increase) is so that the linear regression
/// has an easier time determining the contribution of that component.
pub const API_BENCHMARK_BATCH_SIZE: u32 = 100;
/// Definition of the cost schedule and other parameterizations for wasm vm.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Eq)]
pub struct Schedule {
/// Version of the schedule.
pub version: u32,
/// The weights for individual wasm instructions.
pub instruction_weights: InstructionWeights,
/// The weights for each imported function a contract is allowed to call.
pub host_fn_weights: HostFnWeights,
/// Whether the `seal_println` function is allowed to be used contracts.
/// MUST only be enabled for `dev` chains, NOT for production chains
pub enable_println: bool,
/// The maximum number of topics supported by an event.
pub max_event_topics: u32,
/// Maximum allowed stack height.
///
/// See https://wiki.parity.io/WebAssembly-StackHeight to find out
/// how the stack frame cost is calculated.
pub max_stack_height: u32,
/// Maximum number of memory pages allowed for a contract.
pub max_memory_pages: u32,
/// Maximum allowed size of a declared table.
pub max_table_size: u32,
/// The maximum length of a subject used for PRNG generation.
pub max_subject_len: u32,
/// The maximum length of a contract code in bytes. This limit applies to the uninstrumented
/// and pristine form of the code as supplied to `put_code`.
pub max_code_size: u32,
/// The type parameter is used in the default implementation.
pub _phantom: PhantomData,
}
/// Describes the weight for all categories of supported wasm instructions.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Eq)]
pub struct InstructionWeights {
/// Weight of a growing memory by single page.
pub grow_mem: Weight,
/// Weight of a regular operation.
pub regular: Weight,
}
/// Describes the weight for each imported function that a contract is allowed to call.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, PartialEq, Eq)]
pub struct HostFnWeights {
/// Weight of calling `seal_caller`.
pub caller: Weight,
/// Weight of calling `seal_address`.
pub address: Weight,
/// Weight of calling `seal_gas_left`.
pub gas_left: Weight,
/// Weight of calling `seal_balance`.
pub balance: Weight,
/// Weight of calling `seal_value_transferred`.
pub value_transferred: Weight,
/// Weight of calling `seal_minimum_balance`.
pub minimum_balance: Weight,
/// Weight of calling `seal_tombstone_deposit`.
pub tombstone_deposit: Weight,
/// Weight of calling `seal_rent_allowance`.
pub rent_allowance: Weight,
/// Weight of calling `seal_block_number`.
pub block_number: Weight,
/// Weight of calling `seal_now`.
pub now: Weight,
/// Weight of calling `seal_weight_to_fee`.
pub weight_to_fee: Weight,
/// Weight of calling `gas`.
pub gas: Weight,
/// Weight of calling `seal_input`.
pub input: Weight,
/// Weight per input byte copied to contract memory by `seal_input`.
pub input_per_byte: Weight,
/// Weight of calling `seal_return`.
pub r#return: Weight,
/// Weight per byte returned through `seal_return`.
pub return_per_byte: Weight,
/// Weight of calling `seal_terminate`.
pub terminate: Weight,
/// Weight of calling `seal_restore_to`.
pub restore_to: Weight,
/// Weight per delta key supplied to `seal_restore_to`.
pub restore_to_per_delta: Weight,
/// Weight of calling `seal_random`.
pub random: Weight,
/// Weight of calling `seal_reposit_event`.
pub deposit_event: Weight,
/// Weight per topic supplied to `seal_deposit_event`.
pub deposit_event_per_topic: Weight,
/// Weight per byte of an event deposited through `seal_deposit_event`.
pub deposit_event_per_byte: Weight,
/// Weight of calling `seal_set_rent_allowance`.
pub set_rent_allowance: Weight,
/// Weight of calling `seal_set_storage`.
pub set_storage: Weight,
/// Weight per byte of an item stored with `seal_set_storage`.
pub set_storage_per_byte: Weight,
/// Weight of calling `seal_clear_storage`.
pub clear_storage: Weight,
/// Weight of calling `seal_get_storage`.
pub get_storage: Weight,
/// Weight per byte of an item received via `seal_get_storage`.
pub get_storage_per_byte: Weight,
/// Weight of calling `seal_transfer`.
pub transfer: Weight,
/// Weight of calling `seal_call`.
pub call: Weight,
/// Weight surcharge that is claimed if `seal_call` does a balance transfer.
pub call_transfer_surcharge: Weight,
/// Weight per input byte supplied to `seal_call`.
pub call_per_input_byte: Weight,
/// Weight per output byte received through `seal_call`.
pub call_per_output_byte: Weight,
/// Weight of calling `seal_instantiate`.
pub instantiate: Weight,
/// Weight per input byte supplied to `seal_instantiate`.
pub instantiate_per_input_byte: Weight,
/// Weight per output byte received through `seal_instantiate`.
pub instantiate_per_output_byte: Weight,
/// Weight of calling `seal_hash_sha_256`.
pub hash_sha2_256: Weight,
/// Weight per byte hashed by `seal_hash_sha_256`.
pub hash_sha2_256_per_byte: Weight,
/// Weight of calling `seal_hash_keccak_256`.
pub hash_keccak_256: Weight,
/// Weight per byte hashed by `seal_hash_keccak_256`.
pub hash_keccak_256_per_byte: Weight,
/// Weight of calling `seal_hash_blake2_256`.
pub hash_blake2_256: Weight,
/// Weight per byte hashed by `seal_hash_blake2_256`.
pub hash_blake2_256_per_byte: Weight,
/// Weight of calling `seal_hash_blake2_128`.
pub hash_blake2_128: Weight,
/// Weight per byte hashed by `seal_hash_blake2_128`.
pub hash_blake2_128_per_byte: Weight,
}
/// We need to implement Debug manually because the automatic derive enforces T
/// to also implement Debug.
impl fmt::Debug for Schedule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Schedule").finish()
}
}
/// 500 (2 instructions per nano second on 2GHZ) * 1000x slowdown through wasmi
/// This is a wild guess and should be viewed as a rough estimation.
/// Proper benchmarks are needed before this value and its derivatives can be used in production.
const WASM_INSTRUCTION_COST: Weight = 500_000;
macro_rules! replace_token {
($_in:tt $replacement:tt) => { $replacement };
}
macro_rules! call_zero {
($name:ident, $( $arg:expr ),*) => {
T::WeightInfo::$name($( replace_token!($arg 0) ),*)
};
}
macro_rules! cost_args {
($name:ident, $( $arg: expr ),+) => {
(T::WeightInfo::$name($( $arg ),+).saturating_sub(call_zero!($name, $( $arg ),+)))
}
}
macro_rules! cost_batched_args {
($name:ident, $( $arg: expr ),+) => {
cost_args!($name, $( $arg ),+) / Weight::from(API_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)
}
}
impl Default for Schedule {
fn default() -> Self {
let instruction_weights = InstructionWeights {
grow_mem: WASM_INSTRUCTION_COST,
regular: WASM_INSTRUCTION_COST,
};
let host_fn_weights = HostFnWeights {
caller: cost_batched!(seal_caller),
address: cost_batched!(seal_address),
gas_left: cost_batched!(seal_gas_left),
balance: cost_batched!(seal_balance),
value_transferred: cost_batched!(seal_value_transferred),
minimum_balance: cost_batched!(seal_minimum_balance),
tombstone_deposit: cost_batched!(seal_tombstone_deposit),
rent_allowance: cost_batched!(seal_rent_allowance),
block_number: cost_batched!(seal_block_number),
now: cost_batched!(seal_now),
weight_to_fee: cost_batched!(seal_weight_to_fee),
gas: cost_batched!(seal_gas),
input: cost!(seal_input),
input_per_byte: cost_byte!(seal_input_per_kb),
r#return: cost!(seal_return),
return_per_byte: cost_byte!(seal_return_per_kb),
terminate: cost!(seal_terminate),
restore_to: cost!(seal_restore_to),
restore_to_per_delta: cost_batched!(seal_restore_to_per_delta),
random: cost_batched!(seal_random),
deposit_event: cost_batched!(seal_deposit_event),
deposit_event_per_topic: cost_batched_args!(seal_deposit_event_per_topic_and_kb, 1, 0),
deposit_event_per_byte: cost_byte_batched_args!(seal_deposit_event_per_topic_and_kb, 0, 1),
set_rent_allowance: cost_batched!(seal_set_rent_allowance),
set_storage: cost_batched!(seal_set_storage),
set_storage_per_byte: cost_byte_batched!(seal_set_storage_per_kb),
clear_storage: cost_batched!(seal_clear_storage),
get_storage: cost_batched!(seal_get_storage),
get_storage_per_byte: cost_byte_batched!(seal_get_storage_per_kb),
transfer: cost_batched!(seal_transfer),
call: cost_batched!(seal_call),
call_transfer_surcharge: cost_batched_args!(seal_call_per_transfer_input_output_kb, 1, 0, 0),
call_per_input_byte: cost_byte_batched_args!(seal_call_per_transfer_input_output_kb, 0, 1, 0),
call_per_output_byte: cost_byte_batched_args!(seal_call_per_transfer_input_output_kb, 0, 0, 1),
instantiate: cost_batched!(seal_instantiate),
instantiate_per_input_byte: cost_byte_batched_args!(seal_instantiate_per_input_output_kb, 1, 0),
instantiate_per_output_byte: cost_byte_batched_args!(seal_instantiate_per_input_output_kb, 0, 1),
hash_sha2_256: cost_batched!(seal_hash_sha2_256),
hash_sha2_256_per_byte: cost_byte_batched!(seal_hash_sha2_256_per_kb),
hash_keccak_256: cost_batched!(seal_hash_keccak_256),
hash_keccak_256_per_byte: cost_byte_batched!(seal_hash_keccak_256_per_kb),
hash_blake2_256: cost_batched!(seal_hash_blake2_256),
hash_blake2_256_per_byte: cost_byte_batched!(seal_hash_blake2_256_per_kb),
hash_blake2_128: cost_batched!(seal_hash_blake2_128),
hash_blake2_128_per_byte: cost_byte_batched!(seal_hash_blake2_128_per_kb),
};
Self {
version: 0,
instruction_weights,
host_fn_weights,
enable_println: false,
max_event_topics: 4,
max_stack_height: 64 * 1024,
max_memory_pages: 16,
max_table_size: 16 * 1024,
max_subject_len: 32,
max_code_size: 512 * 1024,
_phantom: PhantomData,
}
}
}