Glutton pallet (#12833)

* Pov-Limit pallet

* use Perbill & fixes

* fixes

* reads & writes

* update docs

* tests

* calculate weight

* fmt

* benchmark

* logic fix

* naming fix

* caclulate computation weight limit

* make the Hasher generic

* make the code compile

* generate weight

* fix on_idle

* fix

* fix

* make reading generic

* fix?

* fixes

* remove warning

* fix

* hasher fix :D

* change value

* test

* actual weight and expected weight are the same

* update

* fix

* add events

* remove useless line

* using actual hashing algorithm

* better readability

* fix nits

* Update

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* add migration

* hardcode proof_size in weights.rs

* format

* fixes

* Fix weight

* docs

* fix

* Update frame/pov-limit/src/lib.rs

Co-authored-by: Bastian Köcher <git@kchr.de>

* Update frame/pov-limit/src/lib.rs

Co-authored-by: Bastian Köcher <git@kchr.de>

* empty on_ilde & update weight.rs

* remove migration & fix benchmark

* remove migration from migrations

* initialize_pallet

* use blake2

* Update frame/pov-limit/Cargo.toml

Co-authored-by: Bastian Köcher <git@kchr.de>

* Update frame/pov-limit/src/lib.rs

Co-authored-by: Bastian Köcher <git@kchr.de>

* Update frame/pov-limit/Cargo.toml

Co-authored-by: Bastian Köcher <git@kchr.de>

* rename pallet

* make the wasters non-generic

* rename to glutton

* fix

* small fixes & run benchmark

* increase left over ref-time in tests

* Update frame/glutton/src/lib.rs

Co-authored-by: Bastian Köcher <git@kchr.de>

* or values

* fix

* generate weight

* Update frame/glutton/Cargo.toml

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fix nits

* Adjustments

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fixes

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fix

* Update weights

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Tweak constants

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update weights

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* remove genesis config

* passing tests

* More precise results

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* update node-executor test

* Calculate number of iterations in advance

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* increase criteria

* Final fixes

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/glutton/src/tests.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/glutton/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/glutton/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* fix typos

* Update frame/glutton/src/lib.rs

Co-authored-by: Bastian Köcher <git@kchr.de>

* Update frame/glutton/src/lib.rs

Co-authored-by: Bastian Köcher <git@kchr.de>

* expand/shrink

* fmt

* Revert "Update frame/glutton/src/lib.rs"

This reverts commit 98a237afd27de3deb15ba381871e12f71a9d71b1.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use CountedStorageMap

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add benchmark

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use manual map counter

Something with the R/W count in the benchmarking result did not add
up. Need to investigate but for now just using a manual counter.

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Use new template

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Doc+typos

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

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

* Apply suggestions from code review

Co-authored-by: Koute <koute@users.noreply.github.com>

* Add minimal README

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

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

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: command-bot <>
Co-authored-by: Koute <koute@users.noreply.github.com>
This commit is contained in:
Sergej Sakac
2023-02-21 23:39:27 +01:00
committed by GitHub
parent fbba510636
commit d5beaa5542
12 changed files with 1113 additions and 0 deletions
@@ -0,0 +1,98 @@
// This file is part of Substrate.
// Copyright (C) 2022 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.
//! Glutton pallet benchmarking.
//!
//! Has to be compiled and run twice to calibrate on new hardware.
#[cfg(feature = "runtime-benchmarks")]
use super::*;
use frame_benchmarking::benchmarks;
use frame_support::{pallet_prelude::*, weights::constants::*};
use frame_system::RawOrigin as SystemOrigin;
use crate::Pallet as Glutton;
use frame_system::Pallet as System;
benchmarks! {
initialize_pallet_grow {
let n in 0 .. 1_000;
}: {
Glutton::<T>::initialize_pallet(SystemOrigin::Root.into(), n, None).unwrap()
} verify {
assert_eq!(TrashDataCount::<T>::get(), n);
}
initialize_pallet_shrink {
let n in 0 .. 1_000;
Glutton::<T>::initialize_pallet(SystemOrigin::Root.into(), n, None).unwrap();
}: {
Glutton::<T>::initialize_pallet(SystemOrigin::Root.into(), 0, Some(n)).unwrap()
} verify {
assert_eq!(TrashDataCount::<T>::get(), 0);
}
waste_ref_time_iter {
let i in 0..100_000;
}: {
Glutton::<T>::waste_ref_time_iter(vec![0u8; 64], i);
}
waste_proof_size_some {
let i in 0..5_000;
(0..5000).for_each(|i| TrashData::<T>::insert(i, [i as u8; 1024]));
}: {
(0..i).for_each(|i| {
TrashData::<T>::get(i);
})
}
// For manual verification only.
on_idle_high_proof_waste {
(0..5000).for_each(|i| TrashData::<T>::insert(i, [i as u8; 1024]));
let _ = Glutton::<T>::set_compute(SystemOrigin::Root.into(), Perbill::from_percent(100));
let _ = Glutton::<T>::set_storage(SystemOrigin::Root.into(), Perbill::from_percent(100));
}: {
let weight = Glutton::<T>::on_idle(System::<T>::block_number(), Weight::from_parts(WEIGHT_REF_TIME_PER_MILLIS * 100, WEIGHT_PROOF_SIZE_PER_MB * 5));
}
// For manual verification only.
on_idle_low_proof_waste {
(0..5000).for_each(|i| TrashData::<T>::insert(i, [i as u8; 1024]));
let _ = Glutton::<T>::set_compute(SystemOrigin::Root.into(), Perbill::from_percent(100));
let _ = Glutton::<T>::set_storage(SystemOrigin::Root.into(), Perbill::from_percent(100));
}: {
let weight = Glutton::<T>::on_idle(System::<T>::block_number(), Weight::from_parts(WEIGHT_REF_TIME_PER_MILLIS * 100, WEIGHT_PROOF_SIZE_PER_KB * 20));
}
empty_on_idle {
}: {
// Enough weight do do nothing.
Glutton::<T>::on_idle(System::<T>::block_number(), T::WeightInfo::empty_on_idle());
}
set_compute {
}: _(SystemOrigin::Root, Perbill::from_percent(50))
set_storage {
}: _(SystemOrigin::Root, Perbill::from_percent(50))
impl_benchmark_test_suite!(Glutton, crate::mock::new_test_ext(), crate::mock::Test);
}
+292
View File
@@ -0,0 +1,292 @@
// This file is part of Substrate.
// Copyright (C) 2022 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.
//! # Glutton Pallet
//!
//! Pallet that consumes `ref_time` and `proof_size` of a block. Based on the
//! `Compute` and `Storage` parameters the pallet consumes the adequate amount
//! of weight.
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub mod weights;
use blake2::{Blake2b512, Digest};
use frame_support::{pallet_prelude::*, weights::WeightMeter};
use frame_system::pallet_prelude::*;
use sp_runtime::{traits::Zero, Perbill};
use sp_std::{vec, vec::Vec};
pub use pallet::*;
pub use weights::WeightInfo;
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Weight information for this pallet.
type WeightInfo: WeightInfo;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event {
/// The pallet has been (re)initialized by root.
PalletInitialized { reinit: bool },
/// The computation limit has been updated by root.
ComputationLimitSet { compute: Perbill },
/// The storage limit has been updated by root.
StorageLimitSet { storage: Perbill },
}
#[pallet::error]
pub enum Error<T> {
/// The pallet was already initialized.
///
/// Set `witness_count` to `Some` to bypass this error.
AlreadyInitialized,
}
/// Storage value used to specify what percentage of the left over `ref_time`
/// to consume during `on_idle`.
#[pallet::storage]
pub(crate) type Compute<T: Config> = StorageValue<_, Perbill, ValueQuery>;
/// Storage value used the specify what percentage of left over `proof_size`
/// to consume during `on_idle`.
#[pallet::storage]
pub(crate) type Storage<T: Config> = StorageValue<_, Perbill, ValueQuery>;
/// Storage map used for wasting proof size.
///
/// It contains no meaningful data - hence the name "Trash". The maximal number of entries is
/// set to 65k, which is just below the next jump at 16^4. This is important to reduce the proof
/// size benchmarking overestimate. The assumption here is that we won't have more than 65k *
/// 1KiB = 65MiB of proof size wasting in practice. However, this limit is not enforced, so the
/// pallet would also work out of the box with more entries, but its benchmarked proof weight
/// would possibly be underestimated in that case.
#[pallet::storage]
pub(super) type TrashData<T: Config> = StorageMap<
Hasher = Twox64Concat,
Key = u32,
Value = [u8; 1024],
QueryKind = OptionQuery,
MaxValues = ConstU32<65_000>,
>;
/// The current number of entries in `TrashData`.
#[pallet::storage]
pub(crate) type TrashDataCount<T: Config> = StorageValue<_, u32, ValueQuery>;
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn integrity_test() {
assert!(
!T::WeightInfo::waste_ref_time_iter(1).ref_time().is_zero(),
"Weight zero; would get stuck in an infinite loop"
);
assert!(
!T::WeightInfo::waste_proof_size_some(1).proof_size().is_zero(),
"Weight zero; would get stuck in an infinite loop"
);
}
fn on_idle(_: BlockNumberFor<T>, remaining_weight: Weight) -> Weight {
let mut meter = WeightMeter::from_limit(remaining_weight);
if !meter.check_accrue(T::WeightInfo::empty_on_idle()) {
return T::WeightInfo::empty_on_idle()
}
let proof_size_limit = Storage::<T>::get().mul_floor(meter.remaining().proof_size());
let computation_weight_limit =
Compute::<T>::get().mul_floor(meter.remaining().ref_time());
let mut meter = WeightMeter::from_limit(Weight::from_parts(
computation_weight_limit,
proof_size_limit,
));
Self::waste_at_most_proof_size(&mut meter);
Self::waste_at_most_ref_time(&mut meter);
meter.consumed
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Initializes the pallet by writing into `TrashData`.
///
/// Only callable by Root. A good default for `trash_count` is `5_000`.
#[pallet::call_index(0)]
#[pallet::weight(
T::WeightInfo::initialize_pallet_grow(witness_count.unwrap_or_default())
.max(T::WeightInfo::initialize_pallet_shrink(witness_count.unwrap_or_default()))
)]
pub fn initialize_pallet(
origin: OriginFor<T>,
new_count: u32,
witness_count: Option<u32>,
) -> DispatchResult {
ensure_root(origin)?;
let current_count = TrashDataCount::<T>::get();
ensure!(
current_count == witness_count.unwrap_or_default(),
Error::<T>::AlreadyInitialized
);
if new_count > current_count {
(current_count..new_count).for_each(|i| TrashData::<T>::insert(i, [i as u8; 1024]));
} else {
(new_count..current_count).for_each(TrashData::<T>::remove);
}
Self::deposit_event(Event::PalletInitialized { reinit: witness_count.is_some() });
TrashDataCount::<T>::set(new_count);
Ok(())
}
/// Set the `Compute` storage value that determines how much of the
/// block's weight `ref_time` to use during `on_idle`.
///
/// Only callable by Root.
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::set_compute())]
pub fn set_compute(origin: OriginFor<T>, compute: Perbill) -> DispatchResult {
ensure_root(origin)?;
Compute::<T>::set(compute);
Self::deposit_event(Event::ComputationLimitSet { compute });
Ok(())
}
/// Set the `Storage` storage value that determines the PoV size usage
/// for each block.
///
/// Only callable by Root.
#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::set_storage())]
pub fn set_storage(origin: OriginFor<T>, storage: Perbill) -> DispatchResult {
ensure_root(origin)?;
Storage::<T>::set(storage);
Self::deposit_event(Event::StorageLimitSet { storage });
Ok(())
}
}
impl<T: Config> Pallet<T> {
/// Waste at most the remaining proof size of `meter`.
///
/// Tries to come as close to the limit as possible.
pub(crate) fn waste_at_most_proof_size(meter: &mut WeightMeter) {
let Ok(n) = Self::calculate_proof_size_iters(&meter) else {
return;
};
meter.defensive_saturating_accrue(T::WeightInfo::waste_proof_size_some(n));
(0..n).for_each(|i| {
TrashData::<T>::get(i);
});
}
/// Calculate how many times `waste_proof_size_some` should be called to fill up `meter`.
fn calculate_proof_size_iters(meter: &WeightMeter) -> Result<u32, ()> {
let base = T::WeightInfo::waste_proof_size_some(0);
let slope = T::WeightInfo::waste_proof_size_some(1).saturating_sub(base);
let remaining = meter.remaining().saturating_sub(base);
let iter_by_proof_size =
remaining.proof_size().checked_div(slope.proof_size()).ok_or(())?;
let iter_by_ref_time = remaining.ref_time().checked_div(slope.ref_time()).ok_or(())?;
if iter_by_proof_size > 0 && iter_by_proof_size <= iter_by_ref_time {
Ok(iter_by_proof_size as u32)
} else {
Err(())
}
}
/// Waste at most the remaining ref time weight of `meter`.
///
/// Tries to come as close to the limit as possible.
pub(crate) fn waste_at_most_ref_time(meter: &mut WeightMeter) {
let Ok(n) = Self::calculate_ref_time_iters(&meter) else {
return;
};
meter.defensive_saturating_accrue(T::WeightInfo::waste_ref_time_iter(n));
let clobber = Self::waste_ref_time_iter(vec![0u8; 64], n);
// By casting it into a vec we can hopefully prevent the compiler from optimizing it
// out. Note that `Blake2b512` produces 64 bytes, this is therefore impossible - but the
// compiler does not know that (hopefully).
debug_assert!(clobber.len() == 64);
if clobber.len() == 65 {
TrashData::<T>::insert(0, [clobber[0] as u8; 1024]);
}
}
/// Wastes some `ref_time`. Receives the previous result as an argument.
///
/// The ref_time of one iteration should be in the order of 1-10 ms.
pub(crate) fn waste_ref_time_iter(clobber: Vec<u8>, i: u32) -> Vec<u8> {
let mut hasher = Blake2b512::new();
// Blake2 has a very high speed of hashing so we make multiple hashes with it to
// waste more `ref_time` at once.
(0..i).for_each(|_| {
hasher.update(clobber.as_slice());
});
hasher.finalize().to_vec()
}
/// Calculate how many times `waste_ref_time_iter` should be called to fill up `meter`.
fn calculate_ref_time_iters(meter: &WeightMeter) -> Result<u32, ()> {
let base = T::WeightInfo::waste_ref_time_iter(0);
let slope = T::WeightInfo::waste_ref_time_iter(1).saturating_sub(base);
if !slope.proof_size().is_zero() || !base.proof_size().is_zero() {
return Err(())
}
match meter
.remaining()
.ref_time()
.saturating_sub(base.ref_time())
.checked_div(slope.ref_time())
{
Some(0) | None => Err(()),
Some(i) => Ok(i as u32),
}
}
}
}
+80
View File
@@ -0,0 +1,80 @@
// This file is part of Substrate.
// Copyright (C) 2022 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.
use super::*;
use crate as pallet_glutton;
use frame_support::traits::{ConstU32, ConstU64};
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
};
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
Glutton: pallet_glutton::{Pallet, Event},
}
);
impl frame_system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type RuntimeCall = RuntimeCall;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ConstU64<250>;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}
pub fn new_test_ext() -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}
+226
View File
@@ -0,0 +1,226 @@
// This file is part of Substrate.
// Copyright (C) 2023 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.
//! Tests for the glutton pallet.
use super::*;
use mock::{new_test_ext, Glutton, RuntimeOrigin, System, Test};
use frame_support::{assert_err, assert_noop, assert_ok, weights::constants::*};
#[test]
fn initialize_pallet_works() {
new_test_ext().execute_with(|| {
assert_eq!(TrashData::<Test>::get(0), None);
assert_noop!(
Glutton::initialize_pallet(RuntimeOrigin::signed(1), 3, None),
DispatchError::BadOrigin
);
assert_noop!(
Glutton::initialize_pallet(RuntimeOrigin::none(), 3, None),
DispatchError::BadOrigin
);
assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 2, None));
System::assert_last_event(Event::PalletInitialized { reinit: false }.into());
assert_err!(
Glutton::initialize_pallet(RuntimeOrigin::root(), 2, None),
Error::<Test>::AlreadyInitialized
);
assert_eq!(TrashData::<Test>::get(0), Some([0; 1024]));
assert_eq!(TrashData::<Test>::get(1), Some([1; 1024]));
assert_eq!(TrashData::<Test>::get(2), None);
assert_eq!(TrashDataCount::<Test>::get(), 2);
assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 20, Some(2)));
assert_eq!(TrashDataCount::<Test>::get(), 20);
assert_eq!(TrashData::<Test>::iter_keys().count(), 20);
});
}
#[test]
fn expand_and_shrink_trash_data_works() {
new_test_ext().execute_with(|| {
assert_eq!(TrashDataCount::<Test>::get(), 0);
assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 5000, None));
assert_eq!(TrashDataCount::<Test>::get(), 5000);
assert_eq!(TrashData::<Test>::iter_keys().count(), 5000);
assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 8000, Some(5000)));
assert_eq!(TrashDataCount::<Test>::get(), 8000);
assert_eq!(TrashData::<Test>::iter_keys().count(), 8000);
assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 6000, Some(8000)));
assert_eq!(TrashDataCount::<Test>::get(), 6000);
assert_eq!(TrashData::<Test>::iter_keys().count(), 6000);
assert_noop!(
Glutton::initialize_pallet(RuntimeOrigin::root(), 0, None),
Error::<Test>::AlreadyInitialized
);
assert_ok!(Glutton::initialize_pallet(RuntimeOrigin::root(), 0, Some(6000)));
assert_eq!(TrashDataCount::<Test>::get(), 0);
assert_eq!(TrashData::<Test>::iter_keys().count(), 0);
});
}
#[test]
fn setting_compute_works() {
new_test_ext().execute_with(|| {
assert_eq!(Compute::<Test>::get(), Perbill::from_percent(0));
assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), Perbill::from_percent(70)));
assert_eq!(Compute::<Test>::get(), Perbill::from_percent(70));
System::assert_last_event(
Event::ComputationLimitSet { compute: Perbill::from_percent(70) }.into(),
);
assert_noop!(
Glutton::set_compute(RuntimeOrigin::signed(1), Perbill::from_percent(30)),
DispatchError::BadOrigin
);
assert_noop!(
Glutton::set_compute(RuntimeOrigin::none(), Perbill::from_percent(30)),
DispatchError::BadOrigin
);
});
}
#[test]
fn setting_storage_works() {
new_test_ext().execute_with(|| {
assert_eq!(Storage::<Test>::get(), Perbill::from_percent(0));
assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), Perbill::from_percent(30)));
assert_eq!(Storage::<Test>::get(), Perbill::from_percent(30));
System::assert_last_event(
Event::StorageLimitSet { storage: Perbill::from_percent(30) }.into(),
);
assert_noop!(
Glutton::set_storage(RuntimeOrigin::signed(1), Perbill::from_percent(90)),
DispatchError::BadOrigin
);
assert_noop!(
Glutton::set_storage(RuntimeOrigin::none(), Perbill::from_percent(90)),
DispatchError::BadOrigin
);
});
}
#[test]
fn on_idle_works() {
new_test_ext().execute_with(|| {
assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), Perbill::from_percent(100)));
assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), Perbill::from_percent(100)));
Glutton::on_idle(1, Weight::from_ref_time(20_000_000));
});
}
/// Check that the expected is close enough to the consumed weight.
#[test]
fn on_idle_weight_high_proof_is_close_enough_works() {
new_test_ext().execute_with(|| {
assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), Perbill::from_percent(100)));
assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), Perbill::from_percent(100)));
let should = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, WEIGHT_PROOF_SIZE_PER_MB * 5);
let got = Glutton::on_idle(1, should);
assert!(got.all_lte(should), "Consumed too much weight");
let ratio = Perbill::from_rational(got.proof_size(), should.proof_size());
assert!(
ratio >= Perbill::from_percent(99),
"Too few proof size consumed, was only {:?} of expected",
ratio
);
let ratio = Perbill::from_rational(got.ref_time(), should.ref_time());
assert!(
ratio >= Perbill::from_percent(99),
"Too few ref time consumed, was only {:?} of expected",
ratio
);
});
}
#[test]
fn on_idle_weight_low_proof_is_close_enough_works() {
new_test_ext().execute_with(|| {
assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), Perbill::from_percent(100)));
assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), Perbill::from_percent(100)));
let should = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, WEIGHT_PROOF_SIZE_PER_KB * 20);
let got = Glutton::on_idle(1, should);
assert!(got.all_lte(should), "Consumed too much weight");
let ratio = Perbill::from_rational(got.proof_size(), should.proof_size());
// Just a sanity check here.
assert!(
ratio >= Perbill::from_percent(80),
"Too few proof size consumed, was only {:?} of expected",
ratio
);
let ratio = Perbill::from_rational(got.ref_time(), should.ref_time());
assert!(
ratio >= Perbill::from_percent(99),
"Too few ref time consumed, was only {:?} of expected",
ratio
);
});
}
#[test]
fn waste_at_most_ref_time_weight_close_enough() {
new_test_ext().execute_with(|| {
let mut meter =
WeightMeter::from_limit(Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, u64::MAX));
// Over-spending fails defensively.
Glutton::waste_at_most_ref_time(&mut meter);
// We require it to be under-spend by at most 1%.
assert!(
meter.consumed_ratio() >= Perbill::from_percent(99),
"Consumed too few: {:?}",
meter.consumed_ratio()
);
});
}
#[test]
fn waste_at_most_proof_size_weight_close_enough() {
new_test_ext().execute_with(|| {
let mut meter =
WeightMeter::from_limit(Weight::from_parts(u64::MAX, WEIGHT_PROOF_SIZE_PER_MB * 5));
// Over-spending fails defensively.
Glutton::waste_at_most_proof_size(&mut meter);
// We require it to be under-spend by at most 1%.
assert!(
meter.consumed_ratio() >= Perbill::from_percent(99),
"Consumed too few: {:?}",
meter.consumed_ratio()
);
});
}
+324
View File
@@ -0,0 +1,324 @@
// This file is part of Substrate.
// Copyright (C) 2023 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.
//! Autogenerated weights for pallet_glutton
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `runner-ehxwxxsd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// target/production/substrate
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json
// --pallet=pallet_glutton
// --chain=dev
// --header=./HEADER-APACHE2
// --output=./frame/glutton/src/weights.rs
// --template=./.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use sp_std::marker::PhantomData;
/// Weight functions needed for pallet_glutton.
pub trait WeightInfo {
fn initialize_pallet_grow(n: u32, ) -> Weight;
fn initialize_pallet_shrink(n: u32, ) -> Weight;
fn waste_ref_time_iter(i: u32, ) -> Weight;
fn waste_proof_size_some(i: u32, ) -> Weight;
fn on_idle_high_proof_waste() -> Weight;
fn on_idle_low_proof_waste() -> Weight;
fn empty_on_idle() -> Weight;
fn set_compute() -> Weight;
fn set_storage() -> Weight;
}
/// Weights for pallet_glutton using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// Storage: Glutton TrashDataCount (r:1 w:1)
/// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton TrashData (r:0 w:1000)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
/// The range of component `n` is `[0, 1000]`.
fn initialize_pallet_grow(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `4`
// Estimated: `499`
// Minimum execution time: 9_248 nanoseconds.
Weight::from_ref_time(9_582_000)
.saturating_add(Weight::from_proof_size(499))
// Standard Error: 1_144
.saturating_add(Weight::from_ref_time(1_624_225).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into())))
}
/// Storage: Glutton TrashDataCount (r:1 w:1)
/// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton TrashData (r:0 w:1000)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
/// The range of component `n` is `[0, 1000]`.
fn initialize_pallet_shrink(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `65`
// Estimated: `499`
// Minimum execution time: 10_210 nanoseconds.
Weight::from_ref_time(7_260_854)
.saturating_add(Weight::from_proof_size(499))
// Standard Error: 1_459
.saturating_add(Weight::from_ref_time(1_053_844).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into())))
}
/// The range of component `i` is `[0, 100000]`.
fn waste_ref_time_iter(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 605 nanoseconds.
Weight::from_ref_time(1_102_112)
.saturating_add(Weight::from_proof_size(0))
// Standard Error: 28
.saturating_add(Weight::from_ref_time(120_597).saturating_mul(i.into()))
}
/// Storage: Glutton TrashData (r:5000 w:0)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
/// The range of component `i` is `[0, 5000]`.
fn waste_proof_size_some(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `119036 + i * (1053 ±0)`
// Estimated: `0 + i * (3016 ±0)`
// Minimum execution time: 464 nanoseconds.
Weight::from_ref_time(552_000)
.saturating_add(Weight::from_proof_size(0))
// Standard Error: 1_962
.saturating_add(Weight::from_ref_time(5_780_304).saturating_mul(i.into()))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into())))
.saturating_add(Weight::from_proof_size(3016).saturating_mul(i.into()))
}
/// Storage: Glutton Storage (r:1 w:0)
/// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton Compute (r:1 w:0)
/// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton TrashData (r:1738 w:0)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
fn on_idle_high_proof_waste() -> Weight {
// Proof Size summary in bytes:
// Measured: `1955391`
// Estimated: `5242806`
// Minimum execution time: 55_398_405 nanoseconds.
Weight::from_ref_time(55_594_848_000)
.saturating_add(Weight::from_proof_size(5242806))
.saturating_add(T::DbWeight::get().reads(1740_u64))
}
/// Storage: Glutton Storage (r:1 w:0)
/// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton Compute (r:1 w:0)
/// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton TrashData (r:6 w:0)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
fn on_idle_low_proof_waste() -> Weight {
// Proof Size summary in bytes:
// Measured: `11717`
// Estimated: `19094`
// Minimum execution time: 98_888_667 nanoseconds.
Weight::from_ref_time(99_157_239_000)
.saturating_add(Weight::from_proof_size(19094))
.saturating_add(T::DbWeight::get().reads(8_u64))
}
/// Storage: Glutton Storage (r:1 w:0)
/// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton Compute (r:1 w:0)
/// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
fn empty_on_idle() -> Weight {
// Proof Size summary in bytes:
// Measured: `4`
// Estimated: `998`
// Minimum execution time: 3_967 nanoseconds.
Weight::from_ref_time(4_121_000)
.saturating_add(Weight::from_proof_size(998))
.saturating_add(T::DbWeight::get().reads(2_u64))
}
/// Storage: Glutton Compute (r:0 w:1)
/// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
fn set_compute() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 7_112 nanoseconds.
Weight::from_ref_time(7_484_000)
.saturating_add(Weight::from_proof_size(0))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: Glutton Storage (r:0 w:1)
/// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
fn set_storage() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 7_056 nanoseconds.
Weight::from_ref_time(7_414_000)
.saturating_add(Weight::from_proof_size(0))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
/// Storage: Glutton TrashDataCount (r:1 w:1)
/// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton TrashData (r:0 w:1000)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
/// The range of component `n` is `[0, 1000]`.
fn initialize_pallet_grow(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `4`
// Estimated: `499`
// Minimum execution time: 9_248 nanoseconds.
Weight::from_ref_time(9_582_000)
.saturating_add(Weight::from_proof_size(499))
// Standard Error: 1_144
.saturating_add(Weight::from_ref_time(1_624_225).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into())))
}
/// Storage: Glutton TrashDataCount (r:1 w:1)
/// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton TrashData (r:0 w:1000)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
/// The range of component `n` is `[0, 1000]`.
fn initialize_pallet_shrink(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `65`
// Estimated: `499`
// Minimum execution time: 10_210 nanoseconds.
Weight::from_ref_time(7_260_854)
.saturating_add(Weight::from_proof_size(499))
// Standard Error: 1_459
.saturating_add(Weight::from_ref_time(1_053_844).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into())))
}
/// The range of component `i` is `[0, 100000]`.
fn waste_ref_time_iter(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 605 nanoseconds.
Weight::from_ref_time(1_102_112)
.saturating_add(Weight::from_proof_size(0))
// Standard Error: 28
.saturating_add(Weight::from_ref_time(120_597).saturating_mul(i.into()))
}
/// Storage: Glutton TrashData (r:5000 w:0)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
/// The range of component `i` is `[0, 5000]`.
fn waste_proof_size_some(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `119036 + i * (1053 ±0)`
// Estimated: `0 + i * (3016 ±0)`
// Minimum execution time: 464 nanoseconds.
Weight::from_ref_time(552_000)
.saturating_add(Weight::from_proof_size(0))
// Standard Error: 1_962
.saturating_add(Weight::from_ref_time(5_780_304).saturating_mul(i.into()))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into())))
.saturating_add(Weight::from_proof_size(3016).saturating_mul(i.into()))
}
/// Storage: Glutton Storage (r:1 w:0)
/// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton Compute (r:1 w:0)
/// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton TrashData (r:1738 w:0)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
fn on_idle_high_proof_waste() -> Weight {
// Proof Size summary in bytes:
// Measured: `1955391`
// Estimated: `5242806`
// Minimum execution time: 55_398_405 nanoseconds.
Weight::from_ref_time(55_594_848_000)
.saturating_add(Weight::from_proof_size(5242806))
.saturating_add(RocksDbWeight::get().reads(1740_u64))
}
/// Storage: Glutton Storage (r:1 w:0)
/// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton Compute (r:1 w:0)
/// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton TrashData (r:6 w:0)
/// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen)
fn on_idle_low_proof_waste() -> Weight {
// Proof Size summary in bytes:
// Measured: `11717`
// Estimated: `19094`
// Minimum execution time: 98_888_667 nanoseconds.
Weight::from_ref_time(99_157_239_000)
.saturating_add(Weight::from_proof_size(19094))
.saturating_add(RocksDbWeight::get().reads(8_u64))
}
/// Storage: Glutton Storage (r:1 w:0)
/// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Glutton Compute (r:1 w:0)
/// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
fn empty_on_idle() -> Weight {
// Proof Size summary in bytes:
// Measured: `4`
// Estimated: `998`
// Minimum execution time: 3_967 nanoseconds.
Weight::from_ref_time(4_121_000)
.saturating_add(Weight::from_proof_size(998))
.saturating_add(RocksDbWeight::get().reads(2_u64))
}
/// Storage: Glutton Compute (r:0 w:1)
/// Proof: Glutton Compute (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
fn set_compute() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 7_112 nanoseconds.
Weight::from_ref_time(7_484_000)
.saturating_add(Weight::from_proof_size(0))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: Glutton Storage (r:0 w:1)
/// Proof: Glutton Storage (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
fn set_storage() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 7_056 nanoseconds.
Weight::from_ref_time(7_414_000)
.saturating_add(Weight::from_proof_size(0))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
}