Contracts: Refactor API to use WeightMeter (#2943)

Update the Contracts API to use `WeightMeter`, as it simplifies the code
and makes it easier to reason about, rather than taking a mutable weight
or returning a tuple with the weight consumed

---------

Co-authored-by: Alexander Theißen <alex.theissen@me.com>
This commit is contained in:
PG Herveou
2024-04-09 12:22:54 +02:00
committed by GitHub
parent 10ed76437f
commit b6231c79ca
12 changed files with 177 additions and 152 deletions
+17 -19
View File
@@ -28,7 +28,7 @@ use crate::{
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
storage::child::{self, ChildInfo},
weights::Weight,
weights::{Weight, WeightMeter},
CloneNoBound, DefaultNoBound,
};
use scale_info::TypeInfo;
@@ -279,14 +279,15 @@ impl<T: Config> ContractInfo<T> {
/// Calculates the weight that is necessary to remove one key from the trie and how many
/// of those keys can be deleted from the deletion queue given the supplied weight limit.
pub fn deletion_budget(weight_limit: Weight) -> (Weight, u32) {
pub fn deletion_budget(meter: &WeightMeter) -> (Weight, u32) {
let base_weight = T::WeightInfo::on_process_deletion_queue_batch();
let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) -
T::WeightInfo::on_initialize_per_trie_key(0);
// `weight_per_key` being zero makes no sense and would constitute a failure to
// benchmark properly. We opt for not removing any keys at all in this case.
let key_budget = weight_limit
let key_budget = meter
.limit()
.saturating_sub(base_weight)
.checked_div_per_component(&weight_per_key)
.unwrap_or(0) as u32;
@@ -295,24 +296,18 @@ impl<T: Config> ContractInfo<T> {
}
/// Delete as many items from the deletion queue possible within the supplied weight limit.
///
/// It returns the amount of weight used for that task.
pub fn process_deletion_queue_batch(weight_limit: Weight) -> Weight {
pub fn process_deletion_queue_batch(meter: &mut WeightMeter) {
if meter.try_consume(T::WeightInfo::on_process_deletion_queue_batch()).is_err() {
return
};
let mut queue = <DeletionQueueManager<T>>::load();
if queue.is_empty() {
return Weight::zero()
}
let (weight_per_key, mut remaining_key_budget) = Self::deletion_budget(weight_limit);
// We want to check whether we have enough weight to decode the queue before
// proceeding. Too little weight for decoding might happen during runtime upgrades
// which consume the whole block before the other `on_initialize` blocks are called.
if remaining_key_budget == 0 {
return weight_limit
return;
}
let (weight_per_key, budget) = Self::deletion_budget(&meter);
let mut remaining_key_budget = budget;
while remaining_key_budget > 0 {
let Some(entry) = queue.next() else { break };
@@ -324,7 +319,10 @@ impl<T: Config> ContractInfo<T> {
match outcome {
// This happens when our budget wasn't large enough to remove all keys.
KillStorageResult::SomeRemaining(_) => return weight_limit,
KillStorageResult::SomeRemaining(keys_removed) => {
remaining_key_budget.saturating_reduce(keys_removed);
break
},
KillStorageResult::AllRemoved(keys_removed) => {
entry.remove();
// charge at least one key even if none were removed.
@@ -333,7 +331,7 @@ impl<T: Config> ContractInfo<T> {
};
}
weight_limit.saturating_sub(weight_per_key.saturating_mul(u64::from(remaining_key_budget)))
meter.consume(weight_per_key.saturating_mul(u64::from(budget - remaining_key_budget)))
}
/// Returns the code hash of the contract specified by `account` ID.