mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 17:01:09 +00:00
Add hooks to register event types for decoding (#227)
* Global registration of type segmenters for event decoding * Perform type sizes check when building client * Introduce EventTypeRegistry for global runtime type sizes * Fmt * Register runtime type sizes on creation of EventTypeRegistry * Register more default dispatch types * Add missing type sizes * fmt * Fix up register_type_size builder method * Update doc comments * Make register_default_type_sizes public * Don't allow duplicate registered types * Remove call to supertraits type registration, done manually in Runtime * Fix tests and warnings * Fix duplicate type registration * Fmt * review: use is_empty() Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> * Add panic docs Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
+1
-1
@@ -19,7 +19,7 @@ fn_single_line = false
|
|||||||
where_single_line = false
|
where_single_line = false
|
||||||
imports_indent = "Block"
|
imports_indent = "Block"
|
||||||
imports_layout = "Vertical" # changed
|
imports_layout = "Vertical" # changed
|
||||||
imports_granularity= "Crate" # changed
|
imports_granularity = "Crate" # changed
|
||||||
reorder_imports = true
|
reorder_imports = true
|
||||||
reorder_modules = true
|
reorder_modules = true
|
||||||
reorder_impl_items = false
|
reorder_impl_items = false
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ codec = { package = "parity-scale-codec", version = "2.0.0", default-features =
|
|||||||
# temporarily pinning funty via codec -> bitvec until https://github.com/myrrlyn/funty/issues/3
|
# temporarily pinning funty via codec -> bitvec until https://github.com/myrrlyn/funty/issues/3
|
||||||
# and https://github.com/bitvecto-rs/bitvec/issues/105 are resolved
|
# and https://github.com/bitvecto-rs/bitvec/issues/105 are resolved
|
||||||
funty = "=1.1.0"
|
funty = "=1.1.0"
|
||||||
|
dyn-clone = "1.0.4"
|
||||||
|
|
||||||
frame-metadata = "13.0.0"
|
frame-metadata = "13.0.0"
|
||||||
frame-support = "3.0.0"
|
frame-support = "3.0.0"
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
use sp_keyring::AccountKeyring;
|
use sp_keyring::AccountKeyring;
|
||||||
use substrate_subxt::{
|
use substrate_subxt::{
|
||||||
balances::{
|
balances::{
|
||||||
BalancesEventsDecoder,
|
|
||||||
TransferCallExt,
|
TransferCallExt,
|
||||||
TransferEvent,
|
TransferEvent,
|
||||||
},
|
},
|
||||||
@@ -25,7 +24,6 @@ use substrate_subxt::{
|
|||||||
ClientBuilder,
|
ClientBuilder,
|
||||||
DefaultNodeRuntime,
|
DefaultNodeRuntime,
|
||||||
EventSubscription,
|
EventSubscription,
|
||||||
EventsDecoder,
|
|
||||||
PairSigner,
|
PairSigner,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,8 +36,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let client = ClientBuilder::<DefaultNodeRuntime>::new().build().await?;
|
let client = ClientBuilder::<DefaultNodeRuntime>::new().build().await?;
|
||||||
let sub = client.subscribe_events().await?;
|
let sub = client.subscribe_events().await?;
|
||||||
let mut decoder = EventsDecoder::<DefaultNodeRuntime>::new(client.metadata().clone());
|
let decoder = client.events_decoder();
|
||||||
decoder.with_balances();
|
|
||||||
let mut sub = EventSubscription::<DefaultNodeRuntime>::new(sub, decoder);
|
let mut sub = EventSubscription::<DefaultNodeRuntime>::new(sub, decoder);
|
||||||
sub.filter_event::<TransferEvent<_>>();
|
sub.filter_event::<TransferEvent<_>>();
|
||||||
client.transfer(&signer, &dest, 10_000).await?;
|
client.transfer(&signer, &dest, 10_000).await?;
|
||||||
|
|||||||
@@ -32,10 +32,6 @@ pub fn call(s: Structure) -> TokenStream {
|
|||||||
let generics = &s.ast().generics;
|
let generics = &s.ast().generics;
|
||||||
let params = utils::type_params(generics);
|
let params = utils::type_params(generics);
|
||||||
let module = utils::module_name(generics);
|
let module = utils::module_name(generics);
|
||||||
let with_module = format_ident!(
|
|
||||||
"with_{}",
|
|
||||||
utils::path_to_ident(module).to_string().to_snake_case()
|
|
||||||
);
|
|
||||||
let call_name = utils::ident_to_name(ident, "Call").to_snake_case();
|
let call_name = utils::ident_to_name(ident, "Call").to_snake_case();
|
||||||
let bindings = utils::bindings(&s);
|
let bindings = utils::bindings(&s);
|
||||||
let fields = utils::fields(&bindings);
|
let fields = utils::fields(&bindings);
|
||||||
@@ -51,11 +47,6 @@ pub fn call(s: Structure) -> TokenStream {
|
|||||||
impl#generics #subxt::Call<T> for #ident<#(#params),*> {
|
impl#generics #subxt::Call<T> for #ident<#(#params),*> {
|
||||||
const MODULE: &'static str = MODULE;
|
const MODULE: &'static str = MODULE;
|
||||||
const FUNCTION: &'static str = #call_name;
|
const FUNCTION: &'static str = #call_name;
|
||||||
fn events_decoder(
|
|
||||||
decoder: &mut #subxt::EventsDecoder<T>,
|
|
||||||
) {
|
|
||||||
decoder.#with_module();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call extension trait.
|
/// Call extension trait.
|
||||||
@@ -118,11 +109,6 @@ mod tests {
|
|||||||
impl<'a, T: Balances> substrate_subxt::Call<T> for TransferCall<'a, T> {
|
impl<'a, T: Balances> substrate_subxt::Call<T> for TransferCall<'a, T> {
|
||||||
const MODULE: &'static str = MODULE;
|
const MODULE: &'static str = MODULE;
|
||||||
const FUNCTION: &'static str = "transfer";
|
const FUNCTION: &'static str = "transfer";
|
||||||
fn events_decoder(
|
|
||||||
decoder: &mut substrate_subxt::EventsDecoder<T>,
|
|
||||||
) {
|
|
||||||
decoder.with_balances();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call extension trait.
|
/// Call extension trait.
|
||||||
|
|||||||
@@ -62,17 +62,16 @@ use synstructure::{
|
|||||||
///
|
///
|
||||||
/// const MODULE: &str = "Herd";
|
/// const MODULE: &str = "Herd";
|
||||||
///
|
///
|
||||||
/// // `EventsDecoder` extension trait.
|
/// // `EventTypeRegistry` extension trait.
|
||||||
/// pub trait HerdEventsDecoder {
|
/// pub trait HerdEventTypeRegistry {
|
||||||
/// // Registers this modules types.
|
/// // Registers this modules types.
|
||||||
/// fn with_herd(&mut self);
|
/// fn with_herd(&mut self);
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl<T: Herd> HerdEventsDecoder for
|
/// impl<T: Herd + Runtime> EventTypeRegistry for
|
||||||
/// substrate_subxt::EventsDecoder<T>
|
/// substrate_subxt::EventTypeRegistry<T>
|
||||||
/// {
|
/// {
|
||||||
/// fn with_herd(&mut self) {
|
/// fn with_herd(&mut self) {
|
||||||
/// self.with_husbandry();
|
|
||||||
/// self.register_type_size::<T::Hooves>("Hooves");
|
/// self.register_type_size::<T::Hooves>("Hooves");
|
||||||
/// self.register_type_size::<T::Wool>("Wool");
|
/// self.register_type_size::<T::Wool>("Wool");
|
||||||
/// }
|
/// }
|
||||||
|
|||||||
+15
-29
@@ -62,8 +62,8 @@ fn ignore(attrs: &[syn::Attribute]) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn events_decoder_trait_name(module: &syn::Ident) -> syn::Ident {
|
fn event_type_registry_trait_name(module: &syn::Ident) -> syn::Ident {
|
||||||
format_ident!("{}EventsDecoder", module.to_string())
|
format_ident!("{}EventTypeRegistry", module.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_module_ident(module: &syn::Ident) -> syn::Ident {
|
fn with_module_ident(module: &syn::Ident) -> syn::Ident {
|
||||||
@@ -128,20 +128,9 @@ pub fn module(_args: TokenStream, tokens: TokenStream) -> TokenStream {
|
|||||||
let subxt = utils::use_crate("substrate-subxt");
|
let subxt = utils::use_crate("substrate-subxt");
|
||||||
let module = &input.ident;
|
let module = &input.ident;
|
||||||
let module_name = module.to_string();
|
let module_name = module.to_string();
|
||||||
let module_events_decoder = events_decoder_trait_name(module);
|
let module_events_type_registry = event_type_registry_trait_name(module);
|
||||||
let with_module = with_module_ident(module);
|
let with_module = with_module_ident(module);
|
||||||
|
|
||||||
let bounds = input.supertraits.iter().filter_map(|bound| {
|
|
||||||
if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound {
|
|
||||||
let module = utils::path_to_ident(path);
|
|
||||||
let with_module = with_module_ident(module);
|
|
||||||
Some(quote! {
|
|
||||||
self.#with_module();
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let associated_types = input.items.iter().filter_map(|item| {
|
let associated_types = input.items.iter().filter_map(|item| {
|
||||||
if let syn::TraitItem::Type(ty) = item {
|
if let syn::TraitItem::Type(ty) = item {
|
||||||
if ignore(&ty.attrs) {
|
if ignore(&ty.attrs) {
|
||||||
@@ -168,17 +157,16 @@ pub fn module(_args: TokenStream, tokens: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
const MODULE: &str = #module_name;
|
const MODULE: &str = #module_name;
|
||||||
|
|
||||||
/// `EventsDecoder` extension trait.
|
/// `EventTypeRegistry` extension trait.
|
||||||
pub trait #module_events_decoder {
|
pub trait #module_events_type_registry {
|
||||||
/// Registers this modules types.
|
/// Registers this modules types.
|
||||||
fn #with_module(&mut self);
|
fn #with_module(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: #module> #module_events_decoder for
|
impl<T: #module + #subxt::Runtime> #module_events_type_registry for
|
||||||
#subxt::EventsDecoder<T>
|
#subxt::EventTypeRegistry<T>
|
||||||
{
|
{
|
||||||
fn #with_module(&mut self) {
|
fn #with_module(&mut self) {
|
||||||
#(#bounds)*
|
|
||||||
#(#associated_types)*
|
#(#associated_types)*
|
||||||
#(#types)*
|
#(#types)*
|
||||||
}
|
}
|
||||||
@@ -221,17 +209,16 @@ mod tests {
|
|||||||
|
|
||||||
const MODULE: &str = "Balances";
|
const MODULE: &str = "Balances";
|
||||||
|
|
||||||
/// `EventsDecoder` extension trait.
|
/// `EventTypeRegistry` extension trait.
|
||||||
pub trait BalancesEventsDecoder {
|
pub trait BalancesEventTypeRegistry {
|
||||||
/// Registers this modules types.
|
/// Registers this modules types.
|
||||||
fn with_balances(&mut self);
|
fn with_balances(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Balances> BalancesEventsDecoder for
|
impl<T: Balances + substrate_subxt::Runtime> BalancesEventTypeRegistry for
|
||||||
substrate_subxt::EventsDecoder<T>
|
substrate_subxt::EventTypeRegistry<T>
|
||||||
{
|
{
|
||||||
fn with_balances(&mut self) {
|
fn with_balances(&mut self) {
|
||||||
self.with_system();
|
|
||||||
self.register_type_size::<T::Balance>("Balance");
|
self.register_type_size::<T::Balance>("Balance");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,17 +249,16 @@ mod tests {
|
|||||||
|
|
||||||
const MODULE: &str = "Herd";
|
const MODULE: &str = "Herd";
|
||||||
|
|
||||||
/// `EventsDecoder` extension trait.
|
/// `EventTypeRegistry` extension trait.
|
||||||
pub trait HerdEventsDecoder {
|
pub trait HerdEventTypeRegistry {
|
||||||
/// Registers this modules types.
|
/// Registers this modules types.
|
||||||
fn with_herd(&mut self);
|
fn with_herd(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Herd> HerdEventsDecoder for
|
impl<T: Herd + substrate_subxt::Runtime> HerdEventTypeRegistry for
|
||||||
substrate_subxt::EventsDecoder<T>
|
substrate_subxt::EventTypeRegistry<T>
|
||||||
{
|
{
|
||||||
fn with_herd(&mut self) {
|
fn with_herd(&mut self) {
|
||||||
self.with_husbandry();
|
|
||||||
self.register_type_size::<T::Hoves>("Hoves");
|
self.register_type_size::<T::Hoves>("Hoves");
|
||||||
self.register_type_size::<T::Wool>("Wool");
|
self.register_type_size::<T::Wool>("Wool");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,7 @@ use substrate_subxt::{
|
|||||||
MaybeSerialize,
|
MaybeSerialize,
|
||||||
Member,
|
Member,
|
||||||
},
|
},
|
||||||
system::{
|
system::System,
|
||||||
System,
|
|
||||||
SystemEventsDecoder,
|
|
||||||
},
|
|
||||||
ClientBuilder,
|
ClientBuilder,
|
||||||
KusamaRuntime,
|
KusamaRuntime,
|
||||||
PairSigner,
|
PairSigner,
|
||||||
|
|||||||
@@ -57,6 +57,13 @@ pub enum Error {
|
|||||||
/// Metadata error.
|
/// Metadata error.
|
||||||
#[error("Metadata error: {0}")]
|
#[error("Metadata error: {0}")]
|
||||||
Metadata(#[from] MetadataError),
|
Metadata(#[from] MetadataError),
|
||||||
|
/// Unregistered type sizes.
|
||||||
|
#[error(
|
||||||
|
"The following types do not have a type size registered: \
|
||||||
|
{0:?} \
|
||||||
|
Use `ClientBuilder::register_type_size` to register missing type sizes."
|
||||||
|
)]
|
||||||
|
MissingTypeSizes(Vec<String>),
|
||||||
/// Type size unavailable.
|
/// Type size unavailable.
|
||||||
#[error("Type size unavailable while decoding event: {0:?}")]
|
#[error("Type size unavailable while decoding event: {0:?}")]
|
||||||
TypeSizeUnavailable(String),
|
TypeSizeUnavailable(String),
|
||||||
|
|||||||
+191
-148
@@ -22,14 +22,17 @@ use codec::{
|
|||||||
Input,
|
Input,
|
||||||
Output,
|
Output,
|
||||||
};
|
};
|
||||||
use frame_support::dispatch::DispatchInfo;
|
use dyn_clone::DynClone;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
DispatchError,
|
DispatchError,
|
||||||
DispatchResult,
|
DispatchResult,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{
|
collections::{
|
||||||
HashMap,
|
hash_map::{
|
||||||
|
Entry,
|
||||||
|
HashMap,
|
||||||
|
},
|
||||||
HashSet,
|
HashSet,
|
||||||
},
|
},
|
||||||
fmt,
|
fmt,
|
||||||
@@ -49,6 +52,7 @@ use crate::{
|
|||||||
Metadata,
|
Metadata,
|
||||||
},
|
},
|
||||||
Phase,
|
Phase,
|
||||||
|
Runtime,
|
||||||
System,
|
System,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,16 +76,18 @@ impl std::fmt::Debug for RawEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait TypeSegmenter: Send {
|
pub trait TypeSegmenter: DynClone + Send + Sync {
|
||||||
/// Consumes an object from an input stream, and output the serialized bytes.
|
/// Consumes an object from an input stream, and output the serialized bytes.
|
||||||
fn segment(&self, input: &mut &[u8], output: &mut Vec<u8>) -> Result<(), Error>;
|
fn segment(&self, input: &mut &[u8], output: &mut Vec<u8>) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
// derive object safe Clone impl for `Box<dyn TypeSegmenter>`
|
||||||
|
dyn_clone::clone_trait_object!(TypeSegmenter);
|
||||||
|
|
||||||
struct TypeMarker<T>(PhantomData<T>);
|
struct TypeMarker<T>(PhantomData<T>);
|
||||||
impl<T> TypeSegmenter for TypeMarker<T>
|
impl<T> TypeSegmenter for TypeMarker<T>
|
||||||
where
|
where
|
||||||
T: Codec + Send,
|
T: Codec + Send + Sync,
|
||||||
{
|
{
|
||||||
fn segment(&self, input: &mut &[u8], output: &mut Vec<u8>) -> Result<(), Error> {
|
fn segment(&self, input: &mut &[u8], output: &mut Vec<u8>) -> Result<(), Error> {
|
||||||
T::decode(input).map_err(Error::from)?.encode_to(output);
|
T::decode(input).map_err(Error::from)?.encode_to(output);
|
||||||
@@ -89,158 +95,41 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for TypeMarker<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for TypeMarker<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Events decoder.
|
/// Events decoder.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct EventsDecoder<T> {
|
pub struct EventsDecoder<T> {
|
||||||
metadata: Metadata,
|
metadata: Metadata,
|
||||||
type_segmenters: HashMap<String, Box<dyn TypeSegmenter>>,
|
event_type_registry: EventTypeRegistry<T>,
|
||||||
marker: PhantomData<fn() -> T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> fmt::Debug for EventsDecoder<T> {
|
impl<T> Clone for EventsDecoder<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn clone(&self) -> Self {
|
||||||
f.debug_struct("EventsDecoder<T>")
|
Self {
|
||||||
.field("metadata", &self.metadata)
|
metadata: self.metadata.clone(),
|
||||||
.field(
|
event_type_registry: self.event_type_registry.clone(),
|
||||||
"type_segmenters",
|
}
|
||||||
&self.type_segmenters.keys().cloned().collect::<String>(),
|
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: System> EventsDecoder<T> {
|
impl<T: Runtime + System> EventsDecoder<T> {
|
||||||
/// Creates a new `EventsDecoder`.
|
/// Creates a new `EventsDecoder`.
|
||||||
pub fn new(metadata: Metadata) -> Self {
|
pub fn new(metadata: Metadata, event_type_registry: EventTypeRegistry<T>) -> Self {
|
||||||
let mut decoder = Self {
|
Self {
|
||||||
metadata,
|
metadata,
|
||||||
type_segmenters: HashMap::new(),
|
event_type_registry,
|
||||||
marker: PhantomData,
|
|
||||||
};
|
|
||||||
// register default event arg type sizes for dynamic decoding of events
|
|
||||||
decoder.register_type_size::<()>("PhantomData");
|
|
||||||
decoder.register_type_size::<DispatchInfo>("DispatchInfo");
|
|
||||||
decoder.register_type_size::<bool>("bool");
|
|
||||||
decoder.register_type_size::<u32>("ReferendumIndex");
|
|
||||||
decoder.register_type_size::<[u8; 16]>("Kind");
|
|
||||||
decoder.register_type_size::<[u8; 32]>("AuthorityId");
|
|
||||||
decoder.register_type_size::<u8>("u8");
|
|
||||||
decoder.register_type_size::<u32>("u32");
|
|
||||||
decoder.register_type_size::<u64>("u64");
|
|
||||||
decoder.register_type_size::<u128>("u128");
|
|
||||||
decoder.register_type_size::<u32>("AccountIndex");
|
|
||||||
decoder.register_type_size::<u32>("SessionIndex");
|
|
||||||
decoder.register_type_size::<u32>("PropIndex");
|
|
||||||
decoder.register_type_size::<u32>("ProposalIndex");
|
|
||||||
decoder.register_type_size::<u32>("AuthorityIndex");
|
|
||||||
decoder.register_type_size::<u64>("AuthorityWeight");
|
|
||||||
decoder.register_type_size::<u32>("MemberCount");
|
|
||||||
decoder.register_type_size::<T::AccountId>("AccountId");
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register a type.
|
|
||||||
pub fn register_type_size<U>(&mut self, name: &str) -> usize
|
|
||||||
where
|
|
||||||
U: Default + Codec + Send + 'static,
|
|
||||||
{
|
|
||||||
let size = U::default().encode().len();
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check missing type sizes.
|
|
||||||
pub fn check_missing_type_sizes(&self) {
|
|
||||||
let mut missing = HashSet::new();
|
|
||||||
for module in self.metadata.modules_with_events() {
|
|
||||||
for event in module.events() {
|
|
||||||
for arg in event.arguments() {
|
|
||||||
for primitive in arg.primitives() {
|
|
||||||
if !self.type_segmenters.contains_key(&primitive) {
|
|
||||||
missing.insert(format!(
|
|
||||||
"{}::{}::{}",
|
|
||||||
module.name(),
|
|
||||||
event.name,
|
|
||||||
primitive
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !missing.is_empty() {
|
|
||||||
log::warn!(
|
|
||||||
"The following primitive types do not have registered sizes: {:?} \
|
|
||||||
If any of these events are received, an error will occur since we cannot decode them",
|
|
||||||
missing
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decode_raw_bytes<W: Output>(
|
|
||||||
&self,
|
|
||||||
args: &[EventArg],
|
|
||||||
input: &mut &[u8],
|
|
||||||
output: &mut W,
|
|
||||||
errors: &mut Vec<RuntimeError>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
for arg in args {
|
|
||||||
match arg {
|
|
||||||
EventArg::Vec(arg) => {
|
|
||||||
let len = <Compact<u32>>::decode(input)?;
|
|
||||||
len.encode_to(output);
|
|
||||||
for _ in 0..len.0 {
|
|
||||||
self.decode_raw_bytes(&[*arg.clone()], input, output, errors)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EventArg::Option(arg) => {
|
|
||||||
match input.read_byte()? {
|
|
||||||
0 => output.push_byte(0),
|
|
||||||
1 => {
|
|
||||||
output.push_byte(1);
|
|
||||||
self.decode_raw_bytes(&[*arg.clone()], input, output, errors)?
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(Error::Other(
|
|
||||||
"unexpected first byte decoding Option".into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EventArg::Tuple(args) => {
|
|
||||||
self.decode_raw_bytes(args, input, output, errors)?
|
|
||||||
}
|
|
||||||
EventArg::Primitive(name) => {
|
|
||||||
let result = match name.as_str() {
|
|
||||||
"DispatchResult" => DispatchResult::decode(input)?,
|
|
||||||
"DispatchError" => Err(DispatchError::decode(input)?),
|
|
||||||
_ => {
|
|
||||||
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 {
|
|
||||||
return Err(Error::TypeSizeUnavailable(name.to_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(error) = result {
|
|
||||||
// since the input may contain any number of args we propagate
|
|
||||||
// runtime errors to the caller for handling
|
|
||||||
errors.push(RuntimeError::from_dispatch(&self.metadata, error)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode events.
|
/// Decode events.
|
||||||
@@ -290,7 +179,7 @@ impl<T: System> EventsDecoder<T> {
|
|||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
|
|
||||||
if event_errors.len() == 0 {
|
if event_errors.is_empty() {
|
||||||
r.push((phase.clone(), raw));
|
r.push((phase.clone(), raw));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,6 +189,156 @@ impl<T: System> EventsDecoder<T> {
|
|||||||
}
|
}
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decode_raw_bytes<W: Output>(
|
||||||
|
&self,
|
||||||
|
args: &[EventArg],
|
||||||
|
input: &mut &[u8],
|
||||||
|
output: &mut W,
|
||||||
|
errors: &mut Vec<RuntimeError>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
for arg in args {
|
||||||
|
match arg {
|
||||||
|
EventArg::Vec(arg) => {
|
||||||
|
let len = <Compact<u32>>::decode(input)?;
|
||||||
|
len.encode_to(output);
|
||||||
|
for _ in 0..len.0 {
|
||||||
|
self.decode_raw_bytes(&[*arg.clone()], input, output, errors)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EventArg::Option(arg) => {
|
||||||
|
match input.read_byte()? {
|
||||||
|
0 => output.push_byte(0),
|
||||||
|
1 => {
|
||||||
|
output.push_byte(1);
|
||||||
|
self.decode_raw_bytes(&[*arg.clone()], input, output, errors)?
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::Other(
|
||||||
|
"unexpected first byte decoding Option".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EventArg::Tuple(args) => {
|
||||||
|
self.decode_raw_bytes(args, input, output, errors)?
|
||||||
|
}
|
||||||
|
EventArg::Primitive(name) => {
|
||||||
|
let result = match name.as_str() {
|
||||||
|
"DispatchResult" => DispatchResult::decode(input)?,
|
||||||
|
"DispatchError" => Err(DispatchError::decode(input)?),
|
||||||
|
_ => {
|
||||||
|
if let Some(seg) = self.event_type_registry.resolve(name) {
|
||||||
|
let mut buf = Vec::<u8>::new();
|
||||||
|
seg.segment(input, &mut buf)?;
|
||||||
|
output.write(&buf);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
return Err(Error::TypeSizeUnavailable(name.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Err(error) = result {
|
||||||
|
// since the input may contain any number of args we propagate
|
||||||
|
// runtime errors to the caller for handling
|
||||||
|
errors.push(RuntimeError::from_dispatch(&self.metadata, error)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registry for event types which cannot be directly inferred from the metadata.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EventTypeRegistry<T> {
|
||||||
|
segmenters: HashMap<String, Box<dyn TypeSegmenter>>,
|
||||||
|
marker: PhantomData<fn() -> T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for EventTypeRegistry<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
segmenters: self.segmenters.clone(),
|
||||||
|
marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> fmt::Debug for EventTypeRegistry<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("EventTypeRegistry")
|
||||||
|
.field(
|
||||||
|
"segmenters",
|
||||||
|
&self.segmenters.keys().cloned().collect::<String>(),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Runtime> EventTypeRegistry<T> {
|
||||||
|
/// Create a new [`EventTypeRegistry`].
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut registry = Self {
|
||||||
|
segmenters: HashMap::new(),
|
||||||
|
marker: PhantomData,
|
||||||
|
};
|
||||||
|
T::register_type_sizes(&mut registry);
|
||||||
|
registry
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a type.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If there is already a type size registered with this name.
|
||||||
|
pub fn register_type_size<U>(&mut self, name: &str)
|
||||||
|
where
|
||||||
|
U: Codec + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
// A segmenter decodes a type from an input stream (&mut &[u8]) and returns te serialized
|
||||||
|
// type to the output stream (&mut Vec<u8>).
|
||||||
|
match self.segmenters.entry(name.to_string()) {
|
||||||
|
Entry::Occupied(_) => panic!("Already a type registered with key {}", name),
|
||||||
|
Entry::Vacant(entry) => entry.insert(Box::new(TypeMarker::<U>::default())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check missing type sizes.
|
||||||
|
pub fn check_missing_type_sizes(
|
||||||
|
&self,
|
||||||
|
metadata: &Metadata,
|
||||||
|
) -> Result<(), HashSet<String>> {
|
||||||
|
let mut missing = HashSet::new();
|
||||||
|
for module in metadata.modules_with_events() {
|
||||||
|
for event in module.events() {
|
||||||
|
for arg in event.arguments() {
|
||||||
|
for primitive in arg.primitives() {
|
||||||
|
if !self.segmenters.contains_key(&primitive) {
|
||||||
|
missing.insert(format!(
|
||||||
|
"{}::{}::{}",
|
||||||
|
module.name(),
|
||||||
|
event.name,
|
||||||
|
primitive
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !missing.is_empty() {
|
||||||
|
Err(missing)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve a segmenter for a type by its name.
|
||||||
|
pub fn resolve(&self, name: &str) -> Option<&Box<dyn TypeSegmenter>> {
|
||||||
|
self.segmenters.get(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raw event or error event
|
/// Raw event or error event
|
||||||
@@ -331,7 +370,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decode_option() {
|
fn test_decode_option() {
|
||||||
let decoder = EventsDecoder::<TestRuntime>::new(Metadata::default());
|
let decoder = EventsDecoder::<TestRuntime>::new(
|
||||||
|
Metadata::default(),
|
||||||
|
EventTypeRegistry::new(),
|
||||||
|
);
|
||||||
|
|
||||||
let value = Some(0u8);
|
let value = Some(0u8);
|
||||||
let input = value.encode();
|
let input = value.encode();
|
||||||
@@ -419,6 +461,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
EventTypeRegistry::new(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// [(ApplyExtrinsic(0), Event(RawEvent { module: "System", variant: "ExtrinsicSuccess", data: "482d7c09000000000200" })), (ApplyExtrinsic(1), Error(Module(ModuleError { module: "System", error: "NonDefaultComposite" }))), (ApplyExtrinsic(2), Error(Module(ModuleError { module: "System", error: "NonDefaultComposite" })))]
|
// [(ApplyExtrinsic(0), Event(RawEvent { module: "System", variant: "ExtrinsicSuccess", data: "482d7c09000000000200" })), (ApplyExtrinsic(1), Error(Module(ModuleError { module: "System", error: "NonDefaultComposite" }))), (ApplyExtrinsic(2), Error(Module(ModuleError { module: "System", error: "NonDefaultComposite" })))]
|
||||||
|
|||||||
@@ -16,10 +16,7 @@
|
|||||||
|
|
||||||
//! Implements support for the pallet_balances module.
|
//! Implements support for the pallet_balances module.
|
||||||
|
|
||||||
use crate::frame::system::{
|
use crate::frame::system::System;
|
||||||
System,
|
|
||||||
SystemEventsDecoder,
|
|
||||||
};
|
|
||||||
use codec::{
|
use codec::{
|
||||||
Decode,
|
Decode,
|
||||||
Encode,
|
Encode,
|
||||||
@@ -159,7 +156,6 @@ mod tests {
|
|||||||
ModuleError,
|
ModuleError,
|
||||||
RuntimeError,
|
RuntimeError,
|
||||||
},
|
},
|
||||||
events::EventsDecoder,
|
|
||||||
extrinsic::{
|
extrinsic::{
|
||||||
PairSigner,
|
PairSigner,
|
||||||
Signer,
|
Signer,
|
||||||
@@ -301,9 +297,8 @@ mod tests {
|
|||||||
let bob_addr = bob.clone().into();
|
let bob_addr = bob.clone().into();
|
||||||
let (client, _) = test_client().await;
|
let (client, _) = test_client().await;
|
||||||
let sub = client.subscribe_events().await.unwrap();
|
let sub = client.subscribe_events().await.unwrap();
|
||||||
let mut decoder = EventsDecoder::<TestRuntime>::new(client.metadata().clone());
|
let decoder = client.events_decoder();
|
||||||
decoder.with_balances();
|
let mut sub = EventSubscription::<TestRuntime>::new(sub, &decoder);
|
||||||
let mut sub = EventSubscription::<TestRuntime>::new(sub, decoder);
|
|
||||||
sub.filter_event::<TransferEvent<_>>();
|
sub.filter_event::<TransferEvent<_>>();
|
||||||
client.transfer(&alice, &bob_addr, 10_000).await.unwrap();
|
client.transfer(&alice, &bob_addr, 10_000).await.unwrap();
|
||||||
let raw = sub.next().await.unwrap().unwrap();
|
let raw = sub.next().await.unwrap().unwrap();
|
||||||
|
|||||||
@@ -17,14 +17,8 @@
|
|||||||
//! Implements support for the pallet_contracts module.
|
//! Implements support for the pallet_contracts module.
|
||||||
|
|
||||||
use crate::frame::{
|
use crate::frame::{
|
||||||
balances::{
|
balances::Balances,
|
||||||
Balances,
|
system::System,
|
||||||
BalancesEventsDecoder,
|
|
||||||
},
|
|
||||||
system::{
|
|
||||||
System,
|
|
||||||
SystemEventsDecoder,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use codec::{
|
use codec::{
|
||||||
Decode,
|
Decode,
|
||||||
|
|||||||
+3
-8
@@ -16,12 +16,9 @@
|
|||||||
|
|
||||||
//! Implements support for built-in runtime modules.
|
//! Implements support for built-in runtime modules.
|
||||||
|
|
||||||
use crate::{
|
use crate::metadata::{
|
||||||
events::EventsDecoder,
|
Metadata,
|
||||||
metadata::{
|
MetadataError,
|
||||||
Metadata,
|
|
||||||
MetadataError,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use codec::{
|
use codec::{
|
||||||
Decode,
|
Decode,
|
||||||
@@ -63,8 +60,6 @@ pub trait Call<T>: Encode {
|
|||||||
const MODULE: &'static str;
|
const MODULE: &'static str;
|
||||||
/// Function name.
|
/// Function name.
|
||||||
const FUNCTION: &'static str;
|
const FUNCTION: &'static str;
|
||||||
/// Load event decoder.
|
|
||||||
fn events_decoder(_decoder: &mut EventsDecoder<T>) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event trait.
|
/// Event trait.
|
||||||
|
|||||||
@@ -16,14 +16,8 @@
|
|||||||
|
|
||||||
//! Session support
|
//! Session support
|
||||||
use crate::frame::{
|
use crate::frame::{
|
||||||
balances::{
|
balances::Balances,
|
||||||
Balances,
|
system::System,
|
||||||
BalancesEventsDecoder as _,
|
|
||||||
},
|
|
||||||
system::{
|
|
||||||
System,
|
|
||||||
SystemEventsDecoder as _,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use frame_support::Parameter;
|
use frame_support::Parameter;
|
||||||
|
|||||||
+1
-12
@@ -16,10 +16,7 @@
|
|||||||
|
|
||||||
//! Implements support for the pallet_staking module.
|
//! Implements support for the pallet_staking module.
|
||||||
|
|
||||||
use super::balances::{
|
use super::balances::Balances;
|
||||||
Balances,
|
|
||||||
BalancesEventsDecoder as _,
|
|
||||||
};
|
|
||||||
use codec::{
|
use codec::{
|
||||||
Decode,
|
Decode,
|
||||||
Encode,
|
Encode,
|
||||||
@@ -62,20 +59,12 @@ pub struct SetPayeeCall<T: Staking> {
|
|||||||
pub _runtime: PhantomData<T>,
|
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.
|
/// The subset of the `frame::Trait` that a client must implement.
|
||||||
#[module]
|
#[module]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub trait Staking: Balances {
|
pub trait Staking: Balances {
|
||||||
#![event_alias(ElectionCompute = u8)]
|
#![event_alias(ElectionCompute = u8)]
|
||||||
#![event_type(EraIndex)]
|
#![event_type(EraIndex)]
|
||||||
#![event_type(AuthorityList)]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of eras to keep in history.
|
/// Number of eras to keep in history.
|
||||||
|
|||||||
+1
-4
@@ -17,10 +17,7 @@
|
|||||||
//! Implements support for the frame_sudo module.
|
//! Implements support for the frame_sudo module.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frame::system::{
|
frame::system::System,
|
||||||
System,
|
|
||||||
SystemEventsDecoder,
|
|
||||||
},
|
|
||||||
Encoded,
|
Encoded,
|
||||||
};
|
};
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
|
|||||||
+63
-15
@@ -49,7 +49,10 @@ pub use substrate_subxt_client as client;
|
|||||||
pub use sp_core;
|
pub use sp_core;
|
||||||
pub use sp_runtime;
|
pub use sp_runtime;
|
||||||
|
|
||||||
use codec::Decode;
|
use codec::{
|
||||||
|
Codec,
|
||||||
|
Decode,
|
||||||
|
};
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use jsonrpsee::client::Subscription;
|
use jsonrpsee::client::Subscription;
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
@@ -76,6 +79,7 @@ mod subscription;
|
|||||||
pub use crate::{
|
pub use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
events::{
|
events::{
|
||||||
|
EventTypeRegistry,
|
||||||
EventsDecoder,
|
EventsDecoder,
|
||||||
RawEvent,
|
RawEvent,
|
||||||
},
|
},
|
||||||
@@ -115,20 +119,22 @@ use crate::{
|
|||||||
/// ClientBuilder for constructing a Client.
|
/// ClientBuilder for constructing a Client.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ClientBuilder<T: Runtime> {
|
pub struct ClientBuilder<T: Runtime> {
|
||||||
_marker: std::marker::PhantomData<T>,
|
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
client: Option<jsonrpsee::Client>,
|
client: Option<jsonrpsee::Client>,
|
||||||
page_size: Option<u32>,
|
page_size: Option<u32>,
|
||||||
|
event_type_registry: EventTypeRegistry<T>,
|
||||||
|
skip_type_sizes_check: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Runtime> ClientBuilder<T> {
|
impl<T: Runtime> ClientBuilder<T> {
|
||||||
/// Creates a new ClientBuilder.
|
/// Creates a new ClientBuilder.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
_marker: std::marker::PhantomData,
|
|
||||||
url: None,
|
url: None,
|
||||||
client: None,
|
client: None,
|
||||||
page_size: None,
|
page_size: None,
|
||||||
|
event_type_registry: EventTypeRegistry::new(),
|
||||||
|
skip_type_sizes_check: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,8 +156,30 @@ impl<T: Runtime> ClientBuilder<T> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register a custom type segmenter, for consuming types in events where the size cannot
|
||||||
|
/// be inferred from the metadata.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If there is already a type size registered with this name.
|
||||||
|
pub fn register_type_size<U>(mut self, name: &str) -> Self
|
||||||
|
where
|
||||||
|
U: Codec + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
self.event_type_registry.register_type_size::<U>(name);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the check for missing type sizes on `build`.
|
||||||
|
///
|
||||||
|
/// *WARNING* can lead to runtime errors if receiving events with unknown types.
|
||||||
|
pub fn skip_type_sizes_check(mut self) -> Self {
|
||||||
|
self.skip_type_sizes_check = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new Client.
|
/// Creates a new Client.
|
||||||
pub async fn build(self) -> Result<Client<T>, Error> {
|
pub async fn build<'a>(self) -> Result<Client<T>, Error> {
|
||||||
let client = if let Some(client) = self.client {
|
let client = if let Some(client) = self.client {
|
||||||
client
|
client
|
||||||
} else {
|
} else {
|
||||||
@@ -170,10 +198,33 @@ impl<T: Runtime> ClientBuilder<T> {
|
|||||||
rpc.system_properties(),
|
rpc.system_properties(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
let metadata = metadata?;
|
||||||
|
|
||||||
|
if let Err(missing) = self.event_type_registry.check_missing_type_sizes(&metadata)
|
||||||
|
{
|
||||||
|
if self.skip_type_sizes_check {
|
||||||
|
log::warn!(
|
||||||
|
"The following types do not have registered type segmenters: {:?} \
|
||||||
|
If any events containing these types are received, this can cause a \
|
||||||
|
`TypeSizeUnavailable` error and prevent decoding the actual event \
|
||||||
|
being listened for.\
|
||||||
|
\
|
||||||
|
Use `ClientBuilder::register_type_size` to register missing type sizes.",
|
||||||
|
missing
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Err(Error::MissingTypeSizes(missing.into_iter().collect()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let events_decoder =
|
||||||
|
EventsDecoder::new(metadata.clone(), self.event_type_registry);
|
||||||
|
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
rpc,
|
rpc,
|
||||||
genesis_hash: genesis_hash?,
|
genesis_hash: genesis_hash?,
|
||||||
metadata: metadata?,
|
metadata,
|
||||||
|
events_decoder,
|
||||||
properties: properties.unwrap_or_else(|_| Default::default()),
|
properties: properties.unwrap_or_else(|_| Default::default()),
|
||||||
runtime_version: runtime_version?,
|
runtime_version: runtime_version?,
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
@@ -187,6 +238,7 @@ pub struct Client<T: Runtime> {
|
|||||||
rpc: Rpc<T>,
|
rpc: Rpc<T>,
|
||||||
genesis_hash: T::Hash,
|
genesis_hash: T::Hash,
|
||||||
metadata: Metadata,
|
metadata: Metadata,
|
||||||
|
events_decoder: EventsDecoder<T>,
|
||||||
properties: SystemProperties,
|
properties: SystemProperties,
|
||||||
runtime_version: RuntimeVersion,
|
runtime_version: RuntimeVersion,
|
||||||
_marker: PhantomData<(fn() -> T::Signature, T::Extra)>,
|
_marker: PhantomData<(fn() -> T::Signature, T::Extra)>,
|
||||||
@@ -199,6 +251,7 @@ impl<T: Runtime> Clone for Client<T> {
|
|||||||
rpc: self.rpc.clone(),
|
rpc: self.rpc.clone(),
|
||||||
genesis_hash: self.genesis_hash,
|
genesis_hash: self.genesis_hash,
|
||||||
metadata: self.metadata.clone(),
|
metadata: self.metadata.clone(),
|
||||||
|
events_decoder: self.events_decoder.clone(),
|
||||||
properties: self.properties.clone(),
|
properties: self.properties.clone(),
|
||||||
runtime_version: self.runtime_version.clone(),
|
runtime_version: self.runtime_version.clone(),
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
@@ -466,12 +519,9 @@ impl<T: Runtime> Client<T> {
|
|||||||
Ok(signed)
|
Ok(signed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an events decoder for a call.
|
/// Returns the events decoder.
|
||||||
pub fn events_decoder<C: Call<T>>(&self) -> EventsDecoder<T> {
|
pub fn events_decoder(&self) -> &EventsDecoder<T> {
|
||||||
let metadata = self.metadata().clone();
|
&self.events_decoder
|
||||||
let mut decoder = EventsDecoder::new(metadata);
|
|
||||||
C::events_decoder(&mut decoder);
|
|
||||||
decoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create and submit an extrinsic and return corresponding Hash if successful
|
/// Create and submit an extrinsic and return corresponding Hash if successful
|
||||||
@@ -486,10 +536,9 @@ impl<T: Runtime> Client<T> {
|
|||||||
pub async fn submit_and_watch_extrinsic(
|
pub async fn submit_and_watch_extrinsic(
|
||||||
&self,
|
&self,
|
||||||
extrinsic: UncheckedExtrinsic<T>,
|
extrinsic: UncheckedExtrinsic<T>,
|
||||||
decoder: EventsDecoder<T>,
|
|
||||||
) -> Result<ExtrinsicSuccess<T>, Error> {
|
) -> Result<ExtrinsicSuccess<T>, Error> {
|
||||||
self.rpc
|
self.rpc
|
||||||
.submit_and_watch_extrinsic(extrinsic, decoder)
|
.submit_and_watch_extrinsic(extrinsic, &self.events_decoder)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,8 +567,7 @@ impl<T: Runtime> Client<T> {
|
|||||||
Send + Sync,
|
Send + Sync,
|
||||||
{
|
{
|
||||||
let extrinsic = self.create_signed(call, signer).await?;
|
let extrinsic = self.create_signed(call, signer).await?;
|
||||||
let decoder = self.events_decoder::<C>();
|
self.submit_and_watch_extrinsic(extrinsic).await
|
||||||
self.submit_and_watch_extrinsic(extrinsic, decoder).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a key into the keystore.
|
/// Insert a key into the keystore.
|
||||||
|
|||||||
+3
-3
@@ -423,10 +423,10 @@ impl<T: Runtime> Rpc<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create and submit an extrinsic and return corresponding Event if successful
|
/// Create and submit an extrinsic and return corresponding Event if successful
|
||||||
pub async fn submit_and_watch_extrinsic<E: Encode + 'static>(
|
pub async fn submit_and_watch_extrinsic<'a, E: Encode + 'static>(
|
||||||
&self,
|
&self,
|
||||||
extrinsic: E,
|
extrinsic: E,
|
||||||
decoder: EventsDecoder<T>,
|
decoder: &'a EventsDecoder<T>,
|
||||||
) -> Result<ExtrinsicSuccess<T>, Error> {
|
) -> Result<ExtrinsicSuccess<T>, Error> {
|
||||||
let ext_hash = T::Hashing::hash_of(&extrinsic);
|
let ext_hash = T::Hashing::hash_of(&extrinsic);
|
||||||
log::info!("Submitting Extrinsic `{:?}`", ext_hash);
|
log::info!("Submitting Extrinsic `{:?}`", ext_hash);
|
||||||
@@ -465,7 +465,7 @@ impl<T: Runtime> Rpc<T> {
|
|||||||
ext_hash,
|
ext_hash,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let mut sub = EventSubscription::new(events_sub, decoder);
|
let mut sub = EventSubscription::new(events_sub, &decoder);
|
||||||
sub.filter_extrinsic(block_hash, ext_index);
|
sub.filter_extrinsic(block_hash, ext_index);
|
||||||
let mut events = vec![];
|
let mut events = vec![];
|
||||||
while let Some(event) = sub.next().await {
|
while let Some(event) = sub.next().await {
|
||||||
|
|||||||
+133
-5
@@ -147,13 +147,30 @@ use crate::{
|
|||||||
balances::{
|
balances::{
|
||||||
AccountData,
|
AccountData,
|
||||||
Balances,
|
Balances,
|
||||||
|
BalancesEventTypeRegistry,
|
||||||
|
},
|
||||||
|
contracts::{
|
||||||
|
Contracts,
|
||||||
|
ContractsEventTypeRegistry,
|
||||||
|
},
|
||||||
|
session::{
|
||||||
|
Session,
|
||||||
|
SessionEventTypeRegistry,
|
||||||
|
},
|
||||||
|
staking::{
|
||||||
|
Staking,
|
||||||
|
StakingEventTypeRegistry,
|
||||||
|
},
|
||||||
|
sudo::{
|
||||||
|
Sudo,
|
||||||
|
SudoEventTypeRegistry,
|
||||||
|
},
|
||||||
|
system::{
|
||||||
|
System,
|
||||||
|
SystemEventTypeRegistry,
|
||||||
},
|
},
|
||||||
contracts::Contracts,
|
|
||||||
session::Session,
|
|
||||||
staking::Staking,
|
|
||||||
sudo::Sudo,
|
|
||||||
system::System,
|
|
||||||
},
|
},
|
||||||
|
EventTypeRegistry,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Runtime trait.
|
/// Runtime trait.
|
||||||
@@ -162,6 +179,9 @@ pub trait Runtime: System + Sized + Send + Sync + 'static {
|
|||||||
type Signature: Verify + Encode + Send + Sync + 'static;
|
type Signature: Verify + Encode + Send + Sync + 'static;
|
||||||
/// Transaction extras.
|
/// Transaction extras.
|
||||||
type Extra: SignedExtra<Self> + Send + Sync + 'static;
|
type Extra: SignedExtra<Self> + Send + Sync + 'static;
|
||||||
|
|
||||||
|
/// Register type sizes for this runtime
|
||||||
|
fn register_type_sizes(event_type_registry: &mut EventTypeRegistry<Self>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Concrete type definitions compatible with those in the default substrate `node_runtime`
|
/// Concrete type definitions compatible with those in the default substrate `node_runtime`
|
||||||
@@ -178,6 +198,15 @@ impl Staking for DefaultNodeRuntime {}
|
|||||||
impl Runtime for DefaultNodeRuntime {
|
impl Runtime for DefaultNodeRuntime {
|
||||||
type Signature = MultiSignature;
|
type Signature = MultiSignature;
|
||||||
type Extra = DefaultExtra<Self>;
|
type Extra = DefaultExtra<Self>;
|
||||||
|
|
||||||
|
fn register_type_sizes(event_type_registry: &mut EventTypeRegistry<Self>) {
|
||||||
|
event_type_registry.with_system();
|
||||||
|
event_type_registry.with_balances();
|
||||||
|
event_type_registry.with_session();
|
||||||
|
event_type_registry.with_contracts();
|
||||||
|
event_type_registry.with_sudo();
|
||||||
|
register_default_type_sizes(event_type_registry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl System for DefaultNodeRuntime {
|
impl System for DefaultNodeRuntime {
|
||||||
@@ -217,6 +246,14 @@ pub struct NodeTemplateRuntime;
|
|||||||
impl Runtime for NodeTemplateRuntime {
|
impl Runtime for NodeTemplateRuntime {
|
||||||
type Signature = MultiSignature;
|
type Signature = MultiSignature;
|
||||||
type Extra = DefaultExtra<Self>;
|
type Extra = DefaultExtra<Self>;
|
||||||
|
|
||||||
|
fn register_type_sizes(event_type_registry: &mut EventTypeRegistry<Self>) {
|
||||||
|
event_type_registry.with_system();
|
||||||
|
event_type_registry.with_balances();
|
||||||
|
event_type_registry.with_session();
|
||||||
|
event_type_registry.with_sudo();
|
||||||
|
register_default_type_sizes(event_type_registry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl System for NodeTemplateRuntime {
|
impl System for NodeTemplateRuntime {
|
||||||
@@ -253,6 +290,14 @@ pub struct ContractsTemplateRuntime;
|
|||||||
impl Runtime for ContractsTemplateRuntime {
|
impl Runtime for ContractsTemplateRuntime {
|
||||||
type Signature = <NodeTemplateRuntime as Runtime>::Signature;
|
type Signature = <NodeTemplateRuntime as Runtime>::Signature;
|
||||||
type Extra = DefaultExtra<Self>;
|
type Extra = DefaultExtra<Self>;
|
||||||
|
|
||||||
|
fn register_type_sizes(event_type_registry: &mut EventTypeRegistry<Self>) {
|
||||||
|
event_type_registry.with_system();
|
||||||
|
event_type_registry.with_balances();
|
||||||
|
event_type_registry.with_contracts();
|
||||||
|
event_type_registry.with_sudo();
|
||||||
|
register_default_type_sizes(event_type_registry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl System for ContractsTemplateRuntime {
|
impl System for ContractsTemplateRuntime {
|
||||||
@@ -287,6 +332,14 @@ pub struct KusamaRuntime;
|
|||||||
impl Runtime for KusamaRuntime {
|
impl Runtime for KusamaRuntime {
|
||||||
type Signature = MultiSignature;
|
type Signature = MultiSignature;
|
||||||
type Extra = DefaultExtra<Self>;
|
type Extra = DefaultExtra<Self>;
|
||||||
|
|
||||||
|
fn register_type_sizes(event_type_registry: &mut EventTypeRegistry<Self>) {
|
||||||
|
event_type_registry.with_system();
|
||||||
|
event_type_registry.with_balances();
|
||||||
|
event_type_registry.with_session();
|
||||||
|
event_type_registry.with_staking();
|
||||||
|
register_default_type_sizes(event_type_registry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl System for KusamaRuntime {
|
impl System for KusamaRuntime {
|
||||||
@@ -311,3 +364,78 @@ impl Staking for KusamaRuntime {}
|
|||||||
impl Balances for KusamaRuntime {
|
impl Balances for KusamaRuntime {
|
||||||
type Balance = u128;
|
type Balance = u128;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)>;
|
||||||
|
|
||||||
|
/// Register default common runtime type sizes
|
||||||
|
pub fn register_default_type_sizes<T: Runtime>(
|
||||||
|
event_type_registry: &mut EventTypeRegistry<T>,
|
||||||
|
) {
|
||||||
|
// primitives
|
||||||
|
event_type_registry.register_type_size::<bool>("bool");
|
||||||
|
event_type_registry.register_type_size::<u8>("u8");
|
||||||
|
event_type_registry.register_type_size::<u32>("u32");
|
||||||
|
event_type_registry.register_type_size::<u64>("u64");
|
||||||
|
event_type_registry.register_type_size::<u128>("u128");
|
||||||
|
|
||||||
|
event_type_registry.register_type_size::<()>("PhantomData");
|
||||||
|
|
||||||
|
// frame_support types
|
||||||
|
event_type_registry
|
||||||
|
.register_type_size::<frame_support::dispatch::DispatchInfo>("DispatchInfo");
|
||||||
|
event_type_registry
|
||||||
|
.register_type_size::<frame_support::dispatch::DispatchResult>("DispatchResult");
|
||||||
|
event_type_registry
|
||||||
|
.register_type_size::<frame_support::dispatch::DispatchError>("DispatchError");
|
||||||
|
event_type_registry
|
||||||
|
.register_type_size::<frame_support::traits::BalanceStatus>("Status");
|
||||||
|
|
||||||
|
// aliases etc.
|
||||||
|
event_type_registry.register_type_size::<u32>("ReferendumIndex");
|
||||||
|
event_type_registry.register_type_size::<[u8; 16]>("Kind");
|
||||||
|
|
||||||
|
event_type_registry.register_type_size::<u32>("AccountIndex");
|
||||||
|
event_type_registry.register_type_size::<u32>("PropIndex");
|
||||||
|
event_type_registry.register_type_size::<u32>("ProposalIndex");
|
||||||
|
event_type_registry.register_type_size::<u32>("AuthorityIndex");
|
||||||
|
event_type_registry.register_type_size::<u32>("MemberCount");
|
||||||
|
|
||||||
|
event_type_registry.register_type_size::<u8>("VoteThreshold");
|
||||||
|
event_type_registry
|
||||||
|
.register_type_size::<(T::BlockNumber, u32)>("TaskAddress<BlockNumber>");
|
||||||
|
|
||||||
|
event_type_registry.register_type_size::<AuthorityId>("AuthorityId");
|
||||||
|
event_type_registry.register_type_size::<AuthorityWeight>("AuthorityWeight");
|
||||||
|
event_type_registry
|
||||||
|
.register_type_size::<Vec<(AuthorityId, AuthorityWeight)>>("AuthorityList");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_register_default_runtime_type_sizes() {
|
||||||
|
EventTypeRegistry::<DefaultNodeRuntime>::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_register_node_template_runtime_type_sizes() {
|
||||||
|
EventTypeRegistry::<NodeTemplateRuntime>::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_register_contracts_template_runtime_type_sizes() {
|
||||||
|
EventTypeRegistry::<ContractsTemplateRuntime>::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_register_kusama_runtime_type_sizes() {
|
||||||
|
EventTypeRegistry::<KusamaRuntime>::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+4
-4
@@ -34,9 +34,9 @@ use crate::{
|
|||||||
|
|
||||||
/// Event subscription simplifies filtering a storage change set stream for
|
/// Event subscription simplifies filtering a storage change set stream for
|
||||||
/// events of interest.
|
/// events of interest.
|
||||||
pub struct EventSubscription<T: Runtime> {
|
pub struct EventSubscription<'a, T: Runtime> {
|
||||||
subscription: Subscription<StorageChangeSet<T::Hash>>,
|
subscription: Subscription<StorageChangeSet<T::Hash>>,
|
||||||
decoder: EventsDecoder<T>,
|
decoder: &'a EventsDecoder<T>,
|
||||||
block: Option<T::Hash>,
|
block: Option<T::Hash>,
|
||||||
extrinsic: Option<usize>,
|
extrinsic: Option<usize>,
|
||||||
event: Option<(&'static str, &'static str)>,
|
event: Option<(&'static str, &'static str)>,
|
||||||
@@ -44,11 +44,11 @@ pub struct EventSubscription<T: Runtime> {
|
|||||||
finished: bool,
|
finished: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Runtime> EventSubscription<T> {
|
impl<'a, T: Runtime> EventSubscription<'a, T> {
|
||||||
/// Creates a new event subscription.
|
/// Creates a new event subscription.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
subscription: Subscription<StorageChangeSet<T::Hash>>,
|
subscription: Subscription<StorageChangeSet<T::Hash>>,
|
||||||
decoder: EventsDecoder<T>,
|
decoder: &'a EventsDecoder<T>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
subscription,
|
subscription,
|
||||||
|
|||||||
Reference in New Issue
Block a user