From ae093bda7d00b2b221cde3fd37a9365fd89d5a2c Mon Sep 17 00:00:00 2001 From: James Wilson Date: Thu, 11 May 2023 13:12:51 +0100 Subject: [PATCH] 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 --- examples/examples/block_extrinsics.rs | 99 -------------- examples/examples/blocks_subscribing.rs | 4 + ...{balance_transfer_basic.rs => tx_basic.rs} | 0 ...r_status_stream.rs => tx_status_stream.rs} | 0 ...nsfer_with_params.rs => tx_with_params.rs} | 0 subxt/src/book/mod.rs | 4 +- subxt/src/book/setup/client.rs | 5 +- subxt/src/book/usage/blocks.rs | 10 +- subxt/src/book/usage/constants.rs | 3 +- subxt/src/book/usage/events.rs | 7 +- subxt/src/book/usage/mod.rs | 4 +- subxt/src/book/usage/runtime_apis.rs | 2 +- subxt/src/book/usage/storage.rs | 13 +- .../usage/{extrinsics.rs => transactions.rs} | 122 ++++++------------ subxt/src/lib.rs | 2 +- 15 files changed, 65 insertions(+), 210 deletions(-) delete mode 100644 examples/examples/block_extrinsics.rs rename examples/examples/{balance_transfer_basic.rs => tx_basic.rs} (100%) rename examples/examples/{balance_transfer_status_stream.rs => tx_status_stream.rs} (100%) rename examples/examples/{balance_transfer_with_params.rs => tx_with_params.rs} (100%) rename subxt/src/book/usage/{extrinsics.rs => transactions.rs} (53%) diff --git a/examples/examples/block_extrinsics.rs b/examples/examples/block_extrinsics.rs deleted file mode 100644 index e71182c421..0000000000 --- a/examples/examples/block_extrinsics.rs +++ /dev/null @@ -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> { - // Create a client to use: - let api = OnlineClient::::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::(); - 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::(); - println!(" As root extrinsic {:?}\n", root_extrinsic); - - let pallet_extrinsic = extrinsic - .as_pallet_extrinsic::(); - println!( - " Extrinsic as Balances Pallet call: {:?}\n", - pallet_extrinsic - ); - - let call = extrinsic.as_extrinsic::()?; - println!( - " Extrinsic as polkadot::balances::calls::Transfer: {:?}\n\n", - call - ); - } - - println!("\n"); - } - - Ok(()) -} diff --git a/examples/examples/blocks_subscribing.rs b/examples/examples/blocks_subscribing.rs index a5b585e612..7a7942c667 100644 --- a/examples/examples/blocks_subscribing.rs +++ b/examples/examples/blocks_subscribing.rs @@ -31,8 +31,12 @@ async fn main() -> Result<(), Box> { 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::(); + println!(" Extrinsic #{idx}:"); println!(" Bytes: {bytes_hex}"); + println!(" Decoded: {decoded_ext:?}"); println!(" Events:"); for evt in events.iter() { diff --git a/examples/examples/balance_transfer_basic.rs b/examples/examples/tx_basic.rs similarity index 100% rename from examples/examples/balance_transfer_basic.rs rename to examples/examples/tx_basic.rs diff --git a/examples/examples/balance_transfer_status_stream.rs b/examples/examples/tx_status_stream.rs similarity index 100% rename from examples/examples/balance_transfer_status_stream.rs rename to examples/examples/tx_status_stream.rs diff --git a/examples/examples/balance_transfer_with_params.rs b/examples/examples/tx_with_params.rs similarity index 100% rename from examples/examples/balance_transfer_with_params.rs rename to examples/examples/tx_with_params.rs diff --git a/subxt/src/book/mod.rs b/subxt/src/book/mod.rs index 47124df1ab..5b8164c612 100644 --- a/subxt/src/book/mod.rs +++ b/subxt/src/book/mod.rs @@ -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. diff --git a/subxt/src/book/setup/client.rs b/subxt/src/book/setup/client.rs index 92fd621769..3157914a58 100644 --- a/subxt/src/book/setup/client.rs +++ b/subxt/src/book/setup/client.rs @@ -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")] diff --git a/subxt/src/book/usage/blocks.rs b/subxt/src/book/usage/blocks.rs index b991500f86..a020090c29 100644 --- a/subxt/src/book/usage/blocks.rs +++ b/subxt/src/book/usage/blocks.rs @@ -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")] diff --git a/subxt/src/book/usage/constants.rs b/subxt/src/book/usage/constants.rs index 94074113ee..b11193b7f2 100644 --- a/subxt/src/book/usage/constants.rs +++ b/subxt/src/book/usage/constants.rs @@ -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")] diff --git a/subxt/src/book/usage/events.rs b/subxt/src/book/usage/events.rs index b64382731e..7d7a881539 100644 --- a/subxt/src/book/usage/events.rs +++ b/subxt/src/book/usage/events.rs @@ -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")] //! ``` diff --git a/subxt/src/book/usage/mod.rs b/subxt/src/book/usage/mod.rs index 8243cf2ce2..1742fcfc0a 100644 --- a/subxt/src/book/usage/mod.rs +++ b/subxt/src/book/usage/mod.rs @@ -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; diff --git a/subxt/src/book/usage/runtime_apis.rs b/subxt/src/book/usage/runtime_apis.rs index f5b4dd0799..e42ddb09a7 100644 --- a/subxt/src/book/usage/runtime_apis.rs +++ b/subxt/src/book/usage/runtime_apis.rs @@ -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) diff --git a/subxt/src/book/usage/storage.rs b/subxt/src/book/usage/storage.rs index 92efb13890..2c32e0d8fd 100644 --- a/subxt/src/book/usage/storage.rs +++ b/subxt/src/book/usage/storage.rs @@ -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 diff --git a/subxt/src/book/usage/extrinsics.rs b/subxt/src/book/usage/transactions.rs similarity index 53% rename from subxt/src/book/usage/extrinsics.rs rename to subxt/src/book/usage/transactions.rs index 314ee252dc..f5eddea603 100644 --- a/subxt/src/book/usage/extrinsics.rs +++ b/subxt/src/book/usage/transactions.rs @@ -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::::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> { -//! use subxt::client::OnlineClient; -//! use subxt::config::PolkadotConfig; -//! -//! // Create client: -//! let client = OnlineClient::::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")] -//! ``` -//! diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index f7eeca7bc3..d23633e141 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -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.