From 5cfedac4b35e6fa72e251556ac814175dc3198e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bigna=20H=C3=A4rdi?= <73821294+haerdib@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:32:43 +0100 Subject: [PATCH] Add some system helper functions (#402) * add system api fns * fix build --- primitives/src/types.rs | 43 ++++++++++++ src/api/rpc_api/frame_system.rs | 91 +++++++++++++++++++++++++- testing/examples/frame_system_tests.rs | 27 +++++++- 3 files changed, 159 insertions(+), 2 deletions(-) diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 0cf878cfa..e6a2dd522 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -18,6 +18,7 @@ //! Re-defintion of substrate primitives. //! Needed because substrate pallets compile to wasm in no_std. +use alloc::string::String; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; @@ -215,3 +216,45 @@ impl Default for RewardDestination { RewardDestination::Staked } } + +/// Health struct returned by the RPC +// https://github.com/paritytech/substrate/blob/c172d0f683fab3792b90d876fd6ca27056af9fe9/client/rpc-api/src/system/helpers.rs#L40-L58 +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Health { + /// Number of connected peers + pub peers: usize, + /// Is the node syncing + pub is_syncing: bool, + /// Should this node have any peers + /// + /// Might be false for local chains or when running without discovery. + pub should_have_peers: bool, +} + +impl core::fmt::Display for Health { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(fmt, "{} peers ({})", self.peers, if self.is_syncing { "syncing" } else { "idle" }) + } +} + +/// The type of a chain. +/// +/// This can be used by tools to determine the type of a chain for displaying +/// additional information or enabling additional features. +// https://github.com/paritytech/substrate/blob/c172d0f683fab3792b90d876fd6ca27056af9fe9/client/chain-spec/src/lib.rs#L193-L207 +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub enum ChainType { + /// A development chain that runs mainly on one node. + Development, + /// A local chain that runs locally on multiple nodes for testing purposes. + Local, + /// A live chain. + Live, + /// Some custom chain type. + Custom(String), +} + +/// Arbitrary properties defined in chain spec as a JSON object +// https://github.com/paritytech/substrate/blob/c172d0f683fab3792b90d876fd6ca27056af9fe9/client/chain-spec/src/lib.rs#L215-L216 +pub type Properties = serde_json::map::Map; diff --git a/src/api/rpc_api/frame_system.rs b/src/api/rpc_api/frame_system.rs index ae9cfc455..ae0e18714 100644 --- a/src/api/rpc_api/frame_system.rs +++ b/src/api/rpc_api/frame_system.rs @@ -22,7 +22,7 @@ use ac_compose_macros::rpc_params; use ac_primitives::{ AccountInfo, ExtrinsicParams, FrameSystemConfig, StorageChangeSet, StorageKey, }; -use alloc::vec; +use alloc::{string::String, vec, vec::Vec}; use log::*; use serde::de::DeserializeOwned; use sp_core::Pair; @@ -74,6 +74,95 @@ where } } +/// Helper functions for some common SystemApi function. +pub trait SystemApi { + type ChainType; + type Properties; + type Health; + + /// Get the node's implementation name. + fn get_system_name(&self) -> Result; + + /// Get the node implementation's version. Should be a semver string. + fn get_system_version(&self) -> Result; + + /// Get the chain's name. Given as a string identifier. + fn get_system_chain(&self) -> Result; + + /// Get the chain's type. + fn get_system_chain_type(&self) -> Result; + + /// Get a custom set of properties as a JSON object, defined in the chain spec. + fn get_system_properties(&self) -> Result; + + /// Return health status of the node. + /// + /// Node is considered healthy if it is: + /// - connected to some peers (unless running in dev mode) + /// - not performing a major sync + fn get_system_health(&self) -> Result; + + /// Get the base58-encoded PeerId of the node. + fn get_system_local_peer_id(&self) -> Result; + + /// Returns the multi-addresses that the local node is listening on + /// + /// The addresses include a trailing `/p2p/` with the local PeerId, and are thus suitable to + /// be passed to `addReservedPeer` or as a bootnode address for example. + fn get_system_local_listen_addresses(&self) -> Result>; +} + +impl SystemApi for Api +where + Client: Request, + Runtime: FrameSystemConfig, + Params: ExtrinsicParams, +{ + type ChainType = ac_primitives::ChainType; + type Properties = ac_primitives::Properties; + type Health = ac_primitives::Health; + + fn get_system_name(&self) -> Result { + let res = self.client().request("system_name", rpc_params![])?; + Ok(res) + } + + fn get_system_version(&self) -> Result { + let res = self.client().request("system_version", rpc_params![])?; + Ok(res) + } + + fn get_system_chain(&self) -> Result { + let res = self.client().request("system_chain", rpc_params![])?; + Ok(res) + } + + fn get_system_chain_type(&self) -> Result { + let res = self.client().request("system_chainType", rpc_params![])?; + Ok(res) + } + + fn get_system_properties(&self) -> Result { + let res = self.client().request("system_properties", rpc_params![])?; + Ok(res) + } + + fn get_system_health(&self) -> Result { + let res = self.client().request("system_health", rpc_params![])?; + Ok(res) + } + + fn get_system_local_peer_id(&self) -> Result { + let res = self.client().request("system_localPeerId", rpc_params![])?; + Ok(res) + } + + fn get_system_local_listen_addresses(&self) -> Result> { + let res = self.client().request("system_localListenAddresses", rpc_params![])?; + Ok(res) + } +} + pub trait SubscribeFrameSystem where Client: Subscribe, diff --git a/testing/examples/frame_system_tests.rs b/testing/examples/frame_system_tests.rs index 8db1def88..3b7484abe 100644 --- a/testing/examples/frame_system_tests.rs +++ b/testing/examples/frame_system_tests.rs @@ -21,7 +21,7 @@ use kitchensink_runtime::Runtime; use sp_keyring::AccountKeyring; use substrate_api_client::{ rpc::JsonrpseeClient, Api, AssetTipExtrinsicParams, GetAccountInformation, StaticEvent, - SubscribeEvents, SubscribeFrameSystem, + SubscribeEvents, SubscribeFrameSystem, SystemApi, }; /// Check out frame_system::Event::ExtrinsicSuccess: @@ -49,6 +49,31 @@ async fn main() { let _account_info = api.get_account_info(&alice).unwrap().unwrap(); let _account_data = api.get_account_data(&alice).unwrap().unwrap(); + // System Api + let system_name = api.get_system_name().unwrap(); + println!("System name: {}", system_name); + + let system_version = api.get_system_version().unwrap(); + println!("System version: {}", system_version); + + let system_chain = api.get_system_chain().unwrap(); + println!("System chain: {}", system_chain); + + let system_chain_type = api.get_system_chain_type().unwrap(); + println!("System chain type: {:?}", system_chain_type); + + let system_properties = api.get_system_properties().unwrap(); + println!("System properties: {:?}", system_properties); + + let system_health = api.get_system_health().unwrap(); + println!("System health: {}", system_health); + + let system_local_peer_id = api.get_system_local_peer_id().unwrap(); + println!("System local peer id: {:?}", system_local_peer_id); + + let system_local_listen_addresses = api.get_system_local_listen_addresses().unwrap(); + println!("System local listen addresses: {:?}", system_local_listen_addresses); + // Subscribe let mut event_subscription = api.subscribe_system_events().unwrap(); let _event: ExtrinsicSuccess = api.wait_for_event(&mut event_subscription).unwrap();