Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the generated DispatchError instead of the hardcoded Substrate one #394

Merged
merged 23 commits into from
Jan 20, 2022

Conversation

jsdw
Copy link
Collaborator

@jsdw jsdw commented Jan 14, 2022

The intention of this PR is to use the version of DispatchError generated from the metadata, rather than using a baked in version which may not be compatible with the node being targeted.

A rough summary of the changes:

  • Our Error type becomes Error<E> (actually, GenericError<RuntimeError<E>> under the hood) where E is the generated DispatchError.
  • A related type, BasicError (actually GenericError<Infallible> under the hood) also exists for the cases where we don't intend to return a Runtime error (ie most cases).
  • An E param was added where necessary to accomodate this new generic parameter in the error type (although using BasicError negates the need for it in many cases).
  • We previously had logic to obtain nicer error information (strings for name, docs) at runtime when converting from a DispatchError to our error type. This logic has moved to compile-time, so that we can obtain more details from the generated DispatchError more ergonomically
  • Debug has been added as a default trait for generated types to impl, mainly so that the generated DispatchError and its sub types can be debug printed inside our Error type.

@jsdw jsdw marked this pull request as ready for review January 19, 2022 16:38
Copy link
Contributor

@dvdplm dvdplm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM, nice work!

src/error.rs Outdated
GenericError::Invalid(e) => GenericError::Invalid(e),
GenericError::InvalidMetadata(e) => GenericError::InvalidMetadata(e),
GenericError::Metadata(e) => GenericError::Metadata(e),
GenericError::Runtime(e) => GenericError::Runtime(f(e)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a comment here to point the reader to the salient spot?

@@ -117,12 +118,12 @@ impl<'a, T: Config> EventSubscription<'a, T> {
}

/// Filters events by type.
pub fn filter_event<E: Event>(&mut self) {
self.event = Some((E::PALLET, E::EVENT));
pub fn filter_event<Ev: Event>(&mut self) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I for one very much appreciate these renames of the generics. :)

})
.collect::<Result<Vec<(_, _)>, _>>()?;

let errors = pallet_errors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can unify these two chains into a single? Like, why collect the pallet_errors first and then build the errors Vec?

Copy link
Collaborator Author

@jsdw jsdw Jan 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a bit tricky; we end up with Result<_> Items just prior to that collect, so the collecting I think is just there to give a chance to bail if we encounter any errors! I'll see whether I can do it in a clean way!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I settled on standard for loops; it felt tidier:

let mut pallet_errors = vec![];
for pallet in &metadata.pallets {
    let error = match &pallet.error {
        Some(err) => err,
        None => continue
    };

    let type_def_variant = get_type_def_variant(error.ty.id())?;
    for var in type_def_variant.variants().iter() {
        pallet_errors.push(ErrorMetadata {
            pallet_index: pallet.index,
            error_index: var.index(),
            pallet: pallet.name.clone(),
            error: var.name().clone(),
            variant: var.clone(),
        });
    }
}

}

#[derive(Clone, Debug)]
pub struct ErrorMetadata {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing docs here. Also, what do you think about moving this to the top?

pub error_index: u8,
pub pallet: String,
pub error: String,
variant: scale_info::Variant<scale_info::form::PortableForm>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dq: what is the reason for storing the variant for?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, for the description()?

Copy link
Collaborator Author

@jsdw jsdw Jan 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, that's it! I'm not sure why I did/left this now. I've made it be just the doc string, which simplifies things a bit!


impl ErrorMetadata {
pub fn description(&self) -> String {
self.variant.docs().join("\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if the node is compiled to strip the docs, this will be empty? Worth detecting and add some kind of default, "No docs available in the metadata"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the empty case docs will just be "", which seems good enough (that can be detected and handled down the line in whichever way things prefer)

}

#[derive(Debug)]
pub enum InvalidMetadataError {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing docs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah; doesn't need to be pub :)

@@ -16,7 +16,7 @@

#[subxt::subxt(
runtime_metadata_path = "examples/polkadot_metadata.scale",
generated_type_derives = "Clone, Debug"
generated_type_derives = "Clone"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment here and on runtime_metadata_path would be good.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking further, I think this whole example could use some attention. I am not sure what it actually does. :/
(Not a concern for this PR ofc)

Copy link
Collaborator Author

@jsdw jsdw Jan 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave the exampel a wee tidy; it's purpose appears solely to be showing off the generated_type_derives usage, so I made that a little clearer (and I didn't see a need for the strange way of calling clone offhand).

Copy link
Member

@niklasad1 niklasad1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, LGTM

@jsdw jsdw merged commit 6437959 into master Jan 20, 2022
@jsdw jsdw deleted the jsdw-dispatch-error branch January 20, 2022 16:35
@jsdw jsdw mentioned this pull request Jan 21, 2022
18 tasks
0623forbidden pushed a commit to DEIPworld/substrate-subxt that referenced this pull request Feb 15, 2022
paritytech#394)

* WIP DispatchError generic param

* main crate now compiling with new E generic param for DispatchError

* Remove E param from RpcClient since it doesn't really need it

* Point to generated DispatchError in codegen so no need for additional param there

* More error hunting; cargo check --all-targets passes now

* Use our own RuntimeVersion struct (for now) to avoid error decoding into sp_version::RuntimeVersion

* cargo fmt

* fix docs and expose private documented thing that I think should be pub

* move error info to compile time so that we can make DispatchError a little nicer to work with

* cargo fmt

* clippy

* Rework error handling to remove <E> param in most cases

* fix Error doc ambiguity (hopefully)

* doc tweaks

* docs: remove dismbiguation thing that isn't needed now

* One more Error<E> that can be a BasicError

* rewrite pallet errors thing into normal loops to tidy

* tidy errors codegen a little

* tidy examples/custom_type_derives.rs a little

* cargo fmt

* silcnce clippy in example
dvdplm pushed a commit that referenced this pull request Feb 17, 2022
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants