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:
Andrew Jones
2021-02-18 10:28:40 +00:00
committed by GitHub
parent 2c8e5211aa
commit de859e7396
19 changed files with 436 additions and 280 deletions
+63 -15
View File
@@ -49,7 +49,10 @@ pub use substrate_subxt_client as client;
pub use sp_core;
pub use sp_runtime;
use codec::Decode;
use codec::{
Codec,
Decode,
};
use futures::future;
use jsonrpsee::client::Subscription;
use sp_core::{
@@ -76,6 +79,7 @@ mod subscription;
pub use crate::{
error::Error,
events::{
EventTypeRegistry,
EventsDecoder,
RawEvent,
},
@@ -115,20 +119,22 @@ use crate::{
/// ClientBuilder for constructing a Client.
#[derive(Default)]
pub struct ClientBuilder<T: Runtime> {
_marker: std::marker::PhantomData<T>,
url: Option<String>,
client: Option<jsonrpsee::Client>,
page_size: Option<u32>,
event_type_registry: EventTypeRegistry<T>,
skip_type_sizes_check: bool,
}
impl<T: Runtime> ClientBuilder<T> {
/// Creates a new ClientBuilder.
pub fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
url: None,
client: None,
page_size: None,
event_type_registry: EventTypeRegistry::new(),
skip_type_sizes_check: false,
}
}
@@ -150,8 +156,30 @@ impl<T: Runtime> ClientBuilder<T> {
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.
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 {
client
} else {
@@ -170,10 +198,33 @@ impl<T: Runtime> ClientBuilder<T> {
rpc.system_properties(),
)
.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 {
rpc,
genesis_hash: genesis_hash?,
metadata: metadata?,
metadata,
events_decoder,
properties: properties.unwrap_or_else(|_| Default::default()),
runtime_version: runtime_version?,
_marker: PhantomData,
@@ -187,6 +238,7 @@ pub struct Client<T: Runtime> {
rpc: Rpc<T>,
genesis_hash: T::Hash,
metadata: Metadata,
events_decoder: EventsDecoder<T>,
properties: SystemProperties,
runtime_version: RuntimeVersion,
_marker: PhantomData<(fn() -> T::Signature, T::Extra)>,
@@ -199,6 +251,7 @@ impl<T: Runtime> Clone for Client<T> {
rpc: self.rpc.clone(),
genesis_hash: self.genesis_hash,
metadata: self.metadata.clone(),
events_decoder: self.events_decoder.clone(),
properties: self.properties.clone(),
runtime_version: self.runtime_version.clone(),
_marker: PhantomData,
@@ -466,12 +519,9 @@ impl<T: Runtime> Client<T> {
Ok(signed)
}
/// Returns an events decoder for a call.
pub fn events_decoder<C: Call<T>>(&self) -> EventsDecoder<T> {
let metadata = self.metadata().clone();
let mut decoder = EventsDecoder::new(metadata);
C::events_decoder(&mut decoder);
decoder
/// Returns the events decoder.
pub fn events_decoder(&self) -> &EventsDecoder<T> {
&self.events_decoder
}
/// 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(
&self,
extrinsic: UncheckedExtrinsic<T>,
decoder: EventsDecoder<T>,
) -> Result<ExtrinsicSuccess<T>, Error> {
self.rpc
.submit_and_watch_extrinsic(extrinsic, decoder)
.submit_and_watch_extrinsic(extrinsic, &self.events_decoder)
.await
}
@@ -518,8 +567,7 @@ impl<T: Runtime> Client<T> {
Send + Sync,
{
let extrinsic = self.create_signed(call, signer).await?;
let decoder = self.events_decoder::<C>();
self.submit_and_watch_extrinsic(extrinsic, decoder).await
self.submit_and_watch_extrinsic(extrinsic).await
}
/// Insert a key into the keystore.