mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 07:01:05 +00:00
Obtain DispatchError::Module info dynamically (#453)
* Add error information back into metadata to roll back removal in #394 * Go back to obtaining runtime error info * re-do codegen too to check that it's all gravy * Convert DispatchError module errors into a module variant to make them easier to work with * Fix broken doc link
This commit is contained in:
+5
-2
@@ -19,7 +19,10 @@ use sp_runtime::traits::Hash;
|
||||
pub use sp_runtime::traits::SignedExtension;
|
||||
|
||||
use crate::{
|
||||
error::BasicError,
|
||||
error::{
|
||||
BasicError,
|
||||
HasModuleError,
|
||||
},
|
||||
extrinsic::{
|
||||
self,
|
||||
SignedExtra,
|
||||
@@ -206,7 +209,7 @@ where
|
||||
X: SignedExtra<T>,
|
||||
A: AccountData,
|
||||
C: Call + Send + Sync,
|
||||
E: Decode,
|
||||
E: Decode + HasModuleError,
|
||||
Evs: Decode,
|
||||
{
|
||||
/// Create a new [`SubmittableExtrinsic`].
|
||||
|
||||
@@ -70,6 +70,9 @@ pub enum GenericError<E> {
|
||||
/// Transaction progress error.
|
||||
#[error("Transaction error: {0}")]
|
||||
Transaction(#[from] TransactionError),
|
||||
#[error("Module error: {0}")]
|
||||
/// An error from the `Module` variant of the generated `DispatchError`.
|
||||
Module(ModuleError),
|
||||
/// Other error.
|
||||
#[error("Other error: {0}")]
|
||||
Other(String),
|
||||
@@ -94,6 +97,7 @@ impl<E> GenericError<E> {
|
||||
GenericError::Metadata(e) => GenericError::Metadata(e),
|
||||
GenericError::EventsDecoding(e) => GenericError::EventsDecoding(e),
|
||||
GenericError::Transaction(e) => GenericError::Transaction(e),
|
||||
GenericError::Module(e) => GenericError::Module(e),
|
||||
GenericError::Other(e) => GenericError::Other(e),
|
||||
// This is the only branch we really care about:
|
||||
GenericError::Runtime(e) => GenericError::Runtime(f(e)),
|
||||
@@ -167,3 +171,24 @@ pub enum TransactionError {
|
||||
#[error("The block containing the transaction can no longer be found (perhaps it was on a non-finalized fork?)")]
|
||||
BlockHashNotFound,
|
||||
}
|
||||
|
||||
/// Details about a module error that has occurred.
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[error("{pallet}: {error}\n\n{}", .description.join("\n"))]
|
||||
pub struct ModuleError {
|
||||
/// The name of the pallet that the error came from.
|
||||
pub pallet: String,
|
||||
/// The name of the error.
|
||||
pub error: String,
|
||||
/// A description of the error.
|
||||
pub description: Vec<String>,
|
||||
}
|
||||
|
||||
/// This trait is automatically implemented for the generated `DispatchError`,
|
||||
/// so that we can pluck out information about the `Module` error variant, if`
|
||||
/// it exists.
|
||||
pub trait HasModuleError {
|
||||
/// If the error has a `Module` variant, return a tuple of the
|
||||
/// pallet index and error index. Else, return `None`.
|
||||
fn module_error_indices(&self) -> Option<(u8, u8)>;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ pub use crate::{
|
||||
BasicError,
|
||||
Error,
|
||||
GenericError,
|
||||
HasModuleError,
|
||||
RuntimeError,
|
||||
TransactionError,
|
||||
},
|
||||
@@ -98,6 +99,7 @@ pub use crate::{
|
||||
UncheckedExtrinsic,
|
||||
},
|
||||
metadata::{
|
||||
ErrorMetadata,
|
||||
Metadata,
|
||||
MetadataError,
|
||||
PalletMetadata,
|
||||
|
||||
@@ -84,6 +84,7 @@ pub struct Metadata {
|
||||
metadata: RuntimeMetadataLastVersion,
|
||||
pallets: HashMap<String, PalletMetadata>,
|
||||
events: HashMap<(u8, u8), EventMetadata>,
|
||||
errors: HashMap<(u8, u8), ErrorMetadata>,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
@@ -107,6 +108,19 @@ impl Metadata {
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
/// Returns the metadata for the error at the given pallet and error indices.
|
||||
pub fn error(
|
||||
&self,
|
||||
pallet_index: u8,
|
||||
error_index: u8,
|
||||
) -> Result<&ErrorMetadata, MetadataError> {
|
||||
let error = self
|
||||
.errors
|
||||
.get(&(pallet_index, error_index))
|
||||
.ok_or(MetadataError::ErrorNotFound(pallet_index, error_index))?;
|
||||
Ok(error)
|
||||
}
|
||||
|
||||
/// Resolve a type definition.
|
||||
pub fn resolve_type(&self, id: u32) -> Option<&Type<PortableForm>> {
|
||||
self.metadata.types.resolve(id)
|
||||
@@ -169,6 +183,7 @@ impl PalletMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for specific events.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EventMetadata {
|
||||
pallet: String,
|
||||
@@ -193,6 +208,31 @@ impl EventMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for specific errors.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ErrorMetadata {
|
||||
pallet: String,
|
||||
error: String,
|
||||
variant: Variant<PortableForm>,
|
||||
}
|
||||
|
||||
impl ErrorMetadata {
|
||||
/// Get the name of the pallet from which the error originates.
|
||||
pub fn pallet(&self) -> &str {
|
||||
&self.pallet
|
||||
}
|
||||
|
||||
/// Get the name of the specific pallet error.
|
||||
pub fn error(&self) -> &str {
|
||||
&self.error
|
||||
}
|
||||
|
||||
/// Get the description of the specific pallet error.
|
||||
pub fn description(&self) -> &[String] {
|
||||
self.variant.docs()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum InvalidMetadataError {
|
||||
#[error("Invalid prefix")]
|
||||
@@ -293,10 +333,36 @@ impl TryFrom<RuntimeMetadataPrefixed> for Metadata {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let pallet_errors = metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.filter_map(|pallet| {
|
||||
pallet.error.as_ref().map(|error| {
|
||||
let type_def_variant = get_type_def_variant(error.ty.id())?;
|
||||
Ok((pallet, type_def_variant))
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let errors = pallet_errors
|
||||
.iter()
|
||||
.flat_map(|(pallet, type_def_variant)| {
|
||||
type_def_variant.variants().iter().map(move |var| {
|
||||
let key = (pallet.index, var.index());
|
||||
let value = ErrorMetadata {
|
||||
pallet: pallet.name.clone(),
|
||||
error: var.name().clone(),
|
||||
variant: var.clone(),
|
||||
};
|
||||
(key, value)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
metadata,
|
||||
pallets,
|
||||
events,
|
||||
errors,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ use crate::{
|
||||
error::{
|
||||
BasicError,
|
||||
Error,
|
||||
HasModuleError,
|
||||
ModuleError,
|
||||
RuntimeError,
|
||||
TransactionError,
|
||||
},
|
||||
@@ -54,7 +56,7 @@ use jsonrpsee::core::{
|
||||
/// returned from [`crate::SubmittableExtrinsic::sign_and_submit_then_watch()`].
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = ""))]
|
||||
pub struct TransactionProgress<'client, T: Config, E: Decode, Evs: Decode> {
|
||||
pub struct TransactionProgress<'client, T: Config, E, Evs> {
|
||||
sub: Option<RpcSubscription<SubstrateTransactionStatus<T::Hash, T::Hash>>>,
|
||||
ext_hash: T::Hash,
|
||||
client: &'client Client<T>,
|
||||
@@ -64,12 +66,11 @@ pub struct TransactionProgress<'client, T: Config, E: Decode, Evs: Decode> {
|
||||
// The above type is not `Unpin` by default unless the generic param `T` is,
|
||||
// so we manually make it clear that Unpin is actually fine regardless of `T`
|
||||
// (we don't care if this moves around in memory while it's "pinned").
|
||||
impl<'client, T: Config, E: Decode, Evs: Decode> Unpin
|
||||
for TransactionProgress<'client, T, E, Evs>
|
||||
{
|
||||
}
|
||||
impl<'client, T: Config, E, Evs> Unpin for TransactionProgress<'client, T, E, Evs> {}
|
||||
|
||||
impl<'client, T: Config, E: Decode, Evs: Decode> TransactionProgress<'client, T, E, Evs> {
|
||||
impl<'client, T: Config, E: Decode + HasModuleError, Evs: Decode>
|
||||
TransactionProgress<'client, T, E, Evs>
|
||||
{
|
||||
/// Instantiate a new [`TransactionProgress`] from a custom subscription.
|
||||
pub fn new(
|
||||
sub: RpcSubscription<SubstrateTransactionStatus<T::Hash, T::Hash>>,
|
||||
@@ -171,7 +172,7 @@ impl<'client, T: Config, E: Decode, Evs: Decode> TransactionProgress<'client, T,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'client, T: Config, E: Decode, Evs: Decode> Stream
|
||||
impl<'client, T: Config, E: Decode + HasModuleError, Evs: Decode> Stream
|
||||
for TransactionProgress<'client, T, E, Evs>
|
||||
{
|
||||
type Item = Result<TransactionStatus<'client, T, E, Evs>, BasicError>;
|
||||
@@ -340,7 +341,9 @@ pub struct TransactionInBlock<'client, T: Config, E: Decode, Evs: Decode> {
|
||||
_error: PhantomDataSendSync<(E, Evs)>,
|
||||
}
|
||||
|
||||
impl<'client, T: Config, E: Decode, Evs: Decode> TransactionInBlock<'client, T, E, Evs> {
|
||||
impl<'client, T: Config, E: Decode + HasModuleError, Evs: Decode>
|
||||
TransactionInBlock<'client, T, E, Evs>
|
||||
{
|
||||
pub(crate) fn new(
|
||||
block_hash: T::Hash,
|
||||
ext_hash: T::Hash,
|
||||
@@ -387,7 +390,18 @@ impl<'client, T: Config, E: Decode, Evs: Decode> TransactionInBlock<'client, T,
|
||||
let ev = ev?;
|
||||
if &ev.pallet == "System" && &ev.variant == "ExtrinsicFailed" {
|
||||
let dispatch_error = E::decode(&mut &*ev.data)?;
|
||||
return Err(Error::Runtime(RuntimeError(dispatch_error)))
|
||||
if let Some((pallet_idx, error_idx)) =
|
||||
dispatch_error.module_error_indices()
|
||||
{
|
||||
let details = self.client.metadata().error(pallet_idx, error_idx)?;
|
||||
return Err(Error::Module(ModuleError {
|
||||
pallet: details.pallet().to_string(),
|
||||
error: details.error().to_string(),
|
||||
description: details.description().to_vec(),
|
||||
}))
|
||||
} else {
|
||||
return Err(Error::Runtime(RuntimeError(dispatch_error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user