Skip to content

Commit

Permalink
Support all DispatchError thrown by substrate.
Browse files Browse the repository at this point in the history
  • Loading branch information
echevrier committed May 2, 2023
1 parent 5dae754 commit 46b5785
Showing 1 changed file with 127 additions and 16 deletions.
143 changes: 127 additions & 16 deletions node-api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
metadata::Metadata,
};

use codec::Decode;
use codec::{Decode, Encode};
use core::fmt::Debug;
use derive_more::From;
use log::*;
Expand Down Expand Up @@ -78,10 +78,32 @@ impl From<&str> for Error {
/// appropriate static type to hand.
#[derive(Debug)]
pub enum DispatchError {
/// An error was emitted from a specific pallet/module.
Module(ModuleError),
/// Some other error was emitted.
/// Some error occurred.
Other(Vec<u8>),
/// Failed to lookup some data.
CannotLookup,
/// A bad origin.
BadOrigin,
/// A custom error in a module.
Module(ModuleError),
/// At least one consumer is remaining so the account cannot be destroyed.
ConsumerRemaining,
/// There are no providers so the account cannot be created.
NoProviders,
/// There are too many consumers so the account cannot be created.
TooManyConsumers,
/// An error to do with tokens.
Token(TokenError),
/// An arithmetic error.
Arithmetic(ArithmeticError),
/// The number of transactional layers has been reached, or we are not in a transactional layer.
Transactional(TransactionalError),
/// Resources exhausted, e.g. attempt to read/write data which is too large to manipulate.
Exhausted,
/// The state is corrupt; this is generally not going to fix itself.
Corruption,
/// Some resource (e.g. a preimage) is unavailable right now. This might fix itself later.
Unavailable,
}

impl DispatchError {
Expand Down Expand Up @@ -115,33 +137,79 @@ impl DispatchError {
},
};

let module_variant_idx =
variant.variants.iter().find(|v| v.name == "Module").map(|v| v.index);
let module_variant_idx = match module_variant_idx {
Some(idx) => idx,
let variant_name =
variant.variants.iter().find(|v| v.index == bytes[0]).map(|v| v.name.as_str());
let name = match variant_name {
Some(name) => name,
None => {
warn!("Can't decode error: sp_runtime::DispatchError does not have a 'Module' variant");
warn!("Can't decode error: sp_runtime::DispatchError does not have a name variant");
return DispatchError::Other(bytes.into_owned())
},
};
let err = match name {
"Module" => Self::decode_module_error(bytes, metadata), // We apply custom logic to transform the module error into the outward facing version
"Token" => {
let token_error =
match TokenError::decode(&mut bytes.clone().into_owned().as_slice()) {
Ok(err) => err,
Err(_) => {
warn!(
"Can't decode token error: TokenError does not match known formats"
);
return DispatchError::Other(bytes.to_vec())
},
};
DispatchError::Token(token_error)
},
"Arithmetic" => {
let arithmetic_error =
match ArithmeticError::decode(&mut bytes.clone().into_owned().as_slice()) {
Ok(err) => err,
Err(_) => {
warn!("Can't decode token error: ArithmeticError does not match known formats");
return DispatchError::Other(bytes.to_vec())
},
};
DispatchError::Arithmetic(arithmetic_error)
},
"-" => {
let error =
match TransactionalError::decode(&mut bytes.clone().into_owned().as_slice()) {
Ok(err) => err,
Err(_) => {
warn!("Can't decode token error: ArithmeticError does not match known formats");
return DispatchError::Other(bytes.to_vec())
},
};
DispatchError::Transactional(error)
},
"CannotLookup" => DispatchError::CannotLookup,
"BadOrigin" => DispatchError::BadOrigin,
"ConsumerRemaining" => DispatchError::ConsumerRemaining,
"NoProviders" => DispatchError::NoProviders,
"TooManyConsumers" => DispatchError::TooManyConsumers,
"Exhausted" => DispatchError::Exhausted,
"Corruption" => DispatchError::Corruption,
"Unavailable" => DispatchError::Unavailable,
_ => DispatchError::Other(bytes.into_owned()),
};
return err
}

// If the error bytes don't correspond to a ModuleError, just return the bytes.
// This is perfectly reasonable and expected, so no logging.
if bytes[0] != module_variant_idx {
return DispatchError::Other(bytes.into_owned())
}

fn decode_module_error(bytes: Cow<[u8]>, metadata: &Metadata) -> Self {
// The remaining bytes are the module error, all being well:
let bytes = &bytes[1..];

// The oldest and second oldest type of error decode to this shape:
// The oldest and second oldest type of error decode to this shape.
// The old version is 2 bytes; a pallet and error index.
#[derive(Decode)]
struct LegacyModuleError {
index: u8,
error: u8,
}

// The newer case expands the error for forward compat:
// The new version is 5 bytes; a pallet and error index and then 3 extra bytes.
#[derive(Decode)]
struct CurrentModuleError {
index: u8,
Expand Down Expand Up @@ -180,6 +248,49 @@ impl DispatchError {
}
}

/// An error relating to tokens when dispatching a transaction.
#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode)]
pub enum TokenError {
/// Funds are unavailable.
FundsUnavailable,
/// Some part of the balance gives the only provider reference to the account and thus cannot be (re)moved.
OnlyProvider,
/// Account cannot exist with the funds that would be given.
BelowMinimum,
/// Account cannot be created.
CannotCreate,
/// The asset in question is unknown.
UnknownAsset,
/// Funds exist but are frozen.
Frozen,
/// Operation is not supported by the asset.
Unsupported,
/// Account cannot be created for a held balance.
CannotCreateHold,
/// Withdrawal would cause unwanted loss of account.
NotExpendable,
}

/// An error relating to arithmetic when dispatching a transaction.
#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode)]
pub enum ArithmeticError {
/// Underflow.
Underflow,
/// Overflow.
Overflow,
/// Division by zero.
DivisionByZero,
}

/// An error relating to thr transactional layers when dispatching a transaction.
#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode)]
pub enum TransactionalError {
/// Too many transactional layers have been spawned.
LimitReached,
/// A transactional layer was expected, but does not exist.
NoLayer,
}

/// Block error
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BlockError {
Expand Down

0 comments on commit 46b5785

Please sign in to comment.