Skip to content

Commit

Permalink
Merge pull request #339 from paritytech/AndreiEres/whois
Browse files Browse the repository at this point in the history
Add search by validator key to whois
  • Loading branch information
AndreiEres authored May 2, 2023
2 parents 78e5538 + a49eafe commit 7880cad
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 48 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ Use the output file to replace the older in the `assets` folder then rebuild.
- [polkadot-parachain-tracer](parachain-tracer/README.md) - Parachain progress monitoring and debugging utility
- [polkadot-block-time](block-time/README.md) - display the current block time in the substrate based network
- [polkadot-kvdb](kvdb/README.md) - inspect key-value database used by parachains or the relay chain
- [polkadot-whois](whois/README.md) - tracking of validators using a substrate telemetry data.
33 changes: 24 additions & 9 deletions essentials/src/api/subxt_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,14 @@ pub use crate::metadata::polkadot::{
},
};
use crate::{
types::{AccountId32, Timestamp, H256},
types::{AccountId32, SessionKeys, SubxtCall, Timestamp, H256},
utils::{Retry, RetryOptions},
};
use codec::Decode;
use log::error;
use thiserror::Error;

#[cfg(feature = "rococo")]
pub use subxt_runtime_types::rococo_runtime::RuntimeCall as SubxtCall;

#[cfg(feature = "polkadot")]
pub use subxt_runtime_types::polkadot_runtime::RuntimeCall as SubxtCall;

use std::{collections::hash_map::HashMap, fmt::Debug};
use subxt::{OnlineClient, PolkadotConfig};
use thiserror::Error;

