Extend the new api.blocks() to be the primary way to subscribe and fetch blocks/extrinsics/events (#691)

* First pass adding functions to get blocks and extrinsics

* cargo fmt and cache block events

* prefix block hash with 0x

* pin streams for better ergonomics and add an example of subscribing to blocks

* remove unused var

* standardise on _all, _best and _finalized for different block header subs

* WIP center subscribing around blocks

* Remove the event filtering/subscribing  stuff

* clippy

* we need tokio, silly clippy

* add extrinsic_index() call

* Update subxt/src/blocks/block_types.rs

Co-authored-by: Andrew Jones <ascjones@gmail.com>

Co-authored-by: Andrew Jones <ascjones@gmail.com>
This commit is contained in:
James Wilson
2022-11-01 16:53:35 +01:00
committed by GitHub
parent 52d4762d13
commit 33a9ec91af
25 changed files with 646 additions and 1279 deletions
-1
View File
@@ -39,7 +39,6 @@ impl Config for MyConfig {
type Address = <SubstrateConfig as Config>::Address;
type Header = <SubstrateConfig as Config>::Header;
type Signature = <SubstrateConfig as Config>::Signature;
type Extrinsic = <SubstrateConfig as Config>::Extrinsic;
// ExtrinsicParams makes use of the index type, so we need to adjust it
// too to align with our modified index type, above:
type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
@@ -29,7 +29,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// For non-finalised blocks use `.subscribe_blocks()`
let mut blocks: Subscription<Header<u32, BlakeTwo256>> =
api.rpc().subscribe_finalized_blocks().await?;
api.rpc().subscribe_finalized_block_headers().await?;
while let Some(Ok(block)) = blocks.next().await {
println!(
@@ -31,8 +31,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Subscribe to any events that occur:
let mut event_sub = api.events().subscribe().await?;
// Subscribe to (in this case, finalized) blocks.
let mut block_sub = api.blocks().subscribe_finalized().await?;
// While this subscription is active, balance transfers are made somewhere:
tokio::task::spawn({
@@ -58,10 +58,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
});
// 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();
// Get each finalized block as it arrives.
while let Some(block) = block_sub.next().await {
let block = block?;
// Ask for the events for this block.
let events = block.events().await?;
let block_hash = block.hash();
// We can dynamically decode events:
println!(" Dynamic event details: {block_hash:?}:");
+64
View File
@@ -0,0 +1,64 @@
// 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.
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.29-41a9d84b152.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.29/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::StreamExt;
use subxt::{
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Subscribe to all finalized blocks:
let mut blocks_sub = api.blocks().subscribe_finalized().await?;
while let Some(block) = blocks_sub.next().await {
let block = block?;
let block_number = block.header().number;
let block_hash = block.hash();
println!("Block #{block_number}:");
println!(" Hash: {block_hash}");
println!(" Extrinsics:");
let body = block.body().await?;
for ext in body.extrinsics() {
let idx = ext.index();
let events = ext.events().await?;
let bytes_hex = format!("0x{}", hex::encode(ext.bytes()));
println!(" Extrinsic #{idx}:");
println!(" Bytes: {bytes_hex}");
println!(" Events:");
for evt in events.iter() {
let evt = evt?;
let pallet_name = evt.pallet_name();
let event_name = evt.variant_name();
println!(" {pallet_name}_{event_name}");
}
}
}
Ok(())
}
-72
View File
@@ -1,72 +0,0 @@
// 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.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::StreamExt;
use sp_keyring::AccountKeyring;
use std::time::Duration;
use subxt::{
tx::PairSigner,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/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.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Subscribe to just balance transfer events, making use of `filter_events`
// to select a single event type (note the 1-tuple) to filter out and return.
let mut transfer_events = api
.events()
.subscribe()
.await?
.filter_events::<(polkadot::balances::events::Transfer,)>();
// While this subscription is active, balance transfers are made somewhere:
tokio::task::spawn({
let api = api.clone();
async move {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let mut transfer_amount = 1_000_000_000;
// Make small balance transfers from Alice to Bob in a loop:
loop {
let transfer_tx = polkadot::tx().balances().transfer(
AccountKeyring::Bob.to_account_id().into(),
transfer_amount,
);
api.tx()
.sign_and_submit_default(&transfer_tx, &signer)
.await
.unwrap();
tokio::time::sleep(Duration::from_secs(10)).await;
transfer_amount += 100_000_000;
}
}
});
// 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(())
}
@@ -1,87 +0,0 @@
// 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.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::StreamExt;
use sp_keyring::AccountKeyring;
use std::time::Duration;
use subxt::{
tx::PairSigner,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/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.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Subscribe to several balance related events. If we ask for more than one event,
// we'll be given a correpsonding tuple of `Option`'s, with exactly one
// variant populated each time.
let mut balance_events = api.events().subscribe().await?.filter_events::<(
polkadot::balances::events::Withdraw,
polkadot::balances::events::Transfer,
polkadot::balances::events::Deposit,
)>();
// While this subscription is active, balance transfers are made somewhere:
tokio::task::spawn({
let api = api.clone();
async move {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let mut transfer_amount = 1_000_000_000;
// Make small balance transfers from Alice to Bob in a loop:
loop {
let transfer_tx = polkadot::tx().balances().transfer(
AccountKeyring::Bob.to_account_id().into(),
transfer_amount,
);
api.tx()
.sign_and_submit_default(&transfer_tx, &signer)
.await
.unwrap();
tokio::time::sleep(Duration::from_secs(10)).await;
transfer_amount += 100_000_000;
}
}
});
// Our subscription will see all of the balance events we're filtering on:
while let Some(ev) = balance_events.next().await {
let event_details = ev?;
let block_hash = event_details.block_hash;
let event = event_details.event;
println!("Event at {:?}:", block_hash);
if let (Some(withdraw), _, _) = &event {
println!(" Withdraw event: {withdraw:?}");
}
if let (_, Some(transfer), _) = &event {
println!(" Transfer event: {transfer:?}");
}
if let (_, _, Some(deposit)) = &event {
println!(" Deposit event: {deposit:?}");
}
}
Ok(())
}