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

Refactor event type decoding and declaration #221

Merged
merged 9 commits into from
Jan 20, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ frame-metadata = "12.0.1"
frame-support = "2.0.1"
sp-runtime = "2.0.1"
sp-version = "2.0.1"
sp-finality-grandpa = { version = "2.0.1", default-features = false }
pallet-indices = "2.0.1"
hex = "0.4.2"
sp-std = "2.0.1"
Expand Down
57 changes: 55 additions & 2 deletions proc-macro/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand All @@ -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
Expand All @@ -127,6 +179,7 @@ pub fn module(_args: TokenStream, tokens: TokenStream) -> TokenStream {
{
fn #with_module(&mut self) {
#(#bounds)*
#(#associated_types)*
#(#types)*
}
}
Expand Down
15 changes: 15 additions & 0 deletions proc-macro/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
42 changes: 36 additions & 6 deletions src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use std::{
HashMap,
HashSet,
},
fmt,
marker::{
PhantomData,
Send,
Expand Down Expand Up @@ -72,19 +73,32 @@ impl std::fmt::Debug for RawEvent {
}

/// Events decoder.
#[derive(Debug)]
pub struct EventsDecoder<T> {
metadata: Metadata,
type_sizes: HashMap<String, usize>,
type_segmenters: HashMap<
String,
Box<dyn Fn(&mut &[u8], &mut Vec<u8>) -> Result<(), Error> + Send>,
>,
marker: PhantomData<fn() -> T>,
}

impl<T> fmt::Debug for EventsDecoder<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventsDecoder<T>")
.field("metadata", &self.metadata)
.field("type_sizes", &self.type_sizes)
.finish()
}
}

impl<T: System> EventsDecoder<T> {
/// Creates a new `EventsDecoder`.
pub fn new(metadata: Metadata) -> Self {
let mut decoder = Self {
metadata,
type_sizes: HashMap::new(),
type_segmenters: HashMap::new(),
marker: PhantomData,
};
// register default event arg type sizes for dynamic decoding of events
Expand All @@ -109,6 +123,8 @@ impl<T: System> EventsDecoder<T> {
decoder.register_type_size::<T::BlockNumber>("BlockNumber");
decoder.register_type_size::<T::Hash>("Hash");
decoder.register_type_size::<u8>("VoteThreshold");
// Additional types
decoder.register_type_size::<(T::BlockNumber, u32)>("TaskAddress<BlockNumber>");
decoder
}

Expand All @@ -119,6 +135,17 @@ impl<T: System> EventsDecoder<T> {
{
let size = U::default().encode().len();
self.type_sizes.insert(name.to_string(), size);
// A segmenter decodes a type from an input stream (&mut &[u8]) and returns the serialized
// type to the output stream (&mut Vec<u8>).
self.type_segmenters.insert(
name.to_string(),
Box::new(
|input: &mut &[u8], output: &mut Vec<u8>| -> Result<(), Error> {
U::decode(input).map_err(Error::from)?.encode_to(output);
Ok(())
},
),
);
size
}

Expand Down Expand Up @@ -150,10 +177,10 @@ impl<T: System> EventsDecoder<T> {
}
}

fn decode_raw_bytes<I: Input, W: Output>(
fn decode_raw_bytes<W: Output>(
&self,
args: &[EventArg],
input: &mut I,
input: &mut &[u8],
output: &mut W,
errors: &mut Vec<RuntimeError>,
) -> Result<(), Error> {
Expand Down Expand Up @@ -188,9 +215,9 @@ impl<T: System> EventsDecoder<T> {
"DispatchResult" => DispatchResult::decode(input)?,
"DispatchError" => Err(DispatchError::decode(input)?),
_ => {
if let Some(size) = self.type_sizes.get(name) {
let mut buf = vec![0; *size];
input.read(&mut buf)?;
if let Some(seg) = self.type_segmenters.get(name) {
let mut buf = Vec::<u8>::new();
seg(input, &mut buf)?;
output.write(&buf);
Ok(())
} else {
Expand Down Expand Up @@ -268,9 +295,12 @@ impl<T: System> EventsDecoder<T> {
}
}

/// Raw event or error event
#[derive(Debug)]
pub enum Raw {
/// Event
Event(RawEvent),
/// Error
Error(RuntimeError),
}

Expand Down
23 changes: 19 additions & 4 deletions src/frame/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@
// along with substrate-subxt. If not, see <http://www.gnu.org/licenses/>.

//! Session support
use crate::frame::system::{
System,
SystemEventsDecoder as _,
use crate::frame::{
balances::{
Balances,
BalancesEventsDecoder as _,
},
system::{
System,
SystemEventsDecoder as _,
},
};
use codec::Encode;
use frame_support::Parameter;
Expand Down Expand Up @@ -45,9 +51,18 @@ macro_rules! default_impl {
};
}

type IdentificationTuple<T> = (
<T as Session>::ValidatorId,
pallet_staking::Exposure<<T as System>::AccountId, <T as Balances>::Balance>,
);

/// The trait needed for this module.
#[module]
pub trait Session: System {
pub trait Session: System + Balances {
#![event_alias(IdentificationTuple = IdentificationTuple<T>)]
#![event_alias(OpaqueTimeSlot = Vec<u8>)]
#![event_alias(SessionIndex = u32)]

/// The validator account identifier type for the runtime.
type ValidatorId: Parameter + Debug + Ord + Default + Send + Sync + 'static;

Expand Down
9 changes: 8 additions & 1 deletion src/frame/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ use std::{
marker::PhantomData,
};

use sp_finality_grandpa::AuthorityList;

pub use pallet_staking::{
ActiveEraInfo,
EraIndex,
Expand Down Expand Up @@ -64,7 +66,12 @@ pub struct SetPayeeCall<T: Staking> {

/// The subset of the `frame::Trait` that a client must implement.
#[module]
pub trait Staking: Balances {}
#[rustfmt::skip]
pub trait Staking: Balances {
#![event_alias(ElectionCompute = u8)]
#![event_type(EraIndex)]
#![event_type(AuthorityList)]
}

/// Number of eras to keep in history.
///
Expand Down