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
@@ -30,9 +30,13 @@
use crate::{
CodeHash, CodeStorage, PristineCode, Schedule, Config, Error,
wasm::{prepare, PrefabWasmModule}, Module as Contracts, RawEvent,
gas::{Gas, GasMeter, Token},
weights::WeightInfo,
};
use sp_core::crypto::UncheckedFrom;
use frame_support::{StorageMap, dispatch::{DispatchError, DispatchResult}};
use frame_support::{StorageMap, dispatch::DispatchError};
#[cfg(feature = "runtime-benchmarks")]
pub use self::private::reinstrument as reinstrument;
/// Put the instrumented module in storage.
///
@@ -77,14 +81,14 @@ where
}
/// Increment the refcount of a code in-storage by one.
pub fn increment_refcount<T: Config>(code_hash: CodeHash<T>) -> DispatchResult
pub fn increment_refcount<T: Config>(code_hash: CodeHash<T>) -> Result<u32, DispatchError>
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
{
<CodeStorage<T>>::mutate(code_hash, |existing| {
if let Some(module) = existing {
increment_64(&mut module.refcount);
Ok(())
Ok(module.original_code_len)
} else {
Err(Error::<T>::CodeNotFound.into())
}
@@ -92,19 +96,23 @@ where
}
/// Decrement the refcount of a code in-storage by one and remove the code when it drops to zero.
pub fn decrement_refcount<T: Config>(code_hash: CodeHash<T>)
pub fn decrement_refcount<T: Config>(code_hash: CodeHash<T>) -> u32
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
{
<CodeStorage<T>>::mutate_exists(code_hash, |existing| {
if let Some(module) = existing {
let code_len = module.original_code_len;
module.refcount = module.refcount.saturating_sub(1);
if module.refcount == 0 {
*existing = None;
finish_removal::<T>(code_hash);
}
code_len
} else {
0
}
});
})
}
/// Load code with the given code hash.
@@ -114,31 +122,48 @@ where
/// re-instrumentation and update the cache in the storage.
pub fn load<T: Config>(
code_hash: CodeHash<T>,
schedule: Option<&Schedule<T>>,
reinstrument: Option<(&Schedule<T>, &mut GasMeter<T>)>,
) -> Result<PrefabWasmModule<T>, DispatchError>
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
{
let mut prefab_module = <CodeStorage<T>>::get(code_hash)
.ok_or_else(|| Error::<T>::CodeNotFound)?;
prefab_module.code_hash = code_hash;
if let Some(schedule) = schedule {
if let Some((schedule, gas_meter)) = reinstrument {
if prefab_module.schedule_version < schedule.version {
// The current schedule version is greater than the version of the one cached
// in the storage.
//
// We need to re-instrument the code with the latest schedule here.
let original_code = <PristineCode<T>>::get(code_hash)
.ok_or_else(|| Error::<T>::CodeNotFound)?;
prefab_module.code = prepare::reinstrument_contract::<T>(original_code, schedule)?;
prefab_module.schedule_version = schedule.version;
<CodeStorage<T>>::insert(&code_hash, &prefab_module);
gas_meter.charge(&(), InstrumentToken(prefab_module.original_code_len))?;
private::reinstrument(&mut prefab_module, schedule)?;
}
}
prefab_module.code_hash = code_hash;
Ok(prefab_module)
}
mod private {
use super::*;
/// Instruments the passed prefab wasm module with the supplied schedule.
pub fn reinstrument<T: Config>(
prefab_module: &mut PrefabWasmModule<T>,
schedule: &Schedule<T>,
) -> Result<(), DispatchError>
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
{
let original_code = <PristineCode<T>>::get(&prefab_module.code_hash)
.ok_or_else(|| Error::<T>::CodeNotFound)?;
prefab_module.code = prepare::reinstrument_contract::<T>(original_code, schedule)?;
prefab_module.schedule_version = schedule.version;
<CodeStorage<T>>::insert(&prefab_module.code_hash, &*prefab_module);
Ok(())
}
}
/// Finish removal of a code by deleting the pristine code and emitting an event.
fn finish_removal<T: Config>(code_hash: CodeHash<T>)
where
@@ -161,3 +186,17 @@ fn increment_64(refcount: &mut u64) {
qed
");
}
/// Token to be supplied to the gas meter which charges the weight needed for reinstrumenting
/// a contract of the specified size in bytes.
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
#[derive(Clone, Copy)]
struct InstrumentToken(u32);
impl<T: Config> Token<T> for InstrumentToken {
type Metadata = ();
fn calculate_amount(&self, _metadata: &Self::Metadata) -> Gas {
T::WeightInfo::instrument(self.0 / 1024)
}
}