Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

pallet-glutton: over-unity consumption #14338

Merged
merged 10 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frame/glutton/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# WARNING

Do not use on value-bearing chains. This pallet is **only** intended for usage on test-chains.
**DO NOT USE ON VALUE-BEARING CHAINS. THIS PALLET IS ONLY INTENDED FOR TESTING USAGE.**

# Glutton Pallet

Expand Down
13 changes: 7 additions & 6 deletions frame/glutton/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use super::*;
use frame_benchmarking::benchmarks;
use frame_support::{pallet_prelude::*, weights::constants::*};
use frame_system::RawOrigin as SystemOrigin;
use sp_runtime::{traits::One, Perbill};

use crate::Pallet as Glutton;
use frame_system::Pallet as System;
Expand Down Expand Up @@ -67,17 +68,17 @@ benchmarks! {
// 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 _ = Glutton::<T>::set_compute(SystemOrigin::Root.into(), One::one());
let _ = Glutton::<T>::set_storage(SystemOrigin::Root.into(), One::one());
}: {
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 _ = Glutton::<T>::set_compute(SystemOrigin::Root.into(), One::one());
let _ = Glutton::<T>::set_storage(SystemOrigin::Root.into(), One::one());
}: {
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));
}
Expand All @@ -89,10 +90,10 @@ benchmarks! {
}

set_compute {
}: _(SystemOrigin::Root, Perbill::from_percent(50))
}: _(SystemOrigin::Root, FixedU64::from_perbill(Perbill::from_percent(50)))

set_storage {
}: _(SystemOrigin::Root, Perbill::from_percent(50))
}: _(SystemOrigin::Root, FixedU64::from_perbill(Perbill::from_percent(50)))

impl_benchmark_test_suite!(Glutton, crate::mock::new_test_ext(), crate::mock::Test);
}
79 changes: 43 additions & 36 deletions frame/glutton/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,26 +36,29 @@ 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::*;
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 {
use super::*;

#[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.
Expand All @@ -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]
Expand All @@ -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.
///
Expand All @@ -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) {
Expand All @@ -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);
}
}
Expand All @@ -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,
Expand All @@ -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())
Expand Down Expand Up @@ -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 });
Expand All @@ -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 });
Expand Down
13 changes: 12 additions & 1 deletion frame/glutton/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
use super::*;
use crate as pallet_glutton;

use frame_support::traits::{ConstU32, ConstU64};
use frame_support::{
assert_ok,
traits::{ConstU32, ConstU64},
};
use sp_core::H256;
use sp_runtime::{
testing::Header,
Expand Down Expand Up @@ -79,3 +82,11 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
ext.execute_with(|| System::set_block_number(1));
ext
}

/// Set the `compute` and `storage` limits.
///
/// `1.0` corresponds to `100%`.
pub fn set_limits(compute: f64, storage: f64) {
assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), FixedU64::from_float(compute)));
assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_float(storage)));
}
Loading