pallet-glutton: over-unity consumption (#14338)

* pallet-glutton: over-unity consumption

* Add hard limit

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

* Tests

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

* Cleanup

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

* Highlight warning

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

* Fix docs

* Review test fixes

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

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet-glutton

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet-glutton

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Guillaume Yu Thiolliere <gui.thiolliere@gmail.com>
Co-authored-by: command-bot <>
This commit is contained in:
Oliver Tale-Yazdi
2023-06-13 17:57:04 +02:00
committed by GitHub
parent 2c7166eb9c
commit 3b6d31f03d
8 changed files with 246 additions and 166 deletions
+43 -36
View File
@@ -15,6 +15,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! # WARNING
//!
//! **DO NOT USE ON VALUE-BEARING CHAINS. THIS PALLET IS ONLY INTENDED FOR TESTING USAGE.**
//!
//! # Glutton Pallet
//!
//! Pallet that consumes `ref_time` and `proof_size` of a block. Based on the
@@ -32,10 +36,10 @@ mod tests;
pub mod weights;
use blake2::{Blake2b512, Digest};
use frame_support::{pallet_prelude::*, weights::WeightMeter};
use frame_support::{pallet_prelude::*, weights::WeightMeter, DefaultNoBound};
use frame_system::pallet_prelude::*;
use sp_io::hashing::twox_256;
use sp_runtime::{traits::Zero, Perbill};
use sp_runtime::{traits::Zero, FixedPointNumber, FixedU64};
use sp_std::{vec, vec::Vec};
pub use pallet::*;
@@ -43,8 +47,10 @@ pub use weights::WeightInfo;
/// The size of each value in the `TrashData` storage in bytes.
pub const VALUE_SIZE: usize = 1024;
/// Max number of entries for `TrashData` storage item
/// Max number of entries for the `TrashData` map.
pub const MAX_TRASH_DATA_ENTRIES: u32 = 65_000;
/// Hard limit for any other resource limit (in units).
pub const RESOURCE_HARD_LIMIT: FixedU64 = FixedU64::from_u32(10);
#[frame_support::pallet]
pub mod pallet {
@@ -52,6 +58,7 @@ pub mod pallet {
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type RuntimeEvent: From<Event> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// The admin origin that can set computational limits and initialize the pallet.
@@ -70,9 +77,9 @@ pub mod pallet {
/// The pallet has been (re)initialized.
PalletInitialized { reinit: bool },
/// The computation limit has been updated.
ComputationLimitSet { compute: Perbill },
ComputationLimitSet { compute: FixedU64 },
/// The storage limit has been updated.
StorageLimitSet { storage: Perbill },
StorageLimitSet { storage: FixedU64 },
}
#[pallet::error]
@@ -81,17 +88,24 @@ pub mod pallet {
///
/// Set `witness_count` to `Some` to bypass this error.
AlreadyInitialized,
/// The limit was over [`crate::RESOURCE_HARD_LIMIT`].
InsaneLimit,
}
/// Storage value used to specify what percentage of the left over `ref_time`
/// to consume during `on_idle`.
/// The proportion of the remaining `ref_time` to consume during `on_idle`.
///
/// `1.0` is mapped to `100%`. Must be at most [`crate::RESOURCE_HARD_LIMIT`]. Setting this to
/// over `1.0` could stall the chain.
#[pallet::storage]
pub(crate) type Compute<T: Config> = StorageValue<_, Perbill, ValueQuery>;
pub(crate) type Compute<T: Config> = StorageValue<_, FixedU64, ValueQuery>;
/// Storage value used the specify what percentage of left over `proof_size`
/// to consume during `on_idle`.
/// The proportion of the remaining `proof_size` to consume during `on_idle`.
///
/// `1.0` is mapped to `100%`. Must be at most [`crate::RESOURCE_HARD_LIMIT`]. Setting this to
/// over `1.0` could stall the chain.
#[pallet::storage]
pub(crate) type Storage<T: Config> = StorageValue<_, Perbill, ValueQuery>;
pub(crate) type Storage<T: Config> = StorageValue<_, FixedU64, ValueQuery>;
/// Storage map used for wasting proof size.
///
@@ -115,22 +129,13 @@ pub mod pallet {
pub(crate) type TrashDataCount<T: Config> = StorageValue<_, u32, ValueQuery>;
#[pallet::genesis_config]
#[derive(DefaultNoBound)]
pub struct GenesisConfig {
pub compute: Perbill,
pub storage: Perbill,
pub compute: FixedU64,
pub storage: FixedU64,
pub trash_data_count: u32,
}
impl Default for GenesisConfig {
fn default() -> Self {
Self {
compute: Default::default(),
storage: Default::default(),
trash_data_count: Default::default(),
}
}
}
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig {
fn build(&self) {
@@ -145,7 +150,10 @@ pub mod pallet {
TrashDataCount::<T>::set(self.trash_data_count);
assert!(self.compute <= RESOURCE_HARD_LIMIT, "Compute limit is insane");
<Compute<T>>::put(self.compute);
assert!(self.storage <= RESOURCE_HARD_LIMIT, "Storage limit is insane");
<Storage<T>>::put(self.storage);
}
}
@@ -169,9 +177,10 @@ pub mod pallet {
return T::WeightInfo::empty_on_idle()
}
let proof_size_limit = Storage::<T>::get().mul_floor(meter.remaining().proof_size());
let proof_size_limit =
Storage::<T>::get().saturating_mul_int(meter.remaining().proof_size());
let computation_weight_limit =
Compute::<T>::get().mul_floor(meter.remaining().ref_time());
Compute::<T>::get().saturating_mul_int(meter.remaining().ref_time());
let mut meter = WeightMeter::from_limit(Weight::from_parts(
computation_weight_limit,
proof_size_limit,
@@ -184,15 +193,14 @@ pub mod pallet {
}
}
#[pallet::call]
#[pallet::call(weight = T::WeightInfo)]
impl<T: Config> Pallet<T> {
/// Initializes the pallet by writing into `TrashData`.
/// Initialize the pallet. Should be called once, if no genesis state was provided.
///
/// `current_count` is the current number of elements in `TrashData`. This can be set to
/// `None` when the pallet is first initialized.
///
/// Only callable by Root or `AdminOrigin`. A good default for `new_count` is
/// `5_000`.
/// Only callable by Root or `AdminOrigin`. A good default for `new_count` is `5_000`.
#[pallet::call_index(0)]
#[pallet::weight(
T::WeightInfo::initialize_pallet_grow(witness_count.unwrap_or_default())
@@ -227,10 +235,10 @@ pub mod pallet {
///
/// Only callable by Root or `AdminOrigin`.
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::set_compute())]
pub fn set_compute(origin: OriginFor<T>, compute: Perbill) -> DispatchResult {
pub fn set_compute(origin: OriginFor<T>, compute: FixedU64) -> DispatchResult {
T::AdminOrigin::try_origin(origin).map(|_| ()).or_else(|o| ensure_root(o))?;
ensure!(compute <= RESOURCE_HARD_LIMIT, Error::<T>::InsaneLimit);
Compute::<T>::set(compute);
Self::deposit_event(Event::ComputationLimitSet { compute });
@@ -239,17 +247,16 @@ pub mod pallet {
/// Set how much of the remaining `proof_size` weight should be consumed by `on_idle`.
//
/// 100% means that all remaining `proof_size` will be consumed. The PoV benchmarking
/// `1.0` means that all remaining `proof_size` will be consumed. The PoV benchmarking
/// results that are used here are likely an over-estimation. 100% intended consumption will
/// therefore translate to less than 100% actual consumption. In the future, this could be
/// counter-acted by allowing the glutton to specify over-unity consumption ratios.
/// therefore translate to less than 100% actual consumption.
///
/// Only callable by Root or `AdminOrigin`.
#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::set_storage())]
pub fn set_storage(origin: OriginFor<T>, storage: Perbill) -> DispatchResult {
pub fn set_storage(origin: OriginFor<T>, storage: FixedU64) -> DispatchResult {
T::AdminOrigin::try_origin(origin).map(|_| ()).or_else(|o| ensure_root(o))?;
ensure!(storage <= RESOURCE_HARD_LIMIT, Error::<T>::InsaneLimit);
Storage::<T>::set(storage);
Self::deposit_event(Event::StorageLimitSet { storage });