Refactor event type decoding and declaration (#221)

* Refactor event type decoding hand declartion

Fixes #196, #181, #28

## Dyanmic sized types

Before this change, the event decoder assume all the event types
have fixed sizes. Some counterexamples are: Hashes, AuthorityList.

In this change, instead of decoding by skipping the fixed-length bytes,
we introduce `type_segmenter` registry which decodes the raw event
bytes with the actual scale codec. So variable length types can be
handled correctly.

## New attribute for pallet type definition

In the past, trait associated type is the only way to add types to
the EventsDecoder implementation of a pallet. But in reality it's
common that the events in a pallet references some types not defined
in the trait associated types. Some examples are: `IdentificationTuple`
and `SessionIndex` in Session pallet.

In this change, we introduce more attributes to add the types:

```rust
#[module]
trait Pallet: System {
    #![event_type(SomeType)]
    #![event_alias(TypeNameAlias = SomeType)]
    #![event_alias(SomeOtherAlias = TypeWithAssociatedTypes<T>)]
}
```

## Tested

Compile with `nightly-2020-10-01`; smoke test to sync a full
Phala bockchain.

* Format code

* Make rustfmt::skip an outer attribute

* Ignore the sample code

* Alias the event segmenter closure

* Copy AuthorityList from sp_finality_grandpa

* Remove the unused static event type size

* Make segmenter as a trait, resue grandpa::Public

* Wrap PhantomData in struct TypeMarker
This commit is contained in:
h4x3rotab
2021-01-20 17:41:58 +08:00
committed by GitHub
parent 5a0201c130
commit 3c46002e67
5 changed files with 149 additions and 17 deletions
+55 -2
View File
@@ -69,16 +69,62 @@ fn events_decoder_trait_name(module: &syn::Ident) -> syn::Ident {
fn with_module_ident(module: &syn::Ident) -> syn::Ident {
format_ident!("with_{}", module.to_string().to_snake_case())
}
type EventAttr = utils::UniAttr<syn::Type>;
type EventAliasAttr = utils::UniAttr<utils::Attr<syn::Ident, syn::Type>>;
/// Parses the event type definition macros within #[module]
///
/// It supports two ways to define the associated event type:
///
/// ```ignore
/// #[module]
/// trait Pallet: System {
/// #![event_type(SomeType)]
/// #![event_alias(TypeNameAlias = SomeType)]
/// #![event_alias(SomeOtherAlias = TypeWithAssociatedTypes<T>)]
/// }
/// ```
fn parse_event_type_attr(attr: &syn::Attribute) -> Option<(String, syn::Type)> {
let ident = utils::path_to_ident(&attr.path);
if ident == "event_type" {
let attrs: EventAttr = syn::parse2(attr.tokens.clone())
.map_err(|err| abort!("{}", err))
.unwrap();
let ty = attrs.attr;
let ident_str = quote!(#ty).to_string();
Some((ident_str, ty))
} else if ident == "event_alias" {
let attrs: EventAliasAttr = syn::parse2(attr.tokens.clone())
.map_err(|err| abort!("{}", err))
.unwrap();
let ty = attrs.attr.value;
let ident_str = attrs.attr.key.to_string();
Some((ident_str, ty))
} else {
None
}
}
/// Attribute macro that registers the type sizes used by the module; also sets the `MODULE` constant.
pub fn module(_args: TokenStream, tokens: TokenStream) -> TokenStream {
let input: Result<syn::ItemTrait, _> = syn::parse2(tokens.clone());
let input = if let Ok(input) = input {
let mut input = if let Ok(input) = input {
input
} else {
// handle #[module(ignore)] by just returning the tokens
return tokens
};
// Parse the inner attributes `event_type` and `event_alias` and remove them from the macro
// outputs.
let (other_attrs, event_types): (Vec<_>, Vec<_>) = input
.attrs
.iter()
.cloned()
.partition(|attr| parse_event_type_attr(attr).is_none());
input.attrs = other_attrs;
let subxt = utils::use_crate("substrate-subxt");
let module = &input.ident;
let module_name = module.to_string();
@@ -96,7 +142,7 @@ pub fn module(_args: TokenStream, tokens: TokenStream) -> TokenStream {
None
}
});
let types = input.items.iter().filter_map(|item| {
let associated_types = input.items.iter().filter_map(|item| {
if let syn::TraitItem::Type(ty) = item {
if ignore(&ty.attrs) {
return None
@@ -110,6 +156,12 @@ pub fn module(_args: TokenStream, tokens: TokenStream) -> TokenStream {
None
}
});
let types = event_types.iter().map(|attr| {
let (ident_str, ty) = parse_event_type_attr(&attr).unwrap();
quote! {
self.register_type_size::<#ty>(#ident_str);
}
});
quote! {
#input
@@ -127,6 +179,7 @@ pub fn module(_args: TokenStream, tokens: TokenStream) -> TokenStream {
{
fn #with_module(&mut self) {
#(#bounds)*
#(#associated_types)*
#(#types)*
}
}
+15
View File
@@ -214,6 +214,21 @@ impl<K: Parse, V: Parse> Parse for Attr<K, V> {
}
}
#[derive(Debug)]
pub struct UniAttr<A> {
pub paren: syn::token::Paren,
pub attr: A,
}
impl<A: Parse> Parse for UniAttr<A> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
let paren = syn::parenthesized!(content in input);
let attr = content.parse()?;
Ok(Self { paren, attr })
}
}
#[cfg(test)]
pub(crate) fn assert_proc_macro(
result: proc_macro2::TokenStream,