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
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
57 changes: 47 additions & 10 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 @@ -71,20 +72,48 @@ impl std::fmt::Debug for RawEvent {
}
}

trait TypeSegmenter: Send {
/// Consumes an object from an input stream, and output the serialized bytes.
fn segment(&self, input: &mut &[u8], output: &mut Vec<u8>) -> Result<(), Error>;
}

#[derive(Default)]
struct TypeMarker<T>(PhantomData<T>);
impl<T> TypeSegmenter for TypeMarker<T>
where
T: Codec + Send,
{
fn segment(&self, input: &mut &[u8], output: &mut Vec<u8>) -> Result<(), Error> {
T::decode(input).map_err(Error::from)?.encode_to(output);
Ok(())
}
}

h4x3rotab marked this conversation as resolved.
Show resolved Hide resolved
/// Events decoder.
#[derive(Debug)]
pub struct EventsDecoder<T> {
metadata: Metadata,
type_sizes: HashMap<String, usize>,
type_segmenters: HashMap<String, Box<dyn TypeSegmenter>>,
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_segmenters",
&self.type_segmenters.keys().cloned().collect::<String>(),
)
.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 +138,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 @@ -118,7 +149,10 @@ impl<T: System> EventsDecoder<T> {
U: Default + Codec + Send + 'static,
{
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(TypeMarker::<U>::default()));
size
}

Expand All @@ -129,7 +163,7 @@ impl<T: System> EventsDecoder<T> {
for event in module.events() {
for arg in event.arguments() {
for primitive in arg.primitives() {
if !self.type_sizes.contains_key(&primitive) {
if !self.type_segmenters.contains_key(&primitive) {
missing.insert(format!(
"{}::{}::{}",
module.name(),
Expand All @@ -150,10 +184,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 +222,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.segment(input, &mut buf)?;
output.write(&buf);
Ok(())
} else {
Expand Down Expand Up @@ -268,9 +302,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
14 changes: 13 additions & 1 deletion src/frame/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,21 @@ pub struct SetPayeeCall<T: Staking> {
pub _runtime: PhantomData<T>,
}

/// Identity of a Grandpa authority.
pub type AuthorityId = crate::runtimes::app::grandpa::Public;
/// The weight of an authority.
pub type AuthorityWeight = u64;
/// A list of Grandpa authorities with associated weights.
pub type AuthorityList = Vec<(AuthorityId, AuthorityWeight)>;

/// 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