Skip to content

Commit

Permalink
New Event Subscription API (#442)
Browse files Browse the repository at this point in the history
* Add reworked event types

* first pass implementing event subscriptions

* make clear that some methods are private

* comment tidy

* use Events in transaction stuff

* align transaction and event APIs

* remove __private_ prefixes; they are ugly

* fix examples and remove old events and subscription code

* better comments on hidden event functions

* re-add find_first_event; it's used a bunch in tests and examples

* cargo check --all-targets now passes

* Fix up existing event tests

* cargo fmt

* change todo to note

* clippy and doc niggles

* revert to find_first_event

* Add specific subscription related tests

* cargo fmt

* Update tests and add/fix examples

* cargo fmt

* add a little to subscribe_all_events example

* cargo fmt

* move an example comment

* easy access to root mod for more clarity

* add a couple of tests to ensure that events properly decoded until naff bytes

* Simplify EventSubscription Stream impl a little

* Address some PR feedback
  • Loading branch information
jsdw authored Feb 14, 2022
1 parent 7615b25 commit b1b7173
Show file tree
Hide file tree
Showing 27 changed files with 11,598 additions and 5,287 deletions.
3 changes: 2 additions & 1 deletion codegen/src/api/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub fn generate_calls(
pub fn #fn_name(
&self,
#( #call_fn_args, )*
) -> ::subxt::SubmittableExtrinsic<'a, T, X, A, #call_struct_name, DispatchError> {
) -> ::subxt::SubmittableExtrinsic<'a, T, X, A, #call_struct_name, DispatchError, root_mod::Event> {
let call = #call_struct_name { #( #call_args, )* };
::subxt::SubmittableExtrinsic::new(self.client, call)
}
Expand All @@ -101,6 +101,7 @@ pub fn generate_calls(

quote! {
pub mod calls {
use super::root_mod;
use super::#types_mod_ident;

type DispatchError = #types_mod_ident::sp_runtime::DispatchError;
Expand Down
29 changes: 27 additions & 2 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ impl RuntimeGenerator {

quote! {
pub mod #mod_name {
use super::root_mod;
use super::#types_mod_ident;
#calls
#event
Expand Down Expand Up @@ -275,6 +276,9 @@ impl RuntimeGenerator {
quote! {
#[allow(dead_code, unused_imports, non_camel_case_types)]
pub mod #mod_ident {
// Make it easy to access the root via `root_mod` at different levels:
use super::#mod_ident as root_mod;

#outer_event
#( #modules )*
#types_mod
Expand Down Expand Up @@ -323,12 +327,33 @@ impl RuntimeGenerator {
pub fn tx(&'a self) -> TransactionApi<'a, T, X, A> {
TransactionApi { client: &self.client, marker: ::core::marker::PhantomData }
}

pub fn events(&'a self) -> EventsApi<'a, T> {
EventsApi { client: &self.client }
}
}

pub struct EventsApi<'a, T: ::subxt::Config> {
client: &'a ::subxt::Client<T>,
}

impl <'a, T: ::subxt::Config> EventsApi<'a, T> {
pub async fn at(&self, block_hash: T::Hash) -> Result<::subxt::events::Events<'a, T, Event>, ::subxt::BasicError> {
::subxt::events::at::<T, Event>(self.client, block_hash).await
}

pub async fn subscribe(&self) -> Result<::subxt::events::EventSubscription<'a, T, Event>, ::subxt::BasicError> {
::subxt::events::subscribe::<T, Event>(self.client).await
}

pub async fn subscribe_finalized(&self) -> Result<::subxt::events::EventSubscription<'a, T, Event>, ::subxt::BasicError> {
::subxt::events::subscribe_finalized::<T, Event>(self.client).await
}
}

pub struct ConstantsApi;

impl ConstantsApi
{
impl ConstantsApi {
#(
pub fn #pallets_with_constants(&self) -> #pallets_with_constants::constants::ConstantsApi {
#pallets_with_constants::constants::ConstantsApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.

//! To run this example, a local polkadot node should be running.
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
Expand Down
8 changes: 8 additions & 0 deletions examples/examples/custom_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.

//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::{
ClientBuilder,
Expand Down
8 changes: 6 additions & 2 deletions examples/examples/custom_type_derives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.

//! Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
#![allow(clippy::redundant_clone)]

#[subxt::subxt(
runtime_metadata_path = "examples/polkadot_metadata.scale",
// We can add (certain) custom derives to the generated types by providing
// a comma separated list to the below attribute. Most useful for adding `Clone`:
generated_type_derives = "Clone, Hash"
// a comma separated list to the below attribute. Most useful for adding `Clone`.
// The derives that we can add ultiamtely is limited to the traits that the base
// types relied upon by the codegen implement.
generated_type_derives = "Clone, PartialEq"
)]
pub mod polkadot {}

Expand Down
4 changes: 2 additions & 2 deletions examples/examples/fetch_all_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.

//! To run this example, a local polkadot node should be running.
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
Expand Down
Binary file modified examples/examples/polkadot_metadata.scale
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.

//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{
ClientBuilder,
DefaultConfig,
Expand Down Expand Up @@ -42,9 +50,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.await?;

if let Some(hash) = block_hash {
println!("Block hash for block number {}: {}", block_number, hash);
println!("Block hash for block number {block_number}: {hash}");
} else {
println!("Block number {} not found.", block_number);
println!("Block number {block_number} not found.");
}

Ok(())
Expand Down
13 changes: 6 additions & 7 deletions examples/examples/submit_and_watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.

//! To run this example, a local polkadot node should be running.
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
Expand Down Expand Up @@ -70,7 +70,7 @@ async fn simple_transfer() -> Result<(), Box<dyn std::error::Error>> {
balance_transfer.find_first_event::<polkadot::balances::events::Transfer>()?;

if let Some(event) = transfer_event {
println!("Balance transfer success: value: {:?}", event.2);
println!("Balance transfer success: {event:?}");
} else {
println!("Failed to find Balances::Transfer Event");
}
Expand Down Expand Up @@ -119,7 +119,7 @@ async fn simple_transfer_separate_events() -> Result<(), Box<dyn std::error::Err
let transfer_event =
events.find_first_event::<polkadot::balances::events::Transfer>()?;
if let Some(event) = transfer_event {
println!("Balance transfer success: value: {:?}", event.2);
println!("Balance transfer success: {event:?}");
} else {
println!("Failed to find Balances::Transfer Event");
}
Expand Down Expand Up @@ -165,8 +165,7 @@ async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> {

if let Some(event) = transfer_event {
println!(
"Balance transfer is now in block (but not finalized): value: {:?}",
event.2
"Balance transfer is now in block (but not finalized): {event:?}"
);
} else {
println!("Failed to find Balances::Transfer Event");
Expand All @@ -185,7 +184,7 @@ async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> {
events.find_first_event::<polkadot::balances::events::Transfer>()?;

if let Some(event) = transfer_event {
println!("Balance transfer success: value: {:?}", event.2);
println!("Balance transfer success: {event:?}");
} else {
println!("Failed to find Balances::Transfer Event");
}
Expand Down
116 changes: 116 additions & 0 deletions examples/examples/subscribe_all_events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is part of subxt.
//
// subxt is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// subxt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.

//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.13-82616422d0-aarch64-macos.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::StreamExt;
use sp_keyring::AccountKeyring;
use std::time::Duration;
use subxt::{
ClientBuilder,
DefaultConfig,
DefaultExtra,
PairSigner,
};

#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
pub mod polkadot {}

/// Subscribe to all events, and then manually look through them and
/// pluck out the events that we care about.
#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();

let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();

// Subscribe to any events that occur:
let mut event_sub = api.events().subscribe().await?;

// While this subscription is active, balance transfers are made somewhere:
async_std::task::spawn(async {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let api = ClientBuilder::new()
.build()
.await
.unwrap()
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();

let mut transfer_amount = 1_000_000_000;

// Make small balance transfers from Alice to Bob in a loop:
loop {
api.tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), transfer_amount)
.sign_and_submit(&signer)
.await
.unwrap();

async_std::task::sleep(Duration::from_secs(10)).await;
transfer_amount += 100_000_000;
}
});

// Our subscription will see the events emitted as a result of this:
while let Some(events) = event_sub.next().await {
let events = events?;
let block_hash = events.block_hash();

// We can iterate, statically decoding all events if we want:
println!("All events in block {block_hash:?}:");
println!(" Static event details:");
for event in events.iter() {
let event = event?;
println!(" {event:?}");
}

// Or we can dynamically decode events:
println!(" Dynamic event details: {block_hash:?}:");
for event in events.iter_raw() {
let event = event?;
let is_balance_transfer = event
.as_event::<polkadot::balances::events::Transfer>()?
.is_some();
let pallet = event.pallet;
let variant = event.variant;
println!(
" {pallet}::{variant} (is balance transfer? {is_balance_transfer})"
);
}

// Or we can dynamically find the first transfer event, ignoring any others:
let transfer_event =
events.find_first_event::<polkadot::balances::events::Transfer>()?;

if let Some(ev) = transfer_event {
println!(" - Balance transfer success: value: {:?}", ev.amount);
} else {
println!(" - No balance transfer event found in this block");
}
}

Ok(())
}
Loading

0 comments on commit b1b7173

Please sign in to comment.