Newer
Older
use comfy_table::*;
use comfy_table::{ContentArrangement, Table};
use graphql_client::reqwest::post_graphql;
use graphql_client::GraphQLQuery;
use queries::*;
// use sp_core::Bytes;
/// graphql query without error management
async fn query<T: GraphQLQuery>(
&self,
var: <T as GraphQLQuery>::Variables,
) -> <T as GraphQLQuery>::ResponseData {
let response = post_graphql::<T, _>(&self.gql_client, &self.gql_url, var)
.expect("indexer connexion error");
if let Some(errs) = response.errors {
log::debug!("{:?}", errs)
}
response.data.expect("indexer error")
pub async fn username_by_index(&self, index: u32) -> Option<String> {
self.query::<IdentityNameByIndex>(identity_name_by_index::Variables {
index: index.into(),
})
.await
.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
.into_iter()
.map(|idty| (idty.index as IdtyId, idty.name))
.collect()
pub async fn username_by_pubkey(&self, pubkey: &str) -> Option<String> {
self.query::<IdentityNameByPubkey>(identity_name_by_pubkey::Variables {
pubkey: pubkey.to_string(),
})
.pop()
.map(|idty| idty.name)
}
/// pubkey → was name
pub async fn wasname_by_pubkey(&self, pubkey: &str) -> Option<String> {
self.query::<WasIdentityNameByPubkey>(was_identity_name_by_pubkey::Variables {
pubkey: pubkey.to_string(),
.and_then(|mut acc| acc.was_identity.pop())
.map(|idty| idty.identity.unwrap().name)
pub async fn identity_info(&self, index: u32) -> Option<identity_info::IdentityInfoIdentity> {
self.query::<IdentityInfo>(identity_info::Variables {
index: index.into(),
})
pub async fn fetch_latest_block(&self) -> Option<latest_block::LatestBlockBlock> {
self.query::<LatestBlock>(latest_block::Variables {})
.await
}
/// fetch block by number
pub async fn fetch_block_by_number(
&self,
number: BlockNumber,
) -> Option<block_by_number::BlockByNumberBlock> {
self.query::<BlockByNumber>(block_by_number::Variables {
number: number.into(),
})
// 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, _>(
&self.gql_client,
self.gql_url.clone(),
genesis_hash::Variables {},
)
dbg!(_e); // for more info
GcliError::Indexer(format!("can not connect to indexer {}", &self.gql_url))
})?;
// debug errors if any
response.errors.map_or_else(Vec::new, |e| dbg!(e));
// extract hash
let hash = response
.data
.ok_or(GcliError::Indexer(
"no field 'data' when getting genesis hash".to_string(),
))?
.ok_or_else(|| GcliError::Indexer("genesis block not yet indexed".to_string()))?
.hash
.clone();
// convert it
Ok(convert_hash_bytea(hash))
/// convert indexer bytes into hash
// pub fn convert_hash(hash: Bytes) -> Hash {
// let hash = TryInto::<[u8; 32]>::try_into(hash.as_ref()).unwrap();
// hash.into()
// }
/// convert indexer bytes into hash
pub fn convert_hash_bytea(hash: queries::Bytea) -> Hash {
let hash = TryInto::<[u8; 32]>::try_into(hash.bytes.as_ref()).unwrap();
#[derive(Clone, Default, Debug, clap::Parser)]
/// Check that indexer and node are on the same network
/// (genesis hash, latest indexed block...)
pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError> {
// build indexer because it is needed for all subcommands
let data = data.build_client().await?.build_indexer().await?;
let indexer = data
.indexer
.clone()
.ok_or_else(|| GcliError::Logic("indexer needed for this command".to_string()))?;
let d_url = &data.cfg.duniter_endpoint;
let i_url = &indexer.gql_url;
let d_gen_hash = &data.genesis_hash.to_string();
let i_gen_hash = &data.indexer_genesis_hash.to_string();
let (d_finalized_n, d_finalized_h) =
commands::blockchain::fetch_finalized_number_and_hash(&data).await?;
let i_finalized_block = indexer.fetch_block_by_number(d_finalized_n).await;
let (i_finalized_h, i_finalized_n) = if let Some(block) = i_finalized_block {
(Some(convert_hash_bytea(block.hash)), Some(block.height))
(None, None)
};
let (d_latest_n, d_latest_h) =
commands::blockchain::fetch_latest_number_and_hash(&data).await?;
let i_latest_block = indexer.fetch_latest_block().await.expect("no latest block");
let i_latest_h = convert_hash_bytea(i_latest_block.hash);
let i_latest_n = i_latest_block.height;
fn color(x: bool) -> Color {
match x {
true => Color::Green,
false => Color::Red,
}
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
let mut table = Table::new();
table
.load_preset(presets::UTF8_FULL)
.apply_modifier(modifiers::UTF8_ROUND_CORNERS)
.set_content_arrangement(ContentArrangement::Dynamic)
.set_width(120)
.set_header(vec!["Variable", "Duniter", "Indexer"])
.add_row(vec!["URL", d_url, i_url])
.add_row(vec![
Cell::new("genesis hash"),
Cell::new(d_gen_hash),
Cell::new(i_gen_hash).fg(color(d_gen_hash == i_gen_hash)),
])
.add_row(vec![
Cell::new("finalized block number"),
Cell::new(d_finalized_n),
match i_finalized_n {
None => Cell::new("not indexed").fg(Color::Yellow),
Some(n) => {
// if it exists, it must be the same
assert_eq!(n, d_finalized_n as i64);
Cell::new("")
}
},
])
.add_row(vec![
Cell::new("finalized block hash"),
Cell::new(d_finalized_h),
match i_finalized_h {
// number already tells it is not indexed
None => Cell::new(""),
Some(h) => Cell::new(h).fg(color(h == d_finalized_h)),
},
])
.add_row(vec![
Cell::new("latest block number"),
Cell::new(d_latest_n),
Cell::new(i_latest_n).fg(color(i_latest_n == d_latest_n as i64)),
])
.add_row(vec![
Cell::new("latest block hash"),
Cell::new(d_latest_h),
Cell::new(i_latest_h).fg(color(i_latest_h == d_latest_h)),
]);
println!("{table}");