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
+47 -10
View File
@@ -32,6 +32,7 @@ use std::{
HashMap,
HashSet,
},
fmt,
marker::{
PhantomData,
Send,
@@ -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(())
}
}
/// 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
@@ -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
}
@@ -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
}
@@ -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(),
@@ -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> {
@@ -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 {
@@ -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),
}
+19 -4
View File
@@ -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;
@@ -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;
+13 -1
View File
@@ -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.
///