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
+63 -43
View File
@@ -358,26 +358,57 @@ impl From<DispatchError> for DispatchOutcome {
}
}
/// Result of a module function call; either nothing (functions are only called for "side effects")
/// or an error message.
pub type DispatchResult = sp_std::result::Result<(), DispatchError>;
/// Reason why a dispatch call failed
#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize))]
/// Reason why a dispatch call failed
pub struct DispatchError {
/// Module index, matching the metadata module index
pub module: Option<u8>,
/// Module specific error value
pub error: u8,
/// Optional error message.
#[codec(skip)]
pub message: Option<&'static str>,
pub enum DispatchError {
/// Some error occurred.
Other(#[codec(skip)] &'static str),
/// Failed to lookup some data.
CannotLookup,
/// A bad origin.
BadOrigin,
/// A custom error in a module
Module {
/// Module index, matching the metadata module index
index: u8,
/// Module specific error value
error: u8,
/// Optional error message.
#[codec(skip)]
message: Option<&'static str>,
},
}
impl DispatchError {
/// Create a new instance of `DispatchError`.
pub fn new(module: Option<u8>, error: u8, message: Option<&'static str>) -> Self {
Self {
module,
error,
message,
impl From<crate::traits::LookupError> for DispatchError {
fn from(_: crate::traits::LookupError) -> Self {
Self::CannotLookup
}
}
impl From<crate::traits::BadOrigin> for DispatchError {
fn from(_: crate::traits::BadOrigin) -> Self {
Self::BadOrigin
}
}
impl From<&'static str> for DispatchError {
fn from(err: &'static str) -> DispatchError {
DispatchError::Other(err)
}
}
impl Into<&'static str> for DispatchError {
fn into(self) -> &'static str {
match self {
Self::Other(msg) => msg,
Self::CannotLookup => "Can not lookup",
Self::BadOrigin => "Bad origin",
Self::Module { message, .. } => message.unwrap_or("Unknown module error"),
}
}
}
@@ -385,29 +416,18 @@ impl DispatchError {
impl traits::Printable for DispatchError {
fn print(&self) {
"DispatchError".print();
if let Some(module) = self.module {
module.print();
match self {
Self::Other(err) => err.print(),
Self::CannotLookup => "Can not lookup".print(),
Self::BadOrigin => "Bad origin".print(),
Self::Module { index, error, message } => {
index.print();
error.print();
if let Some(msg) = message {
msg.print();
}
}
}
self.error.print();
if let Some(msg) = self.message {
msg.print();
}
}
}
impl traits::ModuleDispatchError for &'static str {
fn as_u8(&self) -> u8 {
0
}
fn as_str(&self) -> &'static str {
self
}
}
impl From<&'static str> for DispatchError {
fn from(err: &'static str) -> DispatchError {
DispatchError::new(None, 0, Some(err))
}
}
@@ -668,18 +688,18 @@ mod tests {
#[test]
fn dispatch_error_encoding() {
let error = DispatchError {
module: Some(1),
let error = DispatchError::Module {
index: 1,
error: 2,
message: Some("error message"),
};
let encoded = error.encode();
let decoded = DispatchError::decode(&mut &encoded[..]).unwrap();
assert_eq!(encoded, vec![1, 1, 2]);
assert_eq!(encoded, vec![3, 1, 2]);
assert_eq!(
decoded,
DispatchError {
module: Some(1),
DispatchError::Module {
index: 1,
error: 2,
message: None,
},