Mandatory dispatch class (#5515)

* Mandatory dispatch class

* Tweaks

* Docs

* Fix test

* Update frame/support/src/weights.rs

Co-Authored-By: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Introduce logic that was stated in PR.

* Use

* Docs.

* Fix test

* Fix merge

* Update frame/support/src/weights.rs

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* Fix.

* Fix

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Gavin Wood
2020-04-05 14:27:30 +02:00
committed by GitHub
parent 392571d78c
commit abd822692d
10 changed files with 132 additions and 25 deletions
+1 -1
View File
@@ -207,7 +207,7 @@ decl_module! {
}
/// Provide a set of uncles.
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
#[weight = SimpleDispatchInfo::FixedMandatory(10_000)]
fn set_uncles(origin, new_uncles: Vec<T::Header>) -> dispatch::DispatchResult {
ensure_none(origin)?;
ensure!(new_uncles.len() <= MAX_UNCLES, Error::<T>::TooManyUncles);
+1 -1
View File
@@ -76,7 +76,7 @@ decl_module! {
/// Hint that the author of this block thinks the best finalized
/// block is the given number.
#[weight = frame_support::weights::SimpleDispatchInfo::default()]
#[weight = frame_support::weights::SimpleDispatchInfo::FixedMandatory(10_000)]
fn final_hint(origin, #[compact] hint: T::BlockNumber) {
ensure_none(origin)?;
ensure!(!<Self as Store>::Update::exists(), Error::<T>::AlreadyUpdated);
+22 -2
View File
@@ -85,6 +85,19 @@ pub enum DispatchClass {
Normal,
/// An operational dispatch.
Operational,
/// A mandatory dispatch. These kinds of dispatch are always included regardless of their
/// weight, therefore it is critical that they are separately validated to ensure that a
/// malicious validator cannot craft a valid but impossibly heavy block. Usually this just means
/// ensuring that the extrinsic can only be included once and that it is always very light.
///
/// Do *NOT* use it for extrinsics that can be heavy.
///
/// The only real use case for this is inherent extrinsics that are required to execute in a
/// block for the block to be valid, and it solves the issue in the case that the block
/// initialization is sufficiently heavy to mean that those inherents do not fit into the
/// block. Essentially, we assume that in these exceptional circumstances, it is better to
/// allow an overweight block to be created than to not allow any block at all to be created.
Mandatory,
}
impl Default for DispatchClass {
@@ -102,6 +115,8 @@ impl From<SimpleDispatchInfo> for DispatchClass {
SimpleDispatchInfo::FixedNormal(_) => DispatchClass::Normal,
SimpleDispatchInfo::MaxNormal => DispatchClass::Normal,
SimpleDispatchInfo::InsecureFreeNormal => DispatchClass::Normal,
SimpleDispatchInfo::FixedMandatory(_) => DispatchClass::Mandatory,
}
}
}
@@ -212,6 +227,11 @@ pub enum SimpleDispatchInfo {
FixedOperational(Weight),
/// An operational dispatch with the maximum weight.
MaxOperational,
/// A mandatory dispatch with fixed weight.
///
/// NOTE: Signed transactions may not (directly) dispatch this kind of a call, so the other
/// attributes concerning transactability (e.g. priority, fee paying) are moot.
FixedMandatory(Weight),
}
impl<T> WeighData<T> for SimpleDispatchInfo {
@@ -220,9 +240,9 @@ impl<T> WeighData<T> for SimpleDispatchInfo {
SimpleDispatchInfo::FixedNormal(w) => *w,
SimpleDispatchInfo::MaxNormal => Bounded::max_value(),
SimpleDispatchInfo::InsecureFreeNormal => Bounded::min_value(),
SimpleDispatchInfo::FixedOperational(w) => *w,
SimpleDispatchInfo::MaxOperational => Bounded::max_value(),
SimpleDispatchInfo::FixedMandatory(w) => *w,
}
}
}
@@ -239,9 +259,9 @@ impl<T> PaysFee<T> for SimpleDispatchInfo {
SimpleDispatchInfo::FixedNormal(_) => true,
SimpleDispatchInfo::MaxNormal => true,
SimpleDispatchInfo::InsecureFreeNormal => true,
SimpleDispatchInfo::FixedOperational(_) => true,
SimpleDispatchInfo::MaxOperational => true,
SimpleDispatchInfo::FixedMandatory(_) => true,
}
}
}
+29 -5
View File
@@ -99,7 +99,7 @@ use sp_std::marker::PhantomData;
use sp_std::fmt::Debug;
use sp_version::RuntimeVersion;
use sp_runtime::{
RuntimeDebug, Perbill, DispatchOutcome, DispatchError,
RuntimeDebug, Perbill, DispatchOutcome, DispatchError, DispatchResult,
generic::{self, Era},
transaction_validity::{
ValidTransaction, TransactionPriority, TransactionLongevity, TransactionValidityError,
@@ -119,7 +119,7 @@ use frame_support::{
Contains, Get, ModuleToIndex, OnNewAccount, OnKilledAccount, IsDeadAccount, Happened,
StoredMap, EnsureOrigin,
},
weights::{Weight, DispatchInfo, DispatchClass, SimpleDispatchInfo, FunctionOf},
weights::{Weight, DispatchInfo, DispatchClass, SimpleDispatchInfo, FunctionOf}
};
use codec::{Encode, Decode, FullCodec, EncodeLike};
@@ -1170,7 +1170,8 @@ impl<T: Trait + Send + Sync> CheckWeight<T> {
/// a portion.
fn get_dispatch_limit_ratio(class: DispatchClass) -> Perbill {
match class {
DispatchClass::Operational => <Perbill as sp_runtime::PerThing>::one(),
DispatchClass::Operational | DispatchClass::Mandatory
=> <Perbill as sp_runtime::PerThing>::one(),
DispatchClass::Normal => T::AvailableBlockRatio::get(),
}
}
@@ -1186,7 +1187,7 @@ impl<T: Trait + Send + Sync> CheckWeight<T> {
let limit = Self::get_dispatch_limit_ratio(info.class) * maximum_weight;
let added_weight = info.weight.min(limit);
let next_weight = current_weight.saturating_add(added_weight);
if next_weight > limit {
if next_weight > limit && info.class != DispatchClass::Mandatory {
Err(InvalidTransaction::ExhaustsResources.into())
} else {
Ok(next_weight)
@@ -1216,7 +1217,9 @@ impl<T: Trait + Send + Sync> CheckWeight<T> {
fn get_priority(info: <Self as SignedExtension>::DispatchInfo) -> TransactionPriority {
match info.class {
DispatchClass::Normal => info.weight.into(),
DispatchClass::Operational => Bounded::max_value()
DispatchClass::Operational => Bounded::max_value(),
// Mandatory extrinsics are only for inherents; never transactions.
DispatchClass::Mandatory => Bounded::min_value(),
}
}
@@ -1271,6 +1274,9 @@ impl<T: Trait + Send + Sync> SignedExtension for CheckWeight<T> {
info: Self::DispatchInfo,
len: usize,
) -> Result<(), TransactionValidityError> {
if info.class == DispatchClass::Mandatory {
Err(InvalidTransaction::MandatoryDispatch)?
}
Self::do_pre_dispatch(info, len)
}
@@ -1281,6 +1287,9 @@ impl<T: Trait + Send + Sync> SignedExtension for CheckWeight<T> {
info: Self::DispatchInfo,
len: usize,
) -> TransactionValidity {
if info.class == DispatchClass::Mandatory {
Err(InvalidTransaction::MandatoryDispatch)?
}
Self::do_validate(info, len)
}
@@ -1299,6 +1308,21 @@ impl<T: Trait + Send + Sync> SignedExtension for CheckWeight<T> {
) -> TransactionValidity {
Self::do_validate(info, len)
}
fn post_dispatch(
_pre: Self::Pre,
info: Self::DispatchInfo,
_len: usize,
result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
// Since mandatory dispatched do not get validated for being overweight, we are sensitive
// to them actually being useful. Block producers are thus not allowed to include mandatory
// extrinsics that result in error.
if info.class == DispatchClass::Mandatory && result.is_err() {
Err(InvalidTransaction::BadMandatory)?
}
Ok(())
}
}
impl<T: Trait + Send + Sync> Debug for CheckWeight<T> {
+1 -1
View File
@@ -147,7 +147,7 @@ decl_module! {
/// `MinimumPeriod`.
///
/// The dispatch origin for this call must be `Inherent`.
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
#[weight = SimpleDispatchInfo::FixedMandatory(10_000)]
fn set(origin, #[compact] now: T::Moment) {
ensure_none(origin)?;
assert!(!<Self as Store>::DidUpdate::exists(), "Timestamp must be updated only once in the block");