Skip to content
Snippets Groups Projects
identity.rs 6.11 KiB
Newer Older
Hugo Trentesaux's avatar
Hugo Trentesaux committed
use crate::*;
Hugo Trentesaux's avatar
Hugo Trentesaux committed
use crate::commands::revocation::generate_revoc_doc;
Hugo Trentesaux's avatar
Hugo Trentesaux committed
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 sp_core::{crypto::AccountId32, sr25519::Pair};
use std::str::FromStr;

/// define identity subcommands
#[derive(Clone, Default, Debug, clap::Parser)]
pub enum Subcommand {
	/// show identity
	#[default]
	Show,
	/// Fetch identity
	Get {
		#[clap(short = 'a', long = "address")]
		account_id: Option<AccountId>,
		#[clap(short = 'i', long = "identity")]
		identity_id: Option<u32>,
		#[clap(short = 'u', long = "username")]
		username: Option<String>,
	},
	/// Create and certify an identity
	///
	/// Caller must be member, and the target account must exist.
	Create { target: AccountId },
	/// Confirm an identity
	///
	/// To be called by the certified not-yet-member account, to become member.
	Confirm { name: String },
	/// Revoke an identity immediately
	Revoke,
}

/// handle identity commands
pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<()> {
	let mut data = data.build_client().await?.build_indexer().await?;
	match command {
		Subcommand::Show => {}
		Subcommand::Get {
			ref account_id,
			identity_id,
			ref username,
		} => {
			commands::identity::get_identity(
				&data,
				account_id.clone(),
				identity_id,
				username.clone(),
			)
			.await?
		}
		Subcommand::Create { target } => {
			data = data.build_keypair();
			commands::identity::create_identity(data.keypair(), data.client(), target).await?;
		}
		Subcommand::Confirm { name } => {
			data = data.build_keypair();
			commands::identity::confirm_identity(data.keypair(), data.client(), name).await?;
		}
		Subcommand::Revoke => {
			data = data.build_keypair().fetch_idty_index().await?;
			commands::identity::revoke_identity(data).await?;
		}
	};

	Ok(())
}

// ======================

/// get identity
pub async fn get_identity(
	data: &Data,
	mut account_id: Option<AccountId32>,
	mut identity_id: Option<u32>,
	mut username: Option<String>,
) -> Result<(), anyhow::Error> {
	let client = data.client();
	let indexer = data.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("<no indexer>".to_string()));
	Ok(())
/// get identity index by account id
pub async fn get_idty_index_by_account_id(
Hugo Trentesaux's avatar
Hugo Trentesaux committed
	client: &Client,
	account_id: &AccountId32,
) -> Result<Option<u32>, anyhow::Error> {
	Ok(client
		.storage()
		.fetch(
Hugo Trentesaux's avatar
Hugo Trentesaux committed
			&runtime::storage().identity().identity_index_of(account_id),
/// get identityt value by index
pub async fn get_identity_by_index(
Hugo Trentesaux's avatar
Hugo Trentesaux committed
	client: &Client,
	idty_index: u32,
) -> Result<Option<IdtyValue<u32, AccountId32, IdtyData>>, anyhow::Error> {
	Ok(client
		.storage()
Hugo Trentesaux's avatar
Hugo Trentesaux committed
		.fetch(&runtime::storage().identity().identities(idty_index), None)
/// created identity
Hugo Trentesaux's avatar
Hugo Trentesaux committed
pub async fn create_identity(
	pair: Pair,
Hugo Trentesaux's avatar
Hugo Trentesaux committed
	client: &Client,
Hugo Trentesaux's avatar
Hugo Trentesaux committed
	target: AccountId32,
) -> Result<(), subxt::Error> {
	let progress = client
		.tx()
		.sign_and_submit_then_watch(
Hugo Trentesaux's avatar
Hugo Trentesaux committed
			&runtime::tx().identity().create_identity(target),
			&PairSigner::new(pair),
			BaseExtrinsicParamsBuilder::new(),
		)
		.await?;

	let events = track_progress(progress).await?;
	if let Some(e) = events.find_first::<runtime::identity::events::IdtyCreated>()? {
		println!("{e:?}");
	}
	Ok(())
/// confirm identity
Hugo Trentesaux's avatar
Hugo Trentesaux committed
pub async fn confirm_identity(
	pair: Pair,
Hugo Trentesaux's avatar
Hugo Trentesaux committed
	client: &Client,
Hugo Trentesaux's avatar
Hugo Trentesaux committed
	name: String,
) -> Result<(), subxt::Error> {
	let progress = client
		.tx()
		.sign_and_submit_then_watch(
Hugo Trentesaux's avatar
Hugo Trentesaux committed
			&runtime::tx().identity().confirm_identity(name),
			&PairSigner::new(pair),
			BaseExtrinsicParamsBuilder::new(),
		)
		.await?;

	let events = track_progress(progress).await?;
	if let Some(e) = events.find_first::<runtime::identity::events::IdtyConfirmed>()? {
		println!("{e:?}");
	}
	Ok(())
Hugo Trentesaux's avatar
Hugo Trentesaux committed
/// generate revokation document and submit it immediately
pub async fn revoke_identity(data: Data) -> Result<(), subxt::Error> {
Hugo Trentesaux's avatar
Hugo Trentesaux committed
	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);

	let progress = data
		.client()
		.tx()
		.sign_and_submit_then_watch(
			&runtime::tx()
				.identity()
Hugo Trentesaux's avatar
Hugo Trentesaux committed
				.revoke_identity(data.idty_index(), data.address(), multisign),
			&PairSigner::new(data.keypair()),
			BaseExtrinsicParamsBuilder::new(),
		)
		.await?;

	let events = track_progress(progress).await?;
	if let Some(e) = events.find_first::<runtime::identity::events::IdtyRemoved>()? {
		println!("{e:?}");
	}
	Ok(())