contracts: Use proof_size from benchmarks (#13268)

* Avoid reading contract code when it is supplied in the extrinsic

* Remove custom proof size injection from schedule

* Set benchmarks pov_mode to Measure

* Reduce overestimation of code size on re-instrument

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* Do not override proof size from benchmark

* Do not charge proof size for basic block

* Incrase gas limit for tests

* Fix deletion queue to also use `proof_size`

* Fix tests

* Update frame/contracts/src/schedule.rs

Co-authored-by: Cyrill Leutwiler <bigcyrill@hotmail.com>

* Fix wrong schedule macro invocations

* Remove stale docs

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* Handle zero components

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* Fix instruction weight

---------

Co-authored-by: command-bot <>
Co-authored-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
Alexander Theißen
2023-02-15 22:56:50 +01:00
committed by GitHub
parent 3c0d8fe149
commit 1c04ab0f6f
8 changed files with 2173 additions and 2021 deletions
@@ -50,53 +50,61 @@ use sp_std::vec;
/// under the specified `code_hash`.
pub fn store<T: Config>(mut module: PrefabWasmModule<T>, instantiated: bool) -> DispatchResult {
let code_hash = sp_std::mem::take(&mut module.code_hash);
<CodeStorage<T>>::mutate(&code_hash, |existing| match existing {
Some(existing) => {
// We instrument any uploaded contract anyways. We might as well store it to save
// a potential re-instrumentation later.
existing.code = module.code;
existing.instruction_weights_version = module.instruction_weights_version;
// When the code was merely uploaded but not instantiated we can skip this.
if instantiated {
<OwnerInfoOf<T>>::mutate(&code_hash, |owner_info| {
if let Some(owner_info) = owner_info {
owner_info.refcount = owner_info.refcount.checked_add(1).expect(
"
refcount is 64bit. Generating this overflow would require to store
_at least_ 18 exabyte of data assuming that a contract consumes only
one byte of data. Any node would run out of storage space before hitting
this overflow.
qed
",
);
}
})
}
Ok(())
},
None => {
let orig_code = module.original_code.take().expect(
"
<OwnerInfoOf<T>>::mutate(&code_hash, |owner_info| {
match owner_info {
// Instantiate existing contract.
//
// No need to update the `CodeStorage` as any re-instrumentation eagerly saves
// the re-instrumented code.
Some(owner_info) if instantiated => {
owner_info.refcount = owner_info.refcount.checked_add(1).expect(
"
refcount is 64bit. Generating this overflow would require to store
_at least_ 18 exabyte of data assuming that a contract consumes only
one byte of data. Any node would run out of storage space before hitting
this overflow.
qed
",
);
Ok(())
},
// Re-upload existing contract without executing it.
//
// We are careful here to just overwrite the code to not include it into the PoV.
// We do this because the uploaded code was instrumented with the latest schedule
// and hence we persist those changes. Otherwise the next execution will pay again
// for the instrumentation.
Some(_) => {
<CodeStorage<T>>::insert(&code_hash, module);
Ok(())
},
// Upload a new contract.
//
// We need to write all data structures and collect the deposit.
None => {
let orig_code = module.original_code.take().expect(
"
If an executable isn't in storage it was uploaded.
If it was uploaded the original code must exist. qed
",
);
let mut owner_info = module.owner_info.take().expect(
"If an executable isn't in storage it was uploaded.
);
let mut new_owner_info = module.owner_info.take().expect(
"If an executable isn't in storage it was uploaded.
If it was uploaded the owner info was generated and attached. qed
",
);
// This `None` case happens only in freshly uploaded modules. This means that
// the `owner` is always the origin of the current transaction.
T::Currency::reserve(&owner_info.owner, owner_info.deposit)
.map_err(|_| <Error<T>>::StorageDepositNotEnoughFunds)?;
owner_info.refcount = if instantiated { 1 } else { 0 };
<PristineCode<T>>::insert(&code_hash, orig_code);
<OwnerInfoOf<T>>::insert(&code_hash, owner_info);
*existing = Some(module);
<Pallet<T>>::deposit_event(vec![code_hash], Event::CodeStored { code_hash });
Ok(())
},
);
// This `None` case happens only in freshly uploaded modules. This means that
// the `owner` is always the origin of the current transaction.
T::Currency::reserve(&new_owner_info.owner, new_owner_info.deposit)
.map_err(|_| <Error<T>>::StorageDepositNotEnoughFunds)?;
new_owner_info.refcount = if instantiated { 1 } else { 0 };
<PristineCode<T>>::insert(&code_hash, orig_code);
<CodeStorage<T>>::insert(&code_hash, module);
*owner_info = Some(new_owner_info);
<Pallet<T>>::deposit_event(vec![code_hash], Event::CodeStored { code_hash });
Ok(())
},
}
})
}
@@ -162,15 +170,16 @@ pub fn load<T: Config>(
let charged = gas_meter.charge(CodeToken::Load(max_code_len))?;
let mut prefab_module = <CodeStorage<T>>::get(code_hash).ok_or(Error::<T>::CodeNotFound)?;
gas_meter.adjust_gas(charged, CodeToken::Load(prefab_module.code.len() as u32));
let instrumented_code_len = prefab_module.code.len() as u32;
gas_meter.adjust_gas(charged, CodeToken::Load(instrumented_code_len));
prefab_module.code_hash = code_hash;
if prefab_module.instruction_weights_version < schedule.instruction_weights.version {
// The instruction weights have changed.
// We need to re-instrument the code with the new instruction weights.
let charged = gas_meter.charge(CodeToken::Reinstrument(max_code_len))?;
let code_size = reinstrument(&mut prefab_module, schedule)?;
gas_meter.adjust_gas(charged, CodeToken::Reinstrument(code_size));
let charged = gas_meter.charge(CodeToken::Reinstrument(instrumented_code_len))?;
let orig_code_len = reinstrument(&mut prefab_module, schedule)?;
gas_meter.adjust_gas(charged, CodeToken::Reinstrument(orig_code_len));
}
Ok(prefab_module)
@@ -224,8 +233,7 @@ impl<T: Config> Token<T> for CodeToken {
match *self {
Reinstrument(len) => T::WeightInfo::reinstrument(len),
Load(len) => T::WeightInfo::call_with_code_per_byte(len)
.saturating_sub(T::WeightInfo::call_with_code_per_byte(0))
.set_proof_size(len.into()),
.saturating_sub(T::WeightInfo::call_with_code_per_byte(0)),
}
}
}
+2 -2
View File
@@ -42,7 +42,7 @@ pub use crate::wasm::{
use crate::{
exec::{ExecResult, Executable, ExportedFunction, Ext},
gas::GasMeter,
AccountIdOf, BalanceOf, CodeHash, CodeStorage, CodeVec, Config, Error, RelaxedCodeVec,
AccountIdOf, BalanceOf, CodeHash, CodeVec, Config, Error, OwnerInfoOf, RelaxedCodeVec,
Schedule,
};
use codec::{Decode, Encode, MaxEncodedLen};
@@ -191,7 +191,7 @@ impl<T: Config> PrefabWasmModule<T> {
/// Returns `0` if the module is already in storage and hence no deposit will
/// be charged when storing it.
pub fn open_deposit(&self) -> BalanceOf<T> {
if <CodeStorage<T>>::contains_key(&self.code_hash) {
if <OwnerInfoOf<T>>::contains_key(&self.code_hash) {
0u32.into()
} else {
// Only already in-storage contracts have their `owner_info` set to `None`.