/// Subxt based APIs for fetching via RPC and processing of extrinsics.
pub enum RequestType {
Expand All @@ -65,6 +58,8 @@ pub enum RequestType {
GetSessionInfo(u32),
/// Get information about validators account keys in some session.
GetSessionAccountKeys(u32),
/// Get information about validator's next session keys.
GetSessionNextKeys(AccountId32),
/// Get information about inbound HRMP channels, accepts block hash and destination ParaId
GetInboundHRMPChannels(<PolkadotConfig as subxt::Config>::Hash, u32),
/// Get data from a specific inbound HRMP channel
Expand Down Expand Up @@ -112,6 +107,9 @@ impl Debug for RequestType {
RequestType::GetSessionAccountKeys(id) => {
format!("get session account keys: {:?}", id)
},
RequestType::GetSessionNextKeys(account) => {
format!("get next session account keys: {:?}", account)
},
RequestType::GetInboundHRMPChannels(h, para_id) => {
format!("get inbound channels: {:?}; para id: {}", h, para_id)
},
Expand Down Expand Up @@ -159,6 +157,8 @@ pub enum Response {
SessionInfo(Option<polkadot_rt_primitives::v2::SessionInfo>),
/// Session keys
SessionAccountKeys(Option<Vec<AccountId32>>),
/// Session next keys for a validator
SessionNextKeys(Option<SessionKeys>),
/// HRMP channels for some parachain (e.g. who are sending messages to us)
HRMPChannels(BTreeMap<u32, SubxtHrmpChannel>),
/// HRMP content for a specific channel
Expand Down Expand Up @@ -226,6 +226,7 @@ impl RequestExecutor {
RequestType::GetSessionInfo(session_index) => subxt_get_session_info(&api, session_index).await,
RequestType::GetSessionAccountKeys(session_index) =>
subxt_get_session_account_keys(&api, session_index).await,
RequestType::GetSessionNextKeys(ref account) => subxt_get_session_next_keys(&api, account).await,
RequestType::GetInboundHRMPChannels(hash, para_id) =>
subxt_get_inbound_hrmp_channels(&api, hash, para_id).await,
RequestType::GetOutboundHRMPChannels(hash, para_id) =>
Expand Down Expand Up @@ -347,6 +348,14 @@ impl RequestExecutor {
wrap_subxt_call!(self, GetSessionAccountKeys, SessionAccountKeys, url, session_index)
}

pub async fn get_session_next_keys(
&mut self,
url: &str,
account: AccountId32,
) -> std::result::Result<Option<SessionKeys>, SubxtWrapperError> {
wrap_subxt_call!(self, GetSessionNextKeys, SessionNextKeys, url, account)
}

pub async fn get_inbound_hrmp_channels(
&mut self,
url: &str,
Expand Down Expand Up @@ -485,6 +494,12 @@ async fn subxt_get_session_account_keys(api: &OnlineClient<PolkadotConfig>, sess
Ok(Response::SessionAccountKeys(session_keys))
}

async fn subxt_get_session_next_keys(api: &OnlineClient<PolkadotConfig>, account: &AccountId32) -> Result {
let addr = polkadot::storage().session().next_keys(account);
let next_keys = api.storage().at_latest().await?.fetch(&addr).await?;
Ok(Response::SessionNextKeys(next_keys))
}

/// A wrapper over subxt HRMP channel configuration
#[derive(Debug, Clone)]
pub struct SubxtHrmpChannel {
Expand Down
167 changes: 166 additions & 1 deletion essentials/src/telemetry_feed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,25 @@
// along with polkadot-introspector. If not, see <http://www.gnu.org/licenses/>.
//

use crate::types::{BlockNumber, Timestamp, H256};
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;

use crate::types::{BlockNumber, Timestamp, H256};
macro_rules! display_or {
($option:expr, $none:expr) => {
if let Some(value) = &$option {
format!("{}", value)
} else {
$none.to_string()
}
};
}

macro_rules! join {
($iter:expr) => {
$iter.iter().map(|x| format!("{}", x)).collect::<Vec<String>>().join(", ")
};
}

pub type FeedNodeId = usize;

Expand All @@ -36,6 +51,25 @@ pub struct BlockDetails {
pub propagation_time: Option<u64>,
}

impl std::fmt::Display for BlockDetails {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Block
block_hash: {}
block_height: {}
block_time: {}
block_timestamp: {}
propagation_time: {}",
self.block.hash,
self.block.height,
self.block_time,
self.block_timestamp,
display_or!(self.propagation_time, "none"),
)
}
}

#[derive(Debug, PartialEq)]
pub struct NodeDetails {
pub name: String,
Expand All @@ -47,6 +81,30 @@ pub struct NodeDetails {
pub sysinfo: Option<NodeSysInfo>,
}

impl std::fmt::Display for NodeDetails {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Details
name: {}
implementation: {}
version: {}
validator: {}
network_id: {}
ip: {}
{}",
self.name,
self.implementation,
self.version,
display_or!(self.validator, "none"),
display_or!(self.network_id, "none"),
display_or!(self.ip, "none"),
display_or!(self.sysinfo, "SysInfo\nnone")
)
}
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct NodeSysInfo {
pub cpu: Option<Box<str>>,
Expand All @@ -57,31 +115,103 @@ pub struct NodeSysInfo {
pub is_virtual_machine: Option<bool>,
}

impl std::fmt::Display for NodeSysInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"SysInfo
cpu: {}
memory: {}
core_count: {}
linux_kernel: {}
linux_distro: {}
is_virtual_machine: {}",
display_or!(self.cpu, "none"),
display_or!(self.memory, "none"),
display_or!(self.core_count, "none"),
display_or!(self.linux_kernel, "none"),
display_or!(self.linux_distro, "none"),
display_or!(self.is_virtual_machine, "none"),
)
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct NodeStats {
pub peers: u64,
pub txcount: u64,
}

impl std::fmt::Display for NodeStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Stats
peers: {}
txcount: {}",
self.peers, self.txcount
)
}
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct NodeLocation {
lat: f32,
long: f32,
city: String,
}

impl std::fmt::Display for NodeLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Location
city: {}
lat: {}
long: {}",
self.city, self.lat, self.long
)
}
}

#[derive(Debug, Default, PartialEq)]
pub struct NodeIO {
pub used_state_cache_size: Vec<f32>,
}

