Make decl_error! errors usable (#4449)

* Make `decl_error!` errors usable

This pr implements support for returning errors of different pallets in
a pallet. These errors need to be declared with `decl_error!`.

The pr changes the following:

- Each dispatchable function now returns a `DispatchResult` which is an
alias for `Result<(), DispatchError>`.
- `DispatchError` is an enum that has 4 variants:
  - `Other`: For storing string error messages
  - `CannotLookup`: Variant that is returned when something returns a
  `sp_runtime::LookupError`
  - `BadOrigin`: Variant that is returned for any kind of bad origin
  - `Module`: The error of a specific module. Contains the `index`,
  `error` and the `message`. The index is the index of the module in
  `construct_runtime!`. `error` is the index of the error in the error
  enum declared by `decl_error!`. `message` is the message to the error
  variant (this will not be encoded).
- `construct_runtime!` now creates a new struct `ModuleToIndex`. This
struct implements the trait `ModuleToIndex`.
- `frame_system::Trait` has a new associated type: `ModuleToIndex` that
expects the `ModuleToIndex` generated by `construct_runtime!`.
- All error strings returned in any module are being converted now to `DispatchError`.
- `BadOrigin` is the default error returned by any type that implements `EnsureOrigin`.

* Fix frame system benchmarks
This commit is contained in:
Bastian Köcher
2019-12-19 14:01:52 +01:00
committed by Gavin Wood
parent 0aab5c659e
commit 8e393aa5a8
69 changed files with 868 additions and 611 deletions
+21 -34
View File
@@ -27,17 +27,11 @@ pub use crate::weights::{
SimpleDispatchInfo, GetDispatchInfo, DispatchInfo, WeighData, ClassifyDispatch,
TransactionPriority, Weight, WeighBlock, PaysFee,
};
pub use sp_runtime::{
traits::{Dispatchable, DispatchResult, ModuleDispatchError},
DispatchError,
};
pub use sp_runtime::{traits::Dispatchable, DispatchError, DispatchResult};
/// A type that cannot be instantiated.
pub enum Never {}
/// Result with string error message. This exists for backward compatibility purpose.
pub type Result = DispatchResult<&'static str>;
/// Serializable version of Dispatchable.
/// This value can be used as a "function" in an extrinsic.
pub trait Callable<T> {
@@ -68,14 +62,14 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
///
/// // Private functions are dispatchable, but not available to other
/// // SRML modules.
/// fn my_function(origin, var: u64) -> dispatch::Result {
/// fn my_function(origin, var: u64) -> dispatch::DispatchResult {
/// // Your implementation
/// Ok(())
/// }
///
/// // Public functions are both dispatchable and available to other
/// // SRML modules.
/// pub fn my_public_function(origin) -> dispatch::Result {
/// pub fn my_public_function(origin) -> dispatch::DispatchResult {
/// // Your implementation
/// Ok(())
/// }
@@ -95,8 +89,8 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
///
/// ### Shorthand Example
///
/// The macro automatically expands a shorthand function declaration to return the `Result` type.
/// These functions are the same:
/// The macro automatically expands a shorthand function declaration to return the
/// [`DispatchResult`] type. These functions are the same:
///
/// ```
/// # #[macro_use]
@@ -106,7 +100,7 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// decl_module! {
/// pub struct Module<T: Trait> for enum Call where origin: T::Origin {
///
/// fn my_long_function(origin) -> dispatch::Result {
/// fn my_long_function(origin) -> dispatch::DispatchResult {
/// // Your implementation
/// Ok(())
/// }
@@ -130,7 +124,7 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// # use frame_system::{self as system, Trait, ensure_signed, ensure_root};
/// decl_module! {
/// pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// fn my_privileged_function(origin) -> dispatch::Result {
/// fn my_privileged_function(origin) -> dispatch::DispatchResult {
/// ensure_root(origin)?;
/// // Your implementation
/// Ok(())
@@ -1043,9 +1037,8 @@ macro_rules! decl_module {
#[allow(unreachable_code)]
$vis fn $name(
$origin: $origin_ty $(, $param: $param_ty )*
) -> $crate::dispatch::DispatchResult<$error_type> {
use $crate::sp_std::if_std;
if_std! {
) -> $crate::dispatch::DispatchResult {
$crate::sp_std::if_std! {
use $crate::tracing;
let span = tracing::span!(tracing::Level::DEBUG, stringify!($name));
let _enter = span.enter();
@@ -1417,8 +1410,7 @@ macro_rules! decl_module {
{
type Trait = $trait_instance;
type Origin = $origin_type;
type Error = $error_type;
fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::DispatchResult<Self::Error> {
fn dispatch(self, _origin: Self::Origin) -> $crate::sp_runtime::DispatchResult {
match self {
$(
$call_type::$fn_name( $( $param_name ),* ) => {
@@ -1446,7 +1438,7 @@ macro_rules! decl_module {
pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance>>(
d: D,
origin: D::Origin
) -> $crate::dispatch::DispatchResult<D::Error> {
) -> $crate::sp_runtime::DispatchResult {
d.dispatch(origin)
}
}
@@ -1514,11 +1506,10 @@ macro_rules! impl_outer_dispatch {
impl $crate::dispatch::Dispatchable for $call_type {
type Origin = $origin;
type Trait = $call_type;
type Error = $crate::dispatch::DispatchError;
fn dispatch(
self,
origin: $origin,
) -> $crate::dispatch::DispatchResult<$crate::dispatch::DispatchError> {
) -> $crate::sp_runtime::DispatchResult {
$crate::impl_outer_dispatch! {
@DISPATCH_MATCH
self
@@ -1565,11 +1556,7 @@ macro_rules! impl_outer_dispatch {
$origin
{
$( $generated )*
$call_type::$name(call) => call.dispatch($origin).map_err(|e| {
let mut error: $crate::dispatch::DispatchError = e.into();
error.module = Some($index);
error
}),
$call_type::$name(call) => call.dispatch($origin),
}
$index + 1;
$( $rest ),*
@@ -1895,13 +1882,13 @@ mod tests {
}
pub mod system {
use super::Result;
use super::*;
pub trait Trait {
type AccountId;
}
pub fn ensure_root<R>(_: R) -> Result {
pub fn ensure_root<R>(_: R) -> DispatchResult {
Ok(())
}
}
@@ -1917,13 +1904,13 @@ mod tests {
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin, T::AccountId: From<u32> {
/// Hi, this is a comment.
fn aux_0(_origin) -> Result { unreachable!() }
fn aux_1(_origin, #[compact] _data: u32,) -> Result { unreachable!() }
fn aux_2(_origin, _data: i32, _data2: String) -> Result { unreachable!() }
fn aux_0(_origin) -> DispatchResult { unreachable!() }
fn aux_1(_origin, #[compact] _data: u32,) -> DispatchResult { unreachable!() }
fn aux_2(_origin, _data: i32, _data2: String) -> DispatchResult { unreachable!() }
#[weight = SimpleDispatchInfo::FixedNormal(3)]
fn aux_3(_origin) -> Result { unreachable!() }
fn aux_4(_origin, _data: i32) -> Result { unreachable!() }
fn aux_5(_origin, _data: i32, #[compact] _data2: u32,) -> Result { unreachable!() }
fn aux_3(_origin) -> DispatchResult { unreachable!() }
fn aux_4(_origin, _data: i32) -> DispatchResult { unreachable!() }
fn aux_5(_origin, _data: i32, #[compact] _data2: u32,) -> DispatchResult { unreachable!() }
#[weight = SimpleDispatchInfo::FixedNormal(7)]
fn on_initialize(n: T::BlockNumber,) { if n.into() == 42 { panic!("on_initialize") } }
+62 -44
View File
@@ -17,18 +17,19 @@
//! Macro for declaring a module error.
#[doc(hidden)]
pub use sp_runtime::traits::LookupError;
pub use sp_runtime::traits::{LookupError, BadOrigin};
#[doc(hidden)]
pub use frame_metadata::{ModuleErrorMetadata, ErrorMetadata, DecodeDifferent};
/// Declare an error type for a runtime module.
///
/// The generated error type inherently has the variants `Other` and `CannotLookup`. `Other` can
/// hold any `&'static str` error message and is present for convenience/backward compatibility.
/// The `CannotLookup` variant indicates that some lookup could not be done. For both variants the
/// error type implements `From<&'static str>` and `From<LookupError>` to make them usable with the
/// try operator.
/// `decl_error!` supports only variants that do not hold any data. The dispatchable
/// functions return [`DispatchResult`](sp_runtime::DispatchResult). The error type
/// implements `From<ErrorType> for DispatchResult` to make the error type usable as error
/// in the dispatchable functions.
///
/// `decl_error!` supports only variants that do not hold any data.
/// It is required that the error type is registed in `decl_module!` to make the error
/// exported in the metadata.
///
/// # Usage
///
@@ -36,7 +37,7 @@ pub use frame_metadata::{ModuleErrorMetadata, ErrorMetadata, DecodeDifferent};
/// # use frame_support::{decl_error, decl_module};
/// decl_error! {
/// /// Errors that can occur in my module.
/// pub enum MyError {
/// pub enum MyError for Module<T: Trait> {
/// /// Hey this is an error message that indicates bla.
/// MyCoolErrorMessage,
/// /// You are just not cool enough for my module!
@@ -44,27 +45,36 @@ pub use frame_metadata::{ModuleErrorMetadata, ErrorMetadata, DecodeDifferent};
/// }
/// }
///
/// # use frame_system::{self as system, Trait, ensure_signed};
/// # use frame_system::{self as system, Trait};
///
/// // You need to register the error type in `decl_module!` as well.
/// // You need to register the error type in `decl_module!` as well to make the error
/// // exported in the metadata.
///
/// decl_module! {
/// pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// type Error = MyError;
/// type Error = MyError<T>;
///
/// fn do_something(origin) -> Result<(), MyError> {
/// Err(MyError::YouAreNotCoolEnough)
/// fn do_something(origin) -> frame_support::dispatch::DispatchResult {
/// Err(MyError::<T>::YouAreNotCoolEnough.into())
/// }
/// }
/// }
///
/// # fn main() {}
/// ```
///
/// For instantiable modules you also need to give the instance generic type and bound to the
/// error declaration.
#[macro_export]
macro_rules! decl_error {
(
$(#[$attr:meta])*
pub enum $error:ident {
pub enum $error:ident
for $module:ident<
$generic:ident: $trait:path
$(, $inst_generic:ident: $instance:path)?
>
{
$(
$( #[doc = $doc_attr:tt] )*
$name:ident
@@ -72,33 +82,42 @@ macro_rules! decl_error {
$(,)?
}
) => {
#[derive(Clone, PartialEq, Eq, $crate::RuntimeDebug)]
$(#[$attr])*
pub enum $error {
Other(&'static str),
CannotLookup,
pub enum $error<$generic: $trait $(, $inst_generic: $instance)?> {
#[doc(hidden)]
__Ignore(
$crate::sp_std::marker::PhantomData<($generic $(, $inst_generic)?)>,
$crate::dispatch::Never,
),
$(
$( #[doc = $doc_attr] )*
$name
),*
}
impl $crate::dispatch::ModuleDispatchError for $error {
impl<$generic: $trait $(, $inst_generic: $instance)?> $crate::sp_std::fmt::Debug
for $error<$generic $(, $inst_generic)?>
{
fn fmt(&self, f: &mut $crate::sp_std::fmt::Formatter<'_>) -> $crate::sp_std::fmt::Result {
f.write_str(self.as_str())
}
}
impl<$generic: $trait $(, $inst_generic: $instance)?> $error<$generic $(, $inst_generic)?> {
fn as_u8(&self) -> u8 {
$crate::decl_error! {
@GENERATE_AS_U8
self
$error
{}
2,
0,
$( $name ),*
}
}
fn as_str(&self) -> &'static str {
match self {
$error::Other(err) => err,
$error::CannotLookup => "Can not lookup",
Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
$(
$error::$name => stringify!($name),
)*
@@ -106,33 +125,33 @@ macro_rules! decl_error {
}
}
impl From<&'static str> for $error {
fn from(val: &'static str) -> $error {
$error::Other(val)
}
}
impl From<$crate::error::LookupError> for $error {
fn from(_: $crate::error::LookupError) -> $error {
$error::CannotLookup
}
}
impl From<$error> for &'static str {
fn from(err: $error) -> &'static str {
use $crate::dispatch::ModuleDispatchError;
impl<$generic: $trait $(, $inst_generic: $instance)?> From<$error<$generic $(, $inst_generic)?>>
for &'static str
{
fn from(err: $error<$generic $(, $inst_generic)?>) -> &'static str {
err.as_str()
}
}
impl Into<$crate::dispatch::DispatchError> for $error {
fn into(self) -> $crate::dispatch::DispatchError {
use $crate::dispatch::ModuleDispatchError;
$crate::dispatch::DispatchError::new(None, self.as_u8(), Some(self.as_str()))
impl<$generic: $trait $(, $inst_generic: $instance)?> From<$error<$generic $(, $inst_generic)?>>
for $crate::sp_runtime::DispatchError
{
fn from(err: $error<$generic $(, $inst_generic)?>) -> Self {
let index = <$generic::ModuleToIndex as $crate::traits::ModuleToIndex>
::module_to_index::<$module<$generic $(, $inst_generic)?>>()
.expect("Every active module has an index in the runtime; qed") as u8;
$crate::sp_runtime::DispatchError::Module {
index,
error: err.as_u8(),
message: Some(err.as_str()),
}
}
}
impl $crate::error::ModuleErrorMetadata for $error {
impl<$generic: $trait $(, $inst_generic: $instance)?> $crate::error::ModuleErrorMetadata
for $error<$generic $(, $inst_generic)?>
{
fn metadata() -> &'static [$crate::error::ErrorMetadata] {
&[
$(
@@ -174,8 +193,7 @@ macro_rules! decl_error {
$index:expr,
) => {
match $self {
$error::Other(_) => 0,
$error::CannotLookup => 1,
$error::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
$( $generated )*
}
}
+2 -2
View File
@@ -124,7 +124,7 @@ pub use frame_support_procedural::{decl_storage, construct_runtime};
#[macro_export]
macro_rules! fail {
( $y:expr ) => {{
return Err($y);
return Err($y.into());
}}
}
@@ -168,7 +168,7 @@ macro_rules! assert_noop {
#[cfg(feature = "std")]
macro_rules! assert_err {
( $x:expr , $y:expr $(,)? ) => {
assert_eq!($x, Err($y));
assert_eq!($x, Err($y.into()));
}
}
+7 -9
View File
@@ -240,13 +240,14 @@ mod tests {
mod system {
use super::*;
pub trait Trait {
pub trait Trait: 'static {
const ASSOCIATED_CONST: u64 = 500;
type Origin: Into<Result<RawOrigin<Self::AccountId>, Self::Origin>>
+ From<RawOrigin<Self::AccountId>>;
type AccountId: From<u32> + Encode;
type BlockNumber: From<u32> + Encode;
type SomeValue: Get<u32>;
type ModuleToIndex: crate::traits::ModuleToIndex;
}
decl_module! {
@@ -286,10 +287,8 @@ mod tests {
mod event_module {
use crate::dispatch::DispatchResult;
pub trait Trait {
type Origin;
pub trait Trait: super::system::Trait {
type Balance;
type BlockNumber;
}
decl_event!(
@@ -302,14 +301,14 @@ mod tests {
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
type Error = Error;
type Error = Error<T>;
fn aux_0(_origin) -> DispatchResult<Error> { unreachable!() }
fn aux_0(_origin) -> DispatchResult { unreachable!() }
}
}
crate::decl_error! {
pub enum Error {
pub enum Error for Module<T: Trait> {
/// Some user input error
UserInputError,
/// Something bad happened
@@ -372,9 +371,7 @@ mod tests {
}
impl event_module::Trait for TestRuntime {
type Origin = Origin;
type Balance = u32;
type BlockNumber = u32;
}
impl event_module2::Trait for TestRuntime {
@@ -392,6 +389,7 @@ mod tests {
type AccountId = u32;
type BlockNumber = u32;
type SomeValue = SystemValue;
type ModuleToIndex = ();
}
impl_runtime_metadata!(
+20 -8
View File
@@ -22,7 +22,7 @@ use sp_std::{prelude::*, result, marker::PhantomData, ops::Div, fmt::Debug};
use codec::{FullCodec, Codec, Encode, Decode};
use sp_core::u32_trait::Value as U32;
use sp_runtime::{
ConsensusEngineId,
ConsensusEngineId, DispatchResult, DispatchError,
traits::{MaybeSerializeDeserialize, SimpleArithmetic, Saturating},
};
@@ -386,7 +386,7 @@ pub trait Currency<AccountId> {
_amount: Self::Balance,
reasons: WithdrawReasons,
new_balance: Self::Balance,
) -> result::Result<(), &'static str>;
) -> DispatchResult;
// PUBLIC MUTABLES (DANGEROUS)
@@ -399,7 +399,7 @@ pub trait Currency<AccountId> {
dest: &AccountId,
value: Self::Balance,
existence_requirement: ExistenceRequirement,
) -> result::Result<(), &'static str>;
) -> DispatchResult;
/// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the
/// free balance. This function cannot fail.
@@ -419,7 +419,7 @@ pub trait Currency<AccountId> {
fn deposit_into_existing(
who: &AccountId,
value: Self::Balance
) -> result::Result<Self::PositiveImbalance, &'static str>;
) -> result::Result<Self::PositiveImbalance, DispatchError>;
/// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on
/// success.
@@ -465,7 +465,7 @@ pub trait Currency<AccountId> {
value: Self::Balance,
reasons: WithdrawReasons,
liveness: ExistenceRequirement,
) -> result::Result<Self::NegativeImbalance, &'static str>;
) -> result::Result<Self::NegativeImbalance, DispatchError>;
/// Similar to withdraw, only accepts a `PositiveImbalance` and returns nothing on success.
fn settle(
@@ -528,7 +528,7 @@ pub trait ReservableCurrency<AccountId>: Currency<AccountId> {
///
/// If the free balance is lower than `value`, then no funds will be moved and an `Err` will
/// be returned to notify of this. This is different behavior than `unreserve`.
fn reserve(who: &AccountId, value: Self::Balance) -> result::Result<(), &'static str>;
fn reserve(who: &AccountId, value: Self::Balance) -> DispatchResult;
/// Moves up to `value` from reserved balance to free balance. This function cannot fail.
///
@@ -552,7 +552,7 @@ pub trait ReservableCurrency<AccountId>: Currency<AccountId> {
slashed: &AccountId,
beneficiary: &AccountId,
value: Self::Balance
) -> result::Result<Self::Balance, &'static str>;
) -> result::Result<Self::Balance, DispatchError>;
}
/// An identifier for a lock. Used for disambiguating different locks so that
@@ -619,7 +619,7 @@ pub trait VestingCurrency<AccountId>: Currency<AccountId> {
locked: Self::Balance,
per_block: Self::Balance,
starting_block: Self::Moment,
) -> result::Result<(), &'static str>;
) -> DispatchResult;
/// Remove a vesting schedule for a given account.
fn remove_vesting_schedule(who: &AccountId);
@@ -777,3 +777,15 @@ pub trait ValidatorRegistration<ValidatorId> {
/// module
fn is_registered(id: &ValidatorId) -> bool;
}
/// Something that can convert a given module into the index of the module in the runtime.
///
/// The index of a module is determined by the position it appears in `construct_runtime!`.
pub trait ModuleToIndex {
/// Convert the given module `M` into an index.
fn module_to_index<M: 'static>() -> Option<usize>;
}
impl ModuleToIndex for () {
fn module_to_index<M: 'static>() -> Option<usize> { Some(0) }
}