From 0c301af1adea3e18ce405f43498c37ac3eb3cf14 Mon Sep 17 00:00:00 2001 From: haerdib Date: Mon, 26 Feb 2024 18:05:19 +0100 Subject: [PATCH 1/5] add new example --- .github/workflows/ci.yml | 1 + .../async/examples/unstable_rpc_api_calls.rs | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 examples/async/examples/unstable_rpc_api_calls.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b4a4d75a..8145b56a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -174,6 +174,7 @@ jobs: staking_batch_payout, subscribe_events, sudo, + unstable_rpc_api_calls, transfer_with_tungstenite_client, transfer_with_ws_client, author_tests, diff --git a/examples/async/examples/unstable_rpc_api_calls.rs b/examples/async/examples/unstable_rpc_api_calls.rs new file mode 100644 index 000000000..79aca9fda --- /dev/null +++ b/examples/async/examples/unstable_rpc_api_calls.rs @@ -0,0 +1,60 @@ +/* + Copyright 2024 Supercomputing Systems AG + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +//! This example shows how to use the compose_extrinsic_offline macro which generates an extrinsic +//! without asking the node for nonce and does not need to know the metadata + +use kitchensink_runtime::{BalancesCall, RuntimeCall}; +use sp_keyring::AccountKeyring; +use sp_runtime::{generic::Era, MultiAddress}; +use substrate_api_client::{ + ac_primitives::{AssetRuntimeConfig, GenericAdditionalParams}, + rpc::JsonrpseeClient, + Api, Error, GetChainInfo, SubmitAndWatch, UnexpectedTxStatus, XtStatus, +}; + +// To test this example with CI we run it against the Substrate kitchensink node, which uses the asset pallet. +// Therefore, we need to use the `AssetRuntimeConfig` in this example. +// ! However, most Substrate runtimes do not use the asset pallet at all. So if you run an example against your own node +// you most likely should use `DefaultRuntimeConfig` instead. + +#[tokio::main] +async fn main() { + env_logger::init(); + + // Initialize api and set the signer (sender) that is used to sign the extrinsics. + let signer = AccountKeyring::Alice.pair(); + let client = JsonrpseeClient::with_default_url().await.unwrap(); + let mut api = Api::::new(client).await.unwrap(); + api.set_signer(signer.into()); + + let chain_name: String = + api.client().request("chainSpec_v1_chainName", rpc_params![]).await.unwrap(); + + println!("{chain_name}"); + + let pending_transactions: Vec = + api.client().request("sudo_unstable_pendingTransactions", rpc_params![]).await.unwrap(); + + println!("{pending_transactions}"); + + let version: String = + api.client().request("sudo_unstable_version", rpc_params![]).await.unwrap(); + + println!("{version}"); + + + +} From 36ff8315e5fcc0ea31cec06a1d5ed00a759d91fe Mon Sep 17 00:00:00 2001 From: haerdib Date: Tue, 27 Feb 2024 08:27:40 +0100 Subject: [PATCH 2/5] fix example --- .../async/examples/unstable_rpc_api_calls.rs | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/examples/async/examples/unstable_rpc_api_calls.rs b/examples/async/examples/unstable_rpc_api_calls.rs index 79aca9fda..73a928654 100644 --- a/examples/async/examples/unstable_rpc_api_calls.rs +++ b/examples/async/examples/unstable_rpc_api_calls.rs @@ -16,13 +16,12 @@ //! This example shows how to use the compose_extrinsic_offline macro which generates an extrinsic //! without asking the node for nonce and does not need to know the metadata -use kitchensink_runtime::{BalancesCall, RuntimeCall}; use sp_keyring::AccountKeyring; -use sp_runtime::{generic::Era, MultiAddress}; use substrate_api_client::{ - ac_primitives::{AssetRuntimeConfig, GenericAdditionalParams}, - rpc::JsonrpseeClient, - Api, Error, GetChainInfo, SubmitAndWatch, UnexpectedTxStatus, XtStatus, + ac_compose_macros::rpc_params, + ac_primitives::AssetRuntimeConfig, + rpc::{JsonrpseeClient, Request}, + Api, }; // To test this example with CI we run it against the Substrate kitchensink node, which uses the asset pallet. @@ -40,21 +39,21 @@ async fn main() { let mut api = Api::::new(client).await.unwrap(); api.set_signer(signer.into()); - let chain_name: String = - api.client().request("chainSpec_v1_chainName", rpc_params![]).await.unwrap(); + let chain_name: String = + api.client().request("chainSpec_v1_chainName", rpc_params![]).await.unwrap(); - println!("{chain_name}"); + println!("{chain_name}"); - let pending_transactions: Vec = - api.client().request("sudo_unstable_pendingTransactions", rpc_params![]).await.unwrap(); - - println!("{pending_transactions}"); - - let version: String = - api.client().request("sudo_unstable_version", rpc_params![]).await.unwrap(); - - println!("{version}"); + let pending_transactions: Vec = api + .client() + .request("sudo_unstable_pendingTransactions", rpc_params![]) + .await + .unwrap(); + println!("{pending_transactions:?}"); + let version: String = + api.client().request("sudo_unstable_version", rpc_params![]).await.unwrap(); + println!("{version}"); } From 636689145e1e67968baeae353d4a15d1372d51ea Mon Sep 17 00:00:00 2001 From: haerdib Date: Tue, 27 Feb 2024 09:17:08 +0100 Subject: [PATCH 3/5] fix example --- .../async/examples/unstable_rpc_api_calls.rs | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/examples/async/examples/unstable_rpc_api_calls.rs b/examples/async/examples/unstable_rpc_api_calls.rs index 73a928654..fa5d2b97d 100644 --- a/examples/async/examples/unstable_rpc_api_calls.rs +++ b/examples/async/examples/unstable_rpc_api_calls.rs @@ -16,10 +16,14 @@ //! This example shows how to use the compose_extrinsic_offline macro which generates an extrinsic //! without asking the node for nonce and does not need to know the metadata +use codec::Encode; +use serde_json::Value; +use sp_core::Bytes; use sp_keyring::AccountKeyring; use substrate_api_client::{ ac_compose_macros::rpc_params, ac_primitives::AssetRuntimeConfig, + extrinsic::BalancesExtrinsics, rpc::{JsonrpseeClient, Request}, Api, }; @@ -39,21 +43,39 @@ async fn main() { let mut api = Api::::new(client).await.unwrap(); api.set_signer(signer.into()); - let chain_name: String = - api.client().request("chainSpec_v1_chainName", rpc_params![]).await.unwrap(); + let json_value: Value = api.client().request("rpc_methods", rpc_params![]).await.unwrap(); + let json_string = serde_json::to_string(&json_value).unwrap(); + println!("{json_string}"); - println!("{chain_name}"); + let chain_name: String = api + .client() + .request("chainSpec_unstable_chainName", rpc_params![]) + .await + .unwrap(); - let pending_transactions: Vec = api + println!("Our chain is called: {chain_name}"); + + let genesishash: String = api .client() - .request("sudo_unstable_pendingTransactions", rpc_params![]) + .request("chainSpec_unstable_genesisHash", rpc_params![]) .await .unwrap(); - println!("{pending_transactions:?}"); + println!("Genesis Hash: {genesishash}"); - let version: String = - api.client().request("sudo_unstable_version", rpc_params![]).await.unwrap(); + let bob = AccountKeyring::Bob.to_account_id(); + let encoded_extrinsic: Bytes = api + .balance_transfer_allow_death(bob.into(), 1000) + .await + .unwrap() + .encode() + .into(); + + let subscription_string: String = api + .client() + .request("transaction_unstable_submitAndWatch", rpc_params![encoded_extrinsic]) + .await + .unwrap(); - println!("{version}"); + println!("Successfully submitted extrinsic. Watchable with the following subscription: {subscription_string}"); } From ba580499ca8862f7664d5b1db0730c694ea9e9f3 Mon Sep 17 00:00:00 2001 From: haerdib Date: Tue, 27 Feb 2024 11:28:42 +0100 Subject: [PATCH 4/5] add subscription example --- .../async/examples/unstable_rpc_api_calls.rs | 64 +++++++++++++------ 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/examples/async/examples/unstable_rpc_api_calls.rs b/examples/async/examples/unstable_rpc_api_calls.rs index fa5d2b97d..098cbaf0d 100644 --- a/examples/async/examples/unstable_rpc_api_calls.rs +++ b/examples/async/examples/unstable_rpc_api_calls.rs @@ -24,7 +24,7 @@ use substrate_api_client::{ ac_compose_macros::rpc_params, ac_primitives::AssetRuntimeConfig, extrinsic::BalancesExtrinsics, - rpc::{JsonrpseeClient, Request}, + rpc::{HandleSubscription, JsonrpseeClient, Request, Subscribe}, Api, }; @@ -43,26 +43,39 @@ async fn main() { let mut api = Api::::new(client).await.unwrap(); api.set_signer(signer.into()); + // Retrieve all available rpc methods: let json_value: Value = api.client().request("rpc_methods", rpc_params![]).await.unwrap(); let json_string = serde_json::to_string(&json_value).unwrap(); - println!("{json_string}"); + println!("Available methods: {json_string} \n"); - let chain_name: String = api - .client() - .request("chainSpec_unstable_chainName", rpc_params![]) - .await - .unwrap(); + // Since it's an unstable api and might change anytime, we first check if our calls are still + // available: + let chain_name_request = "chainSpec_unstable_chainName"; + let chain_genesis_hash_request = "chainSpec_unstable_genesisHash"; + let transaction_submit_watch = "transaction_unstable_submitAndWatch"; + let transaction_unwatch = "transaction_unstable_unwatch"; - println!("Our chain is called: {chain_name}"); + let request_vec = [ + chain_name_request, + chain_genesis_hash_request, + transaction_submit_watch, + transaction_unwatch, + ]; + for request in request_vec { + if !json_string.contains(request) { + panic!("Api has changed, please update the call {request}."); + } + } - let genesishash: String = api - .client() - .request("chainSpec_unstable_genesisHash", rpc_params![]) - .await - .unwrap(); + // Submit the above defiend rpc requests: + let chain_name: String = api.client().request(chain_name_request, rpc_params![]).await.unwrap(); + println!("Our chain is called: {chain_name}"); - println!("Genesis Hash: {genesishash}"); + let genesishash: String = + api.client().request(chain_genesis_hash_request, rpc_params![]).await.unwrap(); + println!("Chain genesis Hash: {genesishash}"); + // Submit and watch a transaction: let bob = AccountKeyring::Bob.to_account_id(); let encoded_extrinsic: Bytes = api .balance_transfer_allow_death(bob.into(), 1000) @@ -71,11 +84,26 @@ async fn main() { .encode() .into(); - let subscription_string: String = api + let mut subscription = api .client() - .request("transaction_unstable_submitAndWatch", rpc_params![encoded_extrinsic]) + .subscribe::( + transaction_submit_watch, + rpc_params![encoded_extrinsic], + transaction_unwatch, + ) .await .unwrap(); - - println!("Successfully submitted extrinsic. Watchable with the following subscription: {subscription_string}"); + while let Some(notification) = subscription.next().await { + let notification = notification.unwrap(); + println!("Subscription notification: {notification:?}"); + let event_object_string = notification["event"].as_str().unwrap(); + //let event_object_string = serde_json::from_string().unwrap(); + match event_object_string { + "finalized" => break, + "bestChainBlockIncluded" | "validated" => println!("Got {event_object_string} event"), + _ => panic!("Unexpected event: {event_object_string}"), + }; + } + println!("Transaction got finalized, unsubscribing."); + subscription.unsubscribe().await.unwrap(); } From 0573aa4bb5203a95a0fa3c3a94444d90d1013199 Mon Sep 17 00:00:00 2001 From: haerdib Date: Tue, 27 Feb 2024 11:45:37 +0100 Subject: [PATCH 5/5] fix description --- examples/async/examples/unstable_rpc_api_calls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/async/examples/unstable_rpc_api_calls.rs b/examples/async/examples/unstable_rpc_api_calls.rs index 098cbaf0d..1583cc84c 100644 --- a/examples/async/examples/unstable_rpc_api_calls.rs +++ b/examples/async/examples/unstable_rpc_api_calls.rs @@ -13,8 +13,8 @@ limitations under the License. */ -//! This example shows how to use the compose_extrinsic_offline macro which generates an extrinsic -//! without asking the node for nonce and does not need to know the metadata +//! This example shows how to call the unstable rpc api with self defined functions. +//! This includes simple requests as well as subscription. use codec::Encode; use serde_json::Value;