mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-19 22:51:03 +00:00
codegen for root level error (#930)
* code gen for root error * cargo fmt * polkadot.rs regenerated * use pallet name and decode with metadata * remove pallet by name fn * test that we can decode a ModuleError via as_root_error * nits --------- Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||||
|
// see LICENSE for license details.
|
||||||
|
|
||||||
|
use frame_metadata::v15::PalletMetadata;
|
||||||
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
use quote::quote;
|
||||||
|
use scale_info::form::PortableForm;
|
||||||
|
|
||||||
|
use crate::types::TypeGenerator;
|
||||||
|
|
||||||
|
use super::CodegenError;
|
||||||
|
|
||||||
|
/// Generate error type alias from the provided pallet metadata.
|
||||||
|
pub fn generate_error_type_alias(
|
||||||
|
type_gen: &TypeGenerator,
|
||||||
|
pallet: &PalletMetadata<PortableForm>,
|
||||||
|
should_gen_docs: bool,
|
||||||
|
) -> Result<TokenStream2, CodegenError> {
|
||||||
|
let Some(error) = &pallet.error else {
|
||||||
|
return Ok(quote!());
|
||||||
|
};
|
||||||
|
|
||||||
|
let error_type = type_gen.resolve_type_path(error.ty.id);
|
||||||
|
let error_ty = type_gen.resolve_type(error.ty.id);
|
||||||
|
let docs = &error_ty.docs;
|
||||||
|
let docs = should_gen_docs
|
||||||
|
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||||
|
.unwrap_or_default();
|
||||||
|
Ok(quote! {
|
||||||
|
#docs
|
||||||
|
pub type Error = #error_type;
|
||||||
|
})
|
||||||
|
}
|
||||||
+52
-1
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
mod calls;
|
mod calls;
|
||||||
mod constants;
|
mod constants;
|
||||||
|
mod errors;
|
||||||
mod events;
|
mod events;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
||||||
@@ -320,10 +321,13 @@ impl RuntimeGenerator {
|
|||||||
should_gen_docs,
|
should_gen_docs,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let errors = errors::generate_error_type_alias(&type_gen, pallet, should_gen_docs)?;
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
pub mod #mod_name {
|
pub mod #mod_name {
|
||||||
use super::root_mod;
|
use super::root_mod;
|
||||||
use super::#types_mod_ident;
|
use super::#types_mod_ident;
|
||||||
|
#errors
|
||||||
#calls
|
#calls
|
||||||
#event
|
#event
|
||||||
#storage_mod
|
#storage_mod
|
||||||
@@ -371,6 +375,43 @@ impl RuntimeGenerator {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let outer_error_variants = self.metadata.pallets.iter().filter_map(|p| {
|
||||||
|
let variant_name = format_ident!("{}", p.name);
|
||||||
|
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
|
||||||
|
let index = proc_macro2::Literal::u8_unsuffixed(p.index);
|
||||||
|
|
||||||
|
p.error.as_ref().map(|_| {
|
||||||
|
quote! {
|
||||||
|
#[codec(index = #index)]
|
||||||
|
#variant_name(#mod_name::Error),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let outer_error = quote! {
|
||||||
|
#default_derives
|
||||||
|
pub enum Error {
|
||||||
|
#( #outer_error_variants )*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let root_error_if_arms = self.metadata.pallets.iter().filter_map(|p| {
|
||||||
|
let variant_name_str = &p.name;
|
||||||
|
let variant_name = format_ident!("{}", variant_name_str);
|
||||||
|
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||||
|
p.error.as_ref().map(|err|
|
||||||
|
{
|
||||||
|
let type_id = err.ty.id;
|
||||||
|
quote! {
|
||||||
|
if pallet_name == #variant_name_str {
|
||||||
|
let variant_error = #mod_name::Error::decode_with_metadata(cursor, #type_id, metadata)?;
|
||||||
|
return Ok(Error::#variant_name(variant_error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let mod_ident = &item_mod_ir.ident;
|
let mod_ident = &item_mod_ir.ident;
|
||||||
let pallets_with_constants: Vec<_> = pallets_with_mod_names
|
let pallets_with_constants: Vec<_> = pallets_with_mod_names
|
||||||
.iter()
|
.iter()
|
||||||
@@ -424,6 +465,16 @@ impl RuntimeGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#outer_error
|
||||||
|
impl #crate_path::error::RootError for Error {
|
||||||
|
fn root_error(pallet_bytes: &[u8], pallet_name: &str, metadata: &#crate_path::Metadata) -> Result<Self, #crate_path::Error> {
|
||||||
|
use #crate_path::metadata::DecodeWithMetadata;
|
||||||
|
let cursor = &mut &pallet_bytes[..];
|
||||||
|
#( #root_error_if_arms )*
|
||||||
|
Err(#crate_path::ext::scale_decode::Error::custom(format!("Pallet name '{}' not found in root Error enum", pallet_name)).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn constants() -> ConstantsApi {
|
pub fn constants() -> ConstantsApi {
|
||||||
ConstantsApi
|
ConstantsApi
|
||||||
}
|
}
|
||||||
@@ -495,7 +546,7 @@ where
|
|||||||
let ty = type_gen.resolve_type(type_id);
|
let ty = type_gen.resolve_type(type_id);
|
||||||
|
|
||||||
let scale_info::TypeDef::Variant(variant) = &ty.type_def else {
|
let scale_info::TypeDef::Variant(variant) = &ty.type_def else {
|
||||||
return Err(CodegenError::InvalidType(error_message_type_name.into()))
|
return Err(CodegenError::InvalidType(error_message_type_name.into()));
|
||||||
};
|
};
|
||||||
|
|
||||||
variant
|
variant
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ use core::fmt::Debug;
|
|||||||
use scale_decode::visitor::DecodeAsTypeResult;
|
use scale_decode::visitor::DecodeAsTypeResult;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use super::Error;
|
||||||
|
use crate::error::RootError;
|
||||||
|
|
||||||
/// An error dispatching a transaction.
|
/// An error dispatching a transaction.
|
||||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@@ -133,12 +136,13 @@ impl PartialEq for ModuleError {
|
|||||||
self.raw == other.raw
|
self.raw == other.raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for ModuleError {}
|
impl Eq for ModuleError {}
|
||||||
|
|
||||||
impl std::fmt::Display for ModuleError {
|
impl std::fmt::Display for ModuleError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let Ok(details) = self.details() else {
|
let Ok(details) = self.details() else {
|
||||||
return f.write_str("Unknown pallet error (pallet and error details cannot be retrieved)")
|
return f.write_str("Unknown pallet error (pallet and error details cannot be retrieved)");
|
||||||
};
|
};
|
||||||
|
|
||||||
let pallet = details.pallet();
|
let pallet = details.pallet();
|
||||||
@@ -159,6 +163,12 @@ impl ModuleError {
|
|||||||
pub fn raw(&self) -> RawModuleError {
|
pub fn raw(&self) -> RawModuleError {
|
||||||
self.raw
|
self.raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to decode the ModuleError into a value implementing the trait `RootError`
|
||||||
|
/// where the actual type of value is the generated top level enum `Error`.
|
||||||
|
pub fn as_root_error<E: RootError>(&self) -> Result<E, Error> {
|
||||||
|
E::root_error(&self.raw.error, self.details()?.pallet(), &self.metadata)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The error details about a module error that has occurred.
|
/// The error details about a module error that has occurred.
|
||||||
|
|||||||
+13
-1
@@ -14,7 +14,7 @@ pub use dispatch_error::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Re-expose the errors we use from other crates here:
|
// Re-expose the errors we use from other crates here:
|
||||||
pub use crate::metadata::{InvalidMetadataError, MetadataError};
|
pub use crate::metadata::{InvalidMetadataError, Metadata, MetadataError};
|
||||||
pub use scale_decode::Error as DecodeError;
|
pub use scale_decode::Error as DecodeError;
|
||||||
pub use scale_encode::Error as EncodeError;
|
pub use scale_encode::Error as EncodeError;
|
||||||
|
|
||||||
@@ -162,3 +162,15 @@ pub enum StorageAddressError {
|
|||||||
fields: usize,
|
fields: usize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This trait is implemented on the statically generated root ModuleError type
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub trait RootError: Sized {
|
||||||
|
/// Given details of the pallet error we want to decode
|
||||||
|
fn root_error(
|
||||||
|
// typically a [u8; 4] encodes the error of a pallet
|
||||||
|
pallet_bytes: &[u8],
|
||||||
|
pallet_name: &str,
|
||||||
|
metadata: &Metadata,
|
||||||
|
) -> Result<Self, Error>;
|
||||||
|
}
|
||||||
|
|||||||
@@ -350,6 +350,43 @@ async fn submit_large_extrinsic() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn decode_a_module_error() {
|
||||||
|
use node_runtime::runtime_types::pallet_assets::pallet as assets;
|
||||||
|
|
||||||
|
let ctx = test_context().await;
|
||||||
|
let api = ctx.client();
|
||||||
|
|
||||||
|
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||||
|
let alice_addr = alice.account_id().clone().into();
|
||||||
|
|
||||||
|
// Trying to work with an asset ID 1 which doesn't exist should return an
|
||||||
|
// "unknown" module error from the assets pallet.
|
||||||
|
let freeze_unknown_asset = node_runtime::tx().assets().freeze(1, alice_addr);
|
||||||
|
|
||||||
|
let err = api
|
||||||
|
.tx()
|
||||||
|
.sign_and_submit_then_watch_default(&freeze_unknown_asset, &alice)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.wait_for_finalized_success()
|
||||||
|
.await
|
||||||
|
.expect_err("an 'unknown asset' error");
|
||||||
|
|
||||||
|
let Error::Runtime(DispatchError::Module(module_err)) = err else {
|
||||||
|
panic!("Expected a ModuleError, got {err:?}");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Decode the error into our generated Error type.
|
||||||
|
let decoded_err = module_err.as_root_error::<node_runtime::Error>().unwrap();
|
||||||
|
|
||||||
|
// Decoding should result in an Assets.Unknown error:
|
||||||
|
assert_eq!(
|
||||||
|
decoded_err,
|
||||||
|
node_runtime::Error::Assets(assets::Error::Unknown)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn unsigned_extrinsic_is_same_shape_as_polkadotjs() {
|
async fn unsigned_extrinsic_is_same_shape_as_polkadotjs() {
|
||||||
let ctx = test_context().await;
|
let ctx = test_context().await;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user