Rework Subxt API to support offline and dynamic transactions (#593)

* WIP API changes

* debug impls

* Get main crate compiling with first round of changes

* Some tidy up

* Add WithExtrinsicParams, and have SubstrateConfig + PolkadotConfig, not DefaultConfig

* move transaction into extrinsic folder

* Add runtime updates back to OnlineClient

* rework to be 'client first' to fit better with storage + events

* add support for events to Client

* tidy dupe trait bound

* Wire storage into client, but need to remove static reliance

* various tidy up and start stripping codegen to remove bits we dont need now

* First pass updating calls and constants codegen

* WIP storage client updates

* First pass migrated runtime storage over to new format

* pass over codegen to generate StorageAddresses and throw other stuff out

* don't need a Call trait any more

* shuffle things around a bit

* Various proc_macro fixes to get 'cargo check' working

* organise what's exposed from subxt

* Get first example working; balance_transfer_with_params

* get balance_transfer example compiling

* get concurrent_storage_requests.rs example compiling

* get fetch_all_accounts example compiling

* get a bunch more of the examples compiling

* almost get final example working; type mismatch to look into

* wee tweaks

* move StorageAddress to separate file

* pass Defaultable/Iterable info to StorageAddress in codegen

* fix storage validation ne, and partial run through example code

* Remove static iteration and strip a generic param from everything

* fix doc tests in subxt crate

* update test utils and start fixing frame tests

* fix frame staking tests

* fix the rest of the test compile issues, Borrow on storage values

* cargo fmt

* remove extra logging during tests

* Appease clippy and no more need for into_iter on events

* cargo fmt

* fix dryRun tests by waiting for blocks

* wait for blocks instead of sleeping or other test hacks

* cargo fmt

* Fix doc links

* Traitify StorageAddress

* remove out-of-date doc comments

* optimise decoding storage a little

* cleanup tx stuff, trait for TxPayload, remove Err type param and decode at runtime

* clippy fixes

* fix doc links

* fix doc example

* constant address trait for consistency

* fix a typo and remove EncodeWithMetadata stuff

* Put EventDetails behind a proper interface and allow decoding into top level event, too

* fix docs

* tweak StorageAddress docs

* re-export StorageAddress at root for consistency

* fix clippy things

* Add support for dynamic values

* fix double encoding of storage map key after refactor

* clippy fix

* Fixes and add a dynamic usage example (needs new scale_value release)

* bump scale_value version

* cargo fmt

* Tweak event bits

* cargo fmt

* Add a test and bump scale-value to 0.4.0 to support this

* remove unnecessary vec from dynamic example

* Various typo/grammar fixes

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>

* Address PR nits

* Undo accidental rename in changelog

* Small PR nits/tidyups

* fix tests; codegen change against latest substrate

* tweak storage address util names

* move error decoding to DecodeError and expose

* impl some basic traits on the extrinsic param builder

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
James Wilson
2022-08-08 11:55:20 +01:00
committed by GitHub
parent 7a09ac6cd7
commit e48f0e3b1d
84 changed files with 23097 additions and 35863 deletions
+65 -135
View File
@@ -5,18 +5,14 @@
//! Subscribing to events.
use crate::{
error::BasicError,
Client,
client::OnlineClientT,
error::Error,
events::EventsClient,
Config,
};
use codec::Decode;
use derivative::Derivative;
use futures::{
future::Either,
stream::{
self,
BoxStream,
},
stream::BoxStream,
Future,
FutureExt,
Stream,
@@ -30,112 +26,16 @@ use std::{
};
pub use super::{
at,
EventDetails,
EventFilter,
Events,
FilterEvents,
RawEventDetails,
};
/// Subscribe to events from blocks.
///
/// **Note:** these blocks haven't necessarily been finalised yet; prefer
/// [`Events::subscribe_finalized()`] if that is important.
///
/// **Note:** This function is hidden from the documentation
/// and is exposed only to be called via the codegen. It may
/// break between minor releases.
#[doc(hidden)]
pub async fn subscribe<T: Config, Evs: Decode + 'static>(
client: &Client<T>,
) -> Result<EventSubscription<EventSub<T::Header>, T, Evs>, BasicError> {
let block_subscription = client.rpc().subscribe_blocks().await?;
Ok(EventSubscription::new(client, block_subscription))
}
/// Subscribe to events from finalized blocks.
///
/// **Note:** This function is hidden from the documentation
/// and is exposed only to be called via the codegen. It may
/// break between minor releases.
#[doc(hidden)]
pub async fn subscribe_finalized<T: Config, Evs: Decode + 'static>(
client: &Client<T>,
) -> Result<EventSubscription<FinalizedEventSub<T::Header>, T, Evs>, BasicError> {
// fetch the last finalised block details immediately, so that we'll get
// events for each block after this one.
let last_finalized_block_hash = client.rpc().finalized_head().await?;
let last_finalized_block_number = client
.rpc()
.header(Some(last_finalized_block_hash))
.await?
.map(|h| (*h.number()).into());
// Fill in any gaps between the block above and the finalized blocks reported.
let block_subscription = subscribe_to_block_headers_filling_in_gaps(
client,
last_finalized_block_number,
client.rpc().subscribe_finalized_blocks().await?,
);
Ok(EventSubscription::new(client, Box::pin(block_subscription)))
}
/// Take a subscription that returns block headers, and if any block numbers are missed out
/// betweem the block number provided and what's returned from the subscription, we fill in
/// the gaps and get hold of all intermediate block headers.
///
/// **Note:** This is exposed so that we can run integration tests on it, but otherwise
/// should not be used directly and may break between minor releases.
#[doc(hidden)]
pub fn subscribe_to_block_headers_filling_in_gaps<'a, S, E, T: Config>(
client: &'a Client<T>,
mut last_block_num: Option<u64>,
sub: S,
) -> impl Stream<Item = Result<T::Header, BasicError>> + Send + 'a
where
S: Stream<Item = Result<T::Header, E>> + Send + 'a,
E: Into<BasicError> + Send + 'static,
{
sub.flat_map(move |s| {
// Get the header, or return a stream containing just the error. Our EventSubscription
// stream will return `None` as soon as it hits an error like this.
let header = match s {
Ok(header) => header,
Err(e) => return Either::Left(stream::once(async { Err(e.into()) })),
};
// We want all previous details up to, but not including this current block num.
let end_block_num = (*header.number()).into();
// This is one after the last block we returned details for last time.
let start_block_num = last_block_num.map(|n| n + 1).unwrap_or(end_block_num);
// Iterate over all of the previous blocks we need headers for, ignoring the current block
// (which we already have the header info for):
let previous_headers = stream::iter(start_block_num..end_block_num)
.then(move |n| {
async move {
let hash = client.rpc().block_hash(Some(n.into())).await?;
let header = client.rpc().header(hash).await?;
Ok::<_, BasicError>(header)
}
})
.filter_map(|h| async { h.transpose() });
// On the next iteration, we'll get details starting just after this end block.
last_block_num = Some(end_block_num);
// Return a combination of any previous headers plus the new header.
Either::Right(previous_headers.chain(stream::once(async { Ok(header) })))
})
}
/// A `jsonrpsee` Subscription. This forms a part of the `EventSubscription` type handed back
/// in codegen from `subscribe_finalized`, and is exposed to be used in codegen.
#[doc(hidden)]
pub type FinalizedEventSub<'a, Header> = BoxStream<'a, Result<Header, BasicError>>;
pub type FinalizedEventSub<Header> = BoxStream<'static, Result<Header, Error>>;
/// A `jsonrpsee` Subscription. This forms a part of the `EventSubscription` type handed back
/// in codegen from `subscribe`, and is exposed to be used in codegen.
@@ -144,52 +44,80 @@ pub type EventSub<Item> = Subscription<Item>;
/// A subscription to events that implements [`Stream`], and returns [`Events`] objects for each block.
#[derive(Derivative)]
#[derivative(Debug(bound = "Sub: std::fmt::Debug"))]
pub struct EventSubscription<'a, Sub, T: Config, Evs: 'static> {
#[derivative(Debug(bound = "Sub: std::fmt::Debug, Client: std::fmt::Debug"))]
pub struct EventSubscription<T: Config, Client, Sub> {
finished: bool,
client: &'a Client<T>,
client: Client,
block_header_subscription: Sub,
#[derivative(Debug = "ignore")]
at: Option<
std::pin::Pin<
Box<dyn Future<Output = Result<Events<T, Evs>, BasicError>> + Send + 'a>,
>,
>,
_event_type: std::marker::PhantomData<Evs>,
at: Option<std::pin::Pin<Box<dyn Future<Output = Result<Events<T>, Error>> + Send>>>,
}
impl<'a, Sub, T: Config, Evs: Decode, E: Into<BasicError>>
EventSubscription<'a, Sub, T, Evs>
impl<T: Config, Client, Sub, E: Into<Error>> EventSubscription<T, Client, Sub>
where
Sub: Stream<Item = Result<T::Header, E>> + Unpin + 'a,
Sub: Stream<Item = Result<T::Header, E>> + Unpin,
{
fn new(client: &'a Client<T>, block_header_subscription: Sub) -> Self {
/// Create a new [`EventSubscription`] from a client and a subscription
/// which returns block headers.
pub fn new(client: Client, block_header_subscription: Sub) -> Self {
EventSubscription {
finished: false,
client,
block_header_subscription,
at: None,
_event_type: std::marker::PhantomData,
}
}
/// Return only specific events matching the tuple of 1 or more event
/// types that has been provided as the `Filter` type parameter.
pub fn filter_events<Filter: EventFilter>(self) -> FilterEvents<'a, Self, T, Filter> {
///
/// # Example
///
/// ```no_run
/// use futures::StreamExt;
/// use subxt::{OnlineClient, PolkadotConfig};
///
/// #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
/// pub mod polkadot {}
///
/// # #[tokio::main]
/// # async fn main() {
/// let api = OnlineClient::<PolkadotConfig>::new().await.unwrap();
///
/// let mut events = api
/// .events()
/// .subscribe()
/// .await
/// .unwrap()
/// .filter_events::<(
/// polkadot::balances::events::Transfer,
/// polkadot::balances::events::Deposit
/// )>();
///
/// while let Some(ev) = events.next().await {
/// let event_details = ev.unwrap();
/// match event_details.event {
/// (Some(transfer), None) => println!("Balance transfer event: {transfer:?}"),
/// (None, Some(deposit)) => println!("Balance deposit event: {deposit:?}"),
/// _ => unreachable!()
/// }
/// }
/// # }
/// ```
pub fn filter_events<Filter: EventFilter>(
self,
) -> FilterEvents<'static, Self, T, Filter> {
FilterEvents::new(self)
}
}
impl<'a, T: Config, Sub: Unpin, Evs: Decode> Unpin
for EventSubscription<'a, Sub, T, Evs>
{
}
impl<T: Config, Client, Sub: Unpin> Unpin for EventSubscription<T, Client, Sub> {}
// We want `EventSubscription` to implement Stream. The below implementation is the rather verbose
// way to roughly implement the following function:
//
// ```
// fn subscribe_events<T: Config, Evs: Decode>(client: &'_ Client<T>, block_sub: Subscription<T::Header>) -> impl Stream<Item=Result<Events<'_, T, Evs>, BasicError>> + '_ {
// fn subscribe_events<T: Config, Evs: Decode>(client: &'_ Client<T>, block_sub: Subscription<T::Header>) -> impl Stream<Item=Result<Events<'_, T, Evs>, Error>> + '_ {
// use futures::StreamExt;
// block_sub.then(move |block_header_res| async move {
// use sp_runtime::traits::Header;
@@ -202,14 +130,14 @@ impl<'a, T: Config, Sub: Unpin, Evs: Decode> Unpin
//
// The advantage of this manual implementation is that we have a named type that we (and others)
// can derive things on, store away, alias etc.
impl<'a, Sub, T, Evs, E> Stream for EventSubscription<'a, Sub, T, Evs>
impl<T, Client, Sub, E> Stream for EventSubscription<T, Client, Sub>
where
T: Config,
Evs: Decode,
Sub: Stream<Item = Result<T::Header, E>> + Unpin + 'a,
E: Into<BasicError>,
Client: OnlineClientT<T>,
Sub: Stream<Item = Result<T::Header, E>> + Unpin,
E: Into<Error>,
{
type Item = Result<Events<T, Evs>, BasicError>;
type Item = Result<Events<T>, Error>;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
@@ -235,7 +163,9 @@ where
Some(Ok(block_header)) => {
// Note [jsdw]: We may be able to get rid of the per-item allocation
// with https://github.com/oblique/reusable-box-future.
self.at = Some(Box::pin(at(self.client, block_header.hash())));
let at = EventsClient::new(self.client.clone())
.at(Some(block_header.hash()));
self.at = Some(Box::pin(at));
// Continue, so that we poll this function future we've just created.
}
}
@@ -263,16 +193,16 @@ mod test {
fn assert_send<T: Send>() {}
assert_send::<
EventSubscription<
EventSub<<crate::DefaultConfig as Config>::Header>,
crate::DefaultConfig,
crate::SubstrateConfig,
(),
EventSub<<crate::SubstrateConfig as Config>::Header>,
>,
>();
assert_send::<
EventSubscription<
FinalizedEventSub<<crate::DefaultConfig as Config>::Header>,
crate::DefaultConfig,
crate::SubstrateConfig,
(),
FinalizedEventSub<<crate::SubstrateConfig as Config>::Header>,
>,
>();
}
+249
View File
@@ -0,0 +1,249 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::{
client::OnlineClientT,
error::Error,
events::{
EventSub,
EventSubscription,
Events,
FinalizedEventSub,
},
Config,
};
use derivative::Derivative;
use futures::{
future::Either,
stream,
Stream,
StreamExt,
};
use sp_core::{
storage::StorageKey,
twox_128,
};
use sp_runtime::traits::Header;
use std::future::Future;
/// A client for working with events.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
pub struct EventsClient<T, Client> {
client: Client,
_marker: std::marker::PhantomData<T>,
}
impl<T, Client> EventsClient<T, Client> {
/// Create a new [`EventsClient`].
pub fn new(client: Client) -> Self {
Self {
client,
_marker: std::marker::PhantomData,
}
}
}
impl<T, Client> EventsClient<T, Client>
where
T: Config,
Client: OnlineClientT<T>,
{
/// Obtain events at some block hash.
pub fn at(
&self,
block_hash: Option<T::Hash>,
) -> impl Future<Output = Result<Events<T>, Error>> + Send + 'static {
// Clone and pass the client in like this so that we can explicitly
// return a Future that's Send + 'static, rather than tied to &self.
let client = self.client.clone();
async move { at(client, block_hash).await }
}
/// Subscribe to all events from blocks.
///
/// **Note:** these blocks haven't necessarily been finalised yet; prefer
/// [`EventsClient::subscribe_finalized()`] if that is important.
///
/// # Example
///
/// ```no_run
/// # #[tokio::main]
/// # async fn main() {
/// use futures::StreamExt;
/// use subxt::{ OnlineClient, PolkadotConfig };
///
/// let api = OnlineClient::<PolkadotConfig>::new().await.unwrap();
///
/// let mut events = api.events().subscribe().await.unwrap();
///
/// while let Some(ev) = events.next().await {
/// // Obtain all events from this block.
/// let ev = ev.unwrap();
/// // Print block hash.
/// println!("Event at block hash {:?}", ev.block_hash());
/// // Iterate over all events.
/// let mut iter = ev.iter();
/// while let Some(event_details) = iter.next() {
/// println!("Event details {:?}", event_details);
/// }
/// }
/// # }
/// ```
pub fn subscribe(
&self,
) -> impl Future<
Output = Result<EventSubscription<T, Client, EventSub<T::Header>>, Error>,
> + Send
+ 'static {
let client = self.client.clone();
async move { subscribe(client).await }
}
/// Subscribe to events from finalized blocks. See [`EventsClient::subscribe()`] for details.
pub fn subscribe_finalized(
&self,
) -> impl Future<
Output = Result<
EventSubscription<T, Client, FinalizedEventSub<T::Header>>,
Error,
>,
> + Send
+ 'static
where
Client: Send + Sync + 'static,
{
let client = self.client.clone();
async move { subscribe_finalized(client).await }
}
}
async fn at<T, Client>(
client: Client,
block_hash: Option<T::Hash>,
) -> Result<Events<T>, Error>
where
T: Config,
Client: OnlineClientT<T>,
{
// If block hash is not provided, get the hash
// for the latest block and use that.
let block_hash = match block_hash {
Some(hash) => hash,
None => {
client
.rpc()
.block_hash(None)
.await?
.expect("didn't pass a block number; qed")
}
};
let event_bytes = client
.rpc()
.storage(&*system_events_key().0, Some(block_hash))
.await?
.map(|e| e.0)
.unwrap_or_else(Vec::new);
Ok(Events::new(client.metadata(), block_hash, event_bytes))
}
async fn subscribe<T, Client>(
client: Client,
) -> Result<EventSubscription<T, Client, EventSub<T::Header>>, Error>
where
T: Config,
Client: OnlineClientT<T>,
{
let block_subscription = client.rpc().subscribe_blocks().await?;
Ok(EventSubscription::new(client, block_subscription))
}
/// Subscribe to events from finalized blocks.
async fn subscribe_finalized<T, Client>(
client: Client,
) -> Result<EventSubscription<T, Client, FinalizedEventSub<T::Header>>, Error>
where
T: Config,
Client: OnlineClientT<T>,
{
// fetch the last finalised block details immediately, so that we'll get
// events for each block after this one.
let last_finalized_block_hash = client.rpc().finalized_head().await?;
let last_finalized_block_number = client
.rpc()
.header(Some(last_finalized_block_hash))
.await?
.map(|h| (*h.number()).into());
let sub = client.rpc().subscribe_finalized_blocks().await?;
// Fill in any gaps between the block above and the finalized blocks reported.
let block_subscription = subscribe_to_block_headers_filling_in_gaps(
client.clone(),
last_finalized_block_number,
sub,
);
Ok(EventSubscription::new(client, Box::pin(block_subscription)))
}
/// Note: This is exposed for testing but is not considered stable and may change
/// without notice in a patch release.
#[doc(hidden)]
pub fn subscribe_to_block_headers_filling_in_gaps<T, Client, S, E>(
client: Client,
mut last_block_num: Option<u64>,
sub: S,
) -> impl Stream<Item = Result<T::Header, Error>> + Send
where
T: Config,
Client: OnlineClientT<T> + Send + Sync,
S: Stream<Item = Result<T::Header, E>> + Send,
E: Into<Error> + Send + 'static,
{
sub.flat_map(move |s| {
let client = client.clone();
// Get the header, or return a stream containing just the error. Our EventSubscription
// stream will return `None` as soon as it hits an error like this.
let header = match s {
Ok(header) => header,
Err(e) => return Either::Left(stream::once(async { Err(e.into()) })),
};
// We want all previous details up to, but not including this current block num.
let end_block_num = (*header.number()).into();
// This is one after the last block we returned details for last time.
let start_block_num = last_block_num.map(|n| n + 1).unwrap_or(end_block_num);
// Iterate over all of the previous blocks we need headers for, ignoring the current block
// (which we already have the header info for):
let previous_headers = stream::iter(start_block_num..end_block_num)
.then(move |n| {
let client = client.clone();
async move {
let hash = client.rpc().block_hash(Some(n.into())).await?;
let header = client.rpc().header(hash).await?;
Ok::<_, Error>(header)
}
})
.filter_map(|h| async { h.transpose() });
// On the next iteration, we'll get details starting just after this end block.
last_block_num = Some(end_block_num);
// Return a combination of any previous headers plus the new header.
Either::Right(previous_headers.chain(stream::once(async { Ok(header) })))
})
}
// The storage key needed to access events.
fn system_events_key() -> StorageKey {
let mut storage_key = twox_128(b"System").to_vec();
storage_key.extend(twox_128(b"Events").to_vec());
StorageKey(storage_key)
}
+208 -435
View File
@@ -4,13 +4,15 @@
//! A representation of a block of events.
use crate::{
error::BasicError,
Client,
Config,
Event,
Metadata,
use super::{
Phase,
StaticEvent,
};
use crate::{
dynamic::DecodedValue,
error::Error,
Config,
Metadata,
};
use codec::{
Compact,
@@ -19,77 +21,48 @@ use codec::{
Input,
};
use derivative::Derivative;
use parking_lot::RwLock;
use sp_core::{
storage::StorageKey,
twox_128,
};
use std::sync::Arc;
/// Obtain events at some block hash. The generic parameter is what we
/// will attempt to decode each event into if using [`Events::iter()`],
/// and is expected to be the outermost event enum that contains all of
/// the possible events across all pallets.
///
/// **Note:** This function is hidden from the documentation
/// and is exposed only to be called via the codegen. Thus, prefer to use
/// `api.events().at(block_hash)` over calling this directly.
#[doc(hidden)]
pub async fn at<T: Config, Evs: Decode>(
client: &'_ Client<T>,
block_hash: T::Hash,
) -> Result<Events<T, Evs>, BasicError> {
let mut event_bytes = client
.rpc()
.storage(&system_events_key(), Some(block_hash))
.await?
.map(|s| s.0)
.unwrap_or_else(Vec::new);
// event_bytes is a SCALE encoded vector of events. So, pluck the
// compact encoded length from the front, leaving the remaining bytes
// for our iterating to decode.
//
// Note: if we get no bytes back, avoid an error reading vec length
// and default to 0 events.
let cursor = &mut &*event_bytes;
let num_events = <Compact<u32>>::decode(cursor).unwrap_or(Compact(0)).0;
let event_bytes_len = event_bytes.len();
let remaining_len = cursor.len();
event_bytes.drain(0..event_bytes_len - remaining_len);
Ok(Events {
metadata: client.metadata(),
block_hash,
event_bytes,
num_events,
_event_type: std::marker::PhantomData,
})
}
// The storage key needed to access events.
fn system_events_key() -> StorageKey {
let mut storage_key = twox_128(b"System").to_vec();
storage_key.extend(twox_128(b"Events").to_vec());
StorageKey(storage_key)
}
/// A collection of events obtained from a block, bundled with the necessary
/// information needed to decode and iterate over them.
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub struct Events<T: Config, Evs> {
metadata: Arc<RwLock<Metadata>>,
pub struct Events<T: Config> {
metadata: Metadata,
block_hash: T::Hash,
// Note; raw event bytes are prefixed with a Compact<u32> containing
// the number of events to be decoded. We should have stripped that off
// before storing the bytes here.
event_bytes: Vec<u8>,
event_bytes: Arc<[u8]>,
num_events: u32,
_event_type: std::marker::PhantomData<Evs>,
}
impl<'a, T: Config, Evs: Decode> Events<T, Evs> {
impl<T: Config> Events<T> {
pub(crate) fn new(
metadata: Metadata,
block_hash: T::Hash,
mut event_bytes: Vec<u8>,
) -> Self {
// event_bytes is a SCALE encoded vector of events. So, pluck the
// compact encoded length from the front, leaving the remaining bytes
// for our iterating to decode.
//
// Note: if we get no bytes back, avoid an error reading vec length
// and default to 0 events.
let cursor = &mut &*event_bytes;
let num_events = <Compact<u32>>::decode(cursor).unwrap_or(Compact(0)).0;
let event_bytes_len = event_bytes.len();
let remaining_len = cursor.len();
event_bytes.drain(0..event_bytes_len - remaining_len);
Self {
metadata,
block_hash,
event_bytes: event_bytes.into(),
num_events,
}
}
/// The number of events.
pub fn len(&self) -> u32 {
self.num_events
@@ -106,83 +79,23 @@ impl<'a, T: Config, Evs: Decode> Events<T, Evs> {
self.block_hash
}
/// Iterate over the events, statically decoding them as we go.
/// If an event is encountered that cannot be statically decoded,
/// a [`codec::Error`] will be returned.
///
/// If the generated code does not know about all of the pallets that exist
/// in the runtime being targeted, it may not know about all of the
/// events either, and so this method should be avoided in favout of [`Events::iter_raw()`],
/// which uses runtime metadata to skip over unknown events.
/// Iterate over all of the events, using metadata to dynamically
/// decode them as we go, and returning the raw bytes and other associated
/// details. If an error occurs, all subsequent iterations return `None`.
pub fn iter(
&self,
) -> impl Iterator<Item = Result<EventDetails<Evs>, BasicError>> + '_ {
let event_bytes = &self.event_bytes;
) -> impl Iterator<Item = Result<EventDetails, Error>> + Send + Sync + 'static {
let event_bytes = self.event_bytes.clone();
let num_events = self.num_events;
let metadata = self.metadata.clone();
let mut pos = 0;
let mut index = 0;
std::iter::from_fn(move || {
let cursor = &mut &event_bytes[pos..];
let start_len = cursor.len();
if start_len == 0 || self.num_events == index {
None
} else {
let mut decode_one_event = || -> Result<_, BasicError> {
let phase = Phase::decode(cursor)?;
let ev = Evs::decode(cursor)?;
let _topics = Vec::<T::Hash>::decode(cursor)?;
Ok((phase, ev))
};
match decode_one_event() {
Ok((phase, event)) => {
// Skip over decoded bytes in next iteration:
pos += start_len - cursor.len();
// Gather the event details before incrementing the index for the next iter.
let res = Some(Ok(EventDetails {
phase,
index,
event,
}));
index += 1;
res
}
Err(e) => {
// By setting the position to the "end" of the event bytes,
// the cursor len will become 0 and the iterator will return `None`
// from now on:
pos = event_bytes.len();
Some(Err(e))
}
}
}
})
}
/// Iterate over all of the events, using metadata to dynamically
/// decode them as we go, and returning the raw bytes and other associated
/// details. If an error occurs, all subsequent iterations return `None`.
///
/// This method is safe to use even if you do not statically know about
/// all of the possible events; it splits events up using the metadata
/// obtained at runtime, which does.
pub fn iter_raw(
&self,
) -> impl Iterator<Item = Result<RawEventDetails, BasicError>> + '_ {
let event_bytes = &self.event_bytes;
let metadata = {
let metadata = self.metadata.read();
metadata.clone()
};
let mut pos = 0;
let mut index = 0;
std::iter::from_fn(move || {
let cursor = &mut &event_bytes[pos..];
let start_len = cursor.len();
if start_len == 0 || self.num_events == index {
if start_len == 0 || num_events == index {
None
} else {
match decode_raw_event_details::<T>(&metadata, index, cursor) {
@@ -206,62 +119,11 @@ impl<'a, T: Config, Evs: Decode> Events<T, Evs> {
})
}
/// Iterate over all of the events, using metadata to dynamically
/// decode them as we go, and returning the raw bytes and other associated
/// details. If an error occurs, all subsequent iterations return `None`.
///
/// This method is safe to use even if you do not statically know about
/// all of the possible events; it splits events up using the metadata
/// obtained at runtime, which does.
///
/// Unlike [`Events::iter_raw()`] this consumes `self`, which can be useful
/// if you need to store the iterator somewhere and avoid lifetime issues.
pub fn into_iter_raw(
self,
) -> impl Iterator<Item = Result<RawEventDetails, BasicError>> + 'a {
let mut pos = 0;
let mut index = 0;
let metadata = {
let metadata = self.metadata.read();
metadata.clone()
};
std::iter::from_fn(move || {
let cursor = &mut &self.event_bytes[pos..];
let start_len = cursor.len();
if start_len == 0 || self.num_events == index {
None
} else {
match decode_raw_event_details::<T>(&metadata, index, cursor) {
Ok(raw_event) => {
// Skip over decoded bytes in next iteration:
pos += start_len - cursor.len();
// Increment the index:
index += 1;
// Return the event details:
Some(Ok(raw_event))
}
Err(e) => {
// By setting the position to the "end" of the event bytes,
// the cursor len will become 0 and the iterator will return `None`
// from now on:
pos = self.event_bytes.len();
Some(Err(e))
}
}
}
})
}
/// Iterate through the events using metadata to dynamically decode and skip
/// them, and return only those which should decode to the provided `Ev` type.
/// If an error occurs, all subsequent iterations return `None`.
///
/// **Note:** This method internally uses [`Events::iter_raw()`], so it is safe to
/// use even if you do not statically know about all of the possible events.
pub fn find<Ev: Event>(&self) -> impl Iterator<Item = Result<Ev, BasicError>> + '_ {
self.iter_raw().filter_map(|ev| {
pub fn find<Ev: StaticEvent>(&self) -> impl Iterator<Item = Result<Ev, Error>> + '_ {
self.iter().filter_map(|ev| {
ev.and_then(|ev| ev.as_event::<Ev>().map_err(Into::into))
.transpose()
})
@@ -269,67 +131,147 @@ impl<'a, T: Config, Evs: Decode> Events<T, Evs> {
/// Iterate through the events using metadata to dynamically decode and skip
/// them, and return the first event found which decodes to the provided `Ev` type.
///
/// **Note:** This method internally uses [`Events::iter_raw()`], so it is safe to
/// use even if you do not statically know about all of the possible events.
pub fn find_first<Ev: Event>(&self) -> Result<Option<Ev>, BasicError> {
pub fn find_first<Ev: StaticEvent>(&self) -> Result<Option<Ev>, Error> {
self.find::<Ev>().next().transpose()
}
/// Find an event that decodes to the type provided. Returns true if it was found.
///
/// **Note:** This method internally uses [`Events::iter_raw()`], so it is safe to
/// use even if you do not statically know about all of the possible events.
pub fn has<Ev: crate::Event>(&self) -> Result<bool, BasicError> {
pub fn has<Ev: StaticEvent>(&self) -> Result<bool, Error> {
Ok(self.find::<Ev>().next().transpose()?.is_some())
}
}
/// A decoded event and associated details.
/// The event details.
#[derive(Debug, Clone, PartialEq)]
pub struct EventDetails<Evs> {
/// During which [`Phase`] was the event produced?
pub phase: Phase,
/// What index is this event in the stored events for this block.
pub index: u32,
/// The event itself.
pub event: Evs,
pub struct EventDetails {
phase: Phase,
index: u32,
pallet: String,
variant: String,
bytes: Vec<u8>,
// Dev note: this is here because we've pretty much had to generate it
// anyway, but expect it to be generated on the fly in future versions,
// and so don't expose it.
fields: Vec<(Option<String>, DecodedValue)>,
}
/// A Value which has been decoded from some raw bytes.
pub type DecodedValue = scale_value::Value<scale_value::scale::TypeId>;
/// The raw bytes for an event with associated details about
/// where and when it was emitted.
/// The raw data associated with some event.
#[derive(Debug, Clone, PartialEq)]
pub struct RawEventDetails {
pub struct EventDetailParts {
/// When was the event produced?
pub phase: Phase,
/// What index is this event in the stored events for this block.
pub index: u32,
/// The name of the pallet from whence the Event originated.
pub pallet: String,
/// The index of the pallet from whence the Event originated.
pub pallet_index: u8,
/// The name of the pallet Event variant.
/// The name of the pallet's Event variant.
pub variant: String,
/// The index of the pallet Event variant.
pub variant_index: u8,
/// The bytes representing the fields contained within the event.
/// All of the bytes representing this event, including the pallet
/// and variant index that the event originated from.
pub bytes: Vec<u8>,
/// Generic values representing each field of the event.
pub fields: Vec<DecodedValue>,
}
impl RawEventDetails {
/// Attempt to decode this [`RawEventDetails`] into a specific event.
pub fn as_event<E: Event>(&self) -> Result<Option<E>, CodecError> {
impl EventDetails {
/// Return the raw data associated with this event. Useful if you want
/// ownership over parts of the event data.
pub fn parts(self) -> EventDetailParts {
EventDetailParts {
phase: self.phase,
index: self.index,
pallet: self.pallet,
variant: self.variant,
bytes: self.bytes,
}
}
/// When was the event produced?
pub fn phase(&self) -> Phase {
self.phase
}
/// What index is this event in the stored events for this block.
pub fn index(&self) -> u32 {
self.index
}
/// The index of the pallet that the event originated from.
pub fn pallet_index(&self) -> u8 {
// Note: never panics because we set the first two bytes
// in `decode_event_details` to build this.
self.bytes[0]
}
/// The index of the event variant that the event originated from.
pub fn variant_index(&self) -> u8 {
// Note: never panics because we set the first two bytes
// in `decode_event_details` to build this.
self.bytes[1]
}
/// The name of the pallet from whence the Event originated.
pub fn pallet_name(&self) -> &str {
&self.pallet
}
/// The name of the pallet's Event variant.
pub fn variant_name(&self) -> &str {
&self.variant
}
/// Return the bytes representing this event, which include the pallet
/// and variant index that the event originated from.
pub fn bytes(&self) -> &[u8] {
&self.bytes
}
/// Return the bytes representing the fields stored in this event.
pub fn field_bytes(&self) -> &[u8] {
&self.bytes[2..]
}
/// Decode and provide the event fields back in the form of a composite
/// type, which represents either the named or unnamed fields that were
/// present.
// Dev note: if we can optimise Value decoding to avoid allocating
// while working through events, or if the event structure changes
// to allow us to skip over them, we'll no longer keep a copy of the
// decoded events in the event, and the actual decoding will happen
// when this method is called. This is why we return an owned vec and
// not a reference.
pub fn field_values(&self) -> scale_value::Composite<scale_value::scale::TypeId> {
if self.fields.is_empty() {
scale_value::Composite::Unnamed(vec![])
} else if self.fields[0].0.is_some() {
let named = self
.fields
.iter()
.map(|(n, f)| (n.clone().unwrap_or_default(), f.clone()))
.collect();
scale_value::Composite::Named(named)
} else {
let unnamed = self.fields.iter().map(|(_n, f)| f.clone()).collect();
scale_value::Composite::Unnamed(unnamed)
}
}
/// Attempt to decode these [`EventDetails`] into a specific static event.
/// This targets the fields within the event directly. You can also attempt to
/// decode the entirety of the event type (including the pallet and event
/// variants) using [`EventDetails::as_root_event()`].
pub fn as_event<E: StaticEvent>(&self) -> Result<Option<E>, CodecError> {
if self.pallet == E::PALLET && self.variant == E::EVENT {
Ok(Some(E::decode(&mut &self.bytes[..])?))
Ok(Some(E::decode(&mut &self.bytes[2..])?))
} else {
Ok(None)
}
}
/// Attempt to decode these [`EventDetails`] into a root event type (which includes
/// the pallet and event enum variants as well as the event fields). A compatible
/// type for this is exposed via static codegen as a root level `Event` type.
pub fn as_root_event<E: Decode>(&self) -> Result<E, CodecError> {
E::decode(&mut &self.bytes[..])
}
}
// Attempt to dynamically decode a single event from our events input.
@@ -337,7 +279,7 @@ fn decode_raw_event_details<T: Config>(
metadata: &Metadata,
index: u32,
input: &mut &[u8],
) -> Result<RawEventDetails, BasicError> {
) -> Result<EventDetails, Error> {
// Decode basic event details:
let phase = Phase::decode(input)?;
let pallet_index = input.read_byte()?;
@@ -358,19 +300,20 @@ fn decode_raw_event_details<T: Config>(
event_metadata.event()
);
// Use metadata to figure out which bytes belong to this event:
let mut event_bytes = Vec::new();
// Use metadata to figure out which bytes belong to this event.
// the event bytes also include the pallet/variant index so that, if we
// like, we can decode them quite easily into a top level event type.
let mut event_bytes = vec![pallet_index, variant_index];
let mut event_fields = Vec::new();
for arg in event_metadata.variant().fields() {
let type_id = arg.ty().id();
for (name, type_id) in event_metadata.fields() {
let all_bytes = *input;
// consume some bytes for each event field, moving the cursor forward:
let value = scale_value::scale::decode_as_type(
input,
type_id,
*type_id,
&metadata.runtime_metadata().types,
)?;
event_fields.push(value);
event_fields.push((name.clone(), value));
// count how many bytes were consumed based on remaining length:
let consumed_len = all_bytes.len() - input.len();
// move those consumed bytes to the output vec unaltered:
@@ -382,12 +325,10 @@ fn decode_raw_event_details<T: Config>(
let topics = Vec::<T::Hash>::decode(input)?;
tracing::debug!("topics: {:?}", topics);
Ok(RawEventDetails {
Ok(EventDetails {
phase,
index,
pallet_index,
pallet: event_metadata.pallet().to_string(),
variant_index,
variant: event_metadata.event().to_string(),
bytes: event_bytes,
fields: event_fields,
@@ -400,8 +341,7 @@ pub(crate) mod test_utils {
use super::*;
use crate::{
Config,
DefaultConfig,
Phase,
SubstrateConfig,
};
use codec::Encode;
use frame_metadata::{
@@ -431,7 +371,7 @@ pub(crate) mod test_utils {
pub struct EventRecord<E: Encode> {
phase: Phase,
event: AllEvents<E>,
topics: Vec<<DefaultConfig as Config>::Hash>,
topics: Vec<<SubstrateConfig as Config>::Hash>,
}
/// Build an EventRecord, which encoded events in the format expected
@@ -474,9 +414,9 @@ pub(crate) mod test_utils {
/// Build an `Events` object for test purposes, based on the details provided,
/// and with a default block hash.
pub fn events<E: Decode + Encode>(
metadata: Arc<RwLock<Metadata>>,
metadata: Metadata,
event_records: Vec<EventRecord<E>>,
) -> Events<DefaultConfig, AllEvents<E>> {
) -> Events<SubstrateConfig> {
let num_events = event_records.len() as u32;
let mut event_bytes = Vec::new();
for ev in event_records {
@@ -487,17 +427,16 @@ pub(crate) mod test_utils {
/// Much like [`events`], but takes pre-encoded events and event count, so that we can
/// mess with the bytes in tests if we need to.
pub fn events_raw<E: Decode + Encode>(
metadata: Arc<RwLock<Metadata>>,
pub fn events_raw(
metadata: Metadata,
event_bytes: Vec<u8>,
num_events: u32,
) -> Events<DefaultConfig, AllEvents<E>> {
) -> Events<SubstrateConfig> {
Events {
block_hash: <DefaultConfig as Config>::Hash::default(),
event_bytes,
block_hash: <SubstrateConfig as Config>::Hash::default(),
event_bytes: event_bytes.into(),
metadata,
num_events,
_event_type: std::marker::PhantomData,
}
}
}
@@ -509,18 +448,16 @@ mod tests {
event_record,
events,
events_raw,
AllEvents,
},
*,
};
use crate::Phase;
use codec::Encode;
use scale_info::TypeInfo;
use scale_value::Value;
/// Build a fake wrapped metadata.
fn metadata<E: TypeInfo + 'static>() -> Arc<RwLock<Metadata>> {
Arc::new(RwLock::new(test_utils::metadata::<E>()))
fn metadata<E: TypeInfo + 'static>() -> Metadata {
test_utils::metadata::<E>()
}
/// [`RawEventDetails`] can be annoying to test, because it contains
@@ -542,170 +479,42 @@ mod tests {
pub fn assert_raw_events_match(
// Just for convenience, pass in the metadata type constructed
// by the `metadata` function above to simplify caller code.
metadata: &Arc<RwLock<Metadata>>,
actual: RawEventDetails,
metadata: &Metadata,
actual: EventDetails,
expected: TestRawEventDetails,
) {
let metadata = metadata.read();
let types = &metadata.runtime_metadata().types;
// Make sure that the bytes handed back line up with the fields handed back;
// encode the fields back into bytes and they should be equal.
let mut actual_bytes = vec![];
for field in &actual.fields {
for (_name, field) in &actual.fields {
scale_value::scale::encode_as_type(
field.clone(),
field,
field.context,
types,
&mut actual_bytes,
)
.expect("should be able to encode properly");
}
assert_eq!(actual_bytes, actual.bytes);
assert_eq!(actual_bytes, actual.field_bytes());
let actual_fields_no_context: Vec<_> = actual
.fields
.into_iter()
.map(|f| f.remove_context())
.field_values()
.into_values()
.map(|value| value.remove_context())
.collect();
// Check each of the other fields:
assert_eq!(actual.phase, expected.phase);
assert_eq!(actual.index, expected.index);
assert_eq!(actual.pallet, expected.pallet);
assert_eq!(actual.pallet_index, expected.pallet_index);
assert_eq!(actual.variant, expected.variant);
assert_eq!(actual.variant_index, expected.variant_index);
assert_eq!(actual.phase(), expected.phase);
assert_eq!(actual.index(), expected.index);
assert_eq!(actual.pallet_name(), expected.pallet);
assert_eq!(actual.pallet_index(), expected.pallet_index);
assert_eq!(actual.variant_name(), expected.variant);
assert_eq!(actual.variant_index(), expected.variant_index);
assert_eq!(actual_fields_no_context, expected.fields);
}
#[test]
fn statically_decode_single_event() {
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
enum Event {
A(u8),
}
// Create fake metadata that knows about our single event, above:
let metadata = metadata::<Event>();
// Encode our events in the format we expect back from a node, and
// construct an Events object to iterate them:
let events = events::<Event>(
metadata,
vec![event_record(Phase::Finalization, Event::A(1))],
);
let event_details: Vec<EventDetails<AllEvents<Event>>> =
events.iter().collect::<Result<_, _>>().unwrap();
assert_eq!(
event_details,
vec![EventDetails {
index: 0,
phase: Phase::Finalization,
event: AllEvents::Test(Event::A(1))
}]
);
}
#[test]
fn statically_decode_multiple_events() {
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
enum Event {
A(u8),
B(bool),
}
// Create fake metadata that knows about our single event, above:
let metadata = metadata::<Event>();
// Encode our events in the format we expect back from a node, and
// construst an Events object to iterate them:
let events = events::<Event>(
metadata,
vec![
event_record(Phase::Initialization, Event::A(1)),
event_record(Phase::ApplyExtrinsic(123), Event::B(true)),
event_record(Phase::Finalization, Event::A(234)),
],
);
let event_details: Vec<EventDetails<AllEvents<Event>>> =
events.iter().collect::<Result<_, _>>().unwrap();
assert_eq!(
event_details,
vec![
EventDetails {
index: 0,
phase: Phase::Initialization,
event: AllEvents::Test(Event::A(1))
},
EventDetails {
index: 1,
phase: Phase::ApplyExtrinsic(123),
event: AllEvents::Test(Event::B(true))
},
EventDetails {
index: 2,
phase: Phase::Finalization,
event: AllEvents::Test(Event::A(234))
},
]
);
}
#[test]
fn statically_decode_multiple_events_until_error() {
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
enum Event {
A(u8),
B(bool),
}
// Create fake metadata that knows about our single event, above:
let metadata = metadata::<Event>();
// Encode 2 events:
let mut event_bytes = vec![];
event_record(Phase::Initialization, Event::A(1)).encode_to(&mut event_bytes);
event_record(Phase::ApplyExtrinsic(123), Event::B(true))
.encode_to(&mut event_bytes);
// Push a few naff bytes to the end (a broken third event):
event_bytes.extend_from_slice(&[3, 127, 45, 0, 2]);
// Encode our events in the format we expect back from a node, and
// construst an Events object to iterate them:
let events = events_raw::<Event>(
metadata,
event_bytes,
3, // 2 "good" events, and then it'll hit the naff bytes.
);
let mut events_iter = events.iter();
assert_eq!(
events_iter.next().unwrap().unwrap(),
EventDetails {
index: 0,
phase: Phase::Initialization,
event: AllEvents::Test(Event::A(1))
}
);
assert_eq!(
events_iter.next().unwrap().unwrap(),
EventDetails {
index: 1,
phase: Phase::ApplyExtrinsic(123),
event: AllEvents::Test(Event::B(true))
}
);
// We'll hit an error trying to decode the third event:
assert!(events_iter.next().unwrap().is_err());
// ... and then "None" from then on.
assert!(events_iter.next().is_none());
assert!(events_iter.next().is_none());
}
#[test]
fn dynamically_decode_single_event() {
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
@@ -724,7 +533,7 @@ mod tests {
vec![event_record(Phase::ApplyExtrinsic(123), event)],
);
let mut event_details = events.iter_raw();
let mut event_details = events.iter();
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
@@ -736,7 +545,7 @@ mod tests {
variant: "A".to_string(),
variant_index: 0,
fields: vec![
Value::uint(1u8),
Value::u128(1),
Value::bool(true),
Value::unnamed_composite(vec![Value::string("Hi")]),
],
@@ -771,7 +580,7 @@ mod tests {
],
);
let mut event_details = events.iter_raw();
let mut event_details = events.iter();
assert_raw_events_match(
&metadata,
@@ -783,7 +592,7 @@ mod tests {
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
fields: vec![Value::uint(1u8)],
fields: vec![Value::u128(1)],
},
);
assert_raw_events_match(
@@ -809,7 +618,7 @@ mod tests {
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
fields: vec![Value::uint(234u8)],
fields: vec![Value::u128(234)],
},
);
assert!(event_details.next().is_none());
@@ -837,13 +646,13 @@ mod tests {
// Encode our events in the format we expect back from a node, and
// construst an Events object to iterate them:
let events = events_raw::<Event>(
let events = events_raw(
metadata.clone(),
event_bytes,
3, // 2 "good" events, and then it'll hit the naff bytes.
);
let mut events_iter = events.iter_raw();
let mut events_iter = events.iter();
assert_raw_events_match(
&metadata,
events_iter.next().unwrap().unwrap(),
@@ -854,7 +663,7 @@ mod tests {
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
fields: vec![Value::uint(1u8)],
fields: vec![Value::u128(1)],
},
);
assert_raw_events_match(
@@ -895,20 +704,8 @@ mod tests {
vec![event_record(Phase::Finalization, Event::A(1))],
);
// Statically decode:
let event_details: Vec<EventDetails<AllEvents<Event>>> =
events.iter().collect::<Result<_, _>>().unwrap();
assert_eq!(
event_details,
vec![EventDetails {
index: 0,
phase: Phase::Finalization,
event: AllEvents::Test(Event::A(1))
}]
);
// Dynamically decode:
let mut event_details = events.iter_raw();
let mut event_details = events.iter();
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
@@ -919,7 +716,7 @@ mod tests {
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
fields: vec![Value::uint(1u8)],
fields: vec![Value::u128(1)],
},
);
assert!(event_details.next().is_none());
@@ -948,20 +745,8 @@ mod tests {
)],
);
// Statically decode:
let event_details: Vec<EventDetails<AllEvents<Event>>> =
events.iter().collect::<Result<_, _>>().unwrap();
assert_eq!(
event_details,
vec![EventDetails {
index: 0,
phase: Phase::Finalization,
event: AllEvents::Test(Event::A(CompactWrapper(1)))
}]
);
// Dynamically decode:
let mut event_details = events.iter_raw();
let mut event_details = events.iter();
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
@@ -972,7 +757,7 @@ mod tests {
pallet_index: 0,
variant: "A".to_string(),
variant_index: 0,
fields: vec![Value::unnamed_composite(vec![Value::uint(1u8)])],
fields: vec![Value::unnamed_composite(vec![Value::u128(1)])],
},
);
assert!(event_details.next().is_none());
@@ -1002,20 +787,8 @@ mod tests {
vec![event_record(Phase::Finalization, Event::A(MyType::B))],
);
// Statically decode:
let event_details: Vec<EventDetails<AllEvents<Event>>> =
events.iter().collect::<Result<_, _>>().unwrap();
assert_eq!(
event_details,
vec![EventDetails {
index: 0,
phase: Phase::Finalization,
event: AllEvents::Test(Event::A(MyType::B))
}]
);
// Dynamically decode:
let mut event_details = events.iter_raw();
let mut event_details = events.iter();
assert_raw_events_match(
&metadata,
event_details.next().unwrap().unwrap(),
+55 -61
View File
@@ -4,14 +4,15 @@
//! Filtering individual events from subscriptions.
use super::Events;
use crate::{
BasicError,
Config,
Event,
use super::{
Events,
Phase,
StaticEvent,
};
use crate::{
Config,
Error,
};
use codec::Decode;
use futures::{
Stream,
StreamExt,
@@ -28,7 +29,7 @@ use std::{
/// exactly one of these will be `Some(event)` each iteration.
pub struct FilterEvents<'a, Sub: 'a, T: Config, Filter: EventFilter> {
// A subscription; in order for the Stream impl to apply, this will
// impl `Stream<Item = Result<Events<'a, T, Evs>, BasicError>> + Unpin + 'a`.
// impl `Stream<Item = Result<Events<'a, T, Evs>, Error>> + Unpin + 'a`.
sub: Sub,
// Each time we get Events from our subscription, they are stored here
// and iterated through in future stream iterations until exhausted.
@@ -37,7 +38,7 @@ pub struct FilterEvents<'a, Sub: 'a, T: Config, Filter: EventFilter> {
dyn Iterator<
Item = Result<
FilteredEventDetails<T::Hash, Filter::ReturnType>,
BasicError,
Error,
>,
> + Send
+ 'a,
@@ -56,14 +57,13 @@ impl<'a, Sub: 'a, T: Config, Filter: EventFilter> FilterEvents<'a, Sub, T, Filte
}
}
impl<'a, Sub, T, Evs, Filter> Stream for FilterEvents<'a, Sub, T, Filter>
impl<'a, Sub, T, Filter> Stream for FilterEvents<'a, Sub, T, Filter>
where
Sub: Stream<Item = Result<Events<T, Evs>, BasicError>> + Unpin + 'a,
Sub: Stream<Item = Result<Events<T>, Error>> + Unpin + 'a,
T: Config,
Evs: Decode + 'static,
Filter: EventFilter,
{
type Item = Result<FilteredEventDetails<T::Hash, Filter::ReturnType>, BasicError>;
type Item = Result<FilteredEventDetails<T::Hash, Filter::ReturnType>, Error>;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
@@ -112,14 +112,11 @@ pub trait EventFilter: private::Sealed {
/// The type we'll be handed back from filtering.
type ReturnType;
/// Filter the events based on the type implementing this trait.
fn filter<'a, T: Config, Evs: Decode + 'static>(
events: Events<T, Evs>,
fn filter<'a, T: Config>(
events: Events<T>,
) -> Box<
dyn Iterator<
Item = Result<
FilteredEventDetails<T::Hash, Self::ReturnType>,
BasicError,
>,
Item = Result<FilteredEventDetails<T::Hash, Self::ReturnType>, Error>,
> + Send
+ 'a,
>;
@@ -134,18 +131,16 @@ pub(crate) mod private {
// A special case impl for searching for a tuple of exactly one event (in this case, we don't
// need to return an `(Option<Event>,)`; we can just return `Event`.
impl<Ev: Event> private::Sealed for (Ev,) {}
impl<Ev: Event> EventFilter for (Ev,) {
impl<Ev: StaticEvent> private::Sealed for (Ev,) {}
impl<Ev: StaticEvent> EventFilter for (Ev,) {
type ReturnType = Ev;
fn filter<'a, T: Config, Evs: Decode + 'static>(
events: Events<T, Evs>,
fn filter<'a, T: Config>(
events: Events<T>,
) -> Box<
dyn Iterator<Item = Result<FilteredEventDetails<T::Hash, Ev>, BasicError>>
+ Send
+ 'a,
dyn Iterator<Item = Result<FilteredEventDetails<T::Hash, Ev>, Error>> + Send + 'a,
> {
let block_hash = events.block_hash();
let mut iter = events.into_iter_raw();
let mut iter = events.iter();
Box::new(std::iter::from_fn(move || {
for ev in iter.by_ref() {
// Forward any error immediately:
@@ -158,7 +153,7 @@ impl<Ev: Event> EventFilter for (Ev,) {
if let Ok(Some(event)) = ev {
// We found a match; return our tuple.
return Some(Ok(FilteredEventDetails {
phase: raw_event.phase,
phase: raw_event.phase(),
block_hash,
event,
}))
@@ -176,14 +171,14 @@ impl<Ev: Event> EventFilter for (Ev,) {
// A generalised impl for tuples of sizes greater than 1:
macro_rules! impl_event_filter {
($($ty:ident $idx:tt),+) => {
impl <$($ty: Event),+> private::Sealed for ( $($ty,)+ ) {}
impl <$($ty: Event),+> EventFilter for ( $($ty,)+ ) {
impl <$($ty: StaticEvent),+> private::Sealed for ( $($ty,)+ ) {}
impl <$($ty: StaticEvent),+> EventFilter for ( $($ty,)+ ) {
type ReturnType = ( $(Option<$ty>,)+ );
fn filter<'a, T: Config, Evs: Decode + 'static>(
events: Events<T, Evs>
) -> Box<dyn Iterator<Item=Result<FilteredEventDetails<T::Hash,Self::ReturnType>, BasicError>> + Send + 'a> {
fn filter<'a, T: Config>(
events: Events<T>
) -> Box<dyn Iterator<Item=Result<FilteredEventDetails<T::Hash,Self::ReturnType>, Error>> + Send + 'a> {
let block_hash = events.block_hash();
let mut iter = events.into_iter_raw();
let mut iter = events.iter();
Box::new(std::iter::from_fn(move || {
let mut out: ( $(Option<$ty>,)+ ) = Default::default();
for ev in iter.by_ref() {
@@ -199,7 +194,7 @@ macro_rules! impl_event_filter {
// We found a match; return our tuple.
out.$idx = Some(ev);
return Some(Ok(FilteredEventDetails {
phase: raw_event.phase,
phase: raw_event.phase(),
block_hash,
event: out
}))
@@ -232,24 +227,24 @@ mod test {
event_record,
events,
metadata,
AllEvents,
},
*,
};
use crate::{
Config,
DefaultConfig,
Metadata,
SubstrateConfig,
};
use codec::{
Decode,
Encode,
};
use codec::Encode;
use futures::{
stream,
Stream,
StreamExt,
};
use parking_lot::RwLock;
use scale_info::TypeInfo;
use std::sync::Arc;
// Some pretend events in a pallet
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
@@ -262,7 +257,7 @@ mod test {
// An event in our pallet that we can filter on.
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
struct EventA(u8);
impl crate::Event for EventA {
impl StaticEvent for EventA {
const PALLET: &'static str = "Test";
const EVENT: &'static str = "A";
}
@@ -270,7 +265,7 @@ mod test {
// An event in our pallet that we can filter on.
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
struct EventB(bool);
impl crate::Event for EventB {
impl StaticEvent for EventB {
const PALLET: &'static str = "Test";
const EVENT: &'static str = "B";
}
@@ -278,16 +273,15 @@ mod test {
// An event in our pallet that we can filter on.
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo)]
struct EventC(u8, bool);
impl crate::Event for EventC {
impl StaticEvent for EventC {
const PALLET: &'static str = "Test";
const EVENT: &'static str = "C";
}
// A stream of fake events for us to try filtering on.
fn events_stream(
metadata: Arc<RwLock<Metadata>>,
) -> impl Stream<Item = Result<Events<DefaultConfig, AllEvents<PalletEvents>>, BasicError>>
{
metadata: Metadata,
) -> impl Stream<Item = Result<Events<SubstrateConfig>, Error>> {
stream::iter(vec![
events::<PalletEvents>(
metadata.clone(),
@@ -312,16 +306,16 @@ mod test {
],
),
])
.map(Ok::<_, BasicError>)
.map(Ok::<_, Error>)
}
#[tokio::test]
async fn filter_one_event_from_stream() {
let metadata = Arc::new(RwLock::new(metadata::<PalletEvents>()));
let metadata = metadata::<PalletEvents>();
// Filter out fake event stream to select events matching `EventA` only.
let actual: Vec<_> =
FilterEvents::<_, DefaultConfig, (EventA,)>::new(events_stream(metadata))
FilterEvents::<_, SubstrateConfig, (EventA,)>::new(events_stream(metadata))
.map(|e| e.unwrap())
.collect()
.await;
@@ -329,17 +323,17 @@ mod test {
let expected = vec![
FilteredEventDetails {
phase: Phase::Initialization,
block_hash: <DefaultConfig as Config>::Hash::default(),
block_hash: <SubstrateConfig as Config>::Hash::default(),
event: EventA(1),
},
FilteredEventDetails {
phase: Phase::Finalization,
block_hash: <DefaultConfig as Config>::Hash::default(),
block_hash: <SubstrateConfig as Config>::Hash::default(),
event: EventA(2),
},
FilteredEventDetails {
phase: Phase::ApplyExtrinsic(3),
block_hash: <DefaultConfig as Config>::Hash::default(),
block_hash: <SubstrateConfig as Config>::Hash::default(),
event: EventA(3),
},
];
@@ -349,10 +343,10 @@ mod test {
#[tokio::test]
async fn filter_some_events_from_stream() {
let metadata = Arc::new(RwLock::new(metadata::<PalletEvents>()));
let metadata = metadata::<PalletEvents>();
// Filter out fake event stream to select events matching `EventA` or `EventB`.
let actual: Vec<_> = FilterEvents::<_, DefaultConfig, (EventA, EventB)>::new(
let actual: Vec<_> = FilterEvents::<_, SubstrateConfig, (EventA, EventB)>::new(
events_stream(metadata),
)
.map(|e| e.unwrap())
@@ -362,32 +356,32 @@ mod test {
let expected = vec![
FilteredEventDetails {
phase: Phase::Initialization,
block_hash: <DefaultConfig as Config>::Hash::default(),
block_hash: <SubstrateConfig as Config>::Hash::default(),
event: (Some(EventA(1)), None),
},
FilteredEventDetails {
phase: Phase::ApplyExtrinsic(0),
block_hash: <DefaultConfig as Config>::Hash::default(),
block_hash: <SubstrateConfig as Config>::Hash::default(),
event: (None, Some(EventB(true))),
},
FilteredEventDetails {
phase: Phase::Finalization,
block_hash: <DefaultConfig as Config>::Hash::default(),
block_hash: <SubstrateConfig as Config>::Hash::default(),
event: (Some(EventA(2)), None),
},
FilteredEventDetails {
phase: Phase::ApplyExtrinsic(1),
block_hash: <DefaultConfig as Config>::Hash::default(),
block_hash: <SubstrateConfig as Config>::Hash::default(),
event: (None, Some(EventB(false))),
},
FilteredEventDetails {
phase: Phase::ApplyExtrinsic(2),
block_hash: <DefaultConfig as Config>::Hash::default(),
block_hash: <SubstrateConfig as Config>::Hash::default(),
event: (None, Some(EventB(true))),
},
FilteredEventDetails {
phase: Phase::ApplyExtrinsic(3),
block_hash: <DefaultConfig as Config>::Hash::default(),
block_hash: <SubstrateConfig as Config>::Hash::default(),
event: (Some(EventA(3)), None),
},
];
@@ -397,11 +391,11 @@ mod test {
#[tokio::test]
async fn filter_no_events_from_stream() {
let metadata = Arc::new(RwLock::new(metadata::<PalletEvents>()));
let metadata = metadata::<PalletEvents>();
// Filter out fake event stream to select events matching `EventC` (none exist).
let actual: Vec<_> =
FilterEvents::<_, DefaultConfig, (EventC,)>::new(events_stream(metadata))
FilterEvents::<_, SubstrateConfig, (EventC,)>::new(events_stream(metadata))
.map(|e| e.unwrap())
.collect()
.await;
+43 -86
View File
@@ -2,108 +2,65 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! This module exposes the ability to work with events generated by a given block.
//! Subxt can either attempt to statically decode events into known types, or it
//! can hand back details of the raw event without knowing what shape its contents
//! are (this may be useful if we don't know what exactly we're looking for).
//!
//! This module is wrapped by the generated API in `RuntimeAPI::EventsApi`.
//!
//! # Examples
//!
//! ## Subscribe to all events
//!
//! Users can subscribe to all emitted events from blocks using `subscribe()`.
//!
//! To subscribe to all events from just the finalized blocks use `subscribe_finalized()`.
//!
//! To obtain the events from a given block use `at()`.
//!
//! ```no_run
//! # use futures::StreamExt;
//! # use subxt::{ClientBuilder, DefaultConfig, PolkadotExtrinsicParams};
//! # #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! # pub mod polkadot {}
//! # #[tokio::main]
//! # async fn main() {
//! # let api = ClientBuilder::new()
//! # .build()
//! # .await
//! # .unwrap()
//! # .to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
//! let mut events = api.events().subscribe().await.unwrap();
//!
//! while let Some(ev) = events.next().await {
//! // Obtain all events from this block.
//! let ev: subxt::Events<_, _> = ev.unwrap();
//! // Print block hash.
//! println!("Event at block hash {:?}", ev.block_hash());
//! // Iterate over all events.
//! let mut iter = ev.iter();
//! while let Some(event_details) = iter.next() {
//! println!("Event details {:?}", event_details);
//! }
//! }
//! # }
//! ```
//!
//! ## Filter events
//!
//! The subxt exposes the ability to filter events via the `filter_events()` function.
//!
//! The function filters events from the provided tuple. If 1-tuple is provided, the events are
//! returned directly. Otherwise, we'll be given a corresponding tuple of `Option`'s, with exactly
//! one variant populated each time.
//!
//! ```no_run
//! # use futures::StreamExt;
//! # use subxt::{ClientBuilder, DefaultConfig, PolkadotExtrinsicParams};
//!
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! pub mod polkadot {}
//!
//! # #[tokio::main]
//! # async fn main() {
//! # let api = ClientBuilder::new()
//! # .build()
//! # .await
//! # .unwrap()
//! # .to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
//!
//! let mut transfer_events = api
//! .events()
//! .subscribe()
//! .await
//! .unwrap()
//! .filter_events::<(polkadot::balances::events::Transfer,)>();
//!
//! while let Some(transfer_event) = transfer_events.next().await {
//! println!("Balance transfer event: {transfer_event:?}");
//! }
//! # }
//! ```
//! This module exposes the types and such necessary for working with events.
//! The two main entry points into events are [`crate::OnlineClient::events()`]
//! and calls like [crate::tx::TxProgress::wait_for_finalized_success()].
mod event_subscription;
mod events_client;
mod events_type;
mod filter_events;
pub use event_subscription::{
subscribe,
subscribe_finalized,
subscribe_to_block_headers_filling_in_gaps,
EventSub,
EventSubscription,
FinalizedEventSub,
};
pub use events_client::{
// Exposed only for testing:
subscribe_to_block_headers_filling_in_gaps,
EventsClient,
};
pub use events_type::{
at,
DecodedValue,
EventDetails,
Events,
RawEventDetails,
};
pub use filter_events::{
EventFilter,
FilterEvents,
FilteredEventDetails,
};
use codec::{
Decode,
Encode,
};
/// Trait to uniquely identify the events's identity from the runtime metadata.
///
/// Generated API structures that represent an event implement this trait.
///
/// The trait is utilized to decode emitted events from a block, via obtaining the
/// form of the `Event` from the metadata.
pub trait StaticEvent: Decode {
/// Pallet name.
const PALLET: &'static str;
/// Event name.
const EVENT: &'static str;
/// Returns true if the given pallet and event names match this event.
fn is_event(pallet: &str, event: &str) -> bool {
Self::PALLET == pallet && Self::EVENT == event
}
}
/// A phase of a block's execution.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Decode, Encode)]
pub enum Phase {
/// Applying an extrinsic.
ApplyExtrinsic(u32),
/// Finalizing the block.
Finalization,
/// Initializing the block.
Initialization,
}