Make Dispatchable return the actual weight consumed (#5458)

* Make Dispatchable return the actual weight consumed

Add PostInfo associated type to Dispatchable and have frame implement
Dispatchable { type PostInfo = PostDispatchInfo } where PostDispatchInfo
contains the actual weight consumed.

* Fix whitespace issues in docs
This commit is contained in:
Alexander Theißen
2020-04-03 16:45:30 +02:00
committed by GitHub
parent 14505471ee
commit 8f819f4ba6
11 changed files with 202 additions and 23 deletions
+65 -8
View File
@@ -25,18 +25,35 @@ pub use frame_metadata::{
};
pub use crate::weights::{
SimpleDispatchInfo, GetDispatchInfo, DispatchInfo, WeighData, ClassifyDispatch,
TransactionPriority, Weight, PaysFee,
TransactionPriority, Weight, PaysFee, PostDispatchInfo, WithPostDispatchInfo,
};
pub use sp_runtime::{traits::Dispatchable, DispatchError, DispatchResult};
pub use sp_runtime::{traits::Dispatchable, DispatchError};
pub use crate::traits::{CallMetadata, GetCallMetadata, GetCallName};
/// The return typ of a `Dispatchable` in frame. When returned explicitly from
/// a dispatchable function it allows overriding the default `PostDispatchInfo`
/// returned from a dispatch.
pub type DispatchResultWithPostInfo =
sp_runtime::DispatchResultWithInfo<crate::weights::PostDispatchInfo>;
/// Unaugmented version of `DispatchResultWithPostInfo` that can be returned from
/// dispatchable functions and is automatically converted to the augmented type. Should be
/// used whenever the `PostDispatchInfo` does not need to be overwritten. As this should
/// be the common case it is the implicit return type when none is specified.
pub type DispatchResult = Result<(), sp_runtime::DispatchError>;
/// The error type contained in a `DispatchResultWithPostInfo`.
pub type DispatchErrorWithPostInfo =
sp_runtime::DispatchErrorWithPostInfo<crate::weights::PostDispatchInfo>;
/// A type that cannot be instantiated.
pub enum Never {}
/// Serializable version of Dispatchable.
/// This value can be used as a "function" in an extrinsic.
pub trait Callable<T> {
type Call: Dispatchable + Codec + Clone + PartialEq + Eq;
type Call: Dispatchable<PostInfo=PostDispatchInfo> + Codec + Clone + PartialEq + Eq;
}
// dirty hack to work around serde_derive issue
@@ -119,6 +136,44 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// # fn main() {}
/// ```
///
/// ### Consuming only portions of the annotated static weight
///
/// Per default a callable function consumes all of its static weight as declared via
/// the #[weight] attribute. However, there are use cases where only a portion of this
/// weight should be consumed. In that case the static weight is charged pre dispatch and
/// the difference is refunded post dispatch.
///
/// In order to make use of this feature the function must return `DispatchResultWithPostInfo`
/// in place of the default `DispatchResult`. Then the actually consumed weight can be returned.
/// To consume a non default weight while returning an error
/// [`WithPostDispatchInfo::with_weight`](./weight/trait.WithPostDispatchInfo.html) can be used
/// to augment any error with custom weight information.
///
/// ```
/// # #[macro_use]
/// # extern crate frame_support;
/// # use frame_support::dispatch::{DispatchResultWithPostInfo, WithPostDispatchInfo};
/// # use frame_support::weights::SimpleDispatchInfo;
/// # use frame_system::{self as system, Trait, ensure_signed};
/// decl_module! {
/// pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// #[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]
/// fn my_long_function(origin, do_expensive_calc: bool) -> DispatchResultWithPostInfo {
/// ensure_signed(origin).map_err(|e| e.with_weight(100_000))?;
/// if do_expensive_calc {
/// // do the expensive calculation
/// // ...
/// // return None to indicate that we are using all weight (the default)
/// return Ok(None.into());
/// }
/// // expensive calculation not executed: use only a portion of the weight
/// Ok(Some(100_000).into())
/// }
/// }
/// }
/// # fn main() {}
/// ```
///
/// ### Privileged Function Example
///
/// A privileged function checks that the origin of the call is `ROOT`.
@@ -938,7 +993,7 @@ macro_rules! decl_module {
$ignore:ident
$mod_type:ident<$trait_instance:ident $(, $instance:ident)?> $fn_name:ident $origin:ident $system:ident [ $( $param_name:ident),* ]
) => {
<$mod_type<$trait_instance $(, $instance)?>>::$fn_name( $origin $(, $param_name )* )
<$mod_type<$trait_instance $(, $instance)?>>::$fn_name( $origin $(, $param_name )* ).map(Into::into).map_err(Into::into)
};
// no `deposit_event` function wanted
@@ -1538,7 +1593,8 @@ macro_rules! decl_module {
{
type Trait = $trait_instance;
type Origin = $origin_type;
fn dispatch(self, _origin: Self::Origin) -> $crate::sp_runtime::DispatchResult {
type PostInfo = $crate::weights::PostDispatchInfo;
fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::DispatchResultWithPostInfo {
match self {
$(
$call_type::$fn_name( $( $param_name ),* ) => {
@@ -1563,10 +1619,10 @@ macro_rules! decl_module {
where $( $other_where_bounds )*
{
#[doc(hidden)]
pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance>>(
pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance, PostInfo = $crate::weights::PostDispatchInfo>>(
d: D,
origin: D::Origin
) -> $crate::sp_runtime::DispatchResult {
) -> $crate::dispatch::DispatchResultWithPostInfo {
d.dispatch(origin)
}
}
@@ -1664,10 +1720,11 @@ macro_rules! impl_outer_dispatch {
impl $crate::dispatch::Dispatchable for $call_type {
type Origin = $origin;
type Trait = $call_type;
type PostInfo = $crate::weights::PostDispatchInfo;
fn dispatch(
self,
origin: $origin,
) -> $crate::sp_runtime::DispatchResult {
) -> $crate::dispatch::DispatchResultWithPostInfo {
$crate::impl_outer_dispatch! {
@DISPATCH_MATCH
self
+5 -1
View File
@@ -209,7 +209,11 @@ macro_rules! assert_err {
#[cfg(feature = "std")]
macro_rules! assert_ok {
( $x:expr $(,)? ) => {
assert_eq!($x, Ok(()));
let is = $x;
match is {
Ok(_) => (),
_ => assert!(false, "Expected Ok(_). Got {:#?}", is),
}
};
( $x:expr, $y:expr $(,)? ) => {
assert_eq!($x, Ok($y));
+59
View File
@@ -44,6 +44,7 @@ use sp_runtime::{
traits::SignedExtension,
generic::{CheckedExtrinsic, UncheckedExtrinsic},
};
use crate::dispatch::{DispatchErrorWithPostInfo, DispatchError};
/// Re-export priority as type
pub use sp_runtime::transaction_validity::TransactionPriority;
@@ -116,6 +117,64 @@ pub struct DispatchInfo {
pub pays_fee: bool,
}
/// Weight information that is only available post dispatch.
#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
pub struct PostDispatchInfo {
/// Actual weight consumed by a call or `None` which stands for the worst case static weight.
pub actual_weight: Option<Weight>,
}
impl From<Option<Weight>> for PostDispatchInfo {
fn from(actual_weight: Option<Weight>) -> Self {
Self {
actual_weight,
}
}
}
impl From<()> for PostDispatchInfo {
fn from(_: ()) -> Self {
Self {
actual_weight: None,
}
}
}
impl sp_runtime::traits::Printable for PostDispatchInfo {
fn print(&self) {
"actual_weight=".print();
match self.actual_weight {
Some(weight) => weight.print(),
None => "max-weight".print(),
}
}
}
/// Allows easy conversion from `DispatchError` to `DispatchErrorWithPostInfo` for dispatchables
/// that want to return a custom a posteriori weight on error.
pub trait WithPostDispatchInfo {
/// Call this on your modules custom errors type in order to return a custom weight on error.
///
/// # Example
///
/// ```ignore
/// let who = ensure_signed(origin).map_err(|e| e.with_weight(100))?;
/// ensure!(who == me, Error::<T>::NotMe.with_weight(200_000));
/// ```
fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo;
}
impl<T> WithPostDispatchInfo for T where
T: Into<DispatchError>
{
fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo {
DispatchErrorWithPostInfo {
post_info: PostDispatchInfo { actual_weight: Some(actual_weight) },
error: self.into(),
}
}
}
/// A `Dispatchable` function (aka transaction) that can carry some static information along with
/// it, using the `#[weight]` attribute.
pub trait GetDispatchInfo {