From 534e0e472d01f7571e3f4bc549cc9df6dfa3f091 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 25 May 2023 09:23:50 +0300 Subject: [PATCH] xxx: Backup Signed-off-by: Alexandru Vasile --- Cargo.lock | 2 + examples/Cargo.toml | 2 + examples/examples/storage_iterating.rs | 2 +- examples/examples/tx_basic_light_client.rs | 215 +++++++++++++++++++-- subxt/src/rpc/lightclient/background.rs | 5 + subxt/src/rpc/types.rs | 4 + 6 files changed, 211 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30891ebf15..a7978410ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4284,6 +4284,8 @@ dependencies = [ "sp-keyring", "subxt", "tokio", + "tracing", + "tracing-subscriber 0.3.17", ] [[package]] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 79b1615d88..b7948a4ddf 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -18,3 +18,5 @@ tokio = { workspace = true } futures = { workspace = true } hex = { workspace = true } sp-keyring = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } diff --git a/examples/examples/storage_iterating.rs b/examples/examples/storage_iterating.rs index 7b7864cf7c..07704d1d01 100644 --- a/examples/examples/storage_iterating.rs +++ b/examples/examples/storage_iterating.rs @@ -12,7 +12,7 @@ async fn main() -> Result<(), Box> { let storage_query = polkadot::storage().system().account_root(); // Get back an iterator of results (here, we are fetching 10 items at - // a time from the node, but we always iterate over oen at a time). + // a time from the node, but we always iterate over one at a time). let mut results = api .storage() .at_latest() diff --git a/examples/examples/tx_basic_light_client.rs b/examples/examples/tx_basic_light_client.rs index e89c0da5ce..f4f8754782 100644 --- a/examples/examples/tx_basic_light_client.rs +++ b/examples/examples/tx_basic_light_client.rs @@ -1,8 +1,27 @@ -use sp_keyring::AccountKeyring; -use subxt::rpc::LightClient; -use subxt::{tx::PairSigner, OnlineClient, PolkadotConfig}; +//! This is a comprehensive example that utilizes the Light Client +//! for all subxt requests. +//! +//! Includes: +//! - submitting a transaction to a local development chain +//! - Subscribes to all finalized blocks using the old RPC method +//! - Subscribes to the head of the chain using the new `chainHead` RPC method +//! - Dynamically query constants +//! - Dynamically decode the events of the latest block +//! - Various RPC calls to ensure proper shape of the response. +//! +//! # Note +//! +//! This feature is experimental and things might break without notice. +use futures::StreamExt; +use sp_keyring::AccountKeyring; use std::sync::Arc; +use subxt::dynamic::At; +use subxt::dynamic::Value; +use subxt::rpc::types::FollowEvent; +use subxt::rpc::LightClient; +use subxt::utils::AccountId32; +use subxt::{OnlineClient, PolkadotConfig}; // Generate an interface that we can use from the node's metadata. #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")] @@ -10,28 +29,188 @@ pub mod polkadot {} #[tokio::main] async fn main() -> Result<(), Box> { + tracing_subscriber::fmt::init(); + + println!("Creating light client"); // Create a light client from the provided chain spec. let light_client = LightClient::new(include_str!("../../artifacts/dev_spec.json"))?; let api = OnlineClient::::from_rpc_client(Arc::new(light_client)).await?; + println!("Done with creating light client"); // Build a balance transfer extrinsic. - let dest = AccountKeyring::Bob.to_account_id().into(); - let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000); + // { + // let dest = AccountKeyring::Bob.to_account_id().into(); + // let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000); - // Submit the balance transfer extrinsic from Alice, and wait for it to be successful - // and in a finalized block. We get back the extrinsic events if all is well. - let from = PairSigner::new(AccountKeyring::Alice.pair()); - let events = api - .tx() - .sign_and_submit_then_watch_default(&balance_transfer_tx, &from) - .await? - .wait_for_finalized_success() - .await?; + // // Submit the balance transfer extrinsic from Alice, and wait for it to be successful + // // and in a finalized block. We get back the extrinsic events if all is well. + // let from = PairSigner::new(AccountKeyring::Alice.pair()); + // let events = api + // .tx() + // .sign_and_submit_then_watch_default(&balance_transfer_tx, &from) + // .await? + // .wait_for_finalized_success() + // .await?; - // Find a Transfer event and print it. - let transfer_event = events.find_first::()?; - if let Some(event) = transfer_event { - println!("Balance transfer success: {event:?}"); + // // Find a Transfer event and print it. + // let transfer_event = events.find_first::()?; + // if let Some(event) = transfer_event { + // println!("Balance transfer success: {event:?}"); + // } + // } + + // Subscribe to the latest 3 finalized blocks. + { + let mut blocks_sub = api.blocks().subscribe_finalized().await?.take(3); + // For each block, print a bunch of information about it: + 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}"); + } + } + + // Subscribe to a few events from the head of the chain. + { + let blocks = api.rpc().chainhead_unstable_follow(false).await?; + let sub_id = blocks + .subscription_id() + .expect("RPC provides a valid subscription id; qed") + .to_owned(); + + let mut blocks = blocks.take(10); + while let Some(event) = blocks.next().await { + let event = event?; + + println!("chainHead_follow event: {event:?}"); + + // Fetch the body, header and storage of the best blocks only. + let FollowEvent::BestBlockChanged(best_block) = event else { + continue + }; + let hash = best_block.best_block_hash; + + // Subscribe to fetch the block's body. + let mut sub = api + .rpc() + .chainhead_unstable_body(sub_id.clone(), hash) + .await? + .take(5); + + while let Some(event) = sub.next().await { + let event = event?; + + println!(" chainHead_body event: {event:?}"); + } + + // Fetch the block's header. + // let header = api + // .rpc() + // .chainhead_unstable_header(sub_id.clone(), hash) + // .await?; + // let header = header.expect("RPC must have this header in memory; qed"); + + // println!(" chainHead_header: {header}"); + + // Make a storage query. + let account_id: AccountId32 = AccountKeyring::Alice.to_account_id().into(); + let addr = polkadot::storage().system().account(account_id); + let addr_bytes = api.storage().address_bytes(&addr).unwrap(); + + let mut sub = api + .rpc() + .chainhead_unstable_storage(sub_id.clone(), hash, &addr_bytes, None) + .await? + .take(5); + + while let Some(event) = sub.next().await { + let event = event?; + + println!(" chainHead_storage event: {event:?}"); + } + } + } + + // A dynamic query to obtain some contant: + { + let constant_query = subxt::dynamic::constant("System", "BlockLength"); + + // Obtain the value: + let value = api.constants().at(&constant_query)?; + + println!("Constant bytes: {:?}", value.encoded()); + println!("Constant value: {}", value.to_value()?); + } + + // Get events for the latest block: + { + let events = api.events().at_latest().await?; + + // We can dynamically decode events: + println!("Dynamic event details:"); + for event in events.iter() { + let event = event?; + + let pallet = event.pallet_name(); + let variant = event.variant_name(); + let field_values = event.field_values()?; + + println!("{pallet}::{variant}: {field_values}"); + } + } + + // Create a dynamically runtime API payload that calls the + // `AccountNonceApi_account_nonce` function. + { + let account = AccountKeyring::Alice.to_account_id(); + let runtime_api_call = subxt::dynamic::runtime_api_call( + "AccountNonceApi_account_nonce", + vec![Value::from_bytes(account)], + ); + + // Submit the call to get back a result. + let nonce = api + .runtime_api() + .at_latest() + .await? + .call(runtime_api_call) + .await?; + + println!("Account nonce: {:#?}", nonce.to_value()); + } + + // Build a dynamic storage query to access account information. + { + let account = AccountKeyring::Alice.to_account_id(); + let storage_query = + subxt::dynamic::storage("System", "Account", vec![Value::from_bytes(account)]); + + // Use that query to `fetch` a result. Because the query is dynamic, we don't know what the result + // type will be either, and so we get a type back that can be decoded into a dynamic Value type. + let result = api + .storage() + .at_latest() + .await? + .fetch(&storage_query) + .await?; + let value = result.unwrap().to_value()?; + + println!("Alice has free balance: {:?}", value.at("data").at("free")); + } + + { + let system_chain = api.rpc().system_chain().await?; + println!("System chain: {system_chain}"); + + let system_name = api.rpc().system_name().await?; + println!("System name: {system_name}"); + + let finalized_hash = api.rpc().finalized_head().await?; + println!("Finalized hash {finalized_hash:?}"); } Ok(()) diff --git a/subxt/src/rpc/lightclient/background.rs b/subxt/src/rpc/lightclient/background.rs index 02efeb53d2..121fa0be99 100644 --- a/subxt/src/rpc/lightclient/background.rs +++ b/subxt/src/rpc/lightclient/background.rs @@ -113,6 +113,8 @@ impl BackgroundTask { } } Ok(RpcResponse::Subscription { method, id, result }) => { + println!(" BACKRGOUND response {result:?}\n"); + // Subxt calls into `author_submitAndWatchExtrinsic`, however the smoldot produces // `{"event":"broadcasted","numPeers":1}` which is part of the RPC V2 API. Ignore // this spurious event. @@ -131,6 +133,9 @@ impl BackgroundTask { "Cannot send notification to subscription {:?}", id ); + + // Remove the sender if the subscription dropped the receiver. + self.subscriptions.remove(&id); } return; } diff --git a/subxt/src/rpc/types.rs b/subxt/src/rpc/types.rs index 14b7fff42b..bba52a7bc5 100644 --- a/subxt/src/rpc/types.rs +++ b/subxt/src/rpc/types.rs @@ -509,7 +509,11 @@ pub enum FollowEvent { #[serde(rename_all = "camelCase")] pub struct ChainHeadResult { /// Result of the method. + #[cfg(not(feature = "experimental-light-client"))] pub result: T, + /// Result of the method. + #[cfg(feature = "experimental-light-client")] + pub value: Vec, } /// The event generated by the body / call / storage methods.