impl std::fmt::Display for NodeIO {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"IO
used_state_cache_size: {}",
join!(self.used_state_cache_size)
)
}
}

#[derive(Debug, Default, PartialEq)]
pub struct NodeHardware {
pub upload: Vec<f64>,
pub download: Vec<f64>,
pub chart_stamps: Vec<f64>,
}

impl std::fmt::Display for NodeHardware {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Hardware
upload: {}
download: {}
chart_stamps: {}",
join!(self.upload),
join!(self.download),
join!(self.chart_stamps)
)
}
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct NodeHwBench {
pub cpu_hashrate_score: u64,
Expand All @@ -90,6 +220,23 @@ pub struct NodeHwBench {
pub disk_random_write_score: Option<u64>,
}

impl std::fmt::Display for NodeHwBench {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"NodeHwBench
cpu_hashrate_score: {}
memory_memcpy_score: {}
disk_sequential_write_score: {}
disk_random_write_score: {}",
self.cpu_hashrate_score,
self.memory_memcpy_score,
display_or!(self.disk_sequential_write_score, "none"),
display_or!(self.disk_random_write_score, "none"),
)
}
}

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Default)]
pub struct Ranking<K> {
pub list: Vec<(K, u64)>,
Expand Down Expand Up @@ -143,6 +290,24 @@ pub struct AddedNode {
hwbench: Option<NodeHwBench>,
}

impl std::fmt::Display for AddedNode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"node_id: {}\nstartup_time: {}\n\n{}\n\n{}\n\n{}\n\n{}\n\n{}\n\n{}\n\n{}",
self.node_id,
display_or!(self.startup_time, "none"),
self.details,
self.block_details,
display_or!(self.location, "Location:\nnone"),
display_or!(self.hwbench, "HwBench:\nnone"),
self.stats,
self.io,
self.hardware
)
}
}

#[derive(Debug, PartialEq)]
pub struct RemovedNode {
pub node_id: FeedNodeId,
Expand Down
2 changes: 1 addition & 1 deletion essentials/src/telemetry_subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ async fn choose_chain(chains: &HashMap<H256, AddedChain>) -> color_eyre::Result<
return Ok(list[0].genesis_hash)
}

println!("Found {} chains.\n", list.len());
println!("Connected to telemetry backend, {} chains found.\n", list.len());

let chain_index: usize;
let indexed_list: Vec<(usize, &AddedChain)> = list.iter().enumerate().map(|(i, c)| (i + 1, c)).collect();
Expand Down
8 changes: 8 additions & 0 deletions essentials/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,17 @@
// along with polkadot-introspector. If not, see <http://www.gnu.org/licenses/>.
//

use crate::metadata::polkadot::runtime_types as subxt_runtime_types;
use subxt::utils;

#[cfg(feature = "polkadot")]
use subxt_runtime_types::polkadot_runtime as runtime;
#[cfg(feature = "rococo")]
use subxt_runtime_types::rococo_runtime as runtime;

pub type BlockNumber = u32;
pub type H256 = utils::H256;
pub type AccountId32 = utils::AccountId32;
pub type Timestamp = u64;
pub type SessionKeys = runtime::SessionKeys;
pub type SubxtCall = runtime::RuntimeCall;
1 change: 1 addition & 0 deletions whois/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ futures = "0.3.28"
log = "0.4.17"
polkadot-introspector-essentials = { path = "../essentials" }
polkadot-introspector-priority-channel = { path = "../priority-channel" }
thiserror = "1.0.39"
tokio = { version = "1.28.0", features = ["macros", "rt-multi-thread", "signal"] }
9 changes: 9 additions & 0 deletions whois/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# polkadot-whois

Tracking of validators using a substrate telemetry data.

Usage:

```
cargo run --features=polkadot --bin polkadot-whois 1497QNdycmxqMi3VJDxZDhaJh4s9tytr5RFWyrLcNse2xqPD --ws=wss://rpc.polkadot.io:443 --feed=wss://feed.telemetry.polkadot.io/feed
```
Loading

0 comments on commit 7880cad

Please sign in to comment.