From ab145f0940e43aa34717beed6e039c07dd74c14b Mon Sep 17 00:00:00 2001 From: FroVolod Date: Tue, 8 Jun 2021 21:44:21 +0300 Subject: [PATCH 1/9] login: logging with a fixed account id --- Cargo.lock | 11 ++ Cargo.toml | 1 + .../sign_transaction/mod.rs | 4 +- .../sign_with_keychain/mod.rs | 2 +- src/commands/login/mod.rs | 1 + src/commands/login/operation_mode/mod.rs | 74 ++++++++ .../login/operation_mode/online_mode/mod.rs | 36 ++++ .../online_mode/select_server/mod.rs | 92 ++++++++++ .../online_mode/select_server/server/mod.rs | 160 ++++++++++++++++++ src/commands/mod.rs | 10 ++ .../generate_keypair_subcommand/mod.rs | 49 ++++-- src/commands/utils_command/mod.rs | 2 +- src/common.rs | 1 + src/consts.rs | 3 + 14 files changed, 429 insertions(+), 17 deletions(-) create mode 100644 src/commands/login/mod.rs create mode 100644 src/commands/login/operation_mode/mod.rs create mode 100644 src/commands/login/operation_mode/online_mode/mod.rs create mode 100644 src/commands/login/operation_mode/online_mode/select_server/mod.rs create mode 100644 src/commands/login/operation_mode/online_mode/select_server/server/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 22ff9a7c7..87d356613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1821,6 +1821,7 @@ dependencies = [ "strum 0.20.1", "strum_macros 0.20.1 (registry+/~https://github.com/rust-lang/crates.io-index)", "url", + "url_open", ] [[package]] @@ -3463,6 +3464,16 @@ dependencies = [ "serde", ] +[[package]] +name = "url_open" +version = "0.0.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ae0b8a775e36e692c64e6a15a5cd854cc58dee7636d380b07738f6f816e3dc78" +dependencies = [ + "url", + "winapi", +] + [[package]] name = "uuid" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index c55f81d57..467851fca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ dirs = "3.0" # The fix is needed for seemless clap integration: /~https://github.com/wusyong/slip10/pull/3 slip10 = { git = "/~https://github.com/frol/slip10", rev = "a3235dd0acf3a485f547cf23e2dd56838adb45f8" } url = { version = "2", features = ["serde"] } +url_open = "0.0.1" color-eyre = "0.5" diff --git a/src/commands/construct_transaction_command/sign_transaction/mod.rs b/src/commands/construct_transaction_command/sign_transaction/mod.rs index 17ad19387..b747034b3 100644 --- a/src/commands/construct_transaction_command/sign_transaction/mod.rs +++ b/src/commands/construct_transaction_command/sign_transaction/mod.rs @@ -2,8 +2,8 @@ use dialoguer::{theme::ColorfulTheme, Select}; use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; mod sign_manually; -mod sign_with_keychain; -mod sign_with_private_key; +pub mod sign_with_keychain; +pub mod sign_with_private_key; #[derive(Debug, clap::Clap)] pub enum CliSignTransaction { diff --git a/src/commands/construct_transaction_command/sign_transaction/sign_with_keychain/mod.rs b/src/commands/construct_transaction_command/sign_transaction/sign_with_keychain/mod.rs index fee1828bc..3bcf145cc 100644 --- a/src/commands/construct_transaction_command/sign_transaction/sign_with_keychain/mod.rs +++ b/src/commands/construct_transaction_command/sign_transaction/sign_with_keychain/mod.rs @@ -11,7 +11,7 @@ pub struct CliSignKeychain { #[derive(Debug)] pub struct SignKeychain { - submit: Option, + pub submit: Option, } impl From for SignKeychain { diff --git a/src/commands/login/mod.rs b/src/commands/login/mod.rs new file mode 100644 index 000000000..6777ebcf1 --- /dev/null +++ b/src/commands/login/mod.rs @@ -0,0 +1 @@ +pub mod operation_mode; diff --git a/src/commands/login/operation_mode/mod.rs b/src/commands/login/operation_mode/mod.rs new file mode 100644 index 000000000..864768464 --- /dev/null +++ b/src/commands/login/operation_mode/mod.rs @@ -0,0 +1,74 @@ +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; + +pub mod online_mode; + +/// инструмент выбора режима online/offline +#[derive(Debug, Default, clap::Clap)] +pub struct CliOperationMode { + #[clap(subcommand)] + mode: Option, +} + +#[derive(Debug)] +pub struct OperationMode { + pub mode: Mode, +} + +impl From for OperationMode { + fn from(item: CliOperationMode) -> Self { + let mode = match item.mode { + Some(cli_mode) => Mode::from(cli_mode), + None => Mode::choose_mode(), + }; + Self { mode } + } +} + +impl OperationMode { + pub async fn process( + self, + prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + ) -> crate::CliResult { + self.mode.process(prepopulated_unsigned_transaction).await + } +} + +#[derive(Debug, clap::Clap)] +pub enum CliMode { + /// Execute a change method with online mode + Network(self::online_mode::CliNetworkArgs), +} + +#[derive(Debug, EnumDiscriminants)] +#[strum_discriminants(derive(EnumMessage, EnumIter))] +pub enum Mode { + #[strum_discriminants(strum(message = "Yes, I keep it simple"))] + Network(self::online_mode::NetworkArgs), +} + +impl From for Mode { + fn from(item: CliMode) -> Self { + match item { + CliMode::Network(cli_network_args) => Self::Network(cli_network_args.into()), + } + } +} + +impl Mode { + pub fn choose_mode() -> Self { + Self::from(CliMode::Network(Default::default())) + } + + pub async fn process( + self, + prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + ) -> crate::CliResult { + match self { + Self::Network(network_args) => { + network_args + .process(prepopulated_unsigned_transaction) + .await + } + } + } +} diff --git a/src/commands/login/operation_mode/online_mode/mod.rs b/src/commands/login/operation_mode/online_mode/mod.rs new file mode 100644 index 000000000..cef1bbc8c --- /dev/null +++ b/src/commands/login/operation_mode/online_mode/mod.rs @@ -0,0 +1,36 @@ +pub mod select_server; + +/// аргументы, необходимые для создания транзакции в online mode +#[derive(Debug, Default, clap::Clap)] +pub struct CliNetworkArgs { + #[clap(subcommand)] + selected_server: Option, +} + +#[derive(Debug)] +pub struct NetworkArgs { + selected_server: self::select_server::SelectServer, +} + +impl From for NetworkArgs { + fn from(item: CliNetworkArgs) -> Self { + let selected_server = match item.selected_server { + Some(cli_selected_server) => { + self::select_server::SelectServer::from(cli_selected_server) + } + None => self::select_server::SelectServer::choose_server(), + }; + Self { selected_server } + } +} + +impl NetworkArgs { + pub async fn process( + self, + prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + ) -> crate::CliResult { + self.selected_server + .process(prepopulated_unsigned_transaction) + .await + } +} diff --git a/src/commands/login/operation_mode/online_mode/select_server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/mod.rs new file mode 100644 index 000000000..7d905c77d --- /dev/null +++ b/src/commands/login/operation_mode/online_mode/select_server/mod.rs @@ -0,0 +1,92 @@ +use dialoguer::{theme::ColorfulTheme, Select}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; + +pub mod server; + +#[derive(Debug, clap::Clap)] +pub enum CliSelectServer { + /// предоставление данных для сервера https://rpc.testnet.near.org + Testnet(self::server::CliServer), + /// предоставление данных для сервера https://rpc.mainnet.near.org + Mainnet(self::server::CliServer), + /// предоставление данных для сервера https://rpc.betanet.near.org + Betanet(self::server::CliServer), + /// предоставление данных для сервера, указанного вручную + Custom(self::server::CliCustomServer), +} + +#[derive(Debug, EnumDiscriminants)] +#[strum_discriminants(derive(EnumMessage, EnumIter))] +pub enum SelectServer { + #[strum_discriminants(strum(message = "Testnet"))] + Testnet(self::server::Server), + #[strum_discriminants(strum(message = "Mainnet"))] + Mainnet(self::server::Server), + #[strum_discriminants(strum(message = "Betanet"))] + Betanet(self::server::Server), + #[strum_discriminants(strum(message = "Custom"))] + Custom(self::server::Server), +} + +impl From for SelectServer { + fn from(item: CliSelectServer) -> Self { + match item { + CliSelectServer::Testnet(cli_server) => { + Self::Testnet(cli_server.into_server(crate::common::ConnectionConfig::Testnet)) + } + CliSelectServer::Mainnet(cli_server) => { + Self::Mainnet(cli_server.into_server(crate::common::ConnectionConfig::Mainnet)) + } + CliSelectServer::Betanet(cli_server) => { + Self::Betanet(cli_server.into_server(crate::common::ConnectionConfig::Betanet)) + } + CliSelectServer::Custom(cli_custom_server) => { + Self::Custom(cli_custom_server.into_server()) + } + } + } +} + +impl SelectServer { + pub fn choose_server() -> Self { + println!(); + let variants = SelectServerDiscriminants::iter().collect::>(); + let servers = variants + .iter() + .map(|p| p.get_message().unwrap().to_owned()) + .collect::>(); + let selected_server = Select::with_theme(&ColorfulTheme::default()) + .with_prompt("Select NEAR protocol RPC server:") + .items(&servers) + .default(0) + .interact() + .unwrap(); + let cli_select_server = match variants[selected_server] { + SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), + SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), + SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), + SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), + }; + Self::from(cli_select_server) + } + + pub async fn process( + self, + prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + ) -> crate::CliResult { + Ok(match self { + SelectServer::Testnet(server) => { + server.process(prepopulated_unsigned_transaction).await?; + } + SelectServer::Mainnet(server) => { + server.process(prepopulated_unsigned_transaction).await?; + } + SelectServer::Betanet(server) => { + server.process(prepopulated_unsigned_transaction).await?; + } + SelectServer::Custom(server) => { + server.process(prepopulated_unsigned_transaction).await?; + } + }) + } +} diff --git a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs new file mode 100644 index 000000000..762b94a14 --- /dev/null +++ b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs @@ -0,0 +1,160 @@ +use std::io::Write; +use std::str::FromStr; + +use dialoguer::Input; +use url_open::UrlOpen; + +/// предустановленный RPC-сервер +#[derive(Debug, Default, clap::Clap)] +pub struct CliServer {} + +/// данные для custom server +#[derive(Debug, Default, clap::Clap)] +pub struct CliCustomServer { + #[clap(long)] + pub url: Option, +} + +#[derive(Debug)] +pub struct Server { + pub connection_config: crate::common::ConnectionConfig, +} + +impl CliServer { + pub fn into_server(self, connection_config: crate::common::ConnectionConfig) -> Server { + Server { connection_config } + } +} + +impl CliCustomServer { + pub fn into_server(self) -> Server { + let url: crate::common::AvailableRpcServerUrl = match self.url { + Some(url) => url, + None => Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text() + .unwrap(), + }; + Server { + connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, + } + } +} + +impl Server { + pub async fn process( + self, + prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + ) -> crate::CliResult { + let generate_keypair: crate::commands::utils_command::generate_keypair_subcommand::CliGenerateKeypair = + crate::commands::utils_command::generate_keypair_subcommand::CliGenerateKeypair::default(); + + let key_pair_properties: crate::commands::utils_command::generate_keypair_subcommand::KeyPairProperties = + generate_keypair.generate_keypair().await?; + + let url_wallet: url::Url = crate::consts::WALLET_URL.parse()?; + let url_login: url::Url = url_wallet.join("login/?title=NEAR+CLI")?; + let url: url::Url = url::Url::parse_with_params( + url_login.as_str(), + &[ + ("public_key", &key_pair_properties.public_key_str), + ("success_url", &"http://127.0.0.1:8080".to_string()), + ], + )?; + url.open(); + + let public_key: near_crypto::PublicKey = + near_crypto::PublicKey::from_str(&key_pair_properties.public_key_str)?; + + let account_id = get_account_from_cli( + public_key, + prepopulated_unsigned_transaction, + Some(self.connection_config), + ) + .await?; + if !account_id.is_empty() { + save_account(&account_id, key_pair_properties).await? + }; + Ok(()) + } +} + +async fn get_account_from_cli( + public_key: near_crypto::PublicKey, + prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + network_connection_config: Option, +) -> color_eyre::eyre::Result { + let account_id: String = "volodymyr.testnet".to_string(); + add_full_access_key( + account_id.clone(), + public_key, + prepopulated_unsigned_transaction, + network_connection_config, + ) + .await?; + Ok(account_id) +} + +async fn add_full_access_key( + account_id: String, + public_key: near_crypto::PublicKey, + prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + network_connection_config: Option, +) -> crate::CliResult { + let access_key: near_primitives::account::AccessKey = near_primitives::account::AccessKey { + nonce: 0, + permission: near_primitives::account::AccessKeyPermission::FullAccess, + }; + let action = + near_primitives::transaction::Action::AddKey(near_primitives::transaction::AddKeyAction { + public_key, + access_key, + }); + let mut actions = prepopulated_unsigned_transaction.actions.clone(); + actions.push(action); + let unsigned_transaction = near_primitives::transaction::Transaction { + actions, + signer_id: account_id.clone(), + receiver_id: account_id.clone(), + ..prepopulated_unsigned_transaction + }; + + let sign_with_key_chain = crate::commands::construct_transaction_command::sign_transaction::sign_with_keychain::SignKeychain { + submit: Some(crate::commands::construct_transaction_command::sign_transaction::sign_with_private_key::Submit::Send) + }; + sign_with_key_chain + .process(unsigned_transaction, network_connection_config) + .await +} + +async fn save_account( + account_id: &str, + key_pair_properties: crate::commands::utils_command::generate_keypair_subcommand::KeyPairProperties, +) -> crate::CliResult { + let buf = format!( + "{}", + serde_json::json!({ + "account_id": account_id, + "public_key": key_pair_properties.public_key_str.clone(), + "private_key": key_pair_properties.secret_keypair_str.clone(), + }) + ); + let home_dir = dirs::home_dir().expect("Impossible to get your home dir!"); + let file_name: std::path::PathBuf = format!("{}.json", &account_id).into(); + let mut path = std::path::PathBuf::from(&home_dir); + path.push(crate::consts::DIR_NAME_TESTNET); + path.push(file_name); + std::fs::File::create(&path) + .map_err(|err| color_eyre::Report::msg(format!("Failed to create file: {:?}", err)))? + .write(buf.as_bytes()) + .map_err(|err| color_eyre::Report::msg(format!("Failed to write to file: {:?}", err)))?; + println!( + "\n\n\nThe data for the access key is saved in a file {}", + &path.display() + ); + println!( + "Logged in as [ {} ] with public key [ {} ] successfully", + account_id, key_pair_properties.public_key_str + ); + Ok(()) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 47ba381d2..cd5548dd1 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -6,6 +6,7 @@ pub mod construct_transaction_command; pub mod delete_command; pub mod execute_command; pub mod generate_shell_completions_command; +pub mod login; pub mod transfer_command; pub mod utils_command; pub mod view_command; @@ -22,6 +23,8 @@ pub enum CliTopLevelCommand { Execute(self::execute_command::CliOptionMethod), /// Use these to generate static shell completions GenerateShellCompletions(self::generate_shell_completions_command::CliGenerateShellCompletions), + /// Use these to login with wallet authorization + Login(self::login::operation_mode::CliOperationMode), /// Use these to transfer tokens Transfer(self::transfer_command::CliCurrency), /// Helpers @@ -33,6 +36,8 @@ pub enum CliTopLevelCommand { #[derive(Debug, EnumDiscriminants)] #[strum_discriminants(derive(EnumMessage, EnumIter))] pub enum TopLevelCommand { + #[strum_discriminants(strum(message = "Login with wallet authorization"))] + Login(self::login::operation_mode::OperationMode), #[strum_discriminants(strum( message = "View account, contract code, contract state, transaction, nonce" ))] @@ -69,6 +74,9 @@ impl From for TopLevelCommand { CliTopLevelCommand::GenerateShellCompletions(_) => { unreachable!("This variant is handled in the main function") } + CliTopLevelCommand::Login(cli_option_method) => { + TopLevelCommand::Login(cli_option_method.into()) + } CliTopLevelCommand::Transfer(cli_currency) => { TopLevelCommand::Transfer(cli_currency.into()) } @@ -103,6 +111,7 @@ impl TopLevelCommand { TopLevelCommandDiscriminants::Execute => { CliTopLevelCommand::Execute(Default::default()) } + TopLevelCommandDiscriminants::Login => CliTopLevelCommand::Login(Default::default()), TopLevelCommandDiscriminants::Transfer => { CliTopLevelCommand::Transfer(Default::default()) } @@ -126,6 +135,7 @@ impl TopLevelCommand { Self::ConstructTransaction(mode) => mode.process(unsigned_transaction).await, Self::Delete(delete_action) => delete_action.process(unsigned_transaction).await, Self::Execute(option_method) => option_method.process(unsigned_transaction).await, + Self::Login(mode) => mode.process(unsigned_transaction).await, Self::Transfer(currency) => currency.process(unsigned_transaction).await, Self::Utils(util_type) => util_type.process().await, Self::View(view_query_request) => view_query_request.process().await, diff --git a/src/commands/utils_command/generate_keypair_subcommand/mod.rs b/src/commands/utils_command/generate_keypair_subcommand/mod.rs index a66338177..c9a8c24a7 100644 --- a/src/commands/utils_command/generate_keypair_subcommand/mod.rs +++ b/src/commands/utils_command/generate_keypair_subcommand/mod.rs @@ -21,7 +21,7 @@ fn bip32path_to_string(bip32path: &slip10::BIP32Path) -> String { /// Generate a key pair of secret and public keys (use it anywhere you need /// Ed25519 keys) -#[derive(Debug, clap::Clap)] +#[derive(Debug, clap::Clap, Clone)] pub struct CliGenerateKeypair { pub master_seed_phrase: Option, pub new_master_seed_phrase_words_count: usize, @@ -40,8 +40,15 @@ impl Default for CliGenerateKeypair { } } +pub struct KeyPairProperties { + master_seed_phrase: String, + implicit_account_id: String, + pub public_key_str: String, + pub secret_keypair_str: String, +} + impl CliGenerateKeypair { - pub async fn process(self) -> crate::CliResult { + pub async fn generate_keypair(self) -> color_eyre::eyre::Result { let (master_seed_phrase, master_seed) = if let Some(ref master_seed_phrase) = self.master_seed_phrase { ( @@ -87,27 +94,43 @@ impl CliGenerateKeypair { "ed25519:{}", bs58::encode(secret_keypair.to_bytes()).into_string() ); + let key_pair_properties: KeyPairProperties = KeyPairProperties { + master_seed_phrase, + implicit_account_id, + public_key_str, + secret_keypair_str, + }; + Ok(key_pair_properties) + } + + pub async fn process(self) -> crate::CliResult { + let self_clone = self.clone(); + + let seed_phrase_hd_path = bip32path_to_string(&self_clone.seed_phrase_hd_path); + let key_pair_properties = self.generate_keypair().await?; - match self.format { + match self_clone.format { crate::common::OutputFormat::Plaintext => { println!( "Master Seed Phrase: {}\nSeed Phrase HD Path: {}\nImplicit Account ID: {}\nPublic Key: {}\nSECRET KEYPAIR: {}", - master_seed_phrase, - bip32path_to_string(&self.seed_phrase_hd_path), - implicit_account_id, - public_key_str, - secret_keypair_str, + key_pair_properties.master_seed_phrase, + // bip32path_to_string(&self.seed_phrase_hd_path.clone()), + seed_phrase_hd_path, + key_pair_properties.implicit_account_id, + key_pair_properties.public_key_str, + key_pair_properties.secret_keypair_str, ); } crate::common::OutputFormat::Json => { println!( "{:#?}", serde_json::json!({ - "master_seed_phrase": master_seed_phrase, - "seed_phrase_hd_path": bip32path_to_string(&self.seed_phrase_hd_path), - "account_id": implicit_account_id, - "public_key": public_key_str, - "private_key": secret_keypair_str, + "master_seed_phrase": key_pair_properties.master_seed_phrase, + // "seed_phrase_hd_path": bip32path_to_string(&self.seed_phrase_hd_path.clone()), + "seed_phrase_hd_path": seed_phrase_hd_path, + "account_id": key_pair_properties.implicit_account_id, + "public_key": key_pair_properties.public_key_str, + "private_key": key_pair_properties.secret_keypair_str, }) ); } diff --git a/src/commands/utils_command/mod.rs b/src/commands/utils_command/mod.rs index 049ddb006..0c7c1049c 100644 --- a/src/commands/utils_command/mod.rs +++ b/src/commands/utils_command/mod.rs @@ -2,7 +2,7 @@ use dialoguer::{theme::ColorfulTheme, Select}; use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; mod combine_transaction_subcommand_with_signature; -mod generate_keypair_subcommand; +pub mod generate_keypair_subcommand; mod sign_transaction_subcommand_with_secret_key; /// набор утилит-помощников diff --git a/src/common.rs b/src/common.rs index f6df9ad44..87a362786 100644 --- a/src/common.rs +++ b/src/common.rs @@ -4,6 +4,7 @@ use near_primitives::borsh::BorshDeserialize; #[derive( Debug, + Clone, strum_macros::IntoStaticStr, strum_macros::EnumString, strum_macros::EnumVariantNames, diff --git a/src/consts.rs b/src/consts.rs index b430c5535..69045de0b 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -6,4 +6,7 @@ pub const BETANET_API_SERVER_URL: &str = "https://rpc.betanet.near.org"; // NOTE: There is no dedicated archival RPC server for betanet by design pub const BETANET_ARCHIVAL_API_SERVER_URL: &str = "https://rpc.betanet.near.org"; +pub const WALLET_URL: &str = "https://wallet.testnet.near.org"; + pub const DIR_NAME_KEY_CHAIN: &str = ".near-credentials/default/"; +pub const DIR_NAME_TESTNET: &str = ".near-credentials/testnet/"; From 3fa2534b25402146e7be6878340a12472437ac8d Mon Sep 17 00:00:00 2001 From: FroVolod Date: Thu, 10 Jun 2021 23:27:56 +0300 Subject: [PATCH 2/9] login: added args to consts; fixed generate_key --- src/commands/login/operation_mode/mod.rs | 18 +-- .../login/operation_mode/online_mode/mod.rs | 9 +- .../online_mode/select_server/mod.rs | 15 +-- .../online_mode/select_server/server/mod.rs | 104 +++++++----------- src/commands/mod.rs | 2 +- .../generate_keypair_subcommand/mod.rs | 72 ++---------- src/common.rs | 83 +++++++++++++- src/consts.rs | 7 +- 8 files changed, 147 insertions(+), 163 deletions(-) diff --git a/src/commands/login/operation_mode/mod.rs b/src/commands/login/operation_mode/mod.rs index 864768464..c3fb24f4f 100644 --- a/src/commands/login/operation_mode/mod.rs +++ b/src/commands/login/operation_mode/mod.rs @@ -25,11 +25,8 @@ impl From for OperationMode { } impl OperationMode { - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - ) -> crate::CliResult { - self.mode.process(prepopulated_unsigned_transaction).await + pub async fn process(self) -> crate::CliResult { + self.mode.process().await } } @@ -59,16 +56,9 @@ impl Mode { Self::from(CliMode::Network(Default::default())) } - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - ) -> crate::CliResult { + pub async fn process(self) -> crate::CliResult { match self { - Self::Network(network_args) => { - network_args - .process(prepopulated_unsigned_transaction) - .await - } + Self::Network(network_args) => network_args.process().await, } } } diff --git a/src/commands/login/operation_mode/online_mode/mod.rs b/src/commands/login/operation_mode/online_mode/mod.rs index cef1bbc8c..3347a481b 100644 --- a/src/commands/login/operation_mode/online_mode/mod.rs +++ b/src/commands/login/operation_mode/online_mode/mod.rs @@ -25,12 +25,7 @@ impl From for NetworkArgs { } impl NetworkArgs { - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - ) -> crate::CliResult { - self.selected_server - .process(prepopulated_unsigned_transaction) - .await + pub async fn process(self) -> crate::CliResult { + self.selected_server.process().await } } diff --git a/src/commands/login/operation_mode/online_mode/select_server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/mod.rs index 7d905c77d..3365d1902 100644 --- a/src/commands/login/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/login/operation_mode/online_mode/select_server/mod.rs @@ -56,7 +56,7 @@ impl SelectServer { .map(|p| p.get_message().unwrap().to_owned()) .collect::>(); let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") + .with_prompt("Select NEAR protocol wallet url") .items(&servers) .default(0) .interact() @@ -70,22 +70,19 @@ impl SelectServer { Self::from(cli_select_server) } - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - ) -> crate::CliResult { + pub async fn process(self) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + server.process().await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + server.process().await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + server.process().await?; } SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + server.process().await?; } }) } diff --git a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs index 762b94a14..7a80224d9 100644 --- a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs @@ -12,7 +12,7 @@ pub struct CliServer {} #[derive(Debug, Default, clap::Clap)] pub struct CliCustomServer { #[clap(long)] - pub url: Option, + pub url: Option, } #[derive(Debug)] @@ -28,52 +28,46 @@ impl CliServer { impl CliCustomServer { pub fn into_server(self) -> Server { - let url: crate::common::AvailableRpcServerUrl = match self.url { + let url: url::Url = match self.url { Some(url) => url, None => Input::new() - .with_prompt("What is the RPC endpoint?") + .with_prompt("What is the wallet url?") .interact_text() .unwrap(), }; Server { - connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, + connection_config: crate::common::ConnectionConfig::Custom { url }, } } } impl Server { - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - ) -> crate::CliResult { + pub async fn process(self) -> crate::CliResult { let generate_keypair: crate::commands::utils_command::generate_keypair_subcommand::CliGenerateKeypair = crate::commands::utils_command::generate_keypair_subcommand::CliGenerateKeypair::default(); - let key_pair_properties: crate::commands::utils_command::generate_keypair_subcommand::KeyPairProperties = - generate_keypair.generate_keypair().await?; + let key_pair_properties: crate::common::KeyPairProperties = + crate::common::generate_keypair( + generate_keypair.master_seed_phrase.clone(), + generate_keypair.new_master_seed_phrase_words_count.clone(), + generate_keypair.seed_phrase_hd_path.clone(), + ) + .await?; - let url_wallet: url::Url = crate::consts::WALLET_URL.parse()?; - let url_login: url::Url = url_wallet.join("login/?title=NEAR+CLI")?; - let url: url::Url = url::Url::parse_with_params( - url_login.as_str(), - &[ - ("public_key", &key_pair_properties.public_key_str), - ("success_url", &"http://127.0.0.1:8080".to_string()), - ], - )?; + let mut url: url::Url = self.connection_config.wallet_url().join("login/")?; + url.query_pairs_mut() + .append_pair("title", "NEAR+CLI") + .append_pair("public_key", &key_pair_properties.public_key_str) + .append_pair("success_url", "http://127.0.0.1:8080"); + println!("url: {}", &url.as_str()); url.open(); let public_key: near_crypto::PublicKey = near_crypto::PublicKey::from_str(&key_pair_properties.public_key_str)?; - let account_id = get_account_from_cli( - public_key, - prepopulated_unsigned_transaction, - Some(self.connection_config), - ) - .await?; + let account_id = get_account_from_cli(public_key, self.connection_config.clone()).await?; if !account_id.is_empty() { - save_account(&account_id, key_pair_properties).await? + save_account(&account_id, key_pair_properties, self.connection_config).await? }; Ok(()) } @@ -81,55 +75,34 @@ impl Server { async fn get_account_from_cli( public_key: near_crypto::PublicKey, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, + network_connection_config: crate::common::ConnectionConfig, ) -> color_eyre::eyre::Result { - let account_id: String = "volodymyr.testnet".to_string(); - add_full_access_key( - account_id.clone(), - public_key, - prepopulated_unsigned_transaction, - network_connection_config, - ) - .await?; + let account_id = input_account_id(); + verify_account_id(account_id.clone(), public_key, network_connection_config) + .await + .map_err(|err| color_eyre::Report::msg(format!("Failed account ID: {:?}", err)))?; Ok(account_id) } -async fn add_full_access_key( +fn input_account_id() -> String { + Input::new() + .with_prompt("Enter account ID") + .interact_text() + .unwrap() +} + +async fn verify_account_id( account_id: String, public_key: near_crypto::PublicKey, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, + network_connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - let access_key: near_primitives::account::AccessKey = near_primitives::account::AccessKey { - nonce: 0, - permission: near_primitives::account::AccessKeyPermission::FullAccess, - }; - let action = - near_primitives::transaction::Action::AddKey(near_primitives::transaction::AddKeyAction { - public_key, - access_key, - }); - let mut actions = prepopulated_unsigned_transaction.actions.clone(); - actions.push(action); - let unsigned_transaction = near_primitives::transaction::Transaction { - actions, - signer_id: account_id.clone(), - receiver_id: account_id.clone(), - ..prepopulated_unsigned_transaction - }; - - let sign_with_key_chain = crate::commands::construct_transaction_command::sign_transaction::sign_with_keychain::SignKeychain { - submit: Some(crate::commands::construct_transaction_command::sign_transaction::sign_with_private_key::Submit::Send) - }; - sign_with_key_chain - .process(unsigned_transaction, network_connection_config) - .await + Ok(()) } async fn save_account( account_id: &str, - key_pair_properties: crate::commands::utils_command::generate_keypair_subcommand::KeyPairProperties, + key_pair_properties: crate::common::KeyPairProperties, + network_connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { let buf = format!( "{}", @@ -142,7 +115,8 @@ async fn save_account( let home_dir = dirs::home_dir().expect("Impossible to get your home dir!"); let file_name: std::path::PathBuf = format!("{}.json", &account_id).into(); let mut path = std::path::PathBuf::from(&home_dir); - path.push(crate::consts::DIR_NAME_TESTNET); + path.push(network_connection_config.dir_name()); + std::fs::create_dir_all(&path)?; path.push(file_name); std::fs::File::create(&path) .map_err(|err| color_eyre::Report::msg(format!("Failed to create file: {:?}", err)))? diff --git a/src/commands/mod.rs b/src/commands/mod.rs index cd5548dd1..edc6f0d84 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -135,7 +135,7 @@ impl TopLevelCommand { Self::ConstructTransaction(mode) => mode.process(unsigned_transaction).await, Self::Delete(delete_action) => delete_action.process(unsigned_transaction).await, Self::Execute(option_method) => option_method.process(unsigned_transaction).await, - Self::Login(mode) => mode.process(unsigned_transaction).await, + Self::Login(mode) => mode.process().await, Self::Transfer(currency) => currency.process(unsigned_transaction).await, Self::Utils(util_type) => util_type.process().await, Self::View(view_query_request) => view_query_request.process().await, diff --git a/src/commands/utils_command/generate_keypair_subcommand/mod.rs b/src/commands/utils_command/generate_keypair_subcommand/mod.rs index c9a8c24a7..f942f3dff 100644 --- a/src/commands/utils_command/generate_keypair_subcommand/mod.rs +++ b/src/commands/utils_command/generate_keypair_subcommand/mod.rs @@ -40,81 +40,24 @@ impl Default for CliGenerateKeypair { } } -pub struct KeyPairProperties { - master_seed_phrase: String, - implicit_account_id: String, - pub public_key_str: String, - pub secret_keypair_str: String, -} - impl CliGenerateKeypair { - pub async fn generate_keypair(self) -> color_eyre::eyre::Result { - let (master_seed_phrase, master_seed) = - if let Some(ref master_seed_phrase) = self.master_seed_phrase { - ( - master_seed_phrase.clone(), - bip39::Mnemonic::parse(master_seed_phrase)?.to_seed(""), - ) - } else { - let mnemonic = bip39::Mnemonic::generate(self.new_master_seed_phrase_words_count)?; - let mut master_seed_phrase = String::new(); - for (index, word) in mnemonic.word_iter().enumerate() { - if index != 0 { - master_seed_phrase.push(' '); - } - master_seed_phrase.push_str(word); - } - (master_seed_phrase, mnemonic.to_seed("")) - }; - - let derived_private_key = slip10::derive_key_from_path( - &master_seed, - slip10::Curve::Ed25519, - &self.seed_phrase_hd_path, - ) - .map_err(|err| { - color_eyre::Report::msg(format!( - "Failed to derive a key from the master key: {}", - err - )) - })?; - - let secret_keypair = { - let secret = ed25519_dalek::SecretKey::from_bytes(&derived_private_key.key)?; - let public = ed25519_dalek::PublicKey::from(&secret); - ed25519_dalek::Keypair { secret, public } - }; - - let implicit_account_id = hex::encode(&secret_keypair.public); - let public_key_str = format!( - "ed25519:{}", - bs58::encode(&secret_keypair.public).into_string() - ); - let secret_keypair_str = format!( - "ed25519:{}", - bs58::encode(secret_keypair.to_bytes()).into_string() - ); - let key_pair_properties: KeyPairProperties = KeyPairProperties { - master_seed_phrase, - implicit_account_id, - public_key_str, - secret_keypair_str, - }; - Ok(key_pair_properties) - } - pub async fn process(self) -> crate::CliResult { let self_clone = self.clone(); let seed_phrase_hd_path = bip32path_to_string(&self_clone.seed_phrase_hd_path); - let key_pair_properties = self.generate_keypair().await?; + + let key_pair_properties = crate::common::generate_keypair( + self.master_seed_phrase.clone(), + self.new_master_seed_phrase_words_count.clone(), + self.seed_phrase_hd_path.clone(), + ) + .await?; match self_clone.format { crate::common::OutputFormat::Plaintext => { println!( "Master Seed Phrase: {}\nSeed Phrase HD Path: {}\nImplicit Account ID: {}\nPublic Key: {}\nSECRET KEYPAIR: {}", key_pair_properties.master_seed_phrase, - // bip32path_to_string(&self.seed_phrase_hd_path.clone()), seed_phrase_hd_path, key_pair_properties.implicit_account_id, key_pair_properties.public_key_str, @@ -126,7 +69,6 @@ impl CliGenerateKeypair { "{:#?}", serde_json::json!({ "master_seed_phrase": key_pair_properties.master_seed_phrase, - // "seed_phrase_hd_path": bip32path_to_string(&self.seed_phrase_hd_path.clone()), "seed_phrase_hd_path": seed_phrase_hd_path, "account_id": key_pair_properties.implicit_account_id, "public_key": key_pair_properties.public_key_str, diff --git a/src/common.rs b/src/common.rs index 87a362786..f2367c19a 100644 --- a/src/common.rs +++ b/src/common.rs @@ -237,7 +237,7 @@ impl NearGas { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ConnectionConfig { Testnet, Mainnet, @@ -269,6 +269,87 @@ impl ConnectionConfig { Self::Custom { url } => url.clone(), } } + + pub fn wallet_url(&self) -> url::Url { + match self { + Self::Testnet => crate::consts::TESTNET_WALLET_URL.parse().unwrap(), + Self::Mainnet => crate::consts::MAINNET_WALLET_URL.parse().unwrap(), + Self::Betanet => crate::consts::BETANET_WALLET_URL.parse().unwrap(), + Self::Custom { url } => url.clone(), + } + } + + pub fn dir_name(&self) -> &str { + match self { + Self::Testnet => crate::consts::DIR_NAME_TESTNET, + Self::Mainnet => crate::consts::DIR_NAME_MAINNET, + Self::Betanet => crate::consts::DIR_NAME_BETANET, + Self::Custom { url: _ } => crate::consts::DIR_NAME_CUSTOM, + } + } +} + +pub struct KeyPairProperties { + pub master_seed_phrase: String, + pub implicit_account_id: String, + pub public_key_str: String, + pub secret_keypair_str: String, +} + +pub async fn generate_keypair( + master_seed_phrase: Option, + new_master_seed_phrase_words_count: usize, + seed_phrase_hd_path: slip10::BIP32Path, +) -> color_eyre::eyre::Result { + let (master_seed_phrase, master_seed) = if let Some(ref master_seed_phrase) = master_seed_phrase + { + ( + master_seed_phrase.clone(), + bip39::Mnemonic::parse(master_seed_phrase)?.to_seed(""), + ) + } else { + let mnemonic = bip39::Mnemonic::generate(new_master_seed_phrase_words_count)?; + let mut master_seed_phrase = String::new(); + for (index, word) in mnemonic.word_iter().enumerate() { + if index != 0 { + master_seed_phrase.push(' '); + } + master_seed_phrase.push_str(word); + } + (master_seed_phrase, mnemonic.to_seed("")) + }; + + let derived_private_key = + slip10::derive_key_from_path(&master_seed, slip10::Curve::Ed25519, &seed_phrase_hd_path) + .map_err(|err| { + color_eyre::Report::msg(format!( + "Failed to derive a key from the master key: {}", + err + )) + })?; + + let secret_keypair = { + let secret = ed25519_dalek::SecretKey::from_bytes(&derived_private_key.key)?; + let public = ed25519_dalek::PublicKey::from(&secret); + ed25519_dalek::Keypair { secret, public } + }; + + let implicit_account_id = hex::encode(&secret_keypair.public); + let public_key_str = format!( + "ed25519:{}", + bs58::encode(&secret_keypair.public).into_string() + ); + let secret_keypair_str = format!( + "ed25519:{}", + bs58::encode(secret_keypair.to_bytes()).into_string() + ); + let key_pair_properties: KeyPairProperties = KeyPairProperties { + master_seed_phrase, + implicit_account_id, + public_key_str, + secret_keypair_str, + }; + Ok(key_pair_properties) } #[cfg(test)] diff --git a/src/consts.rs b/src/consts.rs index 69045de0b..1cb5b8b08 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -6,7 +6,12 @@ pub const BETANET_API_SERVER_URL: &str = "https://rpc.betanet.near.org"; // NOTE: There is no dedicated archival RPC server for betanet by design pub const BETANET_ARCHIVAL_API_SERVER_URL: &str = "https://rpc.betanet.near.org"; -pub const WALLET_URL: &str = "https://wallet.testnet.near.org"; +pub const TESTNET_WALLET_URL: &str = "https://wallet.testnet.near.org"; +pub const MAINNET_WALLET_URL: &str = "https://wallet.mainnet.near.org"; +pub const BETANET_WALLET_URL: &str = "https://wallet.betanet.near.org"; pub const DIR_NAME_KEY_CHAIN: &str = ".near-credentials/default/"; pub const DIR_NAME_TESTNET: &str = ".near-credentials/testnet/"; +pub const DIR_NAME_MAINNET: &str = ".near-credentials/mainnet/"; +pub const DIR_NAME_BETANET: &str = ".near-credentials/betanet/"; +pub const DIR_NAME_CUSTOM: &str = ".near-credentials/default/"; From 6e9830b71d8387b47acc66e1ad274472b3aa7ef8 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 11 Jun 2021 09:41:27 +0300 Subject: [PATCH 3/9] login: fixed to save file --- .../access_key/public_key_mode/generate_keypair/mod.rs | 1 + src/commands/add_command/contract_code/contract/mod.rs | 6 +++--- .../add_command/implicit_account/generate_keypair/mod.rs | 1 + .../full_access_key/public_key_mode/generate_keypair/mod.rs | 1 + .../view_contract_code/block_id/block_id_hash/mod.rs | 2 ++ .../view_contract_code/block_id/block_id_height/mod.rs | 2 ++ .../view_command/view_contract_code/block_id/mod.rs | 2 ++ 7 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/commands/add_command/access_key/public_key_mode/generate_keypair/mod.rs b/src/commands/add_command/access_key/public_key_mode/generate_keypair/mod.rs index e66676552..8e59fed98 100644 --- a/src/commands/add_command/access_key/public_key_mode/generate_keypair/mod.rs +++ b/src/commands/add_command/access_key/public_key_mode/generate_keypair/mod.rs @@ -108,6 +108,7 @@ impl GenerateKeypair { format!("{}.json", &prepopulated_unsigned_transaction.signer_id).into(); let mut path = std::path::PathBuf::from(&home_dir); path.push(crate::consts::DIR_NAME_KEY_CHAIN); + std::fs::create_dir_all(&path)?; path.push(file_name); if path.exists() { return Err(color_eyre::Report::msg(format!( diff --git a/src/commands/add_command/contract_code/contract/mod.rs b/src/commands/add_command/contract_code/contract/mod.rs index 696f060db..d5fe605e8 100644 --- a/src/commands/add_command/contract_code/contract/mod.rs +++ b/src/commands/add_command/contract_code/contract/mod.rs @@ -111,10 +111,10 @@ impl ContractFile { prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, network_connection_config: Option, ) -> crate::CliResult { - let mut f = std::fs::File::open(&self.file_path.clone()) - .map_err(|err| color_eyre::Report::msg(format!("Failed to open file: {:?}", err)))?; let mut code = Vec::new(); - f.read_to_end(&mut code) + std::fs::File::open(&self.file_path.clone()) + .map_err(|err| color_eyre::Report::msg(format!("Failed to open file: {:?}", err)))? + .read_to_end(&mut code) .map_err(|err| color_eyre::Report::msg(format!("Failed to read file: {:?}", err)))?; let action = near_primitives::transaction::Action::DeployContract( near_primitives::transaction::DeployContractAction { code }, diff --git a/src/commands/add_command/implicit_account/generate_keypair/mod.rs b/src/commands/add_command/implicit_account/generate_keypair/mod.rs index ea114e228..c2fedb0f4 100644 --- a/src/commands/add_command/implicit_account/generate_keypair/mod.rs +++ b/src/commands/add_command/implicit_account/generate_keypair/mod.rs @@ -82,6 +82,7 @@ impl CliGenerateKeypair { let file_name: std::path::PathBuf = format!("{}.json", &implicit_account_id).into(); let mut path = std::path::PathBuf::from(&home_dir); path.push(crate::consts::DIR_NAME_KEY_CHAIN); + std::fs::create_dir_all(&path)?; path.push(file_name); if path.exists() { return Err(color_eyre::Report::msg(format!( diff --git a/src/commands/add_command/sub_account/full_access_key/public_key_mode/generate_keypair/mod.rs b/src/commands/add_command/sub_account/full_access_key/public_key_mode/generate_keypair/mod.rs index 4e4d23166..fcd2f85ba 100644 --- a/src/commands/add_command/sub_account/full_access_key/public_key_mode/generate_keypair/mod.rs +++ b/src/commands/add_command/sub_account/full_access_key/public_key_mode/generate_keypair/mod.rs @@ -105,6 +105,7 @@ impl GenerateKeypair { format!("{}.json", &prepopulated_unsigned_transaction.receiver_id).into(); let mut path = std::path::PathBuf::from(&home_dir); path.push(crate::consts::DIR_NAME_KEY_CHAIN); + std::fs::create_dir_all(&path)?; path.push(file_name); if path.exists() { return Err(color_eyre::Report::msg(format!( diff --git a/src/commands/view_command/view_contract_code/block_id/block_id_hash/mod.rs b/src/commands/view_command/view_contract_code/block_id/block_id_hash/mod.rs index 3fce35526..fcd6cbb29 100644 --- a/src/commands/view_command/view_contract_code/block_id/block_id_hash/mod.rs +++ b/src/commands/view_command/view_contract_code/block_id/block_id_hash/mod.rs @@ -67,6 +67,8 @@ impl BlockIdHash { }; match &file_path { Some(file_path) => { + let dir_name = &file_path.parent().unwrap(); + std::fs::create_dir_all(&dir_name)?; std::fs::File::create(file_path) .map_err(|err| { color_eyre::Report::msg(format!("Failed to create file: {:?}", err)) diff --git a/src/commands/view_command/view_contract_code/block_id/block_id_height/mod.rs b/src/commands/view_command/view_contract_code/block_id/block_id_height/mod.rs index fbe60d53b..9137b61b5 100644 --- a/src/commands/view_command/view_contract_code/block_id/block_id_height/mod.rs +++ b/src/commands/view_command/view_contract_code/block_id/block_id_height/mod.rs @@ -67,6 +67,8 @@ impl BlockIdHeight { }; match &file_path { Some(file_path) => { + let dir_name = &file_path.parent().unwrap(); + std::fs::create_dir_all(&dir_name)?; std::fs::File::create(file_path) .map_err(|err| { color_eyre::Report::msg(format!("Failed to create file: {:?}", err)) diff --git a/src/commands/view_command/view_contract_code/block_id/mod.rs b/src/commands/view_command/view_contract_code/block_id/mod.rs index d4edfd719..b7c773107 100644 --- a/src/commands/view_command/view_contract_code/block_id/mod.rs +++ b/src/commands/view_command/view_contract_code/block_id/mod.rs @@ -122,6 +122,8 @@ impl BlockId { }; match &file_path { Some(file_path) => { + let dir_name = &file_path.parent().unwrap(); + std::fs::create_dir_all(&dir_name)?; std::fs::File::create(file_path) .map_err(|err| { color_eyre::Report::msg(format!("Failed to create file: {:?}", err)) From 9008d27058947b76a166503efbc1071a4da21e2c Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 11 Jun 2021 11:17:12 +0300 Subject: [PATCH 4/9] login: fixed --- .../utils_command/generate_keypair_subcommand/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/commands/utils_command/generate_keypair_subcommand/mod.rs b/src/commands/utils_command/generate_keypair_subcommand/mod.rs index f942f3dff..01beeab4a 100644 --- a/src/commands/utils_command/generate_keypair_subcommand/mod.rs +++ b/src/commands/utils_command/generate_keypair_subcommand/mod.rs @@ -42,9 +42,7 @@ impl Default for CliGenerateKeypair { impl CliGenerateKeypair { pub async fn process(self) -> crate::CliResult { - let self_clone = self.clone(); - - let seed_phrase_hd_path = bip32path_to_string(&self_clone.seed_phrase_hd_path); + let seed_phrase_hd_path = bip32path_to_string(&self.seed_phrase_hd_path); let key_pair_properties = crate::common::generate_keypair( self.master_seed_phrase.clone(), @@ -53,7 +51,7 @@ impl CliGenerateKeypair { ) .await?; - match self_clone.format { + match self.format { crate::common::OutputFormat::Plaintext => { println!( "Master Seed Phrase: {}\nSeed Phrase HD Path: {}\nImplicit Account ID: {}\nPublic Key: {}\nSECRET KEYPAIR: {}", From 2db063ae197ffb87c8c2b84b62f7b1c8dc036e1c Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 11 Jun 2021 12:11:45 +0300 Subject: [PATCH 5/9] login: added verify_account_id --- .../online_mode/select_server/server/mod.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs index 7a80224d9..ad6074676 100644 --- a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs @@ -91,11 +91,30 @@ fn input_account_id() -> String { .unwrap() } +fn rpc_client(selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { + near_jsonrpc_client::new_client(&selected_server_url) +} + async fn verify_account_id( account_id: String, public_key: near_crypto::PublicKey, network_connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { + rpc_client(network_connection_config.rpc_url().as_str()) + .query(near_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: near_primitives::types::Finality::Final.into(), + request: near_primitives::views::QueryRequest::ViewAccessKey { + account_id, + public_key, + }, + }) + .await + .map_err(|err| { + color_eyre::Report::msg(format!( + "Failed to fetch query for view access key: {:?}", + err + )) + })?; Ok(()) } From df691a6af7b2f2f2ce1c212bf615e2f2fcfba4e0 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 11 Jun 2021 12:23:49 +0300 Subject: [PATCH 6/9] login: fixed --- .../online_mode/select_server/server/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs index ad6074676..c7ffbf75e 100644 --- a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs @@ -57,9 +57,12 @@ impl Server { let mut url: url::Url = self.connection_config.wallet_url().join("login/")?; url.query_pairs_mut() .append_pair("title", "NEAR+CLI") - .append_pair("public_key", &key_pair_properties.public_key_str) - .append_pair("success_url", "http://127.0.0.1:8080"); - println!("url: {}", &url.as_str()); + .append_pair("public_key", &key_pair_properties.public_key_str); + // .append_pair("success_url", "http://127.0.0.1:8080"); + println!( + "If your browser doesn't automatically open, please visit this URL:\n {}\n", + &url.as_str() + ); url.open(); let public_key: near_crypto::PublicKey = From 234e568b17b8ccb1019d32eb43e5a9c429db0e4f Mon Sep 17 00:00:00 2001 From: Vlad Frolov Date: Fri, 11 Jun 2021 07:53:40 -0300 Subject: [PATCH 7/9] refactor: cleaned up unnecessary clones a bit (#18) --- .../online_mode/select_server/server/mod.rs | 11 ++++++----- .../generate_keypair_subcommand/mod.rs | 18 ++++++++---------- src/common.rs | 17 +++++++---------- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs index c7ffbf75e..5d185ba3e 100644 --- a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs @@ -48,17 +48,18 @@ impl Server { let key_pair_properties: crate::common::KeyPairProperties = crate::common::generate_keypair( - generate_keypair.master_seed_phrase.clone(), - generate_keypair.new_master_seed_phrase_words_count.clone(), - generate_keypair.seed_phrase_hd_path.clone(), + generate_keypair.master_seed_phrase.as_deref(), + generate_keypair.new_master_seed_phrase_words_count, + generate_keypair.seed_phrase_hd_path, ) .await?; let mut url: url::Url = self.connection_config.wallet_url().join("login/")?; url.query_pairs_mut() - .append_pair("title", "NEAR+CLI") + .append_pair("title", "NEAR CLI") .append_pair("public_key", &key_pair_properties.public_key_str); - // .append_pair("success_url", "http://127.0.0.1:8080"); + // Use `success_url` once capture mode is implemented + //.append_pair("success_url", "http://127.0.0.1:8080"); println!( "If your browser doesn't automatically open, please visit this URL:\n {}\n", &url.as_str() diff --git a/src/commands/utils_command/generate_keypair_subcommand/mod.rs b/src/commands/utils_command/generate_keypair_subcommand/mod.rs index 01beeab4a..6ba35d2a7 100644 --- a/src/commands/utils_command/generate_keypair_subcommand/mod.rs +++ b/src/commands/utils_command/generate_keypair_subcommand/mod.rs @@ -42,12 +42,10 @@ impl Default for CliGenerateKeypair { impl CliGenerateKeypair { pub async fn process(self) -> crate::CliResult { - let seed_phrase_hd_path = bip32path_to_string(&self.seed_phrase_hd_path); - let key_pair_properties = crate::common::generate_keypair( - self.master_seed_phrase.clone(), - self.new_master_seed_phrase_words_count.clone(), - self.seed_phrase_hd_path.clone(), + self.master_seed_phrase.as_deref(), + self.new_master_seed_phrase_words_count, + self.seed_phrase_hd_path, ) .await?; @@ -56,7 +54,7 @@ impl CliGenerateKeypair { println!( "Master Seed Phrase: {}\nSeed Phrase HD Path: {}\nImplicit Account ID: {}\nPublic Key: {}\nSECRET KEYPAIR: {}", key_pair_properties.master_seed_phrase, - seed_phrase_hd_path, + bip32path_to_string(&key_pair_properties.seed_phrase_hd_path), key_pair_properties.implicit_account_id, key_pair_properties.public_key_str, key_pair_properties.secret_keypair_str, @@ -64,14 +62,14 @@ impl CliGenerateKeypair { } crate::common::OutputFormat::Json => { println!( - "{:#?}", - serde_json::json!({ + "{}", + serde_json::to_string_pretty(&serde_json::json!({ "master_seed_phrase": key_pair_properties.master_seed_phrase, - "seed_phrase_hd_path": seed_phrase_hd_path, + "seed_phrase_hd_path": bip32path_to_string(&key_pair_properties.seed_phrase_hd_path), "account_id": key_pair_properties.implicit_account_id, "public_key": key_pair_properties.public_key_str, "private_key": key_pair_properties.secret_keypair_str, - }) + })).unwrap() ); } }; diff --git a/src/common.rs b/src/common.rs index f2367c19a..d42eb5796 100644 --- a/src/common.rs +++ b/src/common.rs @@ -289,7 +289,9 @@ impl ConnectionConfig { } } +#[derive(Debug)] pub struct KeyPairProperties { + pub seed_phrase_hd_path: slip10::BIP32Path, pub master_seed_phrase: String, pub implicit_account_id: String, pub public_key_str: String, @@ -297,25 +299,19 @@ pub struct KeyPairProperties { } pub async fn generate_keypair( - master_seed_phrase: Option, + master_seed_phrase: Option<&str>, new_master_seed_phrase_words_count: usize, seed_phrase_hd_path: slip10::BIP32Path, ) -> color_eyre::eyre::Result { - let (master_seed_phrase, master_seed) = if let Some(ref master_seed_phrase) = master_seed_phrase + let (master_seed_phrase, master_seed) = if let Some(master_seed_phrase) = master_seed_phrase { ( - master_seed_phrase.clone(), + master_seed_phrase.to_owned(), bip39::Mnemonic::parse(master_seed_phrase)?.to_seed(""), ) } else { let mnemonic = bip39::Mnemonic::generate(new_master_seed_phrase_words_count)?; - let mut master_seed_phrase = String::new(); - for (index, word) in mnemonic.word_iter().enumerate() { - if index != 0 { - master_seed_phrase.push(' '); - } - master_seed_phrase.push_str(word); - } + let master_seed_phrase = mnemonic.word_iter().collect::>().join(" "); (master_seed_phrase, mnemonic.to_seed("")) }; @@ -344,6 +340,7 @@ pub async fn generate_keypair( bs58::encode(secret_keypair.to_bytes()).into_string() ); let key_pair_properties: KeyPairProperties = KeyPairProperties { + seed_phrase_hd_path, master_seed_phrase, implicit_account_id, public_key_str, From aa5ec6799e704a996790177913f1a6300d913ece Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 11 Jun 2021 14:12:29 +0300 Subject: [PATCH 8/9] login: cargo fmt --- .../operation_mode/online_mode/select_server/server/mod.rs | 4 ++-- src/common.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs index 5d185ba3e..2f26bb87b 100644 --- a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs @@ -58,8 +58,8 @@ impl Server { url.query_pairs_mut() .append_pair("title", "NEAR CLI") .append_pair("public_key", &key_pair_properties.public_key_str); - // Use `success_url` once capture mode is implemented - //.append_pair("success_url", "http://127.0.0.1:8080"); + // Use `success_url` once capture mode is implemented + //.append_pair("success_url", "http://127.0.0.1:8080"); println!( "If your browser doesn't automatically open, please visit this URL:\n {}\n", &url.as_str() diff --git a/src/common.rs b/src/common.rs index d42eb5796..0435f4e27 100644 --- a/src/common.rs +++ b/src/common.rs @@ -303,8 +303,7 @@ pub async fn generate_keypair( new_master_seed_phrase_words_count: usize, seed_phrase_hd_path: slip10::BIP32Path, ) -> color_eyre::eyre::Result { - let (master_seed_phrase, master_seed) = if let Some(master_seed_phrase) = master_seed_phrase - { + let (master_seed_phrase, master_seed) = if let Some(master_seed_phrase) = master_seed_phrase { ( master_seed_phrase.to_owned(), bip39::Mnemonic::parse(master_seed_phrase)?.to_seed(""), From 4cd05d26cdf21b6ea17d46078dd2b09d22be5cf3 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 11 Jun 2021 14:15:27 +0300 Subject: [PATCH 9/9] login: release 0.1.02 --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8f6c852e..24752a663 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,8 @@ name: release on: workflow_dispatch: - # pull_request: - # branches: [master] + pull_request: + branches: [master] # push: #Enable when testing release infrastructure on a branch. # branches: [master] @@ -20,7 +20,7 @@ jobs: - name: Get the release version from the tag # if: env.NEAR_CLI_VERSION == '' run: | - echo "NEAR_CLI_VERSION=0.1.01" >> $GITHUB_ENV + echo "NEAR_CLI_VERSION=0.1.02" >> $GITHUB_ENV echo "version is: ${{ env.NEAR_CLI_VERSION }}" - name: Create GitHub release