From cf7a42926192e3fbcaa941bb5b36de9618c9b78e Mon Sep 17 00:00:00 2001 From: Nicolas80 <nicolas.pmail@protonmail.com> Date: Tue, 31 Dec 2024 13:49:43 +0100 Subject: [PATCH] * Adapted to have more coherent commands descriptions and added `long_about` more detailed description for `import` and `derivation` * Added the `vault list for` command * Added display of "format" of <Root> derivations in all `vault list` commands * Only allowing derivations for format "substrate" and "seed" --- src/commands/vault.rs | 127 ++++++++++++++++++++++++++++++++++-------- src/conf.rs | 2 +- src/main.rs | 12 ++-- 3 files changed, 110 insertions(+), 31 deletions(-) diff --git a/src/commands/vault.rs b/src/commands/vault.rs index 924bbd2..dca4949 100644 --- a/src/commands/vault.rs +++ b/src/commands/vault.rs @@ -13,10 +13,10 @@ use std::path::PathBuf; /// define universal dividends subcommands #[derive(Clone, Debug, clap::Parser)] pub enum Subcommand { - /// List available keys + /// List available derivations #[clap(subcommand)] List(ListChoice), - /// Use specific vault key (changes the config address) + /// Use specific derivation (changes the config Address) Use { #[clap(flatten)] address_or_vault_name: AddressOrVaultNameGroup, @@ -24,29 +24,40 @@ pub enum Subcommand { /// Generate a mnemonic Generate, /// Import key from (substrate)mnemonic or other format with interactive prompt + #[clap( + long_about = "Import key from (substrate)mnemonic or other format with interactive prompt\n\ + \n\ + If a (substrate)mnemonic is provided with a derivation path, it will ensure the <Root> derivation\n\ + and associated key exists before creating the new derivation; but please use command \n\ + `vault derivation|derive|deriv` to add a derivation to an existing <Root> derivation instead." + )] Import { /// Secret key format (substrate, seed, cesium) #[clap(short = 'S', long, required = false, default_value = SecretFormat::Substrate)] secret_format: SecretFormat, }, - /// Add a derivation to an existing (root) vault key + /// Add a derivation to an existing <Root> derivation + #[clap(long_about = "Add a derivation to an existing <Root> derivation\n\ + \n\ + Only \"substrate\" and \"seed\" format are supported for derivations\n\ + Use command `vault list root` to see available <Root> derivations and their format")] #[clap(alias = "deriv")] #[clap(alias = "derive")] Derivation { #[clap(flatten)] address_or_vault_name: AddressOrVaultNameGroup, }, - /// Give a meaningful vault name to a vault key or derivation + /// Give a meaningful derivation name to a key derivation (can be for a <Root> derivation) Rename { - /// key SS58 Address + /// SS58 Address address: AccountId, }, - /// Remove a vault key (and potential derivations if it's a root key) + /// Remove a derivation (and all linked derivations along with the key if a <Root> derivation is given) Remove { #[clap(flatten)] address_or_vault_name: AddressOrVaultNameGroup, }, - /// (deprecated)List available key files (needs to be migrated with command "vault migrate" in order to use them) + /// (deprecated)List available key files (needs to be migrated with command `vault migrate` in order to use them) ListFiles, /// (deprecated)Migrate old key files into db (will have to provide password for each key) Migrate, @@ -56,10 +67,15 @@ pub enum Subcommand { #[derive(Clone, Default, Debug, clap::Parser)] pub enum ListChoice { - /// List all keys and derivations in the vault + /// List all <Root> derivations and their linked derivations in the vault #[default] All, - /// List only root keys + /// List all derivations linked to the one selected + For { + #[clap(flatten)] + address_or_vault_name: AddressOrVaultNameGroup, + }, + /// List only <Root> derivations in the vault Root, } @@ -107,9 +123,13 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE ) .await?; - let table = compute_vault_derivations_table(&derivations).await?; + let table = compute_vault_derivations_table( + data.connection.as_ref().unwrap(), + &derivations, + ) + .await?; - println!("available keys:"); + println!("available derivations:"); println!("{table}"); } ListChoice::Root => { @@ -118,9 +138,34 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE ) .await?; - let table = compute_vault_derivations_table(&derivations).await?; + let table = compute_vault_derivations_table( + data.connection.as_ref().unwrap(), + &derivations, + ) + .await?; + + println!("available <Root> derivations:"); + println!("{table}"); + } + ListChoice::For { + address_or_vault_name, + } => { + let root_derivation = + retrieve_vault_derivation(&data, address_or_vault_name).await?; + + let linked_derivations = vault_derivation::fetch_all_linked_derivations_in_order( + data.connection.as_ref().unwrap(), + &root_derivation.root_address, + ) + .await?; + + let table = compute_vault_derivations_table( + data.connection.as_ref().unwrap(), + &linked_derivations, + ) + .await?; - println!("available root keys:"); + println!("available derivations linked to {root_derivation}:"); println!("{table}"); } }, @@ -176,7 +221,11 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE .await?; println!("Here are all the linked derivations already present in the vault:"); - let table = compute_vault_derivations_table(&linked_derivations).await?; + let table = compute_vault_derivations_table( + data.connection.as_ref().unwrap(), + &linked_derivations, + ) + .await?; println!("{table}"); return Ok(()); @@ -225,6 +274,16 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE root_derivation.address )))?; + if vault_account.crypto_type == CryptoType::Ed25519Seed { + println!( + "Only \"{}\" and \"{}\" format are supported for derivations", + Into::<&str>::into(SecretFormat::Substrate), + Into::<&str>::into(SecretFormat::Seed) + ); + println!("Use command `vault list root` to see available <Root> derivations and their format"); + return Ok(()); + } + println!("Adding derivation to root key: {root_derivation}"); println!("Enter password to decrypt the root key"); @@ -308,7 +367,8 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE ) .await?; - let table = compute_vault_derivations_table(&all_derivations_to_delete).await?; + let table = + compute_vault_derivations_table(&txn, &all_derivations_to_delete).await?; println!("All derivations linked to the root derivation:"); println!("{table}"); @@ -468,17 +528,22 @@ async fn compute_vault_key_files_table(vault_key_addresses: &[String]) -> Result Ok(table) } -async fn compute_vault_derivations_table( +async fn compute_vault_derivations_table<C>( + db: &C, derivations_ordered: &[vault_derivation::Model], -) -> Result<Table, GcliError> { +) -> Result<Table, GcliError> +where + C: ConnectionTrait, +{ let mut table = Table::new(); table.load_preset(comfy_table::presets::UTF8_BORDERS_ONLY); - table.set_header(vec!["SS58 Address", "Path", "Vault Name"]); - let empty_name = "".to_string(); + table.set_header(vec!["SS58 Address", "Format", "Path", "Derivation Name"]); + let empty_string = "".to_string(); let root_path = "<Root>".to_string(); let mut current_root_address = "".to_string(); let mut current_root_name: Option<String> = None; + let mut current_vault_format: Option<&str> = None; for derivation in derivations_ordered { if derivation.root_address != current_root_address { @@ -490,6 +555,19 @@ async fn compute_vault_derivations_table( } current_root_address = derivation.root_address.clone(); current_root_name = derivation.name.clone(); + + let vault_account = vault_account::Entity::find_by_id(current_root_address.clone()) + .one(db) + .await? + .ok_or(GcliError::Input(format!( + "No vault account found with address:'{current_root_address}'" + )))?; + + current_vault_format = match vault_account.crypto_type { + CryptoType::Sr25519Mnemonic => Some(SecretFormat::Substrate.into()), + CryptoType::Sr25519Seed => Some(SecretFormat::Seed.into()), + CryptoType::Ed25519Seed => Some(SecretFormat::Cesium.into()), + }; } else { // Validate that the RootAddress is the same as current_root_address if derivation.root_address != current_root_address { @@ -505,15 +583,15 @@ async fn compute_vault_derivations_table( " ".to_string() + &derivation.address }; - let path = if derivation.path.is_none() { - root_path.clone() + let (path, format) = if derivation.path.is_none() { + (root_path.clone(), current_vault_format.unwrap()) } else { - derivation.path.clone().unwrap() + (derivation.path.clone().unwrap(), empty_string.as_str()) }; let name = if derivation.name.is_none() { if derivation.path.is_none() { - empty_name.clone() + empty_string.clone() } else if let Some(current_root_name) = ¤t_root_name { format!( "<{}{}>", @@ -521,7 +599,7 @@ async fn compute_vault_derivations_table( derivation.path.clone().unwrap() ) } else { - empty_name.clone() + empty_string.clone() } } else { derivation.name.clone().unwrap() @@ -529,6 +607,7 @@ async fn compute_vault_derivations_table( table.add_row(vec![ Cell::new(&address), + Cell::new(format), Cell::new(&path), Cell::new(&name), ]); diff --git a/src/conf.rs b/src/conf.rs index a6f6ff7..2310958 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -91,7 +91,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE ) .await? { - println!("(Vault entry: {})", derivation); + println!("(Vault derivation: {})", derivation); } } } diff --git a/src/main.rs b/src/main.rs index 295776e..75ee885 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,11 +78,11 @@ trait AddressOrVaultName { #[derive(Debug, clap::Args, Clone)] #[group(required = false, multiple = false)] pub struct AddressOrVaultNameGroupOptional { - /// key SS58 Address + /// SS58 Address #[clap(short)] address: Option<AccountId>, - /// Vault name for a key - #[clap(short = 'v')] + /// Derivation name for a SS58 Address (can be a <Root> derivation name) + #[clap(short = 'd')] name: Option<String>, } @@ -98,11 +98,11 @@ impl AddressOrVaultName for AddressOrVaultNameGroupOptional { #[derive(Debug, clap::Args, Clone)] #[group(required = true, multiple = false)] pub struct AddressOrVaultNameGroup { - /// key SS58 Address + /// SS58 Address #[clap(short)] address: Option<AccountId>, - /// Vault name for a key - #[clap(short = 'v')] + /// Derivation name for a SS58 Address (can be a <Root> derivation name) + #[clap(short = 'd')] name: Option<String>, } -- GitLab