Remove decoding bit from "extrinsics" and reference decoding in "blocks" (#951)

* Remove decoding bits from 'tx' and reference decoding in 'blocks' instead

* newline

* doc and fmt fixes
This commit is contained in:
James Wilson
2023-05-11 13:12:51 +01:00
committed by GitHub
parent 3f16bb8d52
commit ae093bda7d
15 changed files with 65 additions and 210 deletions
-99
View File
@@ -1,99 +0,0 @@
// Copyright 2019-2023 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>> {
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().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({
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;
}
}
});
// Get each finalized block as it arrives.
while let Some(block) = block_sub.next().await {
let block = block?;
let block_hash = block.hash();
println!(" Block {:?}", block_hash);
// Ask for the extrinsics for this block.
let extrinsics = block.body().await?.extrinsics();
let transfer_tx = extrinsics.find_first::<polkadot::balances::calls::types::Transfer>();
println!(" Transfer tx: {:?}", transfer_tx);
// Ask for the extrinsics for this block.
for extrinsic in extrinsics.iter() {
let extrinsic = extrinsic?;
println!(
" Extrinsic block index {:?}, pallet index {:?}, variant index {:?}",
extrinsic.index(),
extrinsic.pallet_index(),
extrinsic.variant_index()
);
let root_extrinsic = extrinsic.as_root_extrinsic::<polkadot::Call>();
println!(" As root extrinsic {:?}\n", root_extrinsic);
let pallet_extrinsic = extrinsic
.as_pallet_extrinsic::<polkadot::runtime_types::pallet_balances::pallet::Call>();
println!(
" Extrinsic as Balances Pallet call: {:?}\n",
pallet_extrinsic
);
let call = extrinsic.as_extrinsic::<polkadot::balances::calls::types::Transfer>()?;
println!(
" Extrinsic as polkadot::balances::calls::Transfer: {:?}\n\n",
call
);
}
println!("\n");
}
Ok(())
}
+4
View File
@@ -31,8 +31,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let events = ext.events().await?;
let bytes_hex = format!("0x{}", hex::encode(ext.bytes()));
// See the API docs for more ways to decode extrinsics:
let decoded_ext = ext.as_root_extrinsic::<polkadot::Call>();
println!(" Extrinsic #{idx}:");
println!(" Bytes: {bytes_hex}");
println!(" Decoded: {decoded_ext:?}");
println!(" Events:");
for evt in events.iter() {
+2 -2
View File
@@ -64,7 +64,7 @@
//! accounts, Alice to Bob:
//!
//! ```rust,ignore
#![doc = include_str!("../../../examples/examples/balance_transfer_basic.rs")]
#![doc = include_str!("../../../examples/examples/tx_basic.rs")]
//! ```
//!
//! This example assumes that a Polkadot node is running locally (Subxt endeavors to support all
@@ -81,7 +81,7 @@
//! Once Subxt is configured, the next step is interacting with a node. Follow the links
//! below to learn more about how to use Subxt for each of the following things:
//!
//! - [Extrinsics](usage::extrinsics): Subxt can build and submit extrinsics, wait until they are in
//! - [Transactions](usage::transactions): Subxt can build and submit transactions, wait until they are in
//! blocks, and retrieve the associated events.
//! - [Storage](usage::storage): Subxt can query the node storage.
//! - [Events](usage::events): Subxt can read the events emitted for recent blocks.
+2 -3
View File
@@ -35,18 +35,17 @@
//!
//! Defining some custom config based off the default Substrate config:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/setup_client_custom_config.rs")]
//! ```
//! Writing a custom [`crate::rpc::RpcClientT`] implementation:
//!
//! Writing a custom [`crate::rpc::RpcClientT`] implementation:
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/setup_client_custom_rpc.rs")]
//! ```
//! Creating an [`crate::OfflineClient`]:
//!
//! Creating an [`crate::OfflineClient`]:
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/setup_client_offline.rs")]
+5 -5
View File
@@ -24,14 +24,14 @@
//! - [events](crate::blocks::Block::events())
//! - [runtime APIs](crate::blocks::Block::runtime_api())
//!
//! Aside from these links to other Subxt APIs, the main thing that we can do here is iterate over and
//! decode the extrinsics in a block body.
//!
//! ## Example
//!
//! Given a block, you can [download the block body](crate::blocks::Block::body()) and iterate over
//! the extrinsics stored within it using [`crate::blocks::BlockBody::extrinsics()`].
//!
//! Here's an example in which we subscribe to blocks and print a bunch of information about each
//! one:
//!
//! the extrinsics stored within it using [`crate::blocks::BlockBody::extrinsics()`]. From there, you
//! can decode the extrinsics and access various details, including the associated events:
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/blocks_subscribing.rs")]
+1 -2
View File
@@ -48,12 +48,11 @@
//!
//! Here's an example using a static query:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/constants_static.rs")]
//! ```
//! And here's one using a dynamic query:
//!
//! And here's one using a dynamic query:
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/constants_dynamic.rs")]
+3 -4
View File
@@ -13,10 +13,10 @@
//! keep a snapshot of the state at a small number of previous blocks, so you can sometimes access
//! older events by using [`crate::events::EventsClient::at()`] and providing an older block hash.
//!
//! When we submit extrinsics using Subxt, methods like [`crate::tx::TxProgress::wait_for_finalized_success()`]
//! When we submit transactions using Subxt, methods like [`crate::tx::TxProgress::wait_for_finalized_success()`]
//! return [`crate::blocks::ExtrinsicEvents`], which can be used to iterate and inspect the events produced
//! for a specific extrinsic. We can also access _all_ of the events produced in a single block using one of these
//! two interfaces:
//! by that transaction being executed. We can also access _all_ of the events produced in a single block using one
//! of these two interfaces:
//!
//! ```rust,no_run
//! # #[tokio::main]
@@ -43,7 +43,6 @@
//!
//! Here's an example which puts this all together:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/events.rs")]
//! ```
+2 -2
View File
@@ -4,7 +4,7 @@
//! This modules contains examples of using Subxt; follow the links for more:
//!
//! - [Extrinsics](extrinsics)
//! - [Transactions](transactions)
//! - [Storage](storage)
//! - [Events](events)
//! - [Constants](constants)
@@ -16,6 +16,6 @@
pub mod blocks;
pub mod constants;
pub mod events;
pub mod extrinsics;
pub mod runtime_apis;
pub mod storage;
pub mod transactions;
+1 -1
View File
@@ -5,7 +5,7 @@
//! # Runtime API interface
//!
//! The Runtime API interface allows Subxt to call runtime APIs exposed by certain pallets in order
//! to obtain information. Much like [`super::storage`] and [`super::extrinsics`], Making a runtime
//! to obtain information. Much like [`super::storage`] and [`super::transactions`], Making a runtime
//! call to a node and getting the response back takes the following steps:
//!
//! 1. [Constructing a runtime call](#constructing-a-runtime-call)
+7 -6
View File
@@ -4,8 +4,9 @@
//! # Storage
//!
//! A Substrate based chain has storage, whose values are determined by the extrinsics added to past
//! blocks. Subxt allows you to query the storage of a node, which consists of the following steps:
//! A Substrate based chain can be seen as a key/value database which starts off at some initial
//! state, and is modified by the extrinsics in each block. This database is referred to as the
//! node storage. With Subxt, you can query this key/value storage with the following steps:
//!
//! 1. [Constructing a storage query](#constructing-a-storage-query).
//! 2. [Submitting the query to get back the associated values](#submitting-it).
@@ -73,33 +74,33 @@
//! [`crate::storage::Storage::fetch()`] or [`crate::storage::Storage::fetch_or_default()`] (the
//! latter will only work for storage queries that have a default value when empty):
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/storage_fetch.rs")]
//! ```
//!
//! For completeness, below is an example using a dynamic query instead. The return type from a
//! dynamic query is a [`crate::dynamic::DecodedValueThunk`], which can be decoded into a
//! [`crate::dynamic::Value`], or else the raw bytes can be accessed instead.
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/storage_fetch_dynamic.rs")]
//! ```
//!
//! ### Iterating storage entries
//!
//! Many storage entries are maps of values; as well as fetching individual values, it's possible to
//! iterate over all of the values stored at that location:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/storage_iterating.rs")]
//! ```
//! Here's the same logic but using dynamically constructed values instead:
//!
//! Here's the same logic but using dynamically constructed values instead:
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/storage_iterating_dynamic.rs")]
//! ```
//!
//! ### Advanced
//!
//! For more advanced use cases, have a look at [`crate::storage::Storage::fetch_raw`] and
@@ -2,35 +2,35 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Extrinsics
//! # Transactions
//!
//! Extrinsics define function calls and their parameters, and are the only way that you can change
//! the state of the blockchain. Submitting extrinsics to a node is one of the core features of
//! Subxt, and generally consists of the following steps:
//! A transaction is an extrinsic that's signed (ie it originates from a given address). The purpose
//! of extrinsics is to modify the node storage in a deterministic way, and so being able to submit
//! transactions to a node is one of the core features of Subxt.
//!
//! 1. [Constructing an extrinsic payload to submit](#constructing-an-extrinsic-payload).
//! > Note: the documentation tends to use the terms _extrinsic_ and _transaction_ interchangeably;
//! > An extrinsic is some data that can be added to a block, and is either signed (a _transaction_)
//! > or unsigned (an _inherent_). Subxt can construct either, but overwhelmingly you'll need to
//! > sign the payload you'd like to submit.
//!
//! Submitting a transaction to a node consists of the following steps:
//!
//! 1. [Constructing a transaction payload to submit](#constructing-a-transaction-payload).
//! 2. [Signing it](#signing-it).
//! 3. [Submitting it (optionally with some additional parameters)](#submitting-it).
//!
//! We'll look at each of these steps in turn.
//!
//! > As a side note, an _extrinsic_ is anything that can be added to a block, and a _transaction_
//! > is an extrinsic that is submitted from a particular user (and is therefore also signed). Subxt
//! > can construct unsigned extrinsics, but overwhelmingly you'll need to sign them, and so the
//! > documentation tends to use the terms _extrinsic_ and _transaction_ interchangeably.
//! ## Constructing a transaction payload
//!
//! Furthermore, Subxt is capable of decoding extrinsics included in blocks.
//!
//! ## Constructing an extrinsic payload
//!
//! We can use the statically generated interface to build extrinsic payloads:
//! We can use the statically generated interface to build transaction payloads:
//!
//! ```rust,no_run
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! pub mod polkadot {}
//!
//! let remark = "Hello there".as_bytes().to_vec();
//! let extrinsic_payload = polkadot::tx().system().remark(remark);
//! let tx_payload = polkadot::tx().system().remark(remark);
//! ```
//!
//! > If you're not sure what types to import and use to build a given payload, you can use the
@@ -38,13 +38,13 @@
//! > interface.rs`, to see what types and things are available (or even just to use directly
//! > instead of the [`#[subxt]`](crate::subxt) macro).
//!
//! Alternately, we can dynamically construct an extrinsic payload. This will not be type checked or
//! Alternately, we can dynamically construct a transaction payload. This will not be type checked or
//! validated until it's submitted:
//!
//! ```rust,no_run
//! use subxt::dynamic::Value;
//!
//! let extrinsic_payload = subxt::dynamic::tx("System", "remark", vec![
//! let tx_payload = subxt::dynamic::tx("System", "remark", vec![
//! Value::from_bytes("Hello there")
//! ]);
//! ```
@@ -53,7 +53,7 @@
//! represents any type of data that can be SCALE encoded or decoded. It can be serialized,
//! deserialized and parsed from/to strings.
//!
//! A valid extrinsic payload is just something that implements the [`crate::tx::TxPayload`] trait;
//! A valid transaction payload is just something that implements the [`crate::tx::TxPayload`] trait;
//! you can implement this trait on your own custom types if the built-in ones are not suitable for
//! your needs.
//!
@@ -96,21 +96,21 @@
//! // Create client:
//! let client = OnlineClient::<PolkadotConfig>::new().await?;
//!
//! // Create a dummy extrinsic payload to sign:
//! // Create a dummy tx payload to sign:
//! let payload = subxt::dynamic::tx("System", "remark", vec![
//! Value::from_bytes("Hello there")
//! ]);
//!
//! // Construct the extrinsic but don't sign it. You need to provide the nonce
//! // Construct the tx but don't sign it. You need to provide the nonce
//! // here, or can use `create_partial_signed` to fetch the correct nonce.
//! let partial_extrinsic = client.tx().create_partial_signed_with_nonce(
//! let partial_tx = client.tx().create_partial_signed_with_nonce(
//! &payload,
//! 0,
//! Default::default()
//! )?;
//!
//! // Fetch the payload that needs to be signed:
//! let signer_payload = partial_extrinsic.signer_payload();
//! let signer_payload = partial_tx.signer_payload();
//!
//! // ... At this point, we can hand off the `signer_payload` to be signed externally.
//! // Ultimately we need to be given back a `signature` (or really, anything
@@ -123,9 +123,9 @@
//! # signature = signer.sign(&signer_payload);
//! # address = signer.address();
//!
//! // Now we can build an extrinsic, which one can call `submit` or `submit_and_watch`
//! // Now we can build an tx, which one can call `submit` or `submit_and_watch`
//! // on to submit to a node and optionally watch the status.
//! let extrinsic = partial_extrinsic.sign_with_address_and_signature(
//! let tx = partial_tx.sign_with_address_and_signature(
//! &address,
//! &signature
//! );
@@ -135,7 +135,7 @@
//!
//! ## Submitting it
//!
//! Once we are able to sign the extrinsic, we need to submit it.
//! Once we have signed the transaction, we need to submit it.
//!
//! ### The high level API
//!
@@ -146,21 +146,20 @@
//! into a finalized block, check for an `ExtrinsicSuccess` event, and then hand back the events for
//! inspection. This looks like:
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/tx_basic.rs")]
//! ```
//!
//! ### Providing transaction parameters
//!
//! If you'd like to provide parameters (such as mortality) to the transaction, you can use
//! [`crate::tx::TxClient::sign_and_submit_then_watch`] instead:
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/balance_transfer_basic.rs")]
#![doc = include_str!("../../../../examples/examples/tx_with_params.rs")]
//! ```
//! ### Providing extrinsic parameters
//!
//! If you'd like to provide extrinsic parameters (such as mortality), you can use
//! [`crate::tx::TxClient::sign_and_submit_then_watch`] instead, and provide parameters for your
//! chain:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/balance_transfer_with_params.rs")]
//! ```
//! This example doesn't wait for the extrinsic to be included in a block; it just submits it and
//! This example doesn't wait for the transaction to be included in a block; it just submits it and
//! hopes for the best!
//!
//! ### Custom handling of transaction status updates
@@ -168,57 +167,10 @@
//! If you'd like more control or visibility over exactly which status updates are being emitted for
//! the transaction, you can monitor them as they are emitted and react however you choose:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/balance_transfer_status_stream.rs")]
#![doc = include_str!("../../../../examples/examples/tx_status_stream.rs")]
//! ```
//!
//! Take a look at the API docs for [`crate::tx::TxProgress`], [`crate::tx::TxStatus`] and
//! [`crate::tx::TxInBlock`] for more options.
//!
//! ## Decoding Extrinsics
//!
//! The block body is made up of extrinsics representing the generalization of the concept of transactions.
//! Extrinsics can contain any external data the underlying chain wishes to validate and track.
//!
//! The process of decoding extrinsics generally involves the following steps:
//!
//! 1. Retrieve a block from the chain: This can be done directly by providing a specific hash using [crate::blocks::BlocksClient::at()]
//! and [crate::blocks::BlocksClient::at_latest()], or indirectly by subscribing to the latest produced blocks of the chain using
//! [crate::blocks::BlocksClient::subscribe_finalized()].
//!
//! 2. Fetch the block's body using [crate::blocks::Block::body()].
//!
//! 3. Obtain the extrinsics from the block's body using [crate::blocks::BlockBody::extrinsics()].
//!
//! ```rust,no_run
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use subxt::client::OnlineClient;
//! use subxt::config::PolkadotConfig;
//!
//! // Create client:
//! let client = OnlineClient::<PolkadotConfig>::new().await?;
//!
//! // Get the lastest block (or use .at() to specify a block hash):
//! let block = client.blocks().at_latest().await?;
//!
//! // Get the block's body which contains the extrinsics.
//! let body = block.body().await?;
//!
//! // Fetch the extrinsics of the block's body.
//! let extrinsics = block.body().await?.extrinsics();
//! # Ok(())
//! # }
//! ```
//!
//! Once the extrinsics are loaded, similar to events, you can iterate through the extrinsics or search for specific extrinsics using methods
//! such as [crate::blocks::Extrinsics::iter()] and [crate::blocks::Extrinsics::find()]. For more information, refer to [crate::blocks::ExtrinsicDetails].
//!
//! ### Example
//!
//! Here's an example that demonstrates the usage of these concepts:
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/block_extrinsics.rs")]
//! ```
//!
+1 -1
View File
@@ -5,7 +5,7 @@
//! Subxt is a library for interacting with Substrate based nodes. Using it looks something like this:
//!
//! ```rust,ignore
#![doc = include_str!("../../examples/examples/balance_transfer_basic.rs")]
#![doc = include_str!("../../examples/examples/tx_basic.rs")]
//! ```
//!
//! Take a look at [the Subxt guide](book) to learn more about how to use Subxt.