Migrate away from SimpleDispatchInfo (#5686)

* Migrate away from SimpleDispatchInfo

* Fix imports

* Better doc

* Update lib.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Kian Paimani
2020-04-22 09:20:28 +02:00
committed by GitHub
parent 25c3ab2c1e
commit 50a7e12b3f
40 changed files with 458 additions and 405 deletions
+184 -126
View File
@@ -16,29 +16,104 @@
//! # Primitives for transaction weighting.
//!
//! All dispatchable functions defined in `decl_module!` must provide two trait implementations:
//! - [`WeightData`]: To determine the weight of the dispatch.
//! - [`ClassifyDispatch`]: To determine the class of the dispatch. See the enum definition for
//! more information on dispatch classes.
//! Every dispatchable function is responsible for providing `#[weight = $x]` attribute. In this
//! snipped, `$x` can be any user provided struct that implements the following traits:
//!
//! Every dispatchable function is responsible for providing this data via an optional `#[weight =
//! $x]` attribute. In this snipped, `$x` can be any user provided struct that implements the
//! two aforementioned traits.
//! - [`WeighData`]: the weight amount.
//! - [`ClassifyDispatch`]: class of the dispatch.
//! - [`PaysFee`]: weather this weight should be translated to fee and deducted upon dispatch.
//!
//! Substrate then bundles then output information of the two traits into [`DispatchInfo`] struct
//! and provides it by implementing the [`GetDispatchInfo`] for all `Call` variants, and opaque
//! extrinsic types.
//! and provides it by implementing the [`GetDispatchInfo`] for all `Call` both inner and outer call
//! types.
//!
//! If no `#[weight]` is defined, the macro automatically injects the `Default` implementation of
//! the [`SimpleDispatchInfo`].
//! Substrate provides two pre-defined ways to annotate weight:
//!
//! Note that the decl_module macro _cannot_ enforce this and will simply fail if an invalid struct
//! (something that does not implement `Weighable`) is passed in.
//! ### 1. Fixed values
//!
//! This can only be used when all 3 traits can be resolved statically. You have 3 degrees of
//! configuration:
//!
//! 1. Define only weight, **in which case `ClassifyDispatch` will be `Normal` and `PaysFee` will be
//! `true`**.
//!
//! ```
//! # use frame_system::{self as system, Trait};
//! frame_support::decl_module! {
//! pub struct Module<T: Trait> for enum Call where origin: T::Origin {
//! #[weight = 1000]
//! fn dispatching(origin) { unimplemented!() }
//! }
//! }
//! # fn main() {}
//! ```
//!
//! 2. Define weight and class, **in which case `PaysFee` would be `true`**.
//!
//! ```
//! # use frame_system::{self as system, Trait};
//! # use frame_support::weights::DispatchClass;
//! frame_support::decl_module! {
//! pub struct Module<T: Trait> for enum Call where origin: T::Origin {
//! #[weight = (1000, DispatchClass::Operational)]
//! fn dispatching(origin) { unimplemented!() }
//! }
//! }
//! # fn main() {}
//! ```
//!
//! 3. Define all 3 parameters.
//!
//! ```
//! # use frame_system::{self as system, Trait};
//! # use frame_support::weights::DispatchClass;
//! frame_support::decl_module! {
//! pub struct Module<T: Trait> for enum Call where origin: T::Origin {
//! #[weight = (1000, DispatchClass::Operational, false)]
//! fn dispatching(origin) { unimplemented!() }
//! }
//! }
//! # fn main() {}
//! ```
//!
//! ### 2. Define weights as a function of input arguments using `FunctionOf` tuple struct. This struct works
//! in a similar manner as above. 3 items must be provided and each can be either a fixed value or a
//! function/closure with the same parameters list as the dispatchable function itself, wrapper in a
//! tuple.
//!
//! Using this only makes sense if you want to use a function for at least one of the elements. If
//! all 3 are static values, providing a raw tuple is easier.
//!
//! ```
//! # use frame_system::{self as system, Trait};
//! # use frame_support::weights::{DispatchClass, FunctionOf};
//! frame_support::decl_module! {
//! pub struct Module<T: Trait> for enum Call where origin: T::Origin {
//! #[weight = FunctionOf(
//! // weight, function.
//! |args: (&u32, &u64)| *args.0 as u64 + args.1,
//! // class, fixed.
//! DispatchClass::Operational,
//! // pays fee, function.
//! |args: (&u32, &u64)| *args.0 > 1000,
//! )]
//! fn dispatching(origin, a: u32, b: u64) { unimplemented!() }
//! }
//! }
//! # fn main() {}
//! ```
//! FRAME assumes a weight of `1_000_000_000_000` equals 1 second of compute on a standard machine.
//!
//! Latest machine specification used to benchmark are:
//! - Digital Ocean: ubuntu-s-2vcpu-4gb-ams3-01
//! - 2x Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz
//! - 4GB RAM
//! - Ubuntu 19.10 (GNU/Linux 5.3.0-18-generic x86_64)
//! - rustc 1.42.0 (b8cedc004 2020-03-09)
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use codec::{Encode, Decode};
use sp_arithmetic::traits::Bounded;
use sp_runtime::{
RuntimeDebug,
traits::SignedExtension,
@@ -50,9 +125,6 @@ use crate::dispatch::{DispatchErrorWithPostInfo, DispatchError};
pub use sp_runtime::transaction_validity::TransactionPriority;
/// Numeric range of a transaction weight.
///
/// FRAME assumes a weight of `1_000_000_000_000` equals 1 second of compute on a standard
/// machine: (TODO: DEFINE STANDARD MACHINE SPECIFICATIONS)
pub type Weight = u64;
/// The smallest total weight an extrinsic should have.
@@ -81,8 +153,7 @@ pub trait PaysFee<T> {
}
}
/// A generalized group of dispatch types. This is only distinguishing normal, user-triggered transactions
/// (`Normal`) and anything beyond which serves a higher purpose to the system (`Operational`).
/// A generalized group of dispatch types.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)]
@@ -108,41 +179,7 @@ pub enum DispatchClass {
impl Default for DispatchClass {
fn default() -> Self {
DispatchClass::Normal
}
}
// Implement traits for raw Weight value
impl<T> WeighData<T> for Weight {
fn weigh_data(&self, _: T) -> Weight {
return *self
}
}
impl<T> ClassifyDispatch<T> for Weight {
fn classify_dispatch(&self, _: T) -> DispatchClass {
DispatchClass::default()
}
}
impl<T> PaysFee<T> for Weight {
fn pays_fee(&self, _: T) -> bool {
true
}
}
impl From<SimpleDispatchInfo> for DispatchClass {
fn from(tx: SimpleDispatchInfo) -> Self {
match tx {
SimpleDispatchInfo::FixedOperational(_) => DispatchClass::Operational,
SimpleDispatchInfo::MaxOperational => DispatchClass::Operational,
SimpleDispatchInfo::FixedNormal(_) => DispatchClass::Normal,
SimpleDispatchInfo::MaxNormal => DispatchClass::Normal,
SimpleDispatchInfo::InsecureFreeNormal => DispatchClass::Normal,
SimpleDispatchInfo::FixedMandatory(_) => DispatchClass::Mandatory,
}
Self::Normal
}
}
@@ -157,6 +194,15 @@ pub struct DispatchInfo {
pub pays_fee: bool,
}
/// A `Dispatchable` function (aka transaction) that can carry some static information along with
/// it, using the `#[weight]` attribute.
pub trait GetDispatchInfo {
/// Return a `DispatchInfo`, containing relevant information of this dispatch.
///
/// This is done independently of its encoded size.
fn get_dispatch_info(&self) -> DispatchInfo;
}
/// Weight information that is only available post dispatch.
#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
pub struct PostDispatchInfo {
@@ -206,7 +252,7 @@ impl sp_runtime::traits::Printable for PostDispatchInfo {
}
/// Allows easy conversion from `DispatchError` to `DispatchErrorWithPostInfo` for dispatchables
/// that want to return a custom a posteriori weight on error.
/// that want to return a custom a posterior weight on error.
pub trait WithPostDispatchInfo {
/// Call this on your modules custom errors type in order to return a custom weight on error.
///
@@ -230,86 +276,75 @@ impl<T> WithPostDispatchInfo for T where
}
}
/// A `Dispatchable` function (aka transaction) that can carry some static information along with
/// it, using the `#[weight]` attribute.
pub trait GetDispatchInfo {
/// Return a `DispatchInfo`, containing relevant information of this dispatch.
///
/// This is done independently of its encoded size.
fn get_dispatch_info(&self) -> DispatchInfo;
}
/// Default type used with the `#[weight = x]` attribute in a substrate chain.
///
/// A user may pass in any other type that implements the correct traits. If not, the `Default`
/// implementation of [`SimpleDispatchInfo`] is used.
///
/// For each generalized group (`Normal` and `Operation`):
/// - A `Fixed` variant means weight fee is charged normally and the weight is the number
/// specified in the inner value of the variant.
/// - A `Free` variant is equal to `::Fixed(0)`. Note that this does not guarantee inclusion.
/// - A `Max` variant is equal to `::Fixed(Weight::max_value())`.
///
/// As for the generalized groups themselves:
/// - `Normal` variants will be assigned a priority proportional to their weight. They can only
/// consume a portion (defined in the system module) of the maximum block resource limits.
/// - `Operational` variants will be assigned the maximum priority. They can potentially consume
/// the entire block resource limit.
#[derive(Clone, Copy)]
pub enum SimpleDispatchInfo {
/// A normal dispatch with fixed weight.
FixedNormal(Weight),
/// A normal dispatch with the maximum weight.
MaxNormal,
/// A normal dispatch with no weight. Base and bytes fees still need to be paid.
InsecureFreeNormal,
/// An operational dispatch with fixed weight.
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 {
impl<T> WeighData<T> for Weight {
fn weigh_data(&self, _: T) -> Weight {
match self {
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,
}
return *self
}
}
impl<T> ClassifyDispatch<T> for SimpleDispatchInfo {
impl<T> ClassifyDispatch<T> for Weight {
fn classify_dispatch(&self, _: T) -> DispatchClass {
DispatchClass::from(*self)
DispatchClass::Normal
}
}
impl<T> PaysFee<T> for SimpleDispatchInfo {
impl<T> PaysFee<T> for Weight {
fn pays_fee(&self, _: T) -> bool {
match self {
SimpleDispatchInfo::FixedNormal(_) => true,
SimpleDispatchInfo::MaxNormal => true,
SimpleDispatchInfo::InsecureFreeNormal => true,
SimpleDispatchInfo::FixedOperational(_) => true,
SimpleDispatchInfo::MaxOperational => true,
SimpleDispatchInfo::FixedMandatory(_) => true,
}
true
}
}
impl SimpleDispatchInfo {
/// An _additive zero_ variant of SimpleDispatchInfo.
pub fn zero() -> Self {
Self::FixedNormal(0)
impl<T> WeighData<T> for (Weight, DispatchClass, bool) {
fn weigh_data(&self, _: T) -> Weight {
return self.0
}
}
impl<T> ClassifyDispatch<T> for (Weight, DispatchClass, bool) {
fn classify_dispatch(&self, _: T) -> DispatchClass {
self.1
}
}
impl<T> PaysFee<T> for (Weight, DispatchClass, bool) {
fn pays_fee(&self, _: T) -> bool {
self.2
}
}
impl<T> WeighData<T> for (Weight, DispatchClass) {
fn weigh_data(&self, _: T) -> Weight {
return self.0
}
}
impl<T> ClassifyDispatch<T> for (Weight, DispatchClass) {
fn classify_dispatch(&self, _: T) -> DispatchClass {
self.1
}
}
impl<T> PaysFee<T> for (Weight, DispatchClass) {
fn pays_fee(&self, _: T) -> bool {
true
}
}
impl<T> WeighData<T> for (Weight, bool) {
fn weigh_data(&self, _: T) -> Weight {
return self.0
}
}
impl<T> ClassifyDispatch<T> for (Weight, bool) {
fn classify_dispatch(&self, _: T) -> DispatchClass {
DispatchClass::Normal
}
}
impl<T> PaysFee<T> for (Weight, bool) {
fn pays_fee(&self, _: T) -> bool {
self.1
}
}
@@ -463,8 +498,14 @@ mod tests {
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
// no arguments, fixed weight
#[weight = SimpleDispatchInfo::FixedNormal(1000)]
fn f0(_origin) { unimplemented!(); }
#[weight = 1000]
fn f00(_origin) { unimplemented!(); }
#[weight = (1000, DispatchClass::Mandatory)]
fn f01(_origin) { unimplemented!(); }
#[weight = (1000, DispatchClass::Operational, false)]
fn f02(_origin) { unimplemented!(); }
// weight = a x 10 + b
#[weight = FunctionOf(|args: (&u32, &u32)| (args.0 * 10 + args.1) as Weight, DispatchClass::Normal, true)]
@@ -484,7 +525,24 @@ mod tests {
#[test]
fn weights_are_correct() {
assert_eq!(Call::<TraitImpl>::f0().get_dispatch_info().weight, 1000);
// #[weight = 1000]
let info = Call::<TraitImpl>::f00().get_dispatch_info();
assert_eq!(info.weight, 1000);
assert_eq!(info.class, DispatchClass::Normal);
assert_eq!(info.pays_fee, true);
// #[weight = (1000, DispatchClass::Mandatory)]
let info = Call::<TraitImpl>::f01().get_dispatch_info();
assert_eq!(info.weight, 1000);
assert_eq!(info.class, DispatchClass::Mandatory);
assert_eq!(info.pays_fee, true);
// #[weight = (1000, DispatchClass::Operational, false)]
let info = Call::<TraitImpl>::f02().get_dispatch_info();
assert_eq!(info.weight, 1000);
assert_eq!(info.class, DispatchClass::Operational);
assert_eq!(info.pays_fee, false);
assert_eq!(Call::<TraitImpl>::f11(10, 20).get_dispatch_info().weight, 120);
assert_eq!(Call::<TraitImpl>::f11(10, 20).get_dispatch_info().class, DispatchClass::Normal);
assert_eq!(Call::<TraitImpl>::f12(10, 20).get_dispatch_info().weight, 0);