diff --git a/neveko-core/src/monero.rs b/neveko-core/src/monero.rs index d95c4af..ae767a8 100644 --- a/neveko-core/src/monero.rs +++ b/neveko-core/src/monero.rs @@ -68,6 +68,7 @@ enum RpcFields { Address, Balance, CheckTxProof, + ChangeWalletPassword, Close, CreateAddress, CreateWallet, @@ -99,6 +100,7 @@ impl RpcFields { match *self { RpcFields::Address => String::from("get_address"), RpcFields::Balance => String::from("get_balance"), + RpcFields::ChangeWalletPassword => String::from("change_wallet_password"), RpcFields::CheckTxProof => String::from("check_tx_proof"), RpcFields::Close => String::from("close_wallet"), RpcFields::CreateAddress => String::from("create_address"), @@ -583,6 +585,49 @@ pub async fn close_wallet(filename: &String, password: &String) -> bool { } } +/// Performs the xmr rpc 'change_wallet_password' method +pub async fn change_wallet_password(new_password: &String) -> bool { + info!("executing {}", RpcFields::ChangeWalletPassword.value()); + let old_password: String = + std::env::var(crate::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password")); + let new_password: String = String::from(new_password); + let client = reqwest::Client::new(); + let host = get_rpc_host(); + let params = reqres::XmrRpcChangePasswordParams { + old_password, + new_password, + }; + let req = reqres::XmrRpcChangePasswordRequest { + jsonrpc: RpcFields::JsonRpcVersion.value(), + id: RpcFields::Id.value(), + method: RpcFields::ChangeWalletPassword.value(), + params, + }; + let login: RpcLogin = get_rpc_creds(); + match client + .post(host) + .json(&req) + .send_with_digest_auth(&login.username, &login.credential) + .await + { + Ok(response) => { + // The result from wallet operation is empty + let res = response.text().await; + debug!("{} response: {:?}", RpcFields::ChangeWalletPassword.value(), res); + match res { + Ok(r) => { + if r.contains("-1") { + return false; + } + return true; + } + _ => false, + } + } + Err(_) => false, + } +} + /// Performs the xmr rpc 'get_balance' method pub async fn get_balance() -> reqres::XmrRpcBalanceResponse { info!("executing {}", RpcFields::Balance.value()); diff --git a/neveko-core/src/reqres.rs b/neveko-core/src/reqres.rs index e9970c7..1da990f 100644 --- a/neveko-core/src/reqres.rs +++ b/neveko-core/src/reqres.rs @@ -146,6 +146,12 @@ pub struct XmrDaemonGetBlockParams { pub height: u64, } +#[derive(Deserialize, Serialize, Debug)] +pub struct XmrRpcChangePasswordParams { + pub old_password: String, + pub new_password: String, +} + // requests #[derive(Deserialize, Serialize, Debug)] pub struct XmrRpcValidateAddressRequest { @@ -304,6 +310,14 @@ pub struct XmrRpcCreateAddressRequest { pub params: XmrRpcCreateAddressParams, } +#[derive(Deserialize, Serialize, Debug)] +pub struct XmrRpcChangePasswordRequest { + pub jsonrpc: String, + pub id: String, + pub method: String, + pub params: XmrRpcChangePasswordParams, +} + // results #[derive(Deserialize, Debug)] pub struct XmrRpcValidateAddressResult { diff --git a/neveko-gui/src/apps/home.rs b/neveko-gui/src/apps/home.rs index 94d723c..57fa588 100644 --- a/neveko-gui/src/apps/home.rs +++ b/neveko-gui/src/apps/home.rs @@ -436,7 +436,7 @@ impl eframe::App for HomeApp { let free_space = xmrd_info.free_space / crate::BYTES_IN_GB; let db_size = xmrd_info.database_size / crate::BYTES_IN_GB; let ver = self.s_xmr_rpc_ver.result.version; - ui.label(format!("- rpc version: {}\n- height: {}%\n- blocks fetched: {}\n- address: {}\n- balance: {} piconero(s)\n- locked balance: {} piconero(s)\n- unlock time (secs): {}\n- daemon info\n\t- net type: {}\n\t- current hash: {}\n\t- height: {}\n\t- synced: {}\n\t- blockchain size : ~{} GB\n\t- free space : ~{} GB\n\t- version: {}\n", + ui.label(format!("- rpc version: {}\n- wallet sync: {}%\n- blocks fetched: {}\n- address: {}\n- balance: {} piconero(s)\n- locked balance: {} piconero(s)\n- unlock time (secs): {}\n- daemon info\n\t- net type: {}\n\t- current hash: {}\n\t- height: {}\n\t- synced: {}\n\t- blockchain size : ~{} GB\n\t- free space : ~{} GB\n\t- version: {}\n", ver, sync, blocks_fetched, address, unlocked_balance, locked_balance, unlock_time, xmrd_info.nettype, xmrd_info.top_block_hash, xmrd_info.height, xmrd_info.synchronized, db_size, free_space, xmrd_info.version)); diff --git a/neveko-gui/src/apps/settings.rs b/neveko-gui/src/apps/settings.rs index 3c59459..96b997f 100644 --- a/neveko-gui/src/apps/settings.rs +++ b/neveko-gui/src/apps/settings.rs @@ -3,25 +3,45 @@ use sha2::{ Digest, Sha512, }; +use std::sync::mpsc::{Receiver, Sender}; use crate::CREDENTIAL_KEY; -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct SettingsApp { credential: String, + change_wallet_password_tx: Sender, + change_wallet_password_rx: Receiver, + is_loading: bool, + is_not_showing_password: bool, } impl Default for SettingsApp { fn default() -> Self { + let (change_wallet_password_tx, change_wallet_password_rx) = std::sync::mpsc::channel(); SettingsApp { credential: utils::empty_string(), + change_wallet_password_rx, + change_wallet_password_tx, + is_loading: false, + is_not_showing_password: true, } } } impl eframe::App for SettingsApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + //--- async hooks + if let Ok(update_password) = self.change_wallet_password_rx.try_recv() { + if !update_password { + log::error!("failed to update wallet password"); + } + self.is_loading = false; + } + egui::CentralPanel::default().show(ctx, |ui| { + if self.is_loading { + ui.add(egui::Spinner::new()); + } ctx.settings_ui(ui); ui.label("\n\n"); ui.heading("Reset Credential"); @@ -29,11 +49,17 @@ impl eframe::App for SettingsApp { "____________________________________________________________________________\n", ); ui.horizontal(|ui| { - // TODO(c2m): call monero-wallet-rpc to update as well - let sweep_label = ui.label("new credential: \t"); - ui.text_edit_singleline(&mut self.credential) - .labelled_by(sweep_label.id); + ui.label("new credential: \t"); + ui.add( + egui::TextEdit::singleline(&mut self.credential) + .password(self.is_not_showing_password), + ); + let mut show_password = self.is_not_showing_password; + if ui.checkbox(&mut show_password, "show password").changed() { + self.is_not_showing_password = !self.is_not_showing_password; + } if ui.button("Change").clicked() { + self.is_loading = true; let s = db::Interface::open(); let k = CREDENTIAL_KEY; db::Interface::delete(&s.env, &s.handle, &k); @@ -41,9 +67,32 @@ impl eframe::App for SettingsApp { hasher.update(self.credential.clone()); let result = hasher.finalize(); db::Interface::write(&s.env, &s.handle, &k, &hex::encode(&result[..])); + // update wallet rpc + change_wallet_password(self.change_wallet_password_tx.clone(), &self.credential, ctx.clone()); self.credential = utils::empty_string(); } }); }); } } + +fn change_wallet_password( + change_wallet_password_tx: Sender, + new_password: &String, + ctx: egui::Context, +) { + let update_password = String::from(new_password); + tokio::spawn(async move { + let wallet_name = String::from(neveko_core::APP_NAME); + let wallet_password = + std::env::var(neveko_core::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password")); + monero::open_wallet(&wallet_name, &wallet_password).await; + let is_changed: bool = monero::change_wallet_password(&update_password).await; + if is_changed { + std::env::set_var(neveko_core::MONERO_WALLET_PASSWORD, update_password); + } + monero::close_wallet(&wallet_name, &wallet_password).await; + let _ = change_wallet_password_tx.send(is_changed); + ctx.request_repaint(); + }); +}