New Event Subscription API (#442)

* 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
This commit is contained in:
James Wilson
2022-02-14 11:18:16 +00:00
committed by GitHub
parent 7615b2586d
commit b1b717332e
27 changed files with 9475 additions and 3164 deletions
@@ -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
//! ```
+8
View File
@@ -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,
+6 -2
View File
@@ -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 {}
+2 -2
View File
@@ -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
//! ```
Binary file not shown.
@@ -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,
@@ -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(())
+6 -7
View File
@@ -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
//! ```
@@ -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");
}
@@ -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");
}
@@ -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");
@@ -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");
}
+116
View File
@@ -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(())
}
+97
View File
@@ -0,0 +1,97 @@
// 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::{
future,
stream,
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();
// Subscribe to any events that occur:
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
// Subscribe to just balance transfer events, making use of `flat_map` and
// `filter_map` from the StreamExt trait to filter everything else out.
let mut transfer_events = api
.events()
.subscribe()
.await?
// Ignore errors returning events:
.filter_map(|events| future::ready(events.ok()))
// Map events to just the one we care about:
.flat_map(|events| {
let transfer_events = events
.find::<polkadot::balances::events::Transfer>()
.collect::<Vec<_>>();
stream::iter(transfer_events)
});
// While this subscription is active, we imagine some balance transfers are made somewhere else:
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>>>();
// Make small balance transfers from Alice to Bob in a loop:
loop {
api.tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), 1_000_000_000)
.sign_and_submit(&signer)
.await
.unwrap();
async_std::task::sleep(Duration::from_secs(10)).await;
}
});
// Our subscription will see all of the transfer events emitted as a result of this:
while let Some(transfer_event) = transfer_events.next().await {
println!("Balance transfer event: {transfer_event:?}");
}
Ok(())
}
-70
View File
@@ -1,70 +0,0 @@
// 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.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::{
ClientBuilder,
DefaultConfig,
DefaultExtra,
EventSubscription,
PairSigner,
};
#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")]
pub mod polkadot {}
#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>>();
let sub = api.client.rpc().subscribe_events().await?;
let decoder = api.client.events_decoder();
let mut sub = EventSubscription::<DefaultConfig>::new(sub, decoder);
sub.filter_event::<polkadot::balances::events::Transfer>();
api.tx()
.balances()
.transfer(dest, 10_000)
.sign_and_submit(&signer)
.await?;
let raw = sub.next().await.unwrap().unwrap();
let event = <polkadot::balances::events::Transfer as subxt::codec::Decode>::decode(
&mut &raw.data[..],
);
if let Ok(e) = event {
println!("Balance transfer success: value: {:?}", e.2);
} else {
println!("Failed to subscribe to Balances::Transfer Event");
}
Ok(())
}