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

Optional PoV block limits #13164

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
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
23 changes: 13 additions & 10 deletions bin/node/runtime/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ mod multiplier_tests {
BlockWeights::get()
.get(DispatchClass::Normal)
.max_total
.unwrap_or_else(|| BlockWeights::get().max_block)
.limited_or(BlockWeights::get().max_block)
}

fn min_multiplier() -> Multiplier {
Expand Down Expand Up @@ -284,8 +284,9 @@ mod multiplier_tests {
// `cargo test congested_chain_simulation -- --nocapture` to get some insight.

// almost full. The entire quota of normal transactions is taken.
let block_weight = BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap() -
Weight::from_ref_time(100);
let block_weight =
BlockWeights::get().get(DispatchClass::Normal).max_total.exact_limits().unwrap() -
Weight::from_ref_time(100);

// Default substrate weight.
let tx_weight = frame_support::weights::constants::ExtrinsicBaseWeight::get();
Expand Down Expand Up @@ -442,12 +443,14 @@ mod multiplier_tests {

// Some values that are all above the target and will cause an increase.
let t = target();
vec![t + Weight::from_ref_time(100), t * 2, t * 4].into_iter().for_each(|i| {
run_with_system_weight(i, || {
let fm = runtime_multiplier_update(max_fm);
// won't grow. The convert saturates everything.
assert_eq!(fm, max_fm);
})
});
vec![t + Weight::from_ref_time(100), t.saturating_mul(2), t.saturating_mul(4), Weight::MAX]
.into_iter()
.for_each(|i| {
run_with_system_weight(i, || {
let fm = runtime_multiplier_update(max_fm);
// won't grow. The convert saturates everything.
assert_eq!(fm, max_fm);
})
});
}
}
25 changes: 14 additions & 11 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use frame_support::{
constants::{
BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND,
},
ConstantMultiplier, IdentityFee, Weight,
ConstantMultiplier, IdentityFee, Weight, WeightLimit,
},
PalletId, RuntimeDebug,
};
Expand Down Expand Up @@ -176,8 +176,11 @@ const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);
/// by Operational extrinsics.
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
/// We allow for 2 seconds of compute with a 6 second average block time, with maximum proof size.
const MAXIMUM_BLOCK_WEIGHT: Weight =
Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX);
const MAXIMUM_BLOCK_WEIGHT: WeightLimit =
WeightLimit::from_time_limit(2 * WEIGHT_REF_TIME_PER_SECOND);
/// The target Proof size that we aim for. This is not a hard limit and is currently only used to
/// print a warning and emit an event on overflow.
pub const BLOCK_POV_TARGET: u64 = 5 * 1024 * 1024;

