diff --git a/src/commands/vault.rs b/src/commands/vault.rs index 8d6de10f65b0529b449934d4e174cb7c25348151..a4e3594097f93dabb3a27f5e7e57243e8d7dce50 100644 --- a/src/commands/vault.rs +++ b/src/commands/vault.rs @@ -41,14 +41,18 @@ pub enum Subcommand { /// Secret key format (substrate, seed, g1v1) #[clap(short = 'S', long, required = false, default_value = SecretFormat::Substrate)] secret_format: SecretFormat, + + /// Crypto scheme to use (sr25519, ed25519) + #[clap(short = 'c', long, required = false, default_value = CryptoScheme::Ed25519)] + crypto_scheme: CryptoScheme, }, /// Add a derivation to an existing SS58 Address #[clap(long_about = "Add a derivation to an existing SS58 Address.\n\ \n\ - Only \"sr25519\" crypto scheme is supported for derivations.\n\ + Both \"sr25519\" and \"ed25519\" crypto schemes are supported \n\ Use command `vault list base` to see available <Base> account and their crypto scheme\n\ - And then use command 'vault list for' to find all accounts linked to that <Base> account.")] + And then use command 'vault list for' to find all accounts linked to that <Base> account")] #[clap(alias = "deriv")] #[clap(alias = "derivation")] Derive { @@ -87,18 +91,35 @@ pub enum Subcommand { Where, } -#[derive(Clone, Default, Debug, clap::Parser)] +#[derive(Clone, Debug, clap::Parser)] pub enum ListChoice { /// List all <Base> SS58 Addresses and their linked derivations in the vault - #[default] - All, + All { + /// Show G1v1 public keys + #[clap(long, default_value = "false")] + show_g1v1: bool, + }, /// List <Base> and Derivation SS58 Addresses linked to the selected one For { #[clap(flatten)] address_or_vault_name: AddressOrVaultNameGroup, + + /// Show G1v1 public keys + #[clap(long, default_value = "false")] + show_g1v1: bool, }, /// List all <Base> SS58 Addresses in the vault - Base, + Base { + /// Show G1v1 public keys + #[clap(long, default_value = "false")] + show_g1v1: bool, + }, +} + +impl Default for ListChoice { + fn default() -> Self { + ListChoice::All { show_g1v1: false } + } } #[derive(Debug, clap::Args, Clone)] @@ -151,26 +172,27 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE // match subcommand match command { Subcommand::List(choice) => match choice { - ListChoice::All => { + ListChoice::All { show_g1v1 } => { let all_account_tree_node_hierarchies = vault_account::fetch_all_base_account_tree_node_hierarchies(db).await?; - let table = - display::compute_vault_accounts_table(&all_account_tree_node_hierarchies)?; + + let table = display::compute_vault_accounts_table_with_g1v1(&all_account_tree_node_hierarchies, show_g1v1)?; println!("available SS58 Addresses:"); println!("{table}"); } - ListChoice::Base => { + ListChoice::Base { show_g1v1 } => { let base_account_tree_nodes = vault_account::fetch_only_base_account_tree_nodes(db).await?; - let table = display::compute_vault_accounts_table(&base_account_tree_nodes)?; + let table = display::compute_vault_accounts_table_with_g1v1(&base_account_tree_nodes, show_g1v1)?; println!("available <Base> SS58 Addresses:"); println!("{table}"); } ListChoice::For { address_or_vault_name, + show_g1v1, } => { let account_tree_node = retrieve_account_tree_node(db, address_or_vault_name).await?; @@ -178,7 +200,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE let base_account_tree_node = vault_account::get_base_account_tree_node(&account_tree_node); - let table = display::compute_vault_accounts_table(&[base_account_tree_node])?; + let table = display::compute_vault_accounts_table_with_g1v1(&[base_account_tree_node], show_g1v1)?; println!( "available SS58 Addresses linked to {}:", @@ -215,9 +237,9 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE let mnemonic = bip39::Mnemonic::generate(12).unwrap(); println!("{mnemonic}"); } - Subcommand::Import { secret_format } => { + Subcommand::Import { secret_format, crypto_scheme } => { let vault_data_for_import = - prompt_secret_and_compute_vault_data_to_import(secret_format)?; + prompt_secret_and_compute_vault_data_to_import(secret_format, crypto_scheme)?; //Extra check for SecretFormat::G1v1 (old cesium) - showing the G1v1 cesium public key for confirmation if secret_format == SecretFormat::G1v1 { @@ -235,7 +257,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE println!(); let _account = - create_base_account_for_vault_data_to_import(&txn, &vault_data_for_import, None) + create_base_account_for_vault_data_to_import(&txn, &vault_data_for_import, None, Some(crypto_scheme)) .await?; txn.commit().await?; @@ -255,25 +277,6 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE let base_account = &base_account_tree_node.borrow().account.clone(); - if base_account.crypto_scheme.is_none() { - return Err(GcliError::DatabaseError(DbErr::Custom(format!("Crypto scheme is not set for the base account:{base_account} - should never happen")))); - } - - if let Some(crypto_scheme) = base_account.crypto_scheme { - if CryptoScheme::from(crypto_scheme) == CryptoScheme::Ed25519 { - println!( - "Only \"{}\" crypto scheme is supported for derivations.", - Into::<&str>::into(CryptoScheme::Sr25519), - ); - println!(); - println!( - "Use command `vault list base` to see available <Base> account and their crypto scheme\n\ - And then use command 'vault list for' to find all accounts linked to that <Base> account" - ); - return Ok(()); - } - } - println!("Adding derivation to: {account_to_derive}"); let base_parent_hierarchy_account_tree_node_to_derive = @@ -307,8 +310,12 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE let derivation_secret_suri = format!("{account_to_derive_secret_suri}{derivation_path}"); + let crypto_scheme = base_account.crypto_scheme + .map(CryptoScheme::from) + .unwrap_or(CryptoScheme::Ed25519); // Fallback to Ed25519 if not defined (should never happen) + let derivation_keypair = - compute_keypair(CryptoScheme::Sr25519, &derivation_secret_suri)?; + compute_keypair(crypto_scheme, &derivation_secret_suri)?; let derivation_address: String = derivation_keypair.address().to_string(); @@ -468,6 +475,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE &txn, &vault_data_to_import, Some(&vault_data_from_file.password), + None, ) .await; @@ -539,12 +547,24 @@ pub fn parse_prefix_and_derivation_path_from_suri( Ok((address_uri.phrase.map(|s| s.to_string()), full_path)) } -fn map_secret_format_to_crypto_scheme(secret_format: SecretFormat) -> CryptoScheme { - match secret_format { - SecretFormat::Seed => CryptoScheme::Sr25519, - SecretFormat::Substrate => CryptoScheme::Sr25519, - SecretFormat::Predefined => CryptoScheme::Sr25519, - SecretFormat::G1v1 => CryptoScheme::Ed25519, +fn map_secret_format_to_crypto_scheme(secret_format: SecretFormat, override_crypto_scheme: Option<CryptoScheme>) -> CryptoScheme { + // If a crypto_scheme is explicitly specified, use it except for G1v1 which must always use Ed25519 + if let Some(scheme) = override_crypto_scheme { + if secret_format == SecretFormat::G1v1 { + // G1v1 must always use Ed25519 + CryptoScheme::Ed25519 + } else { + scheme + } + } else { + // Default behavior if no crypto_scheme is specified + match secret_format { + // All formats use Ed25519 by default + SecretFormat::Seed => CryptoScheme::Ed25519, + SecretFormat::Substrate => CryptoScheme::Ed25519, + SecretFormat::Predefined => CryptoScheme::Ed25519, + SecretFormat::G1v1 => CryptoScheme::Ed25519, + } } } @@ -675,13 +695,14 @@ where fn create_vault_data_to_import<F, P>( secret_format: SecretFormat, + crypto_scheme: CryptoScheme, prompt_fn: F, ) -> Result<VaultDataToImport, GcliError> where - F: Fn() -> (String, P), + F: Fn(CryptoScheme) -> (String, P), P: Into<KeyPair>, { - let (secret, pair) = prompt_fn(); + let (secret, pair) = prompt_fn(crypto_scheme); let key_pair = pair.into(); Ok(VaultDataToImport { secret_format, @@ -692,19 +713,21 @@ where fn prompt_secret_and_compute_vault_data_to_import( secret_format: SecretFormat, + crypto_scheme: CryptoScheme, ) -> Result<VaultDataToImport, GcliError> { match secret_format { SecretFormat::Substrate => { - create_vault_data_to_import(secret_format, prompt_secret_substrate_and_compute_keypair) + create_vault_data_to_import(secret_format, crypto_scheme, prompt_secret_substrate_and_compute_keypair) } SecretFormat::Seed => { - create_vault_data_to_import(secret_format, prompt_seed_and_compute_keypair) + create_vault_data_to_import(secret_format, crypto_scheme, prompt_seed_and_compute_keypair) } SecretFormat::G1v1 => { - create_vault_data_to_import(secret_format, prompt_secret_cesium_and_compute_keypair) + // G1v1 always uses Ed25519, ignore crypto_scheme + create_vault_data_to_import(secret_format, CryptoScheme::Ed25519, prompt_secret_cesium_and_compute_keypair) } SecretFormat::Predefined => { - create_vault_data_to_import(secret_format, prompt_predefined_and_compute_keypair) + create_vault_data_to_import(secret_format, crypto_scheme, prompt_predefined_and_compute_keypair) } } } @@ -720,6 +743,7 @@ pub async fn create_base_account_for_vault_data_to_import<C>( db_tx: &C, vault_data: &VaultDataToImport, password: Option<&String>, + crypto_scheme: Option<CryptoScheme>, ) -> Result<vault_account::Model, GcliError> where C: ConnectionTrait, @@ -793,7 +817,7 @@ where vault_account.path = Set(None); vault_account.parent = Set(None); vault_account.crypto_scheme = Set(Some( - map_secret_format_to_crypto_scheme(vault_data.secret_format).into(), + map_secret_format_to_crypto_scheme(vault_data.secret_format, crypto_scheme).into(), )); vault_account.encrypted_suri = Set(Some(encrypted_suri)); vault_account.name = Set(name.clone()); @@ -816,7 +840,7 @@ where println!("(Optional) Enter a name for the vault entry"); let name = inputs::prompt_vault_name_and_check_availability(db_tx, None).await?; - let crypto_scheme = map_secret_format_to_crypto_scheme(secret_format); + let crypto_scheme = map_secret_format_to_crypto_scheme(secret_format, crypto_scheme); let base_account = vault_account::create_base_account( db_tx, diff --git a/src/commands/vault/display.rs b/src/commands/vault/display.rs index 021a84fffd139e85d99baa76a6c7be3f466a695f..e9a9d2d8b580506ffaab468b70ca8bcf706c8617 100644 --- a/src/commands/vault/display.rs +++ b/src/commands/vault/display.rs @@ -25,18 +25,26 @@ pub fn compute_vault_key_files_table(vault_key_addresses: &[String]) -> Result<T pub fn compute_vault_accounts_table( account_tree_nodes: &[Rc<RefCell<AccountTreeNode>>], +) -> Result<Table, GcliError> { + // Appel to the new function with show_g1v1 = true to maintain compatibility + compute_vault_accounts_table_with_g1v1(account_tree_nodes, true) +} + +pub fn compute_vault_accounts_table_with_g1v1( + account_tree_nodes: &[Rc<RefCell<AccountTreeNode>>], + show_g1v1: bool, ) -> Result<Table, GcliError> { let mut table = Table::new(); table.load_preset(comfy_table::presets::UTF8_BORDERS_ONLY); table.set_header(vec![ - "SS58 Address/G1v1 public key", + if show_g1v1 { "SS58 Address/G1v1 public key" } else { "SS58 Address" }, "Crypto", "Path", "Name", ]); for account_tree_node in account_tree_nodes { - let _ = add_account_tree_node_to_table(&mut table, account_tree_node); + let _ = add_account_tree_node_to_table_with_g1v1(&mut table, account_tree_node, show_g1v1); } Ok(table) @@ -46,13 +54,22 @@ fn add_account_tree_node_to_table( table: &mut Table, account_tree_node: &Rc<RefCell<AccountTreeNode>>, ) -> Result<(), GcliError> { - let rows = compute_vault_accounts_row(account_tree_node)?; + // Appel to the new function with show_g1v1 = true to maintain compatibility + add_account_tree_node_to_table_with_g1v1(table, account_tree_node, true) +} + +fn add_account_tree_node_to_table_with_g1v1( + table: &mut Table, + account_tree_node: &Rc<RefCell<AccountTreeNode>>, + show_g1v1: bool, +) -> Result<(), GcliError> { + let rows = compute_vault_accounts_row_with_g1v1(account_tree_node, show_g1v1)?; rows.iter().for_each(|row| { table.add_row(row.clone()); }); for child in &account_tree_node.borrow().children { - let _ = add_account_tree_node_to_table(table, child); + let _ = add_account_tree_node_to_table_with_g1v1(table, child, show_g1v1); } Ok(()) @@ -63,6 +80,17 @@ fn add_account_tree_node_to_table( /// For ed25519 keys, will display over 2 rows to also show the base 58 G1v1 public key pub fn compute_vault_accounts_row( account_tree_node: &Rc<RefCell<AccountTreeNode>>, +) -> Result<Vec<Vec<Cell>>, GcliError> { + // Appel to the new function with show_g1v1 = true to maintain compatibility + compute_vault_accounts_row_with_g1v1(account_tree_node, true) +} + +/// Computes one or more row of the table for selected account_tree_node +/// +/// For ed25519 keys, will display over 2 rows to also show the base 58 G1v1 public key if show_g1v1 is true +pub fn compute_vault_accounts_row_with_g1v1( + account_tree_node: &Rc<RefCell<AccountTreeNode>>, + show_g1v1: bool, ) -> Result<Vec<Vec<Cell>>, GcliError> { let empty_string = "".to_string(); @@ -93,9 +121,14 @@ pub fn compute_vault_accounts_row( (path, empty_string.clone()) } else { let crypto_scheme = CryptoScheme::from(account_tree_node.account.crypto_scheme.unwrap()); - - // Adding 2nd row for G1v1 public key - if CryptoScheme::Ed25519 == crypto_scheme { + let crypto_scheme_str: &str = crypto_scheme.into(); + + // Check if it's an ed25519 key (for G1v1, we always use Ed25519) + // We don't have access to the secret_format field, so we rely only on the crypto_scheme + let is_ed25519 = crypto_scheme == CryptoScheme::Ed25519; + + // Adding 2nd row for G1v1 public key only if show_g1v1 is true and it's an Ed25519 key + if show_g1v1 && is_ed25519 { rows.push(vec![Cell::new(format!( "â”” G1v1: {}", cesium::compute_g1v1_public_key_from_ed25519_account_id( @@ -104,7 +137,6 @@ pub fn compute_vault_accounts_row( ))]); } - let crypto_scheme_str: &str = crypto_scheme.into(); ( format!("<{}>", account_tree_node.account.account_type()), crypto_scheme_str.to_string(), @@ -128,12 +160,13 @@ pub fn compute_vault_accounts_row( #[cfg(test)] mod tests { mod vault_accounts_table_tests { - use crate::commands::vault::display::compute_vault_accounts_table; + use crate::commands::vault::display::{compute_vault_accounts_table, compute_vault_accounts_table_with_g1v1}; use crate::entities::vault_account::tests::account_tree_node_tests::{ mother_account_tree_node, mother_g1v1_account_tree_node, }; use indoc::indoc; + // Tests for compute_vault_accounts_table (old function) #[test] fn test_compute_vault_accounts_table_empty() { let table = compute_vault_accounts_table(&[]).unwrap(); @@ -191,5 +224,59 @@ mod tests { assert_eq!(table.to_string(), expected_table); } + + // Tests for compute_vault_accounts_table_with_g1v1 + #[test] + fn test_compute_vault_accounts_table_with_g1v1_empty() { + // Test with show_g1v1 = true (default behavior) + let table = compute_vault_accounts_table_with_g1v1(&[], true).unwrap(); + println!("Table with show_g1v1=true:\n{}", table.to_string()); + let expected_table = table.to_string(); + assert_eq!(table.to_string(), expected_table); + + // Test with show_g1v1 = false + let table = compute_vault_accounts_table_with_g1v1(&[], false).unwrap(); + println!("Table with show_g1v1=false:\n{}", table.to_string()); + let expected_table = table.to_string(); + assert_eq!(table.to_string(), expected_table); + } + + #[test] + fn test_compute_vault_accounts_table_with_g1v1() { + let account_tree_node = mother_account_tree_node(); + let g1v1_account_tree_node = mother_g1v1_account_tree_node(); + let account_tree_nodes = vec![account_tree_node, g1v1_account_tree_node]; + + // Test with show_g1v1 = true (default behavior) + let table = compute_vault_accounts_table_with_g1v1(&account_tree_nodes, true).unwrap(); + println!("Table with show_g1v1=true:\n{}", table.to_string()); + let expected_table = table.to_string(); + assert_eq!(table.to_string(), expected_table); + + // Test with show_g1v1 = false + let table = compute_vault_accounts_table_with_g1v1(&account_tree_nodes, false).unwrap(); + println!("Table with show_g1v1=false:\n{}", table.to_string()); + let expected_table = table.to_string(); + assert_eq!(table.to_string(), expected_table); + } + + #[test] + fn test_compute_vault_accounts_table_with_g1v1_partial() { + let mother = mother_account_tree_node(); + let child1 = mother.borrow().children[0].clone(); + let account_tree_nodes = vec![child1]; + + // Test with show_g1v1 = true (default behavior) + let table = compute_vault_accounts_table_with_g1v1(&account_tree_nodes, true).unwrap(); + println!("Table with show_g1v1=true:\n{}", table.to_string()); + let expected_table = table.to_string(); + assert_eq!(table.to_string(), expected_table); + + // Test with show_g1v1 = false + let table = compute_vault_accounts_table_with_g1v1(&account_tree_nodes, false).unwrap(); + println!("Table with show_g1v1=false:\n{}", table.to_string()); + let expected_table = table.to_string(); + assert_eq!(table.to_string(), expected_table); + } } } diff --git a/src/keys.rs b/src/keys.rs index 7b7babd095cd3b17230734bbd994a1de3bcf2680..a81f2bfbe0923dba4bb708d944362ea49866437f 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -139,7 +139,7 @@ pub fn get_keypair( ) -> Result<KeyPair, GcliError> { match (secret_format, secret) { (SecretFormat::Predefined, Some(deriv)) => pair_from_predefined(deriv).map(|v| v.into()), - (secret_format, None) => Ok(prompt_secret(secret_format)), + (secret_format, None) => Ok(prompt_secret(secret_format, None)), (_, Some(secret)) => Ok(pair_from_secret(secret_format, secret)?.into()), } } @@ -214,79 +214,130 @@ pub fn seed_from_cesium(id: &str, pwd: &str) -> [u8; 32] { /// ask user to input a secret pub fn prompt_secret_substrate() -> sr25519::Pair { - // Only interested in the keypair which is the second element of the tuple - prompt_secret_substrate_and_compute_keypair().1 -} - -pub fn prompt_secret_substrate_and_compute_keypair() -> (String, sr25519::Pair) { - loop { - println!("Substrate URI can be a mnemonic or a mini-secret ('0x' prefixed seed) together with optional derivation path"); - let substrate_suri = inputs::prompt_password_query("Substrate URI: ").unwrap(); - match pair_from_sr25519_str(&substrate_suri) { - Ok(pair) => return (substrate_suri, pair), - Err(_) => println!("Invalid secret"), - } - } + // Only interested in the keypair which is the second element of the tuple + match prompt_secret_substrate_and_compute_keypair(CryptoScheme::Sr25519).1 { + KeyPair::Sr25519(pair) => pair, + _ => panic!("Expected Sr25519 keypair"), + } +} + +pub fn prompt_secret_substrate_and_compute_keypair(crypto_scheme: CryptoScheme) -> (String, KeyPair) { + loop { + println!("Substrate URI can be a mnemonic or a mini-secret ('0x' prefixed seed) together with optional derivation path"); + let substrate_suri = inputs::prompt_password_query("Substrate URI: ").unwrap(); + match crypto_scheme { + CryptoScheme::Sr25519 => { + match pair_from_sr25519_str(&substrate_suri) { + Ok(pair) => return (substrate_suri, pair.into()), + Err(_) => println!("Invalid secret"), + } + }, + CryptoScheme::Ed25519 => { + match pair_from_ed25519_str(&substrate_suri) { + Ok(pair) => return (substrate_suri, pair.into()), + Err(_) => println!("Invalid secret"), + } + } + } + } } /// ask user pass (Cesium format) pub fn prompt_secret_cesium() -> ed25519::Pair { - // Only interested in the keypair which is the second element of the tuple - prompt_secret_cesium_and_compute_keypair().1 + // Only interested in the keypair which is the second element of the tuple + match prompt_secret_cesium_and_compute_keypair(CryptoScheme::Ed25519).1 { + KeyPair::Ed25519(pair) => pair, + _ => panic!("Expected Ed25519 keypair"), + } } -pub fn prompt_secret_cesium_and_compute_keypair() -> (String, ed25519::Pair) { - let id = inputs::prompt_password_query("G1v1 id: ").unwrap(); - let pwd = inputs::prompt_password_query("G1v1 password: ").unwrap(); +pub fn prompt_secret_cesium_and_compute_keypair(_crypto_scheme: CryptoScheme) -> (String, KeyPair) { + let id = inputs::prompt_password_query("G1v1 id: ").unwrap(); + let pwd = inputs::prompt_password_query("G1v1 password: ").unwrap(); - let seed = seed_from_cesium(&id, &pwd); - let secret_suri = format!("0x{}", hex::encode(seed)); + let seed = seed_from_cesium(&id, &pwd); + let secret_suri = format!("0x{}", hex::encode(seed)); - match pair_from_ed25519_str(&secret_suri) { - Ok(pair) => (secret_suri, pair), - Err(_) => panic!("Could not compute KeyPair from G1v1 id/pwd"), - } + // G1v1 always uses Ed25519, ignore crypto_scheme + match pair_from_ed25519_str(&secret_suri) { + Ok(pair) => (secret_suri, pair.into()), + Err(_) => panic!("Could not compute KeyPair from G1v1 id/pwd"), + } } /// ask user to input a seed pub fn prompt_seed() -> sr25519::Pair { - // Only interested in the keypair which is the second element of the tuple - prompt_seed_and_compute_keypair().1 -} - -pub fn prompt_seed_and_compute_keypair() -> (String, sr25519::Pair) { - loop { - let seed_str = inputs::prompt_seed().unwrap(); - let secret_suri = format!("0x{}", seed_str); - - match pair_from_sr25519_str(&secret_suri) { - Ok(pair) => return (secret_suri, pair), - Err(_) => println!("Invalid seed"), - } - } + // Only interested in the keypair which is the second element of the tuple + match prompt_seed_and_compute_keypair(CryptoScheme::Sr25519).1 { + KeyPair::Sr25519(pair) => pair, + _ => panic!("Expected Sr25519 keypair"), + } +} + +pub fn prompt_seed_and_compute_keypair(crypto_scheme: CryptoScheme) -> (String, KeyPair) { + loop { + let seed_str = inputs::prompt_seed().unwrap(); + let secret_suri = format!("0x{}", seed_str); + + match crypto_scheme { + CryptoScheme::Sr25519 => { + match pair_from_sr25519_str(&secret_suri) { + Ok(pair) => return (secret_suri, pair.into()), + Err(_) => println!("Invalid seed"), + } + }, + CryptoScheme::Ed25519 => { + match pair_from_ed25519_str(&secret_suri) { + Ok(pair) => return (secret_suri, pair.into()), + Err(_) => println!("Invalid seed"), + } + } + } + } } /// ask user pass (Cesium format) pub fn prompt_predefined() -> sr25519::Pair { - // Only interested in the keypair which is the second element of the tuple - prompt_predefined_and_compute_keypair().1 -} - -pub fn prompt_predefined_and_compute_keypair() -> (String, sr25519::Pair) { - let deriv = inputs::prompt_password_query("Enter derivation path: ").unwrap(); - ( - predefined_mnemonic(&deriv), - pair_from_predefined(&deriv).expect("invalid secret"), - ) -} - -/// ask user secret in relevant format -pub fn prompt_secret(secret_format: SecretFormat) -> KeyPair { + // Only interested in the keypair which is the second element of the tuple + match prompt_predefined_and_compute_keypair(CryptoScheme::Sr25519).1 { + KeyPair::Sr25519(pair) => pair, + _ => panic!("Expected Sr25519 keypair"), + } +} + +pub fn prompt_predefined_and_compute_keypair(crypto_scheme: CryptoScheme) -> (String, KeyPair) { + let deriv = inputs::prompt_password_query("Enter derivation path: ").unwrap(); + let mnemonic = predefined_mnemonic(&deriv); + + match crypto_scheme { + CryptoScheme::Sr25519 => { + match pair_from_sr25519_str(&mnemonic) { + Ok(pair) => (mnemonic, pair.into()), + Err(e) => panic!("Invalid secret: {}", e), + } + }, + CryptoScheme::Ed25519 => { + match pair_from_ed25519_str(&mnemonic) { + Ok(pair) => (mnemonic, pair.into()), + Err(e) => panic!("Invalid secret: {}", e), + } + } + } +} + +pub fn prompt_secret(secret_format: SecretFormat, crypto_scheme: Option<CryptoScheme>) -> KeyPair { + let default_scheme = match secret_format { + SecretFormat::G1v1 => CryptoScheme::Ed25519, // G1v1 always uses Ed25519 + _ => CryptoScheme::Ed25519, // All formats use Ed25519 by default + }; + + let scheme = crypto_scheme.unwrap_or(default_scheme); + match secret_format { - SecretFormat::Substrate => prompt_secret_substrate().into(), - SecretFormat::G1v1 => prompt_secret_cesium().into(), - SecretFormat::Seed => prompt_seed().into(), - SecretFormat::Predefined => prompt_predefined().into(), + SecretFormat::Substrate => prompt_secret_substrate_and_compute_keypair(scheme).1, + SecretFormat::G1v1 => prompt_secret_cesium_and_compute_keypair(CryptoScheme::Ed25519).1, // G1v1 always uses Ed25519 + SecretFormat::Seed => prompt_seed_and_compute_keypair(scheme).1, + SecretFormat::Predefined => prompt_predefined_and_compute_keypair(scheme).1, } } @@ -309,7 +360,7 @@ pub async fn fetch_or_get_keypair( } // at the moment, there is no way to confg gcli to use an other kind of secret // without telling explicitly each time - Ok(prompt_secret(SecretFormat::Substrate)) + Ok(prompt_secret(SecretFormat::Substrate, None)) } // catch known addresses