use crate::indexer::*; use crate::*; use crate::commands::revocation::generate_revoc_doc; use crate::runtime::runtime_types::common_runtime::entities::IdtyData; use crate::runtime::runtime_types::pallet_identity::types::*; use crate::runtime::runtime_types::sp_core::sr25519::Signature; use crate::runtime::runtime_types::sp_runtime::MultiSignature; use anyhow::{anyhow, Result}; use sp_core::{crypto::AccountId32, sr25519::Pair}; use std::str::FromStr; use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner}; pub async fn get_identity( client: &Client, mut account_id: Option<AccountId32>, mut identity_id: Option<u32>, mut username: Option<String>, args: &Args, ) -> Result<()> { // build indexer if not disabled let indexer = if args.no_indexer { None } else { Some(Indexer { gql_client: reqwest::Client::builder() .user_agent("gcli/0.1.0") .build()?, gql_url: args.indexer.clone(), }) }; // fetch missing information match (&account_id, identity_id, &username) { (None, Some(identity_id), None) => { account_id = get_identity_by_index(client, identity_id) .await? .map(|idty| idty.owner_key); } (Some(account_id), None, None) => { identity_id = get_idty_index_by_account_id(client, account_id).await?; } (None, None, Some(username)) => { let indexer = indexer.as_ref().ok_or(anyhow!( "Cannot fetch identity from username without indexer." ))?; if let Some(pubkey) = indexer.pubkey_by_username(username).await? { // convert string to accountid let fetched_account_id = AccountId32::from_str(&pubkey).map_err(|e| anyhow!(e))?; // in the future, also ask indexer the identity index identity_id = get_idty_index_by_account_id(client, &fetched_account_id).await?; account_id = Some(fetched_account_id); } else { return Err(anyhow!("no identity found for this username")); } } _ => { return Err(anyhow!( "One and only one argument is needed to fetch the identity." )); } }; // print result println!( "Account id: {}", account_id .as_ref() .map_or(String::new(), AccountId32::to_string) ); println!( "Identity id: {}", identity_id.map_or(String::new(), |identity_id| format!("{identity_id}")) ); if let (Some(indexer), Some(account_id), None) = (&indexer, &account_id, &username) { username = indexer.username_by_pubkey(&account_id.to_string()).await?; } println!("Username: {}", username.unwrap_or_default()); Ok(()) } pub async fn get_idty_index_by_account_id( client: &Client, account_id: &AccountId32, ) -> Result<Option<u32>> { Ok(client .storage() .fetch( &runtime::storage().identity().identity_index_of(account_id), None, ) .await?) } pub async fn get_identity_by_index( client: &Client, idty_index: u32, ) -> Result<Option<IdtyValue<u32, AccountId32, IdtyData>>> { Ok(client .storage() .fetch(&runtime::storage().identity().identities(idty_index), None) .await?) } pub async fn create_identity( pair: Pair, client: &Client, target: AccountId32, ) -> Result<TxProgress, subxt::Error> { client .tx() .sign_and_submit_then_watch( &runtime::tx().identity().create_identity(target), &PairSigner::new(pair), BaseExtrinsicParamsBuilder::new(), ) .await } pub async fn confirm_identity( pair: Pair, client: &Client, name: String, ) -> Result<TxProgress, subxt::Error> { client .tx() .sign_and_submit_then_watch( &runtime::tx().identity().confirm_identity(name), &PairSigner::new(pair), BaseExtrinsicParamsBuilder::new(), ) .await } /// generate revokation document and submit it immediately pub async fn revoke_identity(data: Data) -> Result<TxProgress, subxt::Error> { let (_payload, signature) = generate_revoc_doc(&data); // Transform signature to MultiSignature // TODO: this is a hack, we should be able to use the signature directly let signature = Signature(signature.0); let multisign = MultiSignature::Sr25519(signature); data.client() .tx() .sign_and_submit_then_watch( &runtime::tx() .identity() .revoke_identity(data.idty_index(), data.address(), multisign), &PairSigner::new(data.keypair()), BaseExtrinsicParamsBuilder::new(), ) .await }