parameter_types! {
pub const BlockHashCount: BlockNumber = 2400;
Expand All @@ -190,16 +193,15 @@ parameter_types! {
weights.base_extrinsic = ExtrinsicBaseWeight::get();
})
.for_class(DispatchClass::Normal, |weights| {
weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);
weights.max_total = NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT;
})
.for_class(DispatchClass::Operational, |weights| {
weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);
weights.max_total = MAXIMUM_BLOCK_WEIGHT;
// Operational transactions have some extra reserved space, so that they
// are included even if block reached `MAXIMUM_BLOCK_WEIGHT`.
weights.reserved = Some(
MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT
);
weights.reserved = MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT;
})
.pov_soft_limit(BLOCK_POV_TARGET)
.avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)
.build_or_panic();
}
Expand Down Expand Up @@ -614,7 +616,8 @@ parameter_types! {
pub const MultiPhaseUnsignedPriority: TransactionPriority = StakingUnsignedPriority::get() - 1u64;
pub MinerMaxWeight: Weight = RuntimeBlockWeights::get()
.get(DispatchClass::Normal)
.max_extrinsic.expect("Normal extrinsics have a weight limit configured; qed")
.max_extrinsic
.limited_or_max()
.saturating_sub(BlockExecutionWeight::get());
// Solution can occupy 90% of normal block size
pub MinerMaxLength: u32 = Perbill::from_rational(9u32, 10) *
Expand Down Expand Up @@ -1187,7 +1190,7 @@ parameter_types! {
.per_class
.get(DispatchClass::Normal)
.max_total
.unwrap_or(RuntimeBlockWeights::get().max_block);
.limited_or(RuntimeBlockWeights::get().max_block);
pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default();
}

Expand Down Expand Up @@ -1495,7 +1498,7 @@ parameter_types! {
pub const MinBid: Balance = 100 * DOLLARS;
pub const MinReceipt: Perquintill = Perquintill::from_percent(1);
pub const IntakePeriod: BlockNumber = 10;
pub MaxIntakeWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 10;
pub MaxIntakeWeight: Weight = (MAXIMUM_BLOCK_WEIGHT / 10).limited_or_max();
pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5);
pub Target: Perquintill = Perquintill::zero();
pub const NisPalletId: PalletId = PalletId(*b"py/nis ");
Expand Down
3 changes: 2 additions & 1 deletion frame/executive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1125,7 +1125,8 @@ mod tests {
// on_initialize weight + base block execution weight
let block_weights = <Runtime as frame_system::Config>::BlockWeights::get();
let base_block_weight = Weight::from_ref_time(175) + block_weights.base_block;
let limit = block_weights.get(DispatchClass::Normal).max_total.unwrap() - base_block_weight;
let limit = block_weights.get(DispatchClass::Normal).max_total.exact_limits().unwrap() -
base_block_weight;
let num_to_exhaust_block = limit.ref_time() / (encoded_len + 5);
t.execute_with(|| {
Executive::initialize_block(&Header::new(
Expand Down
226 changes: 210 additions & 16 deletions frame/support/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,33 +423,35 @@ impl<T: Clone> PerDispatchClass<T> {

impl PerDispatchClass<Weight> {
/// Returns the total weight consumed by all extrinsics in the block.
///
/// Saturates on overflow.
pub fn total(&self) -> Weight {
let mut sum = Weight::zero();
for class in DispatchClass::all() {
sum = sum.saturating_add(*self.get(*class));
sum.saturating_accrue(*self.get(*class));
}
sum
}

/// Add some weight of a specific dispatch class, saturating at the numeric bounds of `Weight`.
pub fn add(&mut self, weight: Weight, class: DispatchClass) {
let value = self.get_mut(class);
*value = value.saturating_add(weight);
/// Add some weight to the given class. Saturates at the numeric bounds.
pub fn saturating_add(mut self, weight: Weight, class: DispatchClass) -> Self {
self.saturating_accrue(weight, class);
self
}

/// Increase the weight of the given class. Saturates at the numeric bounds.
pub fn saturating_accrue(&mut self, weight: Weight, class: DispatchClass) {
self.get_mut(class).saturating_accrue(weight);
}

/// Try to add some weight of a specific dispatch class, returning Err(()) if overflow would
/// occur.
pub fn checked_add(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> {
let value = self.get_mut(class);
*value = value.checked_add(&weight).ok_or(())?;
Ok(())
/// Try to increase the weight of the given class. Saturates at the numeric bounds.
pub fn checked_accrue(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> {
self.get_mut(class).checked_accrue(weight).ok_or(())
}

/// Subtract some weight of a specific dispatch class, saturating at the numeric bounds of
/// `Weight`.
pub fn sub(&mut self, weight: Weight, class: DispatchClass) {
let value = self.get_mut(class);
*value = value.saturating_sub(weight);
/// Reduce the weight of the given class. Saturates at the numeric bounds.
pub fn saturating_reduce(&mut self, weight: Weight, class: DispatchClass) {
self.get_mut(class).saturating_reduce(weight);
}
}

Expand Down Expand Up @@ -3693,3 +3695,195 @@ mod weight_tests {
assert_eq!(extract_actual_pays_fee(&Ok((Some(1000), Pays::Yes).into()), &pre), Pays::No);
}
}

#[cfg(test)]
mod per_dispatch_class_tests {
use super::*;
use sp_runtime::traits::Zero;
use DispatchClass::*;

// helper trait
trait IntoWeight {
fn into_weight(self) -> Weight;
}

impl IntoWeight for u64 {
fn into_weight(self) -> Weight {
Weight::from_all(self)
}
}

impl IntoWeight for (u64, u64) {
fn into_weight(self) -> Weight {
Weight::from_parts(self.0, self.1)
}
}

#[test]
fn saturating_add_works() {
let a = PerDispatchClass {
normal: (5, 10).into_weight(),
operational: (20, 30).into_weight(),
mandatory: Weight::MAX,
};
assert_eq!(
a.clone()
.saturating_add((20, 5).into_weight(), Normal)
.saturating_add((10, 10).into_weight(), Operational)
.saturating_add((u64::MAX, 3).into_weight(), Mandatory),
PerDispatchClass {
normal: (25, 15).into_weight(),
operational: (30, 40).into_weight(),
mandatory: Weight::MAX
}
);
let b = a
.saturating_add(Weight::MAX, Normal)
.saturating_add(Weight::MAX, Operational)
.saturating_add(Weight::MAX, Mandatory);
assert_eq!(
b,
PerDispatchClass {
normal: Weight::MAX,
operational: Weight::MAX,
mandatory: Weight::MAX
}
);
assert_eq!(b.total(), Weight::MAX);
}

#[test]
fn saturating_accrue_works() {
let mut a = PerDispatchClass::default();

a.saturating_accrue((10, 15).into_weight(), Normal);
assert_eq!(a.normal, (10, 15).into_weight());
assert_eq!(a.total(), (10, 15).into_weight());

a.saturating_accrue((20, 25).into_weight(), Operational);
assert_eq!(a.operational, (20, 25).into_weight());
assert_eq!(a.total(), (30, 40).into_weight());

a.saturating_accrue((30, 35).into_weight(), Mandatory);
assert_eq!(a.mandatory, (30, 35).into_weight());
assert_eq!(a.total(), (60, 75).into_weight());

a.saturating_accrue((u64::MAX, 10).into_weight(), Operational);
assert_eq!(a.operational, (u64::MAX, 35).into_weight());
assert_eq!(a.total(), (u64::MAX, 85).into_weight());

a.saturating_accrue((10, u64::MAX).into_weight(), Normal);
assert_eq!(a.normal, (20, u64::MAX).into_weight());
assert_eq!(a.total(), Weight::MAX);
}

#[test]
fn saturating_reduce_works() {
let mut a = PerDispatchClass {
normal: (10, u64::MAX).into_weight(),
mandatory: (u64::MAX, 10).into_weight(),
operational: (20, 20).into_weight(),
};

a.saturating_reduce((5, 100).into_weight(), Normal);
assert_eq!(a.normal, (5, u64::MAX - 100).into_weight());
assert_eq!(a.total(), (u64::MAX, u64::MAX - 70).into_weight());

a.saturating_reduce((15, 5).into_weight(), Operational);
assert_eq!(a.operational, (5, 15).into_weight());
assert_eq!(a.total(), (u64::MAX, u64::MAX - 75).into_weight());

a.saturating_reduce((50, 0).into_weight(), Mandatory);
assert_eq!(a.mandatory, (u64::MAX - 50, 10).into_weight());
assert_eq!(a.total(), (u64::MAX - 40, u64::MAX - 75).into_weight());

a.saturating_reduce((u64::MAX, 100).into_weight(), Operational);
assert!(a.operational.is_zero());
assert_eq!(a.total(), (u64::MAX - 45, u64::MAX - 90).into_weight());

a.saturating_reduce((5, u64::MAX).into_weight(), Normal);
assert!(a.normal.is_zero());
assert_eq!(a.total(), (u64::MAX - 50, 10).into_weight());
}

#[test]
fn checked_accrue_works() {
let mut a = PerDispatchClass::default();

a.checked_accrue((1, 2).into_weight(), Normal).unwrap();
a.checked_accrue((3, 4).into_weight(), Operational).unwrap();
a.checked_accrue((5, 6).into_weight(), Mandatory).unwrap();
a.checked_accrue((7, 8).into_weight(), Operational).unwrap();
a.checked_accrue((9, 0).into_weight(), Normal).unwrap();

assert_eq!(
a,
PerDispatchClass {
normal: (10, 2).into_weight(),
operational: (10, 12).into_weight(),
mandatory: (5, 6).into_weight(),
}
);

a.checked_accrue((u64::MAX - 10, u64::MAX - 2).into_weight(), Normal).unwrap();
a.checked_accrue((0, 0).into_weight(), Normal).unwrap();
a.checked_accrue((1, 0).into_weight(), Normal).unwrap_err();
a.checked_accrue((0, 1).into_weight(), Normal).unwrap_err();

assert_eq!(
a,
PerDispatchClass {
normal: Weight::MAX,
operational: (10, 12).into_weight(),
mandatory: (5, 6).into_weight(),
}
);
}

#[test]
fn checked_accrue_does_not_modify_on_error() {
let mut a = PerDispatchClass {
normal: 0.into_weight(),
operational: Weight::MAX / 2 + 2.into_weight(),
mandatory: 10.into_weight(),
};

a.checked_accrue(Weight::MAX / 2, Operational).unwrap_err();
a.checked_accrue(Weight::MAX - 9.into_weight(), Mandatory).unwrap_err();
a.checked_accrue(Weight::MAX, Normal).unwrap(); // This one works

assert_eq!(
a,
PerDispatchClass {
normal: Weight::MAX,
operational: Weight::MAX / 2 + 2.into_weight(),
mandatory: 10.into_weight(),
}
);
}

#[test]
fn total_works() {
assert!(PerDispatchClass::default().total().is_zero());

assert_eq!(
PerDispatchClass {
normal: 0.into_weight(),
operational: (10, 20).into_weight(),
mandatory: (20, u64::MAX).into_weight(),
}
.total(),
(30, u64::MAX).into_weight()
);

assert_eq!(
PerDispatchClass {
normal: (u64::MAX - 10, 10).into_weight(),
operational: (3, u64::MAX).into_weight(),
mandatory: (4, u64::MAX).into_weight(),
}
.total(),
(u64::MAX - 3, u64::MAX).into_weight()
);
}
}
Loading