mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 08:11:04 +00:00
contracts: Add automated weights for wasm instructions (#7361)
* pallet_contracts: Inline benchmark helper that is only used once * Move all max_* Schedule items into a new struct * Limit the number of globals a module can declare * The current limits are too high for wasmi to even execute * Limit the amount of parameters any wasm function is allowed to have * Limit the size the BrTable's immediate value * Add instruction benchmarks * Add new benchmarks to the schedule and make use of it * Add Benchmark Results generated by the bench bot * Add proc macro that implements `Debug` for `Schedule` * Add missing imports necessary for no_std build * Make the WeightDebug macro available for no_std In this case a dummy implementation is derived in order to not blow up the code size akin to the RuntimeDebug macro. * Rework instr_memory_grow benchmark to use only the maximum amount of pages allowed * Add maximum amount of memory when benching (seal_)call/instantiate * cargo run --release --features runtime-benchmarks --manifest-path bin/node/cli/Cargo.toml -- benchmark --chain dev --steps 50 --repeat 20 --extrinsic * --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output ./bin/node/runtime/src/weights --header ./HEADER --pallet pallet_contracts * Added utility benchmark that allows pretty printing of the real schedule * review: Add missing header to the proc-macro lib.rs * review: Clarify why #[allow(dead_code)] attribute is there * review: Fix pwasm-utils line * review: Fixup rand usage * review: Fix typo * review: Imported -> Exported * cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * contracts: Adapt to new weight structure * contracts: Fixup runtime WeightInfo * contracts: Remove unneeded fullpath of WeightInfo type * Apply suggestions from code review Co-authored-by: Andrew Jones <ascjones@gmail.com> * Fix typo in schedule.rs Co-authored-by: Andrew Jones <ascjones@gmail.com> * Fix docs in schedule.rs * Apply suggestions from code review Co-authored-by: Nikolay Volf <nikvolf@gmail.com> * Don't publish proc-macro crate until 3.0.0 is ready * Optimize imports for less repetition * Break overlong line Co-authored-by: Parity Benchmarking Bot <admin@parity.io> Co-authored-by: Andrew Jones <ascjones@gmail.com> Co-authored-by: Nikolay Volf <nikvolf@gmail.com>
This commit is contained in:
committed by
GitHub
parent
9704c204e6
commit
51c67fe881
Generated
+25
-2
@@ -4472,13 +4472,16 @@ dependencies = [
|
||||
"hex-literal",
|
||||
"pallet-balances",
|
||||
"pallet-contracts-primitives",
|
||||
"pallet-contracts-proc-macro",
|
||||
"pallet-randomness-collective-flip",
|
||||
"pallet-timestamp",
|
||||
"parity-scale-codec",
|
||||
"parity-wasm 0.41.0",
|
||||
"paste 1.0.0",
|
||||
"pretty_assertions",
|
||||
"pwasm-utils",
|
||||
"pwasm-utils 0.16.0",
|
||||
"rand 0.7.3",
|
||||
"rand_pcg 0.2.1",
|
||||
"serde",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
@@ -4499,6 +4502,15 @@ dependencies = [
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-contracts-proc-macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-contracts-rpc"
|
||||
version = "0.8.0"
|
||||
@@ -5835,6 +5847,17 @@ dependencies = [
|
||||
"parity-wasm 0.41.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pwasm-utils"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c8ac87af529432d3a4f0e2b3bbf08af49f28f09cc73ed7e551161bdaef5f78d"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"log",
|
||||
"parity-wasm 0.41.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
@@ -6960,7 +6983,7 @@ dependencies = [
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
"parity-wasm 0.41.0",
|
||||
"pwasm-utils",
|
||||
"pwasm-utils 0.14.0",
|
||||
"sc-executor-common",
|
||||
"scoped-tls",
|
||||
"sp-allocator",
|
||||
|
||||
@@ -87,9 +87,6 @@ pub mod constants;
|
||||
use constants::{time::*, currency::*};
|
||||
use sp_runtime::generic::Era;
|
||||
|
||||
/// Weights for pallets used in the runtime.
|
||||
mod weights;
|
||||
|
||||
// Make the WASM binary available.
|
||||
#[cfg(feature = "std")]
|
||||
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
||||
@@ -697,7 +694,7 @@ impl pallet_contracts::Trait for Runtime {
|
||||
type MaxDepth = pallet_contracts::DefaultMaxDepth;
|
||||
type MaxValueSize = pallet_contracts::DefaultMaxValueSize;
|
||||
type WeightPrice = pallet_transaction_payment::Module<Self>;
|
||||
type WeightInfo = weights::pallet_contracts::WeightInfo<Self>;
|
||||
type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;
|
||||
}
|
||||
|
||||
impl pallet_sudo::Trait for Runtime {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! A list of the different weight modules for our runtime.
|
||||
|
||||
pub mod pallet_contracts;
|
||||
@@ -1,294 +0,0 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Weights for pallet_contracts
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
|
||||
//! DATE: 2020-10-06, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: []
|
||||
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use frame_support::{traits::Get, weights::Weight};
|
||||
use sp_std::marker::PhantomData;
|
||||
|
||||
pub struct WeightInfo<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Trait> pallet_contracts::WeightInfo for WeightInfo<T> {
|
||||
fn update_schedule() -> Weight {
|
||||
(33_207_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
fn put_code(n: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((144_833_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn instantiate(n: u32, ) -> Weight {
|
||||
(223_974_000 as Weight)
|
||||
.saturating_add((1_007_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(6 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn call() -> Weight {
|
||||
(210_638_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn claim_surcharge() -> Weight {
|
||||
(508_079_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(3 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn seal_caller(r: u32, ) -> Weight {
|
||||
(143_336_000 as Weight)
|
||||
.saturating_add((397_788_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_address(r: u32, ) -> Weight {
|
||||
(147_296_000 as Weight)
|
||||
.saturating_add((396_962_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_gas_left(r: u32, ) -> Weight {
|
||||
(141_677_000 as Weight)
|
||||
.saturating_add((393_308_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_balance(r: u32, ) -> Weight {
|
||||
(157_556_000 as Weight)
|
||||
.saturating_add((879_861_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
}
|
||||
fn seal_value_transferred(r: u32, ) -> Weight {
|
||||
(148_867_000 as Weight)
|
||||
.saturating_add((391_678_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_minimum_balance(r: u32, ) -> Weight {
|
||||
(147_252_000 as Weight)
|
||||
.saturating_add((393_977_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_tombstone_deposit(r: u32, ) -> Weight {
|
||||
(144_208_000 as Weight)
|
||||
.saturating_add((394_625_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_rent_allowance(r: u32, ) -> Weight {
|
||||
(135_320_000 as Weight)
|
||||
.saturating_add((925_541_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_block_number(r: u32, ) -> Weight {
|
||||
(145_849_000 as Weight)
|
||||
.saturating_add((390_065_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_now(r: u32, ) -> Weight {
|
||||
(146_363_000 as Weight)
|
||||
.saturating_add((391_772_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_weight_to_fee(r: u32, ) -> Weight {
|
||||
(129_872_000 as Weight)
|
||||
.saturating_add((670_744_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
}
|
||||
fn seal_gas(r: u32, ) -> Weight {
|
||||
(130_985_000 as Weight)
|
||||
.saturating_add((198_427_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_input(r: u32, ) -> Weight {
|
||||
(138_647_000 as Weight)
|
||||
.saturating_add((8_363_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_input_per_kb(n: u32, ) -> Weight {
|
||||
(149_418_000 as Weight)
|
||||
.saturating_add((272_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_return(r: u32, ) -> Weight {
|
||||
(129_116_000 as Weight)
|
||||
.saturating_add((5_745_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_return_per_kb(n: u32, ) -> Weight {
|
||||
(139_601_000 as Weight)
|
||||
.saturating_add((680_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_terminate(r: u32, ) -> Weight {
|
||||
(138_548_000 as Weight)
|
||||
.saturating_add((355_473_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_restore_to(r: u32, ) -> Weight {
|
||||
(239_880_000 as Weight)
|
||||
.saturating_add((138_305_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes((4 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_restore_to_per_delta(d: u32, ) -> Weight {
|
||||
(40_572_000 as Weight)
|
||||
.saturating_add((3_748_632_000 as Weight).saturating_mul(d as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(7 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(d as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(d as Weight)))
|
||||
}
|
||||
fn seal_random(r: u32, ) -> Weight {
|
||||
(148_156_000 as Weight)
|
||||
.saturating_add((1_036_452_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
}
|
||||
fn seal_deposit_event(r: u32, ) -> Weight {
|
||||
(176_039_000 as Weight)
|
||||
.saturating_add((1_497_705_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight {
|
||||
(1_923_547_000 as Weight)
|
||||
.saturating_add((783_354_000 as Weight).saturating_mul(t as Weight))
|
||||
.saturating_add((240_600_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(t as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(t as Weight)))
|
||||
}
|
||||
fn seal_set_rent_allowance(r: u32, ) -> Weight {
|
||||
(151_095_000 as Weight)
|
||||
.saturating_add((1_104_696_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
fn seal_set_storage(r: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((14_975_467_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_set_storage_per_kb(n: u32, ) -> Weight {
|
||||
(2_465_724_000 as Weight)
|
||||
.saturating_add((203_125_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn seal_clear_storage(r: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((5_254_595_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_get_storage(r: u32, ) -> Weight {
|
||||
(60_303_000 as Weight)
|
||||
.saturating_add((1_135_486_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_get_storage_per_kb(n: u32, ) -> Weight {
|
||||
(931_900_000 as Weight)
|
||||
.saturating_add((144_572_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
}
|
||||
fn seal_transfer(r: u32, ) -> Weight {
|
||||
(50_722_000 as Weight)
|
||||
.saturating_add((6_701_164_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_call(r: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((10_589_747_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight {
|
||||
(11_223_388_000 as Weight)
|
||||
.saturating_add((4_965_182_000 as Weight).saturating_mul(t as Weight))
|
||||
.saturating_add((50_603_000 as Weight).saturating_mul(i as Weight))
|
||||
.saturating_add((72_972_000 as Weight).saturating_mul(o as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(105 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((101 as Weight).saturating_mul(t as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes((101 as Weight).saturating_mul(t as Weight)))
|
||||
}
|
||||
fn seal_instantiate(r: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((22_933_938_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(6 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((300 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((200 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_instantiate_per_input_output_kb(i: u32, o: u32, ) -> Weight {
|
||||
(20_986_307_000 as Weight)
|
||||
.saturating_add((152_611_000 as Weight).saturating_mul(i as Weight))
|
||||
.saturating_add((73_457_000 as Weight).saturating_mul(o as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(207 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(202 as Weight))
|
||||
}
|
||||
fn seal_hash_sha2_256(r: u32, ) -> Weight {
|
||||
(145_988_000 as Weight)
|
||||
.saturating_add((343_540_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight {
|
||||
(719_758_000 as Weight)
|
||||
.saturating_add((420_306_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_keccak_256(r: u32, ) -> Weight {
|
||||
(116_261_000 as Weight)
|
||||
.saturating_add((360_601_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight {
|
||||
(583_726_000 as Weight)
|
||||
.saturating_add((333_091_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_blake2_256(r: u32, ) -> Weight {
|
||||
(144_609_000 as Weight)
|
||||
.saturating_add((332_388_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight {
|
||||
(612_987_000 as Weight)
|
||||
.saturating_add((150_030_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_blake2_128(r: u32, ) -> Weight {
|
||||
(142_085_000 as Weight)
|
||||
.saturating_add((329_426_000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight {
|
||||
(632_517_000 as Weight)
|
||||
.saturating_add((149_974_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,9 @@ frame-benchmarking = { version = "2.0.0", default-features = false, path = "../b
|
||||
frame-support = { version = "2.0.0", default-features = false, path = "../support" }
|
||||
frame-system = { version = "2.0.0", default-features = false, path = "../system" }
|
||||
pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "common" }
|
||||
pallet-contracts-proc-macro = { version = "0.1.0", path = "proc-macro" }
|
||||
parity-wasm = { version = "0.41.0", default-features = false }
|
||||
pwasm-utils = { version = "0.14.0", default-features = false }
|
||||
pwasm-utils = { version = "0.16", default-features = false }
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" }
|
||||
sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" }
|
||||
@@ -31,6 +32,10 @@ sp-std = { version = "2.0.0", default-features = false, path = "../../primitives
|
||||
sp-sandbox = { version = "0.8.0", default-features = false, path = "../../primitives/sandbox" }
|
||||
wasmi-validation = { version = "0.3.0", default-features = false }
|
||||
|
||||
# Only used in benchmarking to generate random contract code
|
||||
rand = { version = "0.7.0", optional = true, default-features = false }
|
||||
rand_pcg = { version = "0.2.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
hex-literal = "0.3.1"
|
||||
@@ -57,7 +62,10 @@ std = [
|
||||
"pwasm-utils/std",
|
||||
"wasmi-validation/std",
|
||||
"pallet-contracts-primitives/std",
|
||||
"pallet-contracts-proc-macro/full",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking",
|
||||
"rand",
|
||||
"rand_pcg",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "pallet-contracts-proc-macro"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.dev"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "Procedural macros used in pallet_contracts"
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
[features]
|
||||
# If set the full output is generated. Do NOT set when generating for wasm runtime.
|
||||
full = []
|
||||
@@ -0,0 +1,142 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Proc macros used in the contracts module.
|
||||
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields, Ident};
|
||||
use alloc::string::ToString;
|
||||
|
||||
/// This derives `Debug` for a struct where each field must be of some numeric type.
|
||||
/// It interprets each field as its represents some weight and formats it as times so that
|
||||
/// it is readable by humans.
|
||||
#[proc_macro_derive(WeightDebug)]
|
||||
pub fn derive_weight_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
derive_debug(input, format_weight)
|
||||
}
|
||||
|
||||
/// This is basically identical to the std libs Debug derive but without adding any
|
||||
/// bounds to existing generics.
|
||||
#[proc_macro_derive(ScheduleDebug)]
|
||||
pub fn derive_schedule_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
derive_debug(input, format_default)
|
||||
}
|
||||
|
||||
fn derive_debug(
|
||||
input: proc_macro::TokenStream,
|
||||
fmt: impl Fn(&Ident) -> TokenStream
|
||||
) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = &input.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let data = if let Data::Struct(data) = &input.data {
|
||||
data
|
||||
} else {
|
||||
return quote_spanned! {
|
||||
name.span() =>
|
||||
compile_error!("WeightDebug is only supported for structs.");
|
||||
}.into();
|
||||
};
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
let fields = iterate_fields(data, fmt);
|
||||
|
||||
#[cfg(not(feature = "full"))]
|
||||
let fields = {
|
||||
drop(fmt);
|
||||
drop(data);
|
||||
TokenStream::new()
|
||||
};
|
||||
|
||||
let tokens = quote! {
|
||||
impl #impl_generics core::fmt::Debug for #name #ty_generics #where_clause {
|
||||
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
use ::sp_runtime::{FixedPointNumber, FixedU128 as Fixed};
|
||||
let mut formatter = formatter.debug_struct(stringify!(#name));
|
||||
#fields
|
||||
formatter.finish()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
/// This is only used then the `full` feature is activated.
|
||||
#[cfg(feature = "full")]
|
||||
fn iterate_fields(data: &DataStruct, fmt: impl Fn(&Ident) -> TokenStream) -> TokenStream {
|
||||
match &data.fields {
|
||||
Fields::Named(fields) => {
|
||||
let recurse = fields.named
|
||||
.iter()
|
||||
.filter_map(|f| {
|
||||
let name = f.ident.as_ref()?;
|
||||
if name.to_string().starts_with('_') {
|
||||
return None;
|
||||
}
|
||||
let value = fmt(name);
|
||||
let ret = quote_spanned!{ f.span() =>
|
||||
formatter.field(stringify!(#name), #value);
|
||||
};
|
||||
Some(ret)
|
||||
});
|
||||
quote!{
|
||||
#( #recurse )*
|
||||
}
|
||||
}
|
||||
Fields::Unnamed(fields) => quote_spanned!{
|
||||
fields.span() =>
|
||||
compile_error!("Unnamed fields are not supported")
|
||||
},
|
||||
Fields::Unit => quote!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_weight(field: &Ident) -> TokenStream {
|
||||
quote_spanned! { field.span() =>
|
||||
&if self.#field > 1_000_000_000 {
|
||||
format!(
|
||||
"{:.1?} ms",
|
||||
Fixed::saturating_from_rational(self.#field, 1_000_000_000).to_fraction()
|
||||
)
|
||||
} else if self.#field > 1_000_000 {
|
||||
format!(
|
||||
"{:.1?} µs",
|
||||
Fixed::saturating_from_rational(self.#field, 1_000_000).to_fraction()
|
||||
)
|
||||
} else if self.#field > 1_000 {
|
||||
format!(
|
||||
"{:.1?} ns",
|
||||
Fixed::saturating_from_rational(self.#field, 1_000).to_fraction()
|
||||
)
|
||||
} else {
|
||||
format!("{} ps", self.#field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_default(field: &Ident) -> TokenStream {
|
||||
quote_spanned! { field.span() =>
|
||||
&self.#field
|
||||
}
|
||||
}
|
||||
@@ -28,28 +28,50 @@ use crate::Trait;
|
||||
use crate::Module as Contracts;
|
||||
|
||||
use parity_wasm::elements::{Instruction, Instructions, FuncBody, ValueType, BlockType};
|
||||
use pwasm_utils::stack_height::inject_limiter;
|
||||
use sp_runtime::traits::Hash;
|
||||
use sp_sandbox::{EnvironmentDefinitionBuilder, Memory};
|
||||
use sp_std::{prelude::*, convert::TryFrom};
|
||||
|
||||
/// Pass to `create_code` in order to create a compiled `WasmModule`.
|
||||
///
|
||||
/// This exists to have a more declarative way to describe a wasm module than to use
|
||||
/// parity-wasm directly. It is tailored to fit the structure of contracts that are
|
||||
/// needed for benchmarking.
|
||||
#[derive(Default)]
|
||||
pub struct ModuleDefinition {
|
||||
pub data_segments: Vec<DataSegment>,
|
||||
/// Imported memory attached to the module. No memory is imported if `None`.
|
||||
pub memory: Option<ImportedMemory>,
|
||||
/// Initializers for the imported memory.
|
||||
pub data_segments: Vec<DataSegment>,
|
||||
/// Creates the supplied amount of i64 mutable globals initialized with random values.
|
||||
pub num_globals: u32,
|
||||
/// List of functions that the module should import. They start with index 0.
|
||||
pub imported_functions: Vec<ImportedFunction>,
|
||||
/// Function body of the exported `deploy` function. Body is empty if `None`.
|
||||
/// Its index is `imported_functions.len()`.
|
||||
pub deploy_body: Option<FuncBody>,
|
||||
/// Function body of the exported `call` function. Body is empty if `None`.
|
||||
/// Its index is `imported_functions.len() + 1`.
|
||||
pub call_body: Option<FuncBody>,
|
||||
/// Function body of a non-exported function with index `imported_functions.len() + 2`.
|
||||
pub aux_body: Option<FuncBody>,
|
||||
/// The amount of I64 arguments the aux function should have.
|
||||
pub aux_arg_num: u32,
|
||||
/// If set to true the stack height limiter is injected into the the module. This is
|
||||
/// needed for instruction debugging because the cost of executing the stack height
|
||||
/// instrumentation should be included in the costs for the individual instructions
|
||||
/// that cause more metering code (only call).
|
||||
pub inject_stack_metering: bool,
|
||||
/// Create a table containing function pointers.
|
||||
pub table: Option<TableSegment>,
|
||||
}
|
||||
|
||||
impl Default for ModuleDefinition {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data_segments: vec![],
|
||||
memory: None,
|
||||
imported_functions: vec![],
|
||||
deploy_body: None,
|
||||
call_body: None,
|
||||
}
|
||||
}
|
||||
pub struct TableSegment {
|
||||
/// How many elements should be created inside the table.
|
||||
pub num_elements: u32,
|
||||
/// The function index with which all table elements should be initialized.
|
||||
pub function_index: u32,
|
||||
}
|
||||
|
||||
pub struct DataSegment {
|
||||
@@ -57,6 +79,7 @@ pub struct DataSegment {
|
||||
pub value: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ImportedMemory {
|
||||
pub min_pages: u32,
|
||||
pub max_pages: u32,
|
||||
@@ -80,6 +103,7 @@ pub struct ImportedFunction {
|
||||
pub struct WasmModule<T:Trait> {
|
||||
pub code: Vec<u8>,
|
||||
pub hash: <T::Hashing as Hash>::Output,
|
||||
memory: Option<ImportedMemory>,
|
||||
}
|
||||
|
||||
impl<T: Trait> From<ModuleDefinition> for WasmModule<T> {
|
||||
@@ -91,14 +115,14 @@ impl<T: Trait> From<ModuleDefinition> for WasmModule<T> {
|
||||
let mut contract = parity_wasm::builder::module()
|
||||
// deploy function (first internal function)
|
||||
.function()
|
||||
.signature().with_params(vec![]).with_return_type(None).build()
|
||||
.signature().with_return_type(None).build()
|
||||
.with_body(def.deploy_body.unwrap_or_else(||
|
||||
FuncBody::new(Vec::new(), Instructions::empty())
|
||||
))
|
||||
.build()
|
||||
// call function (second internal function)
|
||||
.function()
|
||||
.signature().with_params(vec![]).with_return_type(None).build()
|
||||
.signature().with_return_type(None).build()
|
||||
.with_body(def.call_body.unwrap_or_else(||
|
||||
FuncBody::new(Vec::new(), Instructions::empty())
|
||||
))
|
||||
@@ -106,8 +130,19 @@ impl<T: Trait> From<ModuleDefinition> for WasmModule<T> {
|
||||
.export().field("deploy").internal().func(func_offset).build()
|
||||
.export().field("call").internal().func(func_offset + 1).build();
|
||||
|
||||
// If specified we add an additional internal function
|
||||
if let Some(body) = def.aux_body {
|
||||
let mut signature = contract
|
||||
.function()
|
||||
.signature().with_return_type(None);
|
||||
for _ in 0 .. def.aux_arg_num {
|
||||
signature = signature.with_param(ValueType::I64);
|
||||
}
|
||||
contract = signature.build().with_body(body).build();
|
||||
}
|
||||
|
||||
// Grant access to linear memory.
|
||||
if let Some(memory) = def.memory {
|
||||
if let Some(memory) = &def.memory {
|
||||
contract = contract.import()
|
||||
.module("env").field("memory")
|
||||
.external().memory(memory.min_pages, Some(memory.max_pages))
|
||||
@@ -136,20 +171,69 @@ impl<T: Trait> From<ModuleDefinition> for WasmModule<T> {
|
||||
.build()
|
||||
}
|
||||
|
||||
let code = contract.build().to_bytes().unwrap();
|
||||
// Add global variables
|
||||
if def.num_globals > 0 {
|
||||
use rand::{prelude::*, distributions::Standard};
|
||||
let rng = rand_pcg::Pcg32::seed_from_u64(3112244599778833558);
|
||||
for val in rng.sample_iter(Standard).take(def.num_globals as usize) {
|
||||
contract = contract
|
||||
.global()
|
||||
.value_type().i64()
|
||||
.mutable()
|
||||
.init_expr(Instruction::I64Const(val))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
// Add function pointer table
|
||||
if let Some(table) = def.table {
|
||||
contract = contract
|
||||
.table()
|
||||
.with_min(table.num_elements)
|
||||
.with_max(Some(table.num_elements))
|
||||
.with_element(0, vec![table.function_index; table.num_elements as usize])
|
||||
.build();
|
||||
}
|
||||
|
||||
let mut code = contract.build();
|
||||
|
||||
// Inject stack height metering
|
||||
if def.inject_stack_metering {
|
||||
code = inject_limiter(
|
||||
code,
|
||||
Contracts::<T>::current_schedule().limits.stack_height
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let code = code.to_bytes().unwrap();
|
||||
let hash = T::Hashing::hash(&code);
|
||||
Self {
|
||||
code,
|
||||
hash
|
||||
hash,
|
||||
memory: def.memory,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> WasmModule<T> {
|
||||
/// Creates a wasm module with an empty `call` and `deploy` function and nothing else.
|
||||
pub fn dummy() -> Self {
|
||||
ModuleDefinition::default().into()
|
||||
}
|
||||
|
||||
/// Same as `dummy` but with maximum sized linear memory.
|
||||
pub fn dummy_with_mem() -> Self {
|
||||
ModuleDefinition {
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
.. Default::default()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Creates a wasm module of `target_bytes` size. Used to benchmark the performance of
|
||||
/// `put_code` for different sizes of wasm modules. The generated module maximizes
|
||||
/// instrumentation runtime by nesting blocks as deeply as possible given the byte budget.
|
||||
pub fn sized(target_bytes: u32) -> Self {
|
||||
use parity_wasm::elements::Instruction::{If, I32Const, Return, End};
|
||||
// Base size of a contract is 47 bytes and each expansion adds 6 bytes.
|
||||
@@ -171,6 +255,9 @@ impl<T: Trait> WasmModule<T> {
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Creates a wasm module that calls the imported function named `getter_name` `repeat`
|
||||
/// times. The imported function is expected to have the "getter signature" of
|
||||
/// (out_ptr: u32, len_ptr: u32) -> ().
|
||||
pub fn getter(getter_name: &'static str, repeat: u32) -> Self {
|
||||
let pages = max_pages::<T>();
|
||||
ModuleDefinition {
|
||||
@@ -198,11 +285,14 @@ impl<T: Trait> WasmModule<T> {
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Creates a wasm module that calls the imported hash function named `name` `repeat` times
|
||||
/// with an input of size `data_size`. Hash functions have the signature
|
||||
/// (input_ptr: u32, input_len: u32, output_ptr: u32) -> ()
|
||||
pub fn hasher(name: &'static str, repeat: u32, data_size: u32) -> Self {
|
||||
ModuleDefinition {
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
imported_functions: vec![ImportedFunction {
|
||||
name: name,
|
||||
name,
|
||||
params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
|
||||
return_type: None,
|
||||
}],
|
||||
@@ -216,16 +306,84 @@ impl<T: Trait> WasmModule<T> {
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Creates a memory instance for use in a sandbox with dimensions declared in this module
|
||||
/// and adds it to `env`. A reference to that memory is returned so that it can be used to
|
||||
/// access the memory contents from the supervisor.
|
||||
pub fn add_memory<S>(&self, env: &mut EnvironmentDefinitionBuilder<S>) -> Option<Memory> {
|
||||
let memory = if let Some(memory) = &self.memory {
|
||||
memory
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let memory = Memory::new(memory.min_pages, Some(memory.max_pages)).unwrap();
|
||||
env.add_memory("env", "memory", memory.clone());
|
||||
Some(memory)
|
||||
}
|
||||
|
||||
pub fn unary_instr(instr: Instruction, repeat: u32) -> Self {
|
||||
use body::DynInstr::{RandomI64Repeated, Regular};
|
||||
ModuleDefinition {
|
||||
call_body: Some(body::repeated_dyn(repeat, vec![
|
||||
RandomI64Repeated(1),
|
||||
Regular(instr),
|
||||
Regular(Instruction::Drop),
|
||||
])),
|
||||
.. Default::default()
|
||||
}.into()
|
||||
}
|
||||
|
||||
pub fn binary_instr(instr: Instruction, repeat: u32) -> Self {
|
||||
use body::DynInstr::{RandomI64Repeated, Regular};
|
||||
ModuleDefinition {
|
||||
call_body: Some(body::repeated_dyn(repeat, vec![
|
||||
RandomI64Repeated(2),
|
||||
Regular(instr),
|
||||
Regular(Instruction::Drop),
|
||||
])),
|
||||
.. Default::default()
|
||||
}.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Mechanisms to create a function body that can be used inside a `ModuleDefinition`.
|
||||
/// Mechanisms to generate a function body that can be used inside a `ModuleDefinition`.
|
||||
pub mod body {
|
||||
use super::*;
|
||||
|
||||
pub enum CountedInstruction {
|
||||
// (offset, increment_by)
|
||||
Counter(u32, u32),
|
||||
/// When generating contract code by repeating a wasm sequence, it's sometimes necessary
|
||||
/// to change those instructions on each repetition. The variants of this enum describe
|
||||
/// various ways in which this can happen.
|
||||
pub enum DynInstr {
|
||||
/// Insert the associated instruction.
|
||||
Regular(Instruction),
|
||||
/// Insert a I32Const with incrementing value for each insertion.
|
||||
/// (start_at, increment_by)
|
||||
Counter(u32, u32),
|
||||
/// Insert a I32Const with a random value in [low, high) not divisible by two.
|
||||
/// (low, high)
|
||||
RandomUnaligned(u32, u32),
|
||||
/// Insert a I32Const with a random value in [low, high).
|
||||
/// (low, high)
|
||||
RandomI32(i32, i32),
|
||||
/// Insert the specified amount of I32Const with a random value.
|
||||
RandomI32Repeated(usize),
|
||||
/// Insert the specified amount of I64Const with a random value.
|
||||
RandomI64Repeated(usize),
|
||||
/// Insert a GetLocal with a random offset in [low, high).
|
||||
/// (low, high)
|
||||
RandomGetLocal(u32, u32),
|
||||
/// Insert a SetLocal with a random offset in [low, high).
|
||||
/// (low, high)
|
||||
RandomSetLocal(u32, u32),
|
||||
/// Insert a TeeLocal with a random offset in [low, high).
|
||||
/// (low, high)
|
||||
RandomTeeLocal(u32, u32),
|
||||
/// Insert a GetGlobal with a random offset in [low, high).
|
||||
/// (low, high)
|
||||
RandomGetGlobal(u32, u32),
|
||||
/// Insert a SetGlobal with a random offset in [low, high).
|
||||
/// (low, high)
|
||||
RandomSetGlobal(u32, u32)
|
||||
}
|
||||
|
||||
pub fn plain(instructions: Vec<Instruction>) -> FuncBody {
|
||||
@@ -245,28 +403,73 @@ pub mod body {
|
||||
FuncBody::new(Vec::new(), instructions)
|
||||
}
|
||||
|
||||
pub fn counted(repetitions: u32, mut instructions: Vec<CountedInstruction>) -> FuncBody {
|
||||
pub fn repeated_dyn(repetitions: u32, mut instructions: Vec<DynInstr>) -> FuncBody {
|
||||
use rand::{prelude::*, distributions::Standard};
|
||||
|
||||
// We do not need to be secure here.
|
||||
let mut rng = rand_pcg::Pcg32::seed_from_u64(8446744073709551615);
|
||||
|
||||
// We need to iterate over indices because we cannot cycle over mutable references
|
||||
let body = (0..instructions.len())
|
||||
.cycle()
|
||||
.take(instructions.len() * usize::try_from(repetitions).unwrap())
|
||||
.map(|idx| {
|
||||
.flat_map(|idx|
|
||||
match &mut instructions[idx] {
|
||||
CountedInstruction::Counter(offset, increment_by) => {
|
||||
DynInstr::Regular(instruction) => vec![instruction.clone()],
|
||||
DynInstr::Counter(offset, increment_by) => {
|
||||
let current = *offset;
|
||||
*offset += *increment_by;
|
||||
Instruction::I32Const(current as i32)
|
||||
vec![Instruction::I32Const(current as i32)]
|
||||
},
|
||||
DynInstr::RandomUnaligned(low, high) => {
|
||||
let unaligned = rng.gen_range(*low, *high) | 1;
|
||||
vec![Instruction::I32Const(unaligned as i32)]
|
||||
},
|
||||
DynInstr::RandomI32(low, high) => {
|
||||
vec![Instruction::I32Const(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomI32Repeated(num) => {
|
||||
(&mut rng).sample_iter(Standard).take(*num).map(|val|
|
||||
Instruction::I32Const(val)
|
||||
)
|
||||
.collect()
|
||||
},
|
||||
DynInstr::RandomI64Repeated(num) => {
|
||||
(&mut rng).sample_iter(Standard).take(*num).map(|val|
|
||||
Instruction::I64Const(val)
|
||||
)
|
||||
.collect()
|
||||
},
|
||||
DynInstr::RandomGetLocal(low, high) => {
|
||||
vec![Instruction::GetLocal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomSetLocal(low, high) => {
|
||||
vec![Instruction::SetLocal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomTeeLocal(low, high) => {
|
||||
vec![Instruction::TeeLocal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomGetGlobal(low, high) => {
|
||||
vec![Instruction::GetGlobal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomSetGlobal(low, high) => {
|
||||
vec![Instruction::SetGlobal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
CountedInstruction::Regular(instruction) => instruction.clone(),
|
||||
}
|
||||
})
|
||||
)
|
||||
.chain(sp_std::iter::once(Instruction::End))
|
||||
.collect();
|
||||
FuncBody::new(Vec::new(), Instructions::new(body))
|
||||
}
|
||||
|
||||
/// Replace the locals of the supplied `body` with `num` i64 locals.
|
||||
pub fn inject_locals(body: &mut FuncBody, num: u32) {
|
||||
use parity_wasm::elements::Local;
|
||||
*body.locals_mut() = (0..num).map(|i| Local::new(i, ValueType::I64)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// The maximum amount of pages any contract is allowed to have according to the current `Schedule`.
|
||||
pub fn max_pages<T: Trait>() -> u32 {
|
||||
Contracts::<T>::current_schedule().max_memory_pages
|
||||
Contracts::<T>::current_schedule().limits.memory_pages
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
///! For instruction benchmarking we do no instantiate a full contract but merely the
|
||||
///! sandbox to execute the wasm code. This is because we do not need the full
|
||||
///! environment that provides the seal interface as imported functions.
|
||||
|
||||
use super::code::WasmModule;
|
||||
use super::Trait;
|
||||
use sp_sandbox::{EnvironmentDefinitionBuilder, Instance, Memory};
|
||||
|
||||
/// Minimal execution environment without any exported functions.
|
||||
pub struct Sandbox {
|
||||
instance: Instance<()>,
|
||||
_memory: Option<Memory>,
|
||||
}
|
||||
|
||||
impl Sandbox {
|
||||
/// Invoke the `call` function of a contract code and panic on any execution error.
|
||||
pub fn invoke(&mut self) {
|
||||
self.instance.invoke("call", &[], &mut ()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> From<&WasmModule<T>> for Sandbox {
|
||||
/// Creates an instance from the supplied module and supplies as much memory
|
||||
/// to the instance as the module declares as imported.
|
||||
fn from(module: &WasmModule<T>) -> Self {
|
||||
let mut env_builder = EnvironmentDefinitionBuilder::new();
|
||||
let memory = module.add_memory(&mut env_builder);
|
||||
let instance = Instance::new(&module.code, &env_builder, &mut ())
|
||||
.expect("Failed to create benchmarking Sandbox instance");
|
||||
Self {
|
||||
instance,
|
||||
_memory: memory,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,18 +88,18 @@ mod wasm;
|
||||
mod rent;
|
||||
mod benchmarking;
|
||||
mod schedule;
|
||||
mod weight_info;
|
||||
pub mod weights;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::exec::ExecutionContext;
|
||||
use crate::wasm::{WasmLoader, WasmVm};
|
||||
use crate::weights::WeightInfo;
|
||||
|
||||
pub use crate::gas::{Gas, GasMeter};
|
||||
pub use crate::wasm::ReturnCode as RuntimeReturnCode;
|
||||
pub use crate::weight_info::WeightInfo;
|
||||
pub use crate::schedule::{Schedule, HostFnWeights, InstructionWeights};
|
||||
pub use crate::schedule::{Schedule, HostFnWeights, InstructionWeights, Limits};
|
||||
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_std::{prelude::*, marker::PhantomData, fmt::Debug};
|
||||
@@ -530,7 +530,7 @@ decl_module! {
|
||||
) -> DispatchResult {
|
||||
ensure_signed(origin)?;
|
||||
let schedule = <Module<T>>::current_schedule();
|
||||
ensure!(code.len() as u32 <= schedule.max_code_size, Error::<T>::CodeTooLarge);
|
||||
ensure!(code.len() as u32 <= schedule.limits.code_size, Error::<T>::CodeTooLarge);
|
||||
let result = wasm::save_code::<T>(code, &schedule);
|
||||
if let Ok(code_hash) = result {
|
||||
Self::deposit_event(RawEvent::CodeStored(code_hash));
|
||||
|
||||
@@ -17,77 +17,180 @@
|
||||
//! This module contains the cost schedule and supporting code that constructs a
|
||||
//! sane default schedule from a `WeightInfo` implementation.
|
||||
|
||||
use crate::{Trait, WeightInfo};
|
||||
use crate::{Trait, weights::WeightInfo};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
use pallet_contracts_proc_macro::{ScheduleDebug, WeightDebug};
|
||||
use frame_support::weights::Weight;
|
||||
use sp_std::{marker::PhantomData, fmt};
|
||||
use sp_std::{marker::PhantomData, vec::Vec};
|
||||
use codec::{Encode, Decode};
|
||||
use parity_wasm::elements;
|
||||
use pwasm_utils::rules;
|
||||
use sp_runtime::RuntimeDebug;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// How many instructions are executed in a single batch. The reasoning is the same
|
||||
/// as for `API_BENCHMARK_BATCH_SIZE`.
|
||||
pub const INSTR_BENCHMARK_BATCH_SIZE: u32 = 1_000;
|
||||
|
||||
/// Definition of the cost schedule and other parameterizations for wasm vm.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Encode, Decode, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", serde(bound(serialize = "", deserialize = "")))]
|
||||
#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug)]
|
||||
pub struct Schedule<T: Trait> {
|
||||
/// 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,
|
||||
/// Describes the upper limits on various metrics.
|
||||
pub limits: Limits,
|
||||
|
||||
/// Maximum allowed stack height.
|
||||
/// The weights for individual wasm instructions.
|
||||
pub instruction_weights: InstructionWeights<T>,
|
||||
|
||||
/// The weights for each imported function a contract is allowed to call.
|
||||
pub host_fn_weights: HostFnWeights<T>,
|
||||
}
|
||||
|
||||
/// Describes the upper limits on various metrics.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)]
|
||||
pub struct Limits {
|
||||
/// The maximum number of topics supported by an event.
|
||||
pub event_topics: u32,
|
||||
|
||||
/// Maximum allowed stack height in number of elements.
|
||||
///
|
||||
/// See https://wiki.parity.io/WebAssembly-StackHeight to find out
|
||||
/// how the stack frame cost is calculated.
|
||||
pub max_stack_height: u32,
|
||||
/// how the stack frame cost is calculated. Each element can be of one of the
|
||||
/// wasm value types. This means the maximum size per element is 64bit.
|
||||
pub stack_height: u32,
|
||||
|
||||
/// Maximum number of globals a module is allowed to declare.
|
||||
///
|
||||
/// Globals are not limited through the `stack_height` as locals are. Neither does
|
||||
/// the linear memory limit `memory_pages` applies to them.
|
||||
pub globals: u32,
|
||||
|
||||
/// Maximum numbers of parameters a function can have.
|
||||
///
|
||||
/// Those need to be limited to prevent a potentially exploitable interaction with
|
||||
/// the stack height instrumentation: The costs of executing the stack height
|
||||
/// instrumentation for an indirectly called function scales linearly with the amount
|
||||
/// of parameters of this function. Because the stack height instrumentation itself is
|
||||
/// is not weight metered its costs must be static (via this limit) and included in
|
||||
/// the costs of the instructions that cause them (call, call_indirect).
|
||||
pub parameters: u32,
|
||||
|
||||
/// Maximum number of memory pages allowed for a contract.
|
||||
pub max_memory_pages: u32,
|
||||
pub memory_pages: u32,
|
||||
|
||||
/// Maximum allowed size of a declared table.
|
||||
pub max_table_size: u32,
|
||||
/// Maximum number of elements allowed in a table.
|
||||
///
|
||||
/// Currently, the only type of element that is allowed in a table is funcref.
|
||||
pub table_size: u32,
|
||||
|
||||
/// The maximum length of a subject used for PRNG generation.
|
||||
pub max_subject_len: u32,
|
||||
/// Maximum number of elements that can appear as immediate value to the br_table instruction.
|
||||
pub br_table_size: u32,
|
||||
|
||||
/// The maximum length of a subject in bytes used for PRNG generation.
|
||||
pub 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,
|
||||
pub code_size: u32,
|
||||
}
|
||||
|
||||
/// Describes the weight for all categories of supported wasm instructions.
|
||||
///
|
||||
/// There there is one field for each wasm instruction that describes the weight to
|
||||
/// execute one instruction of that name. There are a few execptions:
|
||||
///
|
||||
/// 1. If there is a i64 and a i32 variant of an instruction we use the weight
|
||||
/// of the former for both.
|
||||
/// 2. The following instructions are free of charge because they merely structure the
|
||||
/// wasm module and cannot be spammed without making the module invalid (and rejected):
|
||||
/// End, Unreachable, Return, Else
|
||||
/// 3. The following instructions cannot be benchmarked because they are removed by any
|
||||
/// real world execution engine as a preprocessing step and therefore don't yield a
|
||||
/// meaningful benchmark result. However, in contrast to the instructions mentioned
|
||||
/// in 2. they can be spammed. We price them with the same weight as the "default"
|
||||
/// instruction (i64.const): Block, Loop, Nop
|
||||
/// 4. We price both i64.const and drop as InstructionWeights.i64const / 2. The reason
|
||||
/// for that is that we cannot benchmark either of them on its own but we need their
|
||||
/// individual values to derive (by subtraction) the weight of all other instructions
|
||||
/// that use them as supporting instructions. Supporting means mainly pushing arguments
|
||||
/// and dropping return values in order to maintain a valid module.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug)]
|
||||
pub struct InstructionWeights<T: Trait> {
|
||||
pub i64const: u32,
|
||||
pub i64load: u32,
|
||||
pub i64store: 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 local_get: u32,
|
||||
pub local_set: u32,
|
||||
pub local_tee: u32,
|
||||
pub global_get: u32,
|
||||
pub global_set: u32,
|
||||
pub memory_current: u32,
|
||||
pub memory_grow: u32,
|
||||
pub i64clz: u32,
|
||||
pub i64ctz: u32,
|
||||
pub i64popcnt: u32,
|
||||
pub i64eqz: u32,
|
||||
pub i64extendsi32: u32,
|
||||
pub i64extendui32: u32,
|
||||
pub i32wrapi64: u32,
|
||||
pub i64eq: u32,
|
||||
pub i64ne: u32,
|
||||
pub i64lts: u32,
|
||||
pub i64ltu: u32,
|
||||
pub i64gts: u32,
|
||||
pub i64gtu: u32,
|
||||
pub i64les: u32,
|
||||
pub i64leu: u32,
|
||||
pub i64ges: u32,
|
||||
pub i64geu: u32,
|
||||
pub i64add: u32,
|
||||
pub i64sub: u32,
|
||||
pub i64mul: u32,
|
||||
pub i64divs: u32,
|
||||
pub i64divu: u32,
|
||||
pub i64rems: u32,
|
||||
pub i64remu: u32,
|
||||
pub i64and: u32,
|
||||
pub i64or: u32,
|
||||
pub i64xor: u32,
|
||||
pub i64shl: u32,
|
||||
pub i64shrs: u32,
|
||||
pub i64shru: u32,
|
||||
pub i64rotl: u32,
|
||||
pub i64rotr: u32,
|
||||
/// The type parameter is used in the default implementation.
|
||||
pub _phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug)]
|
||||
pub struct HostFnWeights<T: Trait> {
|
||||
/// Weight of calling `seal_caller`.
|
||||
pub caller: Weight,
|
||||
|
||||
@@ -222,21 +325,11 @@ pub struct HostFnWeights {
|
||||
|
||||
/// 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<T: Trait> fmt::Debug for Schedule<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Schedule").finish()
|
||||
}
|
||||
/// The type parameter is used in the default implementation.
|
||||
pub _phantom: PhantomData<T>
|
||||
}
|
||||
|
||||
/// 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 };
|
||||
}
|
||||
@@ -259,6 +352,25 @@ macro_rules! cost_batched_args {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cost_instr_no_params_with_batch_size {
|
||||
($name:ident, $batch_size:expr) => {
|
||||
(cost_args!($name, 1) / Weight::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
|
||||
@@ -297,12 +409,97 @@ macro_rules! cost_byte_batched {
|
||||
|
||||
impl<T: Trait> Default for Schedule<T> {
|
||||
fn default() -> Self {
|
||||
let instruction_weights = InstructionWeights {
|
||||
grow_mem: WASM_INSTRUCTION_COST,
|
||||
regular: WASM_INSTRUCTION_COST,
|
||||
};
|
||||
Self {
|
||||
version: 0,
|
||||
enable_println: false,
|
||||
limits: Default::default(),
|
||||
instruction_weights: Default::default(),
|
||||
host_fn_weights: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let host_fn_weights = HostFnWeights {
|
||||
impl Default for Limits {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
event_topics: 4,
|
||||
// 512 * sizeof(i64) will give us a 4k stack.
|
||||
stack_height: 512,
|
||||
globals: 256,
|
||||
parameters: 128,
|
||||
memory_pages: 16,
|
||||
// 4k function pointers (This is in count not bytes).
|
||||
table_size: 4096,
|
||||
br_table_size: 256,
|
||||
subject_len: 32,
|
||||
code_size: 512 * 1024,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Default for InstructionWeights<T> {
|
||||
fn default() -> Self {
|
||||
let max_pages = Limits::default().memory_pages;
|
||||
Self {
|
||||
i64const: cost_instr!(instr_i64const, 1),
|
||||
i64load: cost_instr!(instr_i64load, 2),
|
||||
i64store: cost_instr!(instr_i64store, 2),
|
||||
select: cost_instr!(instr_select, 4),
|
||||
r#if: cost_instr!(instr_if, 3),
|
||||
br: cost_instr!(instr_br, 2),
|
||||
br_if: cost_instr!(instr_br_if, 5),
|
||||
br_table: cost_instr!(instr_br_table, 3),
|
||||
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, 3),
|
||||
call_indirect_per_param: cost_instr!(instr_call_indirect_per_param, 1),
|
||||
local_get: cost_instr!(instr_local_get, 1),
|
||||
local_set: cost_instr!(instr_local_set, 1),
|
||||
local_tee: cost_instr!(instr_local_tee, 2),
|
||||
global_get: cost_instr!(instr_global_get, 1),
|
||||
global_set: cost_instr!(instr_global_set, 1),
|
||||
memory_current: cost_instr!(instr_memory_current, 1),
|
||||
memory_grow: cost_instr_with_batch_size!(instr_memory_grow, 1, max_pages),
|
||||
i64clz: cost_instr!(instr_i64clz, 2),
|
||||
i64ctz: cost_instr!(instr_i64ctz, 2),
|
||||
i64popcnt: cost_instr!(instr_i64popcnt, 2),
|
||||
i64eqz: cost_instr!(instr_i64eqz, 2),
|
||||
i64extendsi32: cost_instr!(instr_i64extendsi32, 2),
|
||||
i64extendui32: cost_instr!(instr_i64extendui32, 2),
|
||||
i32wrapi64: cost_instr!(instr_i32wrapi64, 2),
|
||||
i64eq: cost_instr!(instr_i64eq, 3),
|
||||
i64ne: cost_instr!(instr_i64ne, 3),
|
||||
i64lts: cost_instr!(instr_i64lts, 3),
|
||||
i64ltu: cost_instr!(instr_i64ltu, 3),
|
||||
i64gts: cost_instr!(instr_i64gts, 3),
|
||||
i64gtu: cost_instr!(instr_i64gtu, 3),
|
||||
i64les: cost_instr!(instr_i64les, 3),
|
||||
i64leu: cost_instr!(instr_i64leu, 3),
|
||||
i64ges: cost_instr!(instr_i64ges, 3),
|
||||
i64geu: cost_instr!(instr_i64geu, 3),
|
||||
i64add: cost_instr!(instr_i64add, 3),
|
||||
i64sub: cost_instr!(instr_i64sub, 3),
|
||||
i64mul: cost_instr!(instr_i64mul, 3),
|
||||
i64divs: cost_instr!(instr_i64divs, 3),
|
||||
i64divu: cost_instr!(instr_i64divu, 3),
|
||||
i64rems: cost_instr!(instr_i64rems, 3),
|
||||
i64remu: cost_instr!(instr_i64remu, 3),
|
||||
i64and: cost_instr!(instr_i64and, 3),
|
||||
i64or: cost_instr!(instr_i64or, 3),
|
||||
i64xor: cost_instr!(instr_i64xor, 3),
|
||||
i64shl: cost_instr!(instr_i64shl, 3),
|
||||
i64shrs: cost_instr!(instr_i64shrs, 3),
|
||||
i64shru: cost_instr!(instr_i64shru, 3),
|
||||
i64rotl: cost_instr!(instr_i64rotl, 3),
|
||||
i64rotr: cost_instr!(instr_i64rotr, 3),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Default for HostFnWeights<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
caller: cost_batched!(seal_caller),
|
||||
address: cost_batched!(seal_address),
|
||||
gas_left: cost_batched!(seal_gas_left),
|
||||
@@ -348,20 +545,119 @@ impl<T: Trait> Default for Schedule<T> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ScheduleRules<'a, T: Trait> {
|
||||
schedule: &'a Schedule<T>,
|
||||
params: Vec<u32>,
|
||||
}
|
||||
|
||||
impl<T: Trait> Schedule<T> {
|
||||
pub fn rules(&self, module: &elements::Module) -> impl rules::Rules + '_ {
|
||||
ScheduleRules {
|
||||
schedule: &self,
|
||||
params: module
|
||||
.type_section()
|
||||
.iter()
|
||||
.flat_map(|section| section.types())
|
||||
.map(|func| {
|
||||
let elements::Type::Function(func) = func;
|
||||
func.params().len() as u32
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Trait> rules::Rules for ScheduleRules<'a, T> {
|
||||
fn instruction_cost(&self, instruction: &elements::Instruction) -> Option<u32> {
|
||||
use parity_wasm::elements::Instruction::*;
|
||||
let w = &self.schedule.instruction_weights;
|
||||
let max_params = self.schedule.limits.parameters;
|
||||
|
||||
let weight = match *instruction {
|
||||
End | Unreachable | Return | Else => 0,
|
||||
I32Const(_) | I64Const(_) | Block(_) | Loop(_) | Nop | Drop => w.i64const,
|
||||
I32Load(_, _) | I32Load8S(_, _) | I32Load8U(_, _) | I32Load16S(_, _) |
|
||||
I32Load16U(_, _) | I64Load(_, _) | I64Load8S(_, _) | I64Load8U(_, _) |
|
||||
I64Load16S(_, _) | I64Load16U(_, _) | I64Load32S(_, _) | I64Load32U(_, _)
|
||||
=> w.i64load,
|
||||
I32Store(_, _) | I32Store8(_, _) | I32Store16(_, _) | 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,
|
||||
GrowMemory(_) => w.memory_grow,
|
||||
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 | I64Clz => w.i64clz,
|
||||
I32Ctz | I64Ctz => w.i64ctz,
|
||||
I32Popcnt | I64Popcnt => w.i64popcnt,
|
||||
I64ExtendSI32 => w.i64extendsi32,
|
||||
I64ExtendUI32 => w.i64extendui32,
|
||||
I32WrapI64 => w.i32wrapi64,
|
||||
I32Eq | I64Eq => w.i64eq,
|
||||
I32Ne | I64Ne => w.i64ne,
|
||||
I32LtS | I64LtS => w.i64lts,
|
||||
I32LtU | I64LtU => w.i64ltu,
|
||||
I32GtS | I64GtS => w.i64gts,
|
||||
I32GtU | I64GtU => w.i64gtu,
|
||||
I32LeS | I64LeS => w.i64les,
|
||||
I32LeU | I64LeU => w.i64leu,
|
||||
I32GeS | I64GeS => w.i64ges,
|
||||
I32GeU | I64GeU => w.i64geu,
|
||||
I32Add | I64Add => w.i64add,
|
||||
I32Sub | I64Sub => w.i64sub,
|
||||
I32Mul | I64Mul => w.i64mul,
|
||||
I32DivS | I64DivS => w.i64divs,
|
||||
I32DivU | I64DivU => w.i64divu,
|
||||
I32RemS | I64RemS => w.i64rems,
|
||||
I32RemU | I64RemU => w.i64remu,
|
||||
I32And | I64And => w.i64and,
|
||||
I32Or | I64Or => w.i64or,
|
||||
I32Xor | I64Xor => w.i64xor,
|
||||
I32Shl | I64Shl => w.i64shl,
|
||||
I32ShrS | I64ShrS => w.i64shrs,
|
||||
I32ShrU | I64ShrU => w.i64shru,
|
||||
I32Rotl | I64Rotl => w.i64rotl,
|
||||
I32Rotr | I64Rotr => w.i64rotr,
|
||||
|
||||
// Returning None makes the gas instrumentation fail which we intend for
|
||||
// unsupported or unknown instructions.
|
||||
_ => return None,
|
||||
};
|
||||
Some(weight)
|
||||
}
|
||||
|
||||
fn memory_grow_cost(&self) -> Option<rules::MemoryGrowCost> {
|
||||
// We benchmarked the memory.grow instruction with the maximum allowed pages.
|
||||
// The cost for growing is therefore already included in the instruction cost.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::tests::Test;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn print_test_schedule() {
|
||||
let schedule = Schedule::<Test>::default();
|
||||
println!("{:#?}", schedule);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ mod tests {
|
||||
use hex_literal::hex;
|
||||
use sp_runtime::DispatchError;
|
||||
use frame_support::weights::Weight;
|
||||
use assert_matches::assert_matches;
|
||||
use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags, ExecError, ErrorOrigin};
|
||||
|
||||
const GAS_LIMIT: Gas = 10_000_000_000;
|
||||
@@ -645,14 +646,14 @@ mod tests {
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.instantiates,
|
||||
&[InstantiateEntry {
|
||||
code_hash: [0x11; 32].into(),
|
||||
assert_matches!(
|
||||
&mock_ext.instantiates[..],
|
||||
[InstantiateEntry {
|
||||
code_hash,
|
||||
endowment: 3,
|
||||
data: vec![1, 2, 3, 4],
|
||||
gas_left: 9392302058,
|
||||
}]
|
||||
data,
|
||||
gas_left: _,
|
||||
}] if code_hash == &[0x11; 32].into() && data == &vec![1, 2, 3, 4]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1461,7 +1462,7 @@ mod tests {
|
||||
vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00])
|
||||
]);
|
||||
|
||||
assert_eq!(gas_meter.gas_left(), 9834099446);
|
||||
assert!(gas_meter.gas_left() > 0);
|
||||
}
|
||||
|
||||
const CODE_DEPOSIT_EVENT_MAX_TOPICS: &str = r#"
|
||||
|
||||
@@ -24,9 +24,7 @@ use crate::{Schedule, Trait};
|
||||
|
||||
use parity_wasm::elements::{self, Internal, External, MemoryType, Type, ValueType};
|
||||
use pwasm_utils;
|
||||
use pwasm_utils::rules;
|
||||
use sp_std::prelude::*;
|
||||
use sp_runtime::traits::{SaturatedConversion};
|
||||
|
||||
/// Currently, all imported functions must be located inside this module. We might support
|
||||
/// additional modules for versioning later.
|
||||
@@ -101,6 +99,33 @@ impl<'a, T: Trait> ContractModule<'a, T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensure that any `br_table` instruction adheres to its immediate value limit.
|
||||
fn ensure_br_table_size_limit(&self, limit: u32) -> Result<(), &'static str> {
|
||||
let code_section = if let Some(type_section) = self.module.code_section() {
|
||||
type_section
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
for instr in code_section.bodies().iter().flat_map(|body| body.code().elements()) {
|
||||
use parity_wasm::elements::Instruction::BrTable;
|
||||
if let BrTable(table) = instr {
|
||||
if table.table.len() > limit as usize {
|
||||
return Err("BrTable's immediate value is too big.")
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_global_variable_limit(&self, limit: u32) -> Result<(), &'static str> {
|
||||
if let Some(global_section) = self.module.global_section() {
|
||||
if global_section.entries().len() > limit as usize {
|
||||
return Err("module declares too many globals")
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures that no floating point types are in use.
|
||||
fn ensure_no_floating_types(&self) -> Result<(), &'static str> {
|
||||
if let Some(global_section) = self.module.global_section() {
|
||||
@@ -145,15 +170,25 @@ impl<'a, T: Trait> ContractModule<'a, T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inject_gas_metering(self) -> Result<Self, &'static str> {
|
||||
let gas_rules =
|
||||
rules::Set::new(
|
||||
self.schedule.instruction_weights.regular.clone().saturated_into(),
|
||||
Default::default(),
|
||||
)
|
||||
.with_grow_cost(self.schedule.instruction_weights.grow_mem.clone().saturated_into())
|
||||
.with_forbidden_floats();
|
||||
/// Ensure that no function exists that has more parameters than allowed.
|
||||
fn ensure_parameter_limit(&self, limit: u32) -> Result<(), &'static str> {
|
||||
let type_section = if let Some(type_section) = self.module.type_section() {
|
||||
type_section
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
for Type::Function(func) in type_section.types() {
|
||||
if func.params().len() > limit as usize {
|
||||
return Err("Use of a function type with too many parameters.");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inject_gas_metering(self) -> Result<Self, &'static str> {
|
||||
let gas_rules = self.schedule.rules(&self.module);
|
||||
let contract_module = pwasm_utils::inject_gas_counter(
|
||||
self.module,
|
||||
&gas_rules,
|
||||
@@ -167,7 +202,8 @@ impl<'a, T: Trait> ContractModule<'a, T> {
|
||||
|
||||
fn inject_stack_height_metering(self) -> Result<Self, &'static str> {
|
||||
let contract_module =
|
||||
pwasm_utils::stack_height::inject_limiter(self.module, self.schedule.max_stack_height)
|
||||
pwasm_utils::stack_height
|
||||
::inject_limiter(self.module, self.schedule.limits.stack_height)
|
||||
.map_err(|_| "stack height instrumentation failed")?;
|
||||
Ok(ContractModule {
|
||||
module: contract_module,
|
||||
@@ -345,7 +381,7 @@ fn get_memory_limits<T: Trait>(module: Option<&MemoryType>, schedule: &Schedule<
|
||||
"Requested initial number of pages should not exceed the requested maximum",
|
||||
);
|
||||
}
|
||||
(_, Some(maximum)) if maximum > schedule.max_memory_pages => {
|
||||
(_, Some(maximum)) if maximum > schedule.limits.memory_pages => {
|
||||
return Err("Maximum number of pages should not exceed the configured maximum.");
|
||||
}
|
||||
(initial, Some(maximum)) => Ok((initial, maximum)),
|
||||
@@ -381,8 +417,11 @@ pub fn prepare_contract<C: ImportSatisfyCheck, T: Trait>(
|
||||
let mut contract_module = ContractModule::new(original_code, schedule)?;
|
||||
contract_module.scan_exports()?;
|
||||
contract_module.ensure_no_internal_memory()?;
|
||||
contract_module.ensure_table_size_limit(schedule.max_table_size)?;
|
||||
contract_module.ensure_table_size_limit(schedule.limits.table_size)?;
|
||||
contract_module.ensure_global_variable_limit(schedule.limits.globals)?;
|
||||
contract_module.ensure_no_floating_types()?;
|
||||
contract_module.ensure_parameter_limit(schedule.limits.parameters)?;
|
||||
contract_module.ensure_br_table_size_limit(schedule.limits.br_table_size)?;
|
||||
|
||||
// We disallow importing `gas` function here since it is treated as implementation detail.
|
||||
let disallowed_imports = [b"gas".as_ref()];
|
||||
@@ -442,7 +481,7 @@ pub mod benchmarking {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::exec::Ext;
|
||||
use crate::{exec::Ext, Limits};
|
||||
use std::fmt;
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
@@ -470,7 +509,17 @@ mod tests {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let wasm = wat::parse_str($wat).unwrap();
|
||||
let schedule = Schedule::default();
|
||||
let schedule = Schedule {
|
||||
limits: Limits {
|
||||
globals: 3,
|
||||
parameters: 3,
|
||||
memory_pages: 16,
|
||||
table_size: 3,
|
||||
br_table_size: 3,
|
||||
.. Default::default()
|
||||
},
|
||||
.. Default::default()
|
||||
};
|
||||
let r = prepare_contract::<TestEnv, crate::tests::Test>(wasm.as_ref(), &schedule);
|
||||
assert_matches!(r, $($expected)*);
|
||||
}
|
||||
@@ -493,14 +542,66 @@ mod tests {
|
||||
Err("gas instrumentation failed")
|
||||
);
|
||||
|
||||
mod memories {
|
||||
mod functions {
|
||||
use super::*;
|
||||
|
||||
// Tests below assumes that maximum page number is configured to a certain number.
|
||||
#[test]
|
||||
fn assume_memory_size() {
|
||||
assert_eq!(<Schedule<crate::tests::Test>>::default().max_memory_pages, 16);
|
||||
}
|
||||
prepare_test!(param_number_valid,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
(func (export "deploy"))
|
||||
(func (param i32 i32 i32))
|
||||
)
|
||||
"#,
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(param_number_invalid,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
(func (export "deploy"))
|
||||
(func (param i32 i32 i32 i32))
|
||||
(func (param i32))
|
||||
)
|
||||
"#,
|
||||
Err("Use of a function type with too many parameters.")
|
||||
);
|
||||
}
|
||||
|
||||
mod globals {
|
||||
use super::*;
|
||||
|
||||
prepare_test!(global_number_valid,
|
||||
r#"
|
||||
(module
|
||||
(global i64 (i64.const 0))
|
||||
(global i64 (i64.const 0))
|
||||
(global i64 (i64.const 0))
|
||||
(func (export "call"))
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#,
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(global_number_too_high,
|
||||
r#"
|
||||
(module
|
||||
(global i64 (i64.const 0))
|
||||
(global i64 (i64.const 0))
|
||||
(global i64 (i64.const 0))
|
||||
(global i64 (i64.const 0))
|
||||
(func (export "call"))
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#,
|
||||
Err("module declares too many globals")
|
||||
);
|
||||
}
|
||||
|
||||
mod memories {
|
||||
use super::*;
|
||||
|
||||
prepare_test!(memory_with_one_page,
|
||||
r#"
|
||||
@@ -561,6 +662,18 @@ mod tests {
|
||||
Err("Maximum number of pages should be always declared.")
|
||||
);
|
||||
|
||||
prepare_test!(requested_maximum_valid,
|
||||
r#"
|
||||
(module
|
||||
(import "env" "memory" (memory 1 16))
|
||||
|
||||
(func (export "call"))
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#,
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(requested_maximum_exceeds_configured_maximum,
|
||||
r#"
|
||||
(module
|
||||
@@ -625,12 +738,6 @@ mod tests {
|
||||
mod tables {
|
||||
use super::*;
|
||||
|
||||
// Tests below assumes that maximum table size is configured to a certain number.
|
||||
#[test]
|
||||
fn assume_table_size() {
|
||||
assert_eq!(<Schedule<crate::tests::Test>>::default().max_table_size, 16384);
|
||||
}
|
||||
|
||||
prepare_test!(no_tables,
|
||||
r#"
|
||||
(module
|
||||
@@ -644,7 +751,7 @@ mod tests {
|
||||
prepare_test!(table_valid_size,
|
||||
r#"
|
||||
(module
|
||||
(table 10000 funcref)
|
||||
(table 3 funcref)
|
||||
|
||||
(func (export "call"))
|
||||
(func (export "deploy"))
|
||||
@@ -656,13 +763,40 @@ mod tests {
|
||||
prepare_test!(table_too_big,
|
||||
r#"
|
||||
(module
|
||||
(table 20000 funcref)
|
||||
(table 4 funcref)
|
||||
|
||||
(func (export "call"))
|
||||
(func (export "deploy"))
|
||||
)"#,
|
||||
Err("table exceeds maximum size allowed")
|
||||
);
|
||||
|
||||
prepare_test!(br_table_valid_size,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
(func (export "deploy"))
|
||||
(func
|
||||
i32.const 0
|
||||
br_table 0 0 0 0
|
||||
)
|
||||
)
|
||||
"#,
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(br_table_too_big,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
(func (export "deploy"))
|
||||
(func
|
||||
i32.const 0
|
||||
br_table 0 0 0 0 0
|
||||
)
|
||||
)"#,
|
||||
Err("BrTable's immediate value is too big.")
|
||||
);
|
||||
}
|
||||
|
||||
mod imports {
|
||||
|
||||
@@ -279,7 +279,7 @@ pub enum RuntimeToken {
|
||||
}
|
||||
|
||||
impl<T: Trait> Token<T> for RuntimeToken {
|
||||
type Metadata = HostFnWeights;
|
||||
type Metadata = HostFnWeights<T>;
|
||||
|
||||
fn calculate_amount(&self, s: &Self::Metadata) -> Gas {
|
||||
use self::RuntimeToken::*;
|
||||
@@ -340,7 +340,7 @@ impl<T: Trait> Token<T> for RuntimeToken {
|
||||
fn charge_gas<E, Tok>(ctx: &mut Runtime<E>, token: Tok) -> Result<(), sp_sandbox::HostError>
|
||||
where
|
||||
E: Ext,
|
||||
Tok: Token<E::T, Metadata=HostFnWeights>,
|
||||
Tok: Token<E::T, Metadata=HostFnWeights<E::T>>,
|
||||
{
|
||||
match ctx.gas_meter.charge(&ctx.schedule.host_fn_weights, token) {
|
||||
GasMeterResult::Proceed => Ok(()),
|
||||
@@ -1024,8 +1024,7 @@ define_env!(Env, <E: Ext>,
|
||||
// The data is encoded as T::Hash.
|
||||
seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => {
|
||||
charge_gas(ctx, RuntimeToken::Random)?;
|
||||
// The length of a subject can't exceed `max_subject_len`.
|
||||
if subject_len > ctx.schedule.max_subject_len {
|
||||
if subject_len > ctx.schedule.limits.subject_len {
|
||||
return Err(sp_sandbox::HostError);
|
||||
}
|
||||
let subject_buf = read_sandbox_memory(ctx, subject_ptr, subject_len)?;
|
||||
@@ -1157,7 +1156,7 @@ define_env!(Env, <E: Ext>,
|
||||
},
|
||||
|
||||
// Deposit a contract event with the data buffer and optional list of topics. There is a limit
|
||||
// on the maximum number of topics specified by `max_event_topics`.
|
||||
// on the maximum number of topics specified by `event_topics`.
|
||||
//
|
||||
// - topics_ptr - a pointer to the buffer of topics encoded as `Vec<T::Hash>`. The value of this
|
||||
// is ignored if `topics_len` is set to 0. The topics list can't contain duplicates.
|
||||
@@ -1181,8 +1180,8 @@ define_env!(Env, <E: Ext>,
|
||||
_ => read_sandbox_memory_as(ctx, topics_ptr, topics_len)?,
|
||||
};
|
||||
|
||||
// If there are more than `max_event_topics`, then trap.
|
||||
if topics.len() > ctx.schedule.max_event_topics as usize {
|
||||
// If there are more than `event_topics`, then trap.
|
||||
if topics.len() > ctx.schedule.limits.event_topics as usize {
|
||||
return Err(sp_sandbox::HostError);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,341 +0,0 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! This module contains the `WeightInfo` trait and its unsafe implementation on `()`.
|
||||
|
||||
use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
|
||||
|
||||
/// Should be implemented by automatically generated code of the benchmarking system for
|
||||
/// every runtime that makes use of this pallet.
|
||||
/// This trait is also implemented on `()`. The implemention on `()` is **unsafe** and must
|
||||
/// only be used during development. Proper weights can be generated by running the
|
||||
/// pallet_contracts benchmark suite for the runtime in question.
|
||||
pub trait WeightInfo {
|
||||
fn update_schedule() -> Weight;
|
||||
fn put_code(n: u32, ) -> Weight;
|
||||
fn instantiate(n: u32, ) -> Weight;
|
||||
fn call() -> Weight;
|
||||
fn claim_surcharge() -> Weight;
|
||||
fn seal_caller(r: u32, ) -> Weight;
|
||||
fn seal_address(r: u32, ) -> Weight;
|
||||
fn seal_gas_left(r: u32, ) -> Weight;
|
||||
fn seal_balance(r: u32, ) -> Weight;
|
||||
fn seal_value_transferred(r: u32, ) -> Weight;
|
||||
fn seal_minimum_balance(r: u32, ) -> Weight;
|
||||
fn seal_tombstone_deposit(r: u32, ) -> Weight;
|
||||
fn seal_rent_allowance(r: u32, ) -> Weight;
|
||||
fn seal_block_number(r: u32, ) -> Weight;
|
||||
fn seal_now(r: u32, ) -> Weight;
|
||||
fn seal_weight_to_fee(r: u32, ) -> Weight;
|
||||
fn seal_gas(r: u32, ) -> Weight;
|
||||
fn seal_input(r: u32, ) -> Weight;
|
||||
fn seal_input_per_kb(n: u32, ) -> Weight;
|
||||
fn seal_return(r: u32, ) -> Weight;
|
||||
fn seal_return_per_kb(n: u32, ) -> Weight;
|
||||
fn seal_terminate(r: u32, ) -> Weight;
|
||||
fn seal_restore_to(r: u32, ) -> Weight;
|
||||
fn seal_restore_to_per_delta(d: u32, ) -> Weight;
|
||||
fn seal_random(r: u32, ) -> Weight;
|
||||
fn seal_deposit_event(r: u32, ) -> Weight;
|
||||
fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight;
|
||||
fn seal_set_rent_allowance(r: u32, ) -> Weight;
|
||||
fn seal_set_storage(r: u32, ) -> Weight;
|
||||
fn seal_set_storage_per_kb(n: u32, ) -> Weight;
|
||||
fn seal_clear_storage(r: u32, ) -> Weight;
|
||||
fn seal_get_storage(r: u32, ) -> Weight;
|
||||
fn seal_get_storage_per_kb(n: u32, ) -> Weight;
|
||||
fn seal_transfer(r: u32, ) -> Weight;
|
||||
fn seal_call(r: u32, ) -> Weight;
|
||||
fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight;
|
||||
fn seal_instantiate(r: u32, ) -> Weight;
|
||||
fn seal_instantiate_per_input_output_kb(i: u32, o: u32, ) -> Weight;
|
||||
fn seal_hash_sha2_256(r: u32, ) -> Weight;
|
||||
fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight;
|
||||
fn seal_hash_keccak_256(r: u32, ) -> Weight;
|
||||
fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight;
|
||||
fn seal_hash_blake2_256(r: u32, ) -> Weight;
|
||||
fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight;
|
||||
fn seal_hash_blake2_128(r: u32, ) -> Weight;
|
||||
fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight;
|
||||
}
|
||||
|
||||
/// Unsafe implementation that must only be used for development.
|
||||
impl WeightInfo for () {
|
||||
fn update_schedule() -> Weight {
|
||||
(45000000 as Weight)
|
||||
.saturating_add(DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
fn put_code(n: u32, ) -> Weight {
|
||||
(263409000 as Weight)
|
||||
.saturating_add((169269000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn instantiate(n: u32, ) -> Weight {
|
||||
(309311000 as Weight)
|
||||
.saturating_add((1018000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(7 as Weight))
|
||||
.saturating_add(DbWeight::get().writes(4 as Weight))
|
||||
}
|
||||
fn call() -> Weight {
|
||||
(291000000 as Weight)
|
||||
.saturating_add(DbWeight::get().reads(6 as Weight))
|
||||
.saturating_add(DbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn claim_surcharge() -> Weight {
|
||||
(766000000 as Weight)
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(DbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn seal_caller(r: u32, ) -> Weight {
|
||||
(182241000 as Weight)
|
||||
.saturating_add((697428000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_address(r: u32, ) -> Weight {
|
||||
(193846000 as Weight)
|
||||
.saturating_add((695989000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_gas_left(r: u32, ) -> Weight {
|
||||
(166031000 as Weight)
|
||||
.saturating_add((702533000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_balance(r: u32, ) -> Weight {
|
||||
(251892000 as Weight)
|
||||
.saturating_add((1392900000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(5 as Weight))
|
||||
}
|
||||
fn seal_value_transferred(r: u32, ) -> Weight {
|
||||
(178472000 as Weight)
|
||||
.saturating_add((694921000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_minimum_balance(r: u32, ) -> Weight {
|
||||
(191301000 as Weight)
|
||||
.saturating_add((697871000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_tombstone_deposit(r: u32, ) -> Weight {
|
||||
(241315000 as Weight)
|
||||
.saturating_add((686403000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_rent_allowance(r: u32, ) -> Weight {
|
||||
(104958000 as Weight)
|
||||
.saturating_add((1459573000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_block_number(r: u32, ) -> Weight {
|
||||
(174140000 as Weight)
|
||||
.saturating_add((698152000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_now(r: u32, ) -> Weight {
|
||||
(203157000 as Weight)
|
||||
.saturating_add((713595000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_weight_to_fee(r: u32, ) -> Weight {
|
||||
(178413000 as Weight)
|
||||
.saturating_add((1071275000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(5 as Weight))
|
||||
}
|
||||
fn seal_gas(r: u32, ) -> Weight {
|
||||
(171395000 as Weight)
|
||||
.saturating_add((371653000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_input(r: u32, ) -> Weight {
|
||||
(184462000 as Weight)
|
||||
.saturating_add((10538000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_input_per_kb(n: u32, ) -> Weight {
|
||||
(194668000 as Weight)
|
||||
.saturating_add((301000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_return(r: u32, ) -> Weight {
|
||||
(175538000 as Weight)
|
||||
.saturating_add((7462000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_return_per_kb(n: u32, ) -> Weight {
|
||||
(189759000 as Weight)
|
||||
.saturating_add((754000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_terminate(r: u32, ) -> Weight {
|
||||
(184385000 as Weight)
|
||||
.saturating_add((542615000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(DbWeight::get().writes((3 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_restore_to(r: u32, ) -> Weight {
|
||||
(380385000 as Weight)
|
||||
.saturating_add((160308000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((3 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(DbWeight::get().writes((4 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_restore_to_per_delta(d: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((4786197000 as Weight).saturating_mul(d as Weight))
|
||||
.saturating_add(DbWeight::get().reads(7 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((100 as Weight).saturating_mul(d as Weight)))
|
||||
.saturating_add(DbWeight::get().writes(5 as Weight))
|
||||
.saturating_add(DbWeight::get().writes((100 as Weight).saturating_mul(d as Weight)))
|
||||
}
|
||||
fn seal_random(r: u32, ) -> Weight {
|
||||
(187944000 as Weight)
|
||||
.saturating_add((1592530000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(5 as Weight))
|
||||
}
|
||||
fn seal_deposit_event(r: u32, ) -> Weight {
|
||||
(126517000 as Weight)
|
||||
.saturating_add((2346945000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32, ) -> Weight {
|
||||
(2953428000 as Weight)
|
||||
.saturating_add((1117651000 as Weight).saturating_mul(t as Weight))
|
||||
.saturating_add((299890000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((100 as Weight).saturating_mul(t as Weight)))
|
||||
.saturating_add(DbWeight::get().writes((100 as Weight).saturating_mul(t as Weight)))
|
||||
}
|
||||
fn seal_set_rent_allowance(r: u32, ) -> Weight {
|
||||
(142094000 as Weight)
|
||||
.saturating_add((1726665000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
fn seal_set_storage(r: u32, ) -> Weight {
|
||||
(4091409000 as Weight)
|
||||
.saturating_add((26440116000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_set_storage_per_kb(n: u32, ) -> Weight {
|
||||
(3683270000 as Weight)
|
||||
.saturating_add((233826000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn seal_clear_storage(r: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((7152747000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_get_storage(r: u32, ) -> Weight {
|
||||
(19007000 as Weight)
|
||||
.saturating_add((1774675000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_get_storage_per_kb(n: u32, ) -> Weight {
|
||||
(1477332000 as Weight)
|
||||
.saturating_add((176601000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(5 as Weight))
|
||||
}
|
||||
fn seal_transfer(r: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((10274385000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_call(r: u32, ) -> Weight {
|
||||
(241916000 as Weight)
|
||||
.saturating_add((14633108000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight {
|
||||
(15664107000 as Weight)
|
||||
.saturating_add((8529984000 as Weight).saturating_mul(t as Weight))
|
||||
.saturating_add((52860000 as Weight).saturating_mul(i as Weight))
|
||||
.saturating_add((81175000 as Weight).saturating_mul(o as Weight))
|
||||
.saturating_add(DbWeight::get().reads(105 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((101 as Weight).saturating_mul(t as Weight)))
|
||||
.saturating_add(DbWeight::get().writes((101 as Weight).saturating_mul(t as Weight)))
|
||||
}
|
||||
fn seal_instantiate(r: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((32247550000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(DbWeight::get().reads((300 as Weight).saturating_mul(r as Weight)))
|
||||
.saturating_add(DbWeight::get().writes(1 as Weight))
|
||||
.saturating_add(DbWeight::get().writes((200 as Weight).saturating_mul(r as Weight)))
|
||||
}
|
||||
fn seal_instantiate_per_input_output_kb(i: u32, o: u32, ) -> Weight {
|
||||
(34376003000 as Weight)
|
||||
.saturating_add((151350000 as Weight).saturating_mul(i as Weight))
|
||||
.saturating_add((82364000 as Weight).saturating_mul(o as Weight))
|
||||
.saturating_add(DbWeight::get().reads(207 as Weight))
|
||||
.saturating_add(DbWeight::get().writes(202 as Weight))
|
||||
}
|
||||
fn seal_hash_sha2_256(r: u32, ) -> Weight {
|
||||
(164203000 as Weight)
|
||||
.saturating_add((565206000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_sha2_256_per_kb(n: u32, ) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((330063000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_keccak_256(r: u32, ) -> Weight {
|
||||
(219038000 as Weight)
|
||||
.saturating_add((567992000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_keccak_256_per_kb(n: u32, ) -> Weight {
|
||||
(434654000 as Weight)
|
||||
.saturating_add((271134000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_blake2_256(r: u32, ) -> Weight {
|
||||
(116374000 as Weight)
|
||||
.saturating_add((566612000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_blake2_256_per_kb(n: u32, ) -> Weight {
|
||||
(756028000 as Weight)
|
||||
.saturating_add((150363000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_blake2_128(r: u32, ) -> Weight {
|
||||
(150126000 as Weight)
|
||||
.saturating_add((564827000 as Weight).saturating_mul(r as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
fn seal_hash_blake2_128_per_kb(n: u32, ) -> Weight {
|
||||
(1021689000 as Weight)
|
||||
.saturating_add((149452000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(DbWeight::get().reads(4 as Weight))
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user