contracts: Consider contract size in weights (#8086)

* contracts: Consider contract size in weights

* Bump spec version

* Whitespace fix

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* Correct pre-charged code weight even in the error case

* Use the instrumented code size in weight calculation

* Charge the cost of re-instrumentation from the gas meter

* Fix benchmark

* 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

* Better documentation of return types

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
Co-authored-by: Parity Benchmarking Bot <admin@parity.io>
This commit is contained in:
Alexander Theißen
2021-02-22 09:52:58 +01:00
committed by GitHub
parent fbd3148bba
commit 84071d6d49
13 changed files with 1267 additions and 843 deletions
+34 -29
View File
@@ -15,14 +15,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::Config;
use crate::{Config, Error};
use sp_std::marker::PhantomData;
use sp_runtime::traits::Zero;
use frame_support::{
dispatch::{DispatchResultWithPostInfo, PostDispatchInfo, DispatchErrorWithPostInfo},
dispatch::{
DispatchResultWithPostInfo, PostDispatchInfo, DispatchErrorWithPostInfo, DispatchError,
},
weights::Weight,
};
use pallet_contracts_primitives::ExecError;
use sp_core::crypto::UncheckedFrom;
#[cfg(test)]
use std::{any::Any, fmt::Debug};
@@ -30,22 +33,6 @@ use std::{any::Any, fmt::Debug};
// Gas is essentially the same as weight. It is a 1 to 1 correspondence.
pub type Gas = Weight;
#[must_use]
#[derive(Debug, PartialEq, Eq)]
pub enum GasMeterResult {
Proceed(ChargedAmount),
OutOfGas,
}
impl GasMeterResult {
pub fn is_out_of_gas(&self) -> bool {
match *self {
GasMeterResult::OutOfGas => true,
GasMeterResult::Proceed(_) => false,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ChargedAmount(Gas);
@@ -103,7 +90,11 @@ pub struct GasMeter<T: Config> {
#[cfg(test)]
tokens: Vec<ErasedToken>,
}
impl<T: Config> GasMeter<T> {
impl<T: Config> GasMeter<T>
where
T::AccountId: UncheckedFrom<<T as frame_system::Config>::Hash> + AsRef<[u8]>
{
pub fn new(gas_limit: Gas) -> Self {
GasMeter {
gas_limit,
@@ -128,7 +119,7 @@ impl<T: Config> GasMeter<T> {
&mut self,
metadata: &Tok::Metadata,
token: Tok,
) -> GasMeterResult {
) -> Result<ChargedAmount, DispatchError> {
#[cfg(test)]
{
// Unconditionally add the token to the storage.
@@ -149,11 +140,25 @@ impl<T: Config> GasMeter<T> {
self.gas_left = new_value.unwrap_or_else(Zero::zero);
match new_value {
Some(_) => GasMeterResult::Proceed(ChargedAmount(amount)),
None => GasMeterResult::OutOfGas,
Some(_) => Ok(ChargedAmount(amount)),
None => Err(Error::<T>::OutOfGas.into()),
}
}
/// Adjust a previously charged amount down to its actual amount.
///
/// This is when a maximum a priori amount was charged and then should be partially
/// refunded to match the actual amount.
pub fn adjust_gas<Tok: Token<T>>(
&mut self,
charged_amount: ChargedAmount,
metadata: &Tok::Metadata,
token: Tok,
) {
let adjustment = charged_amount.0.saturating_sub(token.calculate_amount(metadata));
self.gas_left = self.gas_left.saturating_add(adjustment).min(self.gas_limit);
}
/// Refund previously charged gas back to the gas meter.
///
/// This can be used if a gas worst case estimation must be charged before
@@ -304,7 +309,7 @@ mod tests {
let result = gas_meter
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10));
assert!(!result.is_out_of_gas());
assert!(!result.is_err());
assert_eq!(gas_meter.gas_left(), 49_970);
}
@@ -312,10 +317,10 @@ mod tests {
#[test]
fn tracing() {
let mut gas_meter = GasMeter::<Test>::new(50000);
assert!(!gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas());
assert!(!gas_meter.charge(&(), SimpleToken(1)).is_err());
assert!(!gas_meter
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10))
.is_out_of_gas());
.is_err());
let mut tokens = gas_meter.tokens()[0..2].iter();
match_tokens!(tokens, SimpleToken(1), MultiplierToken(10),);
@@ -325,7 +330,7 @@ mod tests {
#[test]
fn refuse_to_execute_anything_if_zero() {
let mut gas_meter = GasMeter::<Test>::new(0);
assert!(gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas());
assert!(gas_meter.charge(&(), SimpleToken(1)).is_err());
}
// Make sure that if the gas meter is charged by exceeding amount then not only an error
@@ -338,10 +343,10 @@ mod tests {
let mut gas_meter = GasMeter::<Test>::new(200);
// The first charge is should lead to OOG.
assert!(gas_meter.charge(&(), SimpleToken(300)).is_out_of_gas());
assert!(gas_meter.charge(&(), SimpleToken(300)).is_err());
// The gas meter is emptied at this moment, so this should also fail.
assert!(gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas());
assert!(gas_meter.charge(&(), SimpleToken(1)).is_err());
}
@@ -350,6 +355,6 @@ mod tests {
#[test]
fn charge_exact_amount() {
let mut gas_meter = GasMeter::<Test>::new(25);
assert!(!gas_meter.charge(&(), SimpleToken(25)).is_out_of_gas());
assert!(!gas_meter.charge(&(), SimpleToken(25)).is_err());
}
}