123456789101112131415161718192021use crate::{Config, weights::WeightInfo};2223#[cfg(feature = "std")]24use serde::{Serialize, Deserialize};25use pallet_contracts_proc_macro::{ScheduleDebug, WeightDebug};26use frame_support::weights::Weight;27use sp_std::{marker::PhantomData, vec::Vec};28use codec::{Encode, Decode};29use parity_wasm::elements;30use pwasm_utils::rules;31use sp_runtime::RuntimeDebug;3233343536pub const API_BENCHMARK_BATCH_SIZE: u32 = 100;37383940pub const INSTR_BENCHMARK_BATCH_SIZE: u32 = 1_000;41424344454647#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]48#[cfg_attr(feature = "std", serde(bound(serialize = "", deserialize = "")))]49#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug)]50pub struct Schedule<T: Config> {51 52 53 54 55 56 57 58 59 60 pub(crate) version: u32,6162 63 64 pub(crate) enable_println: bool,6566 67 pub(crate) limits: Limits,6869 70 pub(crate) instruction_weights: InstructionWeights<T>,7172 73 pub(crate) host_fn_weights: HostFnWeights<T>,74}7576777879808182#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]83#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)]84pub struct Limits {85 86 pub event_topics: u32,8788 89 90 91 92 93 pub stack_height: u32,9495 96 97 98 99 pub globals: u32,100101 102 103 104 105 106 107 108 109 pub parameters: u32,110111 112 pub memory_pages: u32,113114 115 116 117 pub table_size: u32,118119 120 pub br_table_size: u32,121122 123 pub subject_len: u32,124}125126impl Limits {127 128 pub fn max_memory_size(&self) -> u32 {129 self.memory_pages * 64 * 1024130 }131}132133134135136137138139140141142143144145146147148149150151152153#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]154#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug)]155pub struct InstructionWeights<T: Config> {156 pub i64const: u32,157 pub i64load: u32,158 pub i64store: u32,159 pub select: u32,160 pub r#if: u32,161 pub br: u32,162 pub br_if: u32,163 pub br_table: u32,164 pub br_table_per_entry: u32,165 pub call: u32,166 pub call_indirect: u32,167 pub call_indirect_per_param: u32,168 pub local_get: u32,169 pub local_set: u32,170 pub local_tee: u32,171 pub global_get: u32,172 pub global_set: u32,173 pub memory_current: u32,174 pub memory_grow: u32,175 pub i64clz: u32,176 pub i64ctz: u32,177 pub i64popcnt: u32,178 pub i64eqz: u32,179 pub i64extendsi32: u32,180 pub i64extendui32: u32,181 pub i32wrapi64: u32,182 pub i64eq: u32,183 pub i64ne: u32,184 pub i64lts: u32,185 pub i64ltu: u32,186 pub i64gts: u32,187 pub i64gtu: u32,188 pub i64les: u32,189 pub i64leu: u32,190 pub i64ges: u32,191 pub i64geu: u32,192 pub i64add: u32,193 pub i64sub: u32,194 pub i64mul: u32,195 pub i64divs: u32,196 pub i64divu: u32,197 pub i64rems: u32,198 pub i64remu: u32,199 pub i64and: u32,200 pub i64or: u32,201 pub i64xor: u32,202 pub i64shl: u32,203 pub i64shrs: u32,204 pub i64shru: u32,205 pub i64rotl: u32,206 pub i64rotr: u32,207 208 #[codec(skip)]209 pub _phantom: PhantomData<T>,210}211212213#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]214#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug)]215pub struct HostFnWeights<T: Config> {216 217 pub caller: Weight,218219 220 pub address: Weight,221222 223 pub gas_left: Weight,224225 226 pub balance: Weight,227228 229 pub value_transferred: Weight,230231 232 pub minimum_balance: Weight,233234 235 pub tombstone_deposit: Weight,236237 238 pub rent_allowance: Weight,239240 241 pub block_number: Weight,242243 244 pub now: Weight,245246 247 pub weight_to_fee: Weight,248249 250 pub gas: Weight,251252 253 pub input: Weight,254255 256 pub input_per_byte: Weight,257258 259 pub r#return: Weight,260261 262 pub return_per_byte: Weight,263264 265 pub terminate: Weight,266267 268 pub terminate_per_code_byte: Weight,269270 271 pub restore_to: Weight,272273 274 pub restore_to_per_caller_code_byte: Weight,275276 277 pub restore_to_per_tombstone_code_byte: Weight,278279 280 pub restore_to_per_delta: Weight,281282 283 pub random: Weight,284285 286 pub deposit_event: Weight,287288 289 pub deposit_event_per_topic: Weight,290291 292 pub deposit_event_per_byte: Weight,293294 295 pub set_rent_allowance: Weight,296297 298 pub set_storage: Weight,299300 301 pub set_storage_per_byte: Weight,302303 304 pub clear_storage: Weight,305306 307 pub get_storage: Weight,308309 310 pub get_storage_per_byte: Weight,311312 313 pub transfer: Weight,314315 316 pub call: Weight,317318 319 pub call_per_code_byte: Weight,320321 322 pub call_transfer_surcharge: Weight,323324 325 pub call_per_input_byte: Weight,326327 328 pub call_per_output_byte: Weight,329330 331 pub instantiate: Weight,332333 334 pub instantiate_per_code_byte: Weight,335336 337 pub instantiate_per_input_byte: Weight,338339 340 pub instantiate_per_output_byte: Weight,341342 343 pub instantiate_per_salt_byte: Weight,344345 346 pub hash_sha2_256: Weight,347348 349 pub hash_sha2_256_per_byte: Weight,350351 352 pub hash_keccak_256: Weight,353354 355 pub hash_keccak_256_per_byte: Weight,356357 358 pub hash_blake2_256: Weight,359360 361 pub hash_blake2_256_per_byte: Weight,362363 364 pub hash_blake2_128: Weight,365366 367 pub hash_blake2_128_per_byte: Weight,368369 370 pub rent_params: Weight,371372 373 #[codec(skip)]374 pub _phantom: PhantomData<T>,375}376377macro_rules! replace_token {378 ($_in:tt $replacement:tt) => {379 $replacement380 };381}382383macro_rules! call_zero {384 ($name:ident, $( $arg:expr ),*) => {385 T::WeightInfo::$name($( replace_token!($arg 0) ),*)386 };387}388389macro_rules! cost_args {390 ($name:ident, $( $arg: expr ),+) => {391 (T::WeightInfo::$name($( $arg ),+).saturating_sub(call_zero!($name, $( $arg ),+)))392 }393}394395macro_rules! cost_batched_args {396 ($name:ident, $( $arg: expr ),+) => {397 cost_args!($name, $( $arg ),+) / Weight::from(API_BENCHMARK_BATCH_SIZE)398 }399}400401macro_rules! cost_instr_no_params_with_batch_size {402 ($name:ident, $batch_size:expr) => {403 (cost_args!($name, 1) / Weight::from($batch_size)) as u32404 };405}406407macro_rules! cost_instr_with_batch_size {408 ($name:ident, $num_params:expr, $batch_size:expr) => {409 cost_instr_no_params_with_batch_size!($name, $batch_size).saturating_sub(410 (cost_instr_no_params_with_batch_size!(instr_i64const, $batch_size) / 2)411 .saturating_mul($num_params),412 )413 };414}415416macro_rules! cost_instr {417 ($name:ident, $num_params:expr) => {418 cost_instr_with_batch_size!($name, $num_params, INSTR_BENCHMARK_BATCH_SIZE)419 };420}421422macro_rules! cost_byte_args {423 ($name:ident, $( $arg: expr ),+) => {424 cost_args!($name, $( $arg ),+) / 1024425 }426}427428macro_rules! cost_byte_batched_args {429 ($name:ident, $( $arg: expr ),+) => {430 cost_batched_args!($name, $( $arg ),+) / 1024431 }432}433434macro_rules! cost {435 ($name:ident) => {436 cost_args!($name, 1)437 };438}439440macro_rules! cost_batched {441 ($name:ident) => {442 cost_batched_args!($name, 1)443 };444}445446macro_rules! cost_byte {447 ($name:ident) => {448 cost_byte_args!($name, 1)449 };450}451452macro_rules! cost_byte_batched {453 ($name:ident) => {454 cost_byte_batched_args!($name, 1)455 };456}457458impl<T: Config> Default for Schedule<T> {459 fn default() -> Self {460 Self {461 version: 0,462 enable_println: false,463 limits: Default::default(),464 instruction_weights: Default::default(),465 host_fn_weights: Default::default(),466 }467 }468}469470impl Default for Limits {471 fn default() -> Self {472 Self {473 event_topics: 4,474 475 stack_height: 512,476 globals: 256,477 parameters: 128,478 memory_pages: 16,479 480 table_size: 4096,481 br_table_size: 256,482 subject_len: 32,483 }484 }485}486487impl<T: Config> Default for InstructionWeights<T> {488 fn default() -> Self {489 let max_pages = Limits::default().memory_pages;490 Self {491 i64const: cost_instr!(instr_i64const, 1),492 i64load: cost_instr!(instr_i64load, 2),493 i64store: cost_instr!(instr_i64store, 2),494 select: cost_instr!(instr_select, 4),495 r#if: cost_instr!(instr_if, 3),496 br: cost_instr!(instr_br, 2),497 br_if: cost_instr!(instr_br_if, 5),498 br_table: cost_instr!(instr_br_table, 3),499 br_table_per_entry: cost_instr!(instr_br_table_per_entry, 0),500 call: cost_instr!(instr_call, 2),501 call_indirect: cost_instr!(instr_call_indirect, 3),502 call_indirect_per_param: cost_instr!(instr_call_indirect_per_param, 1),503 local_get: cost_instr!(instr_local_get, 1),504 local_set: cost_instr!(instr_local_set, 1),505 local_tee: cost_instr!(instr_local_tee, 2),506 global_get: cost_instr!(instr_global_get, 1),507 global_set: cost_instr!(instr_global_set, 1),508 memory_current: cost_instr!(instr_memory_current, 1),509 memory_grow: cost_instr_with_batch_size!(instr_memory_grow, 1, max_pages),510 i64clz: cost_instr!(instr_i64clz, 2),511 i64ctz: cost_instr!(instr_i64ctz, 2),512 i64popcnt: cost_instr!(instr_i64popcnt, 2),513 i64eqz: cost_instr!(instr_i64eqz, 2),514 i64extendsi32: cost_instr!(instr_i64extendsi32, 2),515 i64extendui32: cost_instr!(instr_i64extendui32, 2),516 i32wrapi64: cost_instr!(instr_i32wrapi64, 2),517 i64eq: cost_instr!(instr_i64eq, 3),518 i64ne: cost_instr!(instr_i64ne, 3),519 i64lts: cost_instr!(instr_i64lts, 3),520 i64ltu: cost_instr!(instr_i64ltu, 3),521 i64gts: cost_instr!(instr_i64gts, 3),522 i64gtu: cost_instr!(instr_i64gtu, 3),523 i64les: cost_instr!(instr_i64les, 3),524 i64leu: cost_instr!(instr_i64leu, 3),525 i64ges: cost_instr!(instr_i64ges, 3),526 i64geu: cost_instr!(instr_i64geu, 3),527 i64add: cost_instr!(instr_i64add, 3),528 i64sub: cost_instr!(instr_i64sub, 3),529 i64mul: cost_instr!(instr_i64mul, 3),530 i64divs: cost_instr!(instr_i64divs, 3),531 i64divu: cost_instr!(instr_i64divu, 3),532 i64rems: cost_instr!(instr_i64rems, 3),533 i64remu: cost_instr!(instr_i64remu, 3),534 i64and: cost_instr!(instr_i64and, 3),535 i64or: cost_instr!(instr_i64or, 3),536 i64xor: cost_instr!(instr_i64xor, 3),537 i64shl: cost_instr!(instr_i64shl, 3),538 i64shrs: cost_instr!(instr_i64shrs, 3),539 i64shru: cost_instr!(instr_i64shru, 3),540 i64rotl: cost_instr!(instr_i64rotl, 3),541 i64rotr: cost_instr!(instr_i64rotr, 3),542 _phantom: PhantomData,543 }544 }545}546547impl<T: Config> Default for HostFnWeights<T> {548 fn default() -> Self {549 Self {550 caller: cost_batched!(seal_caller),551 address: cost_batched!(seal_address),552 gas_left: cost_batched!(seal_gas_left),553 balance: cost_batched!(seal_balance),554 value_transferred: cost_batched!(seal_value_transferred),555 minimum_balance: cost_batched!(seal_minimum_balance),556 tombstone_deposit: cost_batched!(seal_tombstone_deposit),557 rent_allowance: cost_batched!(seal_rent_allowance),558 block_number: cost_batched!(seal_block_number),559 now: cost_batched!(seal_now),560 weight_to_fee: cost_batched!(seal_weight_to_fee),561 gas: cost_batched!(seal_gas),562 input: cost!(seal_input),563 input_per_byte: cost_byte!(seal_input_per_kb),564 r#return: cost!(seal_return),565 return_per_byte: cost_byte!(seal_return_per_kb),566 terminate: cost!(seal_terminate),567 terminate_per_code_byte: cost_byte!(seal_terminate_per_code_kb),568 restore_to: cost!(seal_restore_to),569 restore_to_per_caller_code_byte: cost_byte_args!(570 seal_restore_to_per_code_kb_delta,571 1,572 0,573 0574 ),575 restore_to_per_tombstone_code_byte: cost_byte_args!(576 seal_restore_to_per_code_kb_delta,577 0,578 1,579 0580 ),581 restore_to_per_delta: cost_batched_args!(seal_restore_to_per_code_kb_delta, 0, 0, 1),582 random: cost_batched!(seal_random),583 deposit_event: cost_batched!(seal_deposit_event),584 deposit_event_per_topic: cost_batched_args!(seal_deposit_event_per_topic_and_kb, 1, 0),585 deposit_event_per_byte: cost_byte_batched_args!(586 seal_deposit_event_per_topic_and_kb,587 0,588 1589 ),590 set_rent_allowance: cost_batched!(seal_set_rent_allowance),591 set_storage: cost_batched!(seal_set_storage),592 set_storage_per_byte: cost_byte_batched!(seal_set_storage_per_kb),593 clear_storage: cost_batched!(seal_clear_storage),594 get_storage: cost_batched!(seal_get_storage),595 get_storage_per_byte: cost_byte_batched!(seal_get_storage_per_kb),596 transfer: cost_batched!(seal_transfer),597 call: cost_batched!(seal_call),598 call_per_code_byte: cost_byte_batched_args!(599 seal_call_per_code_transfer_input_output_kb,600 1,601 0,602 0,603 0604 ),605 call_transfer_surcharge: cost_batched_args!(606 seal_call_per_code_transfer_input_output_kb,607 0,608 1,609 0,610 0611 ),612 call_per_input_byte: cost_byte_batched_args!(613 seal_call_per_code_transfer_input_output_kb,614 0,615 0,616 1,617 0618 ),619 call_per_output_byte: cost_byte_batched_args!(620 seal_call_per_code_transfer_input_output_kb,621 0,622 0,623 0,624 1625 ),626 instantiate: cost_batched!(seal_instantiate),627 instantiate_per_code_byte: cost_byte_batched_args!(628 seal_instantiate_per_code_input_output_salt_kb,629 1,630 0,631 0,632 0633 ),634 instantiate_per_input_byte: cost_byte_batched_args!(635 seal_instantiate_per_code_input_output_salt_kb,636 0,637 1,638 0,639 0640 ),641 instantiate_per_output_byte: cost_byte_batched_args!(642 seal_instantiate_per_code_input_output_salt_kb,643 0,644 0,645 1,646 0647 ),648 instantiate_per_salt_byte: cost_byte_batched_args!(649 seal_instantiate_per_code_input_output_salt_kb,650 0,651 0,652 0,653 1654 ),655 hash_sha2_256: cost_batched!(seal_hash_sha2_256),656 hash_sha2_256_per_byte: cost_byte_batched!(seal_hash_sha2_256_per_kb),657 hash_keccak_256: cost_batched!(seal_hash_keccak_256),658 hash_keccak_256_per_byte: cost_byte_batched!(seal_hash_keccak_256_per_kb),659 hash_blake2_256: cost_batched!(seal_hash_blake2_256),660 hash_blake2_256_per_byte: cost_byte_batched!(seal_hash_blake2_256_per_kb),661 hash_blake2_128: cost_batched!(seal_hash_blake2_128),662 hash_blake2_128_per_byte: cost_byte_batched!(seal_hash_blake2_128_per_kb),663 rent_params: cost_batched!(seal_rent_params),664 _phantom: PhantomData,665 }666 }667}668669struct ScheduleRules<'a, T: Config> {670 schedule: &'a Schedule<T>,671 params: Vec<u32>,672}673674impl<T: Config> Schedule<T> {675 676 677 678 679 680 681 682 683 684 pub fn enable_println(mut self, enable: bool) -> Self {685 self.enable_println = enable;686 self687 }688689 pub(crate) fn rules(&self, module: &elements::Module) -> impl rules::Rules + '_ {690 ScheduleRules {691 schedule: &self,692 params: module693 .type_section()694 .iter()695 .flat_map(|section| section.types())696 .map(|func| {697 let elements::Type::Function(func) = func;698 func.params().len() as u32699 })700 .collect(),701 }702 }703}704705impl<'a, T: Config> rules::Rules for ScheduleRules<'a, T> {706 fn instruction_cost(&self, instruction: &elements::Instruction) -> Option<u32> {707 use parity_wasm::elements::Instruction::*;708 let w = &self.schedule.instruction_weights;709 let max_params = self.schedule.limits.parameters;710711 let weight = match *instruction {712 End | Unreachable | Return | Else => 0,713 I32Const(_) | I64Const(_) | Block(_) | Loop(_) | Nop | Drop => w.i64const,714 I32Load(_, _)715 | I32Load8S(_, _)716 | I32Load8U(_, _)717 | I32Load16S(_, _)718 | I32Load16U(_, _)719 | I64Load(_, _)720 | I64Load8S(_, _)721 | I64Load8U(_, _)722 | I64Load16S(_, _)723 | I64Load16U(_, _)724 | I64Load32S(_, _)725 | I64Load32U(_, _) => w.i64load,726 I32Store(_, _)727 | I32Store8(_, _)728 | I32Store16(_, _)729 | I64Store(_, _)730 | I64Store8(_, _)731 | I64Store16(_, _)732 | I64Store32(_, _) => w.i64store,733 Select => w.select,734 If(_) => w.r#if,735 Br(_) => w.br,736 BrIf(_) => w.br_if,737 Call(_) => w.call,738 GetLocal(_) => w.local_get,739 SetLocal(_) => w.local_set,740 TeeLocal(_) => w.local_tee,741 GetGlobal(_) => w.global_get,742 SetGlobal(_) => w.global_set,743 CurrentMemory(_) => w.memory_current,744 GrowMemory(_) => w.memory_grow,745 CallIndirect(idx, _) => *self.params.get(idx as usize).unwrap_or(&max_params),746 BrTable(ref data) => w747 .br_table748 .saturating_add(w.br_table_per_entry.saturating_mul(data.table.len() as u32)),749 I32Clz | I64Clz => w.i64clz,750 I32Ctz | I64Ctz => w.i64ctz,751 I32Popcnt | I64Popcnt => w.i64popcnt,752 I32Eqz | I64Eqz => w.i64eqz,753 I64ExtendSI32 => w.i64extendsi32,754 I64ExtendUI32 => w.i64extendui32,755 I32WrapI64 => w.i32wrapi64,756 I32Eq | I64Eq => w.i64eq,757 I32Ne | I64Ne => w.i64ne,758 I32LtS | I64LtS => w.i64lts,759 I32LtU | I64LtU => w.i64ltu,760 I32GtS | I64GtS => w.i64gts,761 I32GtU | I64GtU => w.i64gtu,762 I32LeS | I64LeS => w.i64les,763 I32LeU | I64LeU => w.i64leu,764 I32GeS | I64GeS => w.i64ges,765 I32GeU | I64GeU => w.i64geu,766 I32Add | I64Add => w.i64add,767 I32Sub | I64Sub => w.i64sub,768 I32Mul | I64Mul => w.i64mul,769 I32DivS | I64DivS => w.i64divs,770 I32DivU | I64DivU => w.i64divu,771 I32RemS | I64RemS => w.i64rems,772 I32RemU | I64RemU => w.i64remu,773 I32And | I64And => w.i64and,774 I32Or | I64Or => w.i64or,775 I32Xor | I64Xor => w.i64xor,776 I32Shl | I64Shl => w.i64shl,777 I32ShrS | I64ShrS => w.i64shrs,778 I32ShrU | I64ShrU => w.i64shru,779 I32Rotl | I64Rotl => w.i64rotl,780 I32Rotr | I64Rotr => w.i64rotr,781782 783 784 _ => return None,785 };786 Some(weight)787 }788789 fn memory_grow_cost(&self) -> Option<rules::MemoryGrowCost> {790 791 792 None793 }794}795796#[cfg(test)]797mod test {798 use crate::tests::Test;799 use super::*;800801 #[test]802 fn print_test_schedule() {803 let schedule = Schedule::<Test>::default();804 println!("{:#?}", schedule);805 }806}