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:
Alexander Theißen
2020-11-09 15:32:14 +01:00
committed by GitHub
parent 9704c204e6
commit 51c67fe881
17 changed files with 3152 additions and 843 deletions
+25 -2
View File
@@ -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",
+1 -4
View File
@@ -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))
}
}
+9 -1
View File
@@ -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,
}
}
}
+4 -4
View File
@@ -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));
+359 -63
View File
@@ -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);
}
}
+9 -8
View File
@@ -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#"
+163 -29
View File
@@ -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