-
Notifications
You must be signed in to change notification settings - Fork 255
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
Add pallet name as constant #734
Comments
Sorry for the long delay; I was off during December! Perhaps you could elaborate a little? 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>,
/// A byte representation of the error.
pub error_data: ModuleErrorData,
} And the pallet name is available as |
@gregdhill do you still have an issue here? I feel I may have missed something, but if not I'll close this :) |
@jsdw the module error does indeed contain the pallet name but the problem is that we want to pattern match on that from the auto-generated code. Let's take |
Ah I think I see now, so if we were to add into the generated output something like, say, an: pub enum InvalidSpecName {
Foo,
Bar
}
impl InvalidSpecName {
pub fn name(&self) -> {
match self {
Foo => "Foo",
Bar => "Bar",
}
}
} Would that give you what you want? |
Or perhaps we could do more compact codegen by having an |
So I've looked into this a bit more to see what can be done and try to understand it better. So this is what I believe you want to be able to do (please lemme know if I'm wrong as I think I haven't grasped this fully): We get error info back in this form when something goes wrong: 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>,
/// A byte representation of the error.
pub error_data: ModuleErrorData,
} And I think that you want to be able to match these up with the error types in the generated code that look like eg: pub mod api {
// ...
pub mod runtime_types {
// ...
pub mod pallet_authorship {
use super::runtime_types;
pub mod pallet {
// ...
pub enum Error {
#[codec(index = 0)]
#[doc = "The uncle parent not in the chain."]
InvalidUncleParent,
#[codec(index = 1)]
#[doc = "Uncles already set in the block."]
UnclesAlreadySet,
#[codec(index = 2)]
#[doc = "Too many uncles."]
TooManyUncles,
#[codec(index = 3)]
#[doc = "The uncle is genesis."]
GenesisUncle,
#[codec(index = 4)]
#[doc = "The uncle is too high in chain."]
TooHighUncle,
#[codec(index = 5)]
#[doc = "The uncle is already included."]
UncleAlreadyIncluded,
#[codec(index = 6)]
#[doc = "The uncle isn't recent enough to be included."]
OldUncle,
}
}
}
// ...
}
} And in your code, to do that you currently:
It looks like you don't use the generated errors in any other way. I guess what you'd prefer is to be able to do something more like: fn is_module_err(&self, error: &impl PalletError) -> bool {
matches!(
self,
Error::SubxtRuntimeError(SubxtError::Runtime(DispatchError::Module(ModuleError{
pallet, error, ..
}))) if pallet == PalletError::Pallet && error == PalletError::Name,
)
}
// ...
pub fn is_issue_completed(&self) -> bool {
self.is_module_err(&IssuePalletError::IssueCompleted)
} Noting that you'd still need the manual Of course, the fn is_module_err(&self, pallet_name: &str, error_name: &str) -> bool {
match self {
Error::SubxtRuntimeError(subxt_error) => subxe_error.is_module_error(pallet_name, error_name),
_ => false
}
} Ultimately, without a top level Additionally, as the So perhaps we can do some small things to help, but when we get a top level |
You have understood the problem exactly, sorry if this wasn't already clear from my initial description!
That solution looks ideal, some way to have the static metadata change result in a compile-time error would be preferred. That is the reason even
I'm not sure I follow this, when |
Aah I see; verifying that the errors you get back aline up with the generated types then is really the main driver for this all, rather than code complexity, because otherwise you could do something like this quite easily: pub fn is_issue_completed(&self) -> bool {
self.is_module_err("SomePalletName", "IssueCompleted")
} If you want to catch the codegen pallet or error name changing, importing it is enough: pub fn is_issue_completed(&self) -> bool {
use api::runtime_types::pallet_name::Error;
self.is_module_err("SomePalletName", Error::IssueCompleted)
} Here, Since the pallet name is sortof encoded into the error module path, if the pallet name were to change, just importing the error from it at all would likely start leading to a compile error. So it's not perfect in terms of API but I think you can achieve comparable compile-time safety today.
Nope; alas not! Subxt just mindlessly iterates though all of the types seen in the metadata and generates code corresponding to them. Some of those types happen to correspond to errors and such, but to Subxt they are all just "types from the registry". If the metadata pointed explicitly to some overarching enum Error {
Pallet1(path::to::pallet1::Error),
Pallet2(path::to::pallet2::Error),
// ...
} Then we would know that they were special in some way, but also your code could just look something like: pub fn is_issue_completed(&self) -> bool {
use api::runtime_types::Error;
self.is_module_err(Error::Pallet1(path::to::pallet1::IssueCompleted))
} And by encoding that error like |
Random thought that occurred to me after doing some other work around root level Events. We actually generate a root level event manually in Subxt (and then in an upcoming PR will have a trait/way to decode arbitrary things into those root level events. Perhaps we could also manually generate a root level error like this:
And then provide an |
So, we get back errors into this struct: pub struct RawModuleError {
/// Index of the pallet that the error came from.
pub pallet_index: u8,
/// Raw error bytes.
pub error: [u8; 4],
} We have the pallet index, and error index in the pallet ( I think that the next 3 bytes might be the rest of the bytes needed to decode into one of the generated error types (not all error variants are empty; some have data, and looking at the errors generated for Polkadot, none have data more than an additional byte or so). Steps to implement this:
Then, somebody can write a function like the following to see whether a subxt error is some specific module error, for instance: fn is_err(error: SubxtError, target: api::runtime_types::Error) -> bool {
let SubxtError::Runtime(DispatchError::Module(module_err) = subxt_error else {
return false
}
module_err.as_root_error<api::runtime_types::Error>() == target
} |
For error matching we have to compare strings on the
ModuleError
(see related issue #313). We do this by printing the debug variant of the generatedError
field (see here) but there is also no easy way to extract the pallet name which requires us to hard-code the string (see here). I'm not quite sure what better matching support would look like but a nice in-between step for our usage would be to simply extract the name into a constant.The text was updated successfully, but these errors were encountered: