diff --git a/res/indexer-queries.graphql b/res/indexer-queries.graphql index b02516a8d9c45a4a2caa35a61fe1d38c120f5e15..304a8eeb30949d7dffda7a972c37348d88b883fa 100644 --- a/res/indexer-queries.graphql +++ b/res/indexer-queries.graphql @@ -4,6 +4,13 @@ query IdentityNameByIndex($index: Int!) { } } +query NamesByIndexes($indexes: [Int!]!) { + identities(where: {index_in: $indexes}) { + index + name + } +} + query IdentityInfo($index: Int!) { identities(where: { index_eq: $index }) { name diff --git a/src/cache.rs b/src/cache.rs deleted file mode 100644 index 94316b5a36f8e425427967ef2f1ce91f6854a135..0000000000000000000000000000000000000000 --- a/src/cache.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::{indexer::*, *}; - -use std::collections::{hash_map, HashMap}; - -pub struct IdentityCache { - client: Client, - identities: HashMap<IdtyId, String>, - indexer: Option<Indexer>, -} - -impl IdentityCache { - pub fn new(client: Client, indexer: Option<Indexer>) -> Self { - Self { - client, - identities: HashMap::new(), - indexer, - } - } - - pub async fn fetch_identity( - &mut self, - identity_id: IdtyId, - parent_hash: sp_core::H256, - ) -> anyhow::Result<String> { - Ok(match self.identities.entry(identity_id) { - hash_map::Entry::Occupied(entry) => entry.get().clone(), - hash_map::Entry::Vacant(entry) => entry - .insert({ - let pubkey = self - .client - .storage() - .at(parent_hash) - .fetch(&runtime::storage().identity().identities(identity_id)) - .await? - .ok_or_else(|| anyhow!("Identity {} not found", identity_id))? - .owner_key - .to_string(); - format!( - "“ {} â€", - if let Some(indexer) = &self.indexer { - if let Some(username) = indexer.username_by_pubkey(&pubkey).await { - username - } else { - pubkey - } - } else { - pubkey - } - ) - }) - .clone(), - }) - } -} diff --git a/src/commands/expire.rs b/src/commands/expire.rs index d8f7bf0ed9d4b8f6822b4cd6fbef434003f5a976..f70ff42b15bdc4cf1dc37ae1e52d62002a4e9c2b 100644 --- a/src/commands/expire.rs +++ b/src/commands/expire.rs @@ -1,5 +1,4 @@ -use crate::*; - +use crate::{indexer::*, *}; use futures::join; use std::collections::BTreeMap; @@ -27,7 +26,7 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an let end_block = current_block + blocks; - let mut identity_cache = cache::IdentityCache::new(client.clone(), indexer); + let mut identity_cache = IdentityCache::new(client.clone(), indexer); // Certifications let mut basic_certs_iter = client @@ -51,12 +50,12 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an println!( " {} ({}) -> {} ({})", identity_cache - .fetch_identity(issuer_id, parent_hash) + .fetch_identity(issuer_id,) .await .unwrap_or_else(|_| "?".into()), issuer_id, identity_cache - .fetch_identity(receiver_id, parent_hash) + .fetch_identity(receiver_id,) .await .unwrap_or_else(|_| "?".into()), receiver_id, @@ -68,7 +67,8 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an // Memberships let mut basic_membership_iter = client .storage() - .at(parent_hash) + .at_latest() + .await? .iter(runtime::storage().membership().memberships_expire_on_iter()) .await?; let mut basic_memberships = BTreeMap::new(); @@ -90,7 +90,7 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an println!( " {} ({})", identity_cache - .fetch_identity(identity_id, parent_hash) + .fetch_identity(identity_id) .await .unwrap_or_else(|_| "?".into()), identity_id, @@ -101,3 +101,53 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an Ok(()) } + +use std::collections::{hash_map, HashMap}; + +pub struct IdentityCache { + client: Client, + identities: HashMap<IdtyId, String>, + indexer: Option<Indexer>, +} + +impl IdentityCache { + pub fn new(client: Client, indexer: Option<Indexer>) -> Self { + Self { + client, + identities: HashMap::new(), + indexer, + } + } + + pub async fn fetch_identity(&mut self, identity_id: IdtyId) -> anyhow::Result<String> { + Ok(match self.identities.entry(identity_id) { + hash_map::Entry::Occupied(entry) => entry.get().clone(), + hash_map::Entry::Vacant(entry) => entry + .insert({ + let pubkey = self + .client + .storage() + .at_latest() + .await? + .fetch(&runtime::storage().identity().identities(identity_id)) + .await? + .ok_or_else(|| anyhow!("Identity {} not found", identity_id))? + .owner_key + .to_string(); + format!( + "“ {} â€", + if let Some(indexer) = &self.indexer { + if let Some(username) = indexer.username_by_pubkey(&pubkey).await { + username + } else { + pubkey + } + } else { + pubkey + } + ) + }) + .clone(), + }) + } +} diff --git a/src/commands/identity.rs b/src/commands/identity.rs index 448c87b38858800d7e935be1916e440857f70591..052f91689e3cd76ce4b5af1aa866e2afe5411e64 100644 --- a/src/commands/identity.rs +++ b/src/commands/identity.rs @@ -275,7 +275,7 @@ pub async fn get_identity( let pseudo = pseudo.unwrap_or(if let Some(indexer) = &indexer { indexer .username_by_index(index) - .await? + .await .ok_or_else(|| anyhow!("indexer does not have username for this index {index}"))? } else { "<no indexer>".to_string() diff --git a/src/commands/smith.rs b/src/commands/smith.rs index ad2d1cd512a00d0706f68a19ac52f2193fcf1546..034948b617a3e4924863fa4f6d9736095801e485 100644 --- a/src/commands/smith.rs +++ b/src/commands/smith.rs @@ -3,6 +3,7 @@ use crate::*; use commands::identity::try_get_idty_index_by_name; #[cfg(feature = "gdev")] use runtime::runtime_types::gdev_runtime::opaque::SessionKeys as RuntimeSessionKeys; +use std::collections::HashMap; use std::ops::Deref; type SessionKeys = [u8; 128]; @@ -68,7 +69,7 @@ pub enum Subcommand { /// handle smith commands pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError> { - let mut data = data.build_client().await?; + let mut data = data.build_client().await?.build_indexer().await?; match command { Subcommand::GoOnline => { go_online(&data).await?; @@ -191,40 +192,18 @@ pub async fn go_offline(data: &Data) -> Result<(), subxt::Error> { /// get online authorities pub async fn online(data: &Data) -> Result<(), anyhow::Error> { let client = data.client(); - let indexer = data.indexer.clone(); - let parent_hash = client - .clone() + let online_authorities = client .storage() .at_latest() .await? - .fetch(&runtime::storage().system().parent_hash()) - .await? - .unwrap(); - - let mut identity_cache = cache::IdentityCache::new(client.clone(), indexer); - - let online_authorities = client - .storage() - .at(parent_hash) .fetch(&runtime::storage().authority_members().online_authorities()) .await? .unwrap_or_default(); - - println!("Online:"); - for identity_id in online_authorities { - println!( - " {}", - identity_cache - .fetch_identity(identity_id, parent_hash) - .await - .unwrap_or_else(|_| format!("{identity_id}")) - ); - } - let incoming_authorities = client .storage() - .at(parent_hash) + .at_latest() + .await? .fetch( &runtime::storage() .authority_members() @@ -232,21 +211,10 @@ pub async fn online(data: &Data) -> Result<(), anyhow::Error> { ) .await? .unwrap_or_default(); - - println!("Incoming:"); - for identity_id in incoming_authorities { - println!( - " {}", - identity_cache - .fetch_identity(identity_id, parent_hash) - .await - .unwrap_or_else(|_| format!("{identity_id}")) - ); - } - let outgoing_authorities = client .storage() - .at(parent_hash) + .at_latest() + .await? .fetch( &runtime::storage() .authority_members() @@ -255,15 +223,59 @@ pub async fn online(data: &Data) -> Result<(), anyhow::Error> { .await? .unwrap_or_default(); - println!("Outgoing:"); - for identity_id in outgoing_authorities { + if let Some(indexer) = &data.indexer { + let mut names = HashMap::<IdtyId, String>::new(); + indexer + .names_by_indexes(&online_authorities) + .await + .into_iter() + .for_each(|e| { + names.insert(e.0, e.1); + }); + println!("Online:"); + println!( + "{}", + online_authorities + .iter() + .map(|i| &names + .get(i) + .expect("panic! found authorities whith no name")[..]) + .collect::<Vec<&str>>() + .join(", ") + ); + + println!("Incoming:"); println!( - " {}", - identity_cache - .fetch_identity(identity_id, parent_hash) - .await - .unwrap_or_else(|_| format!("{identity_id}")) + "{}", + incoming_authorities + .iter() + .map(|i| &names + .get(i) + .expect("panic! found authorities whith no name")[..]) + .collect::<Vec<&str>>() + .join(", ") ); + + println!("Outgoing:"); + println!( + "{}", + outgoing_authorities + .iter() + .map(|i| &names + .get(i) + .expect("panic! found authorities whith no name")[..]) + .collect::<Vec<&str>>() + .join(", ") + ); + } else { + println!("Online:"); + println!("{online_authorities:?}"); + + println!("Incoming:"); + println!("{incoming_authorities:?}"); + + println!("Outgoing:"); + println!("{outgoing_authorities:?}"); } Ok(()) diff --git a/src/indexer.rs b/src/indexer.rs index 495a99a97797052232ab6cb6e5813ccfb546ce7d..f940b30969db2f3fb1d17d7545d5e20737dafd5c 100644 --- a/src/indexer.rs +++ b/src/indexer.rs @@ -1,67 +1,13 @@ +mod queries; + use crate::*; use comfy_table::*; use comfy_table::{ContentArrangement, Table}; -use graphql_client::{reqwest::post_graphql, GraphQLQuery}; -use identity_info::*; +use graphql_client::reqwest::post_graphql; +use graphql_client::GraphQLQuery; +use queries::*; use sp_core::Bytes; -// type used in parameters query -// #[allow(non_camel_case_types)] -// type jsonb = serde_json::Value; - -// index → identity -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "res/indexer-schema.json", - query_path = "res/indexer-queries.graphql" -)] -pub struct IdentityNameByIndex; - -// index → identity info -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "res/indexer-schema.json", - query_path = "res/indexer-queries.graphql" -)] -pub struct IdentityInfo; - -// pubkey → identity -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "res/indexer-schema.json", - query_path = "res/indexer-queries.graphql" -)] -pub struct IdentityNameByPubkey; - -// pubkey → wasidentity -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "res/indexer-schema.json", - query_path = "res/indexer-queries.graphql" -)] -pub struct WasIdentityNameByPubkey; - -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "res/indexer-schema.json", - query_path = "res/indexer-queries.graphql" -)] -pub struct LatestBlock; - -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "res/indexer-schema.json", - query_path = "res/indexer-queries.graphql" -)] -pub struct BlockByNumber; - -#[derive(GraphQLQuery, Debug)] -#[graphql( - schema_path = "res/indexer-schema.json", - query_path = "res/indexer-queries.graphql" -)] -pub struct GenesisHash; - #[derive(Clone, Debug)] pub struct Indexer { pub gql_client: reqwest::Client, @@ -69,80 +15,79 @@ pub struct Indexer { } impl Indexer { + /// graphql query without error management + async fn query<T: GraphQLQuery>( + &self, + var: <T as GraphQLQuery>::Variables, + ) -> <T as GraphQLQuery>::ResponseData { + post_graphql::<T, _>(&self.gql_client, &self.gql_url, var) + .await + .expect("indexer connexion error") + .data + .expect("indexer error") + } + /// index → name - pub async fn username_by_index(&self, index: u32) -> anyhow::Result<Option<String>> { - Ok(post_graphql::<IdentityNameByIndex, _>( - &self.gql_client, - &self.gql_url, - identity_name_by_index::Variables { - index: index.into(), - }, - ) - .await? - .data - .and_then(move |mut data| data.identities.pop().map(|idty| idty.name))) + pub async fn username_by_index(&self, index: u32) -> Option<String> { + self.query::<IdentityNameByIndex>(identity_name_by_index::Variables { + index: index.into(), + }) + .await + .identities + .pop() + .map(|idty| idty.name) + } + + /// index → name (multiple) + pub async fn names_by_indexes(&self, indexes: &[IdtyId]) -> Vec<(IdtyId, String)> { + self.query::<NamesByIndexes>(names_by_indexes::Variables { + indexes: indexes.iter().map(|i| *i as i64).collect(), + }) + .await + .identities + .into_iter() + .map(|idty| (idty.index as IdtyId, idty.name)) + .collect() } /// pubkey → name pub async fn username_by_pubkey(&self, pubkey: &str) -> Option<String> { - post_graphql::<IdentityNameByPubkey, _>( - &self.gql_client, - &self.gql_url, - identity_name_by_pubkey::Variables { - pubkey: pubkey.to_string(), - }, - ) + self.query::<IdentityNameByPubkey>(identity_name_by_pubkey::Variables { + pubkey: pubkey.to_string(), + }) .await - .expect("indexer connexion error") - .data - .and_then(move |mut data| data.identities.pop().map(|idty| idty.name)) + .identities + .pop() + .map(|idty| idty.name) } /// pubkey → was name pub async fn wasname_by_pubkey(&self, pubkey: &str) -> Option<String> { - post_graphql::<WasIdentityNameByPubkey, _>( - &self.gql_client, - &self.gql_url, - was_identity_name_by_pubkey::Variables { - pubkey: pubkey.to_string(), - }, - ) - .await - .expect("indexer connexion error") - .data - .and_then(move |data| { - data.account_by_id - .and_then(|mut acc| acc.was_identity.pop()) - .map(|idty| idty.identity.name) + self.query::<WasIdentityNameByPubkey>(was_identity_name_by_pubkey::Variables { + pubkey: pubkey.to_string(), }) + .await + .account_by_id + .and_then(|mut acc| acc.was_identity.pop()) + .map(|idty| idty.identity.name) } /// index → info - pub async fn identity_info(&self, index: u32) -> Option<IdentityInfoIdentities> { - post_graphql::<IdentityInfo, _>( - &self.gql_client, - &self.gql_url, - identity_info::Variables { - index: index.into(), - }, - ) + pub async fn identity_info(&self, index: u32) -> Option<identity_info::IdentityInfoIdentities> { + self.query::<IdentityInfo>(identity_info::Variables { + index: index.into(), + }) .await - .expect("indexer connexion error") - .data - .and_then(move |mut data| data.identities.pop()) + .identities + .pop() } /// fetch latest block pub async fn fetch_latest_block(&self) -> Option<latest_block::LatestBlockBlocks> { - post_graphql::<LatestBlock, _>( - &self.gql_client, - self.gql_url.clone(), - latest_block::Variables {}, - ) - .await - .expect("indexer connexion error") - .data - .and_then(move |mut data| data.blocks.pop()) + self.query::<LatestBlock>(latest_block::Variables {}) + .await + .blocks + .pop() } /// fetch block by number @@ -150,21 +95,16 @@ impl Indexer { &self, number: BlockNumber, ) -> Option<block_by_number::BlockByNumberBlocks> { - post_graphql::<BlockByNumber, _>( - &self.gql_client, - self.gql_url.clone(), - block_by_number::Variables { - number: number.into(), - }, - ) + self.query::<BlockByNumber>(block_by_number::Variables { + number: number.into(), + }) .await - .expect("indexer connexion error") - .data - .and_then(move |mut data| data.blocks.pop()) + .blocks + .pop() } /// fetch genesis hash - // since this is always called before any other indexer request, check errors + // since this is always called before any other indexer request, check errors in a more detailed way pub async fn fetch_genesis_hash(&self) -> Result<Hash, GcliError> { // try to connect to indexer let response = post_graphql::<GenesisHash, _>( diff --git a/src/indexer/queries.rs b/src/indexer/queries.rs new file mode 100644 index 0000000000000000000000000000000000000000..29b657286492dc5c12cc77e7722ecab8adb1c80c --- /dev/null +++ b/src/indexer/queries.rs @@ -0,0 +1,58 @@ +use graphql_client::GraphQLQuery; +use sp_core::Bytes; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "res/indexer-schema.json", + query_path = "res/indexer-queries.graphql" +)] +pub struct IdentityNameByIndex; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "res/indexer-schema.json", + query_path = "res/indexer-queries.graphql" +)] +pub struct IdentityInfo; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "res/indexer-schema.json", + query_path = "res/indexer-queries.graphql" +)] +pub struct IdentityNameByPubkey; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "res/indexer-schema.json", + query_path = "res/indexer-queries.graphql" +)] +pub struct WasIdentityNameByPubkey; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "res/indexer-schema.json", + query_path = "res/indexer-queries.graphql" +)] +pub struct LatestBlock; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "res/indexer-schema.json", + query_path = "res/indexer-queries.graphql" +)] +pub struct BlockByNumber; + +#[derive(GraphQLQuery, Debug)] +#[graphql( + schema_path = "res/indexer-schema.json", + query_path = "res/indexer-queries.graphql" +)] +pub struct GenesisHash; + +#[derive(GraphQLQuery, Debug)] +#[graphql( + schema_path = "res/indexer-schema.json", + query_path = "res/indexer-queries.graphql" +)] +pub struct NamesByIndexes; diff --git a/src/main.rs b/src/main.rs index 98458a9e5e55994720c5012ec81cb9bba7a70b39..f6571baaf2746611be5f40814d282f865b620956 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -mod cache; mod commands; mod conf; mod data;