wallets: optimize loading, list refactoring, add wallet closing state, wallet data state and tx loading progress
This commit is contained in:
parent
e024dc34c6
commit
d84bdb6fb5
7 changed files with 486 additions and 281 deletions
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
use egui::{Align, Align2, Layout, Margin, RichText, Rounding, ScrollArea, TextStyle, Widget};
|
use egui::{Align, Align2, Layout, Margin, RichText, Rounding, ScrollArea, TextStyle, Widget};
|
||||||
use egui_extras::{Size, StripBuilder};
|
use egui_extras::{Size, StripBuilder};
|
||||||
use grin_core::global::ChainTypes;
|
|
||||||
|
|
||||||
use crate::AppConfig;
|
use crate::AppConfig;
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
|
@ -24,14 +23,12 @@ use crate::gui::views::{Modal, Root, TitlePanel, View};
|
||||||
use crate::gui::views::types::{ModalContainer, ModalPosition, TitleType};
|
use crate::gui::views::types::{ModalContainer, ModalPosition, TitleType};
|
||||||
use crate::gui::views::wallets::creation::WalletCreation;
|
use crate::gui::views::wallets::creation::WalletCreation;
|
||||||
use crate::gui::views::wallets::WalletContent;
|
use crate::gui::views::wallets::WalletContent;
|
||||||
use crate::wallet::{ConnectionsConfig, ExternalConnection, Wallet, Wallets};
|
use crate::wallet::{ConnectionsConfig, ExternalConnection, Wallet, WalletList};
|
||||||
|
|
||||||
/// Wallets content.
|
/// Wallets content.
|
||||||
pub struct WalletsContent {
|
pub struct WalletsContent {
|
||||||
/// Chain type for loaded wallets.
|
|
||||||
chain_type: ChainTypes,
|
|
||||||
/// Loaded list of wallets.
|
/// Loaded list of wallets.
|
||||||
wallets: Wallets,
|
wallets: WalletList,
|
||||||
|
|
||||||
/// Password to open wallet for [`Modal`].
|
/// Password to open wallet for [`Modal`].
|
||||||
pass_edit: String,
|
pass_edit: String,
|
||||||
|
@ -58,8 +55,7 @@ const OPEN_WALLET_MODAL: &'static str = "open_wallet_modal";
|
||||||
impl Default for WalletsContent {
|
impl Default for WalletsContent {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
chain_type: AppConfig::chain_type(),
|
wallets: WalletList::default(),
|
||||||
wallets: Wallets::default(),
|
|
||||||
pass_edit: "".to_string(),
|
pass_edit: "".to_string(),
|
||||||
hide_pass: true,
|
hide_pass: true,
|
||||||
wrong_pass: false,
|
wrong_pass: false,
|
||||||
|
@ -99,13 +95,8 @@ impl WalletsContent {
|
||||||
// Draw modal content for current ui container.
|
// Draw modal content for current ui container.
|
||||||
self.current_modal_ui(ui, frame, cb);
|
self.current_modal_ui(ui, frame, cb);
|
||||||
|
|
||||||
// Setup list of wallets if chain type was changed.
|
let wallets = self.wallets.list();
|
||||||
let chain_type = AppConfig::chain_type();
|
let empty_list = wallets.is_empty();
|
||||||
if self.chain_type != chain_type {
|
|
||||||
self.wallets.reinit(chain_type);
|
|
||||||
self.chain_type = chain_type;
|
|
||||||
}
|
|
||||||
let empty_list = self.wallets.list.is_empty();
|
|
||||||
|
|
||||||
// Setup wallet content flags.
|
// Setup wallet content flags.
|
||||||
let create_wallet = self.creation_content.can_go_back();
|
let create_wallet = self.creation_content.can_go_back();
|
||||||
|
@ -123,7 +114,7 @@ impl WalletsContent {
|
||||||
// Show wallet panel content.
|
// Show wallet panel content.
|
||||||
egui::SidePanel::right("wallet_panel")
|
egui::SidePanel::right("wallet_panel")
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.min_width(wallet_panel_width)
|
.exact_width(wallet_panel_width)
|
||||||
.frame(egui::Frame {
|
.frame(egui::Frame {
|
||||||
fill: if empty_list && !create_wallet
|
fill: if empty_list && !create_wallet
|
||||||
|| (dual_panel && show_wallet && !self.show_wallets_at_dual_panel) {
|
|| (dual_panel && show_wallet && !self.show_wallets_at_dual_panel) {
|
||||||
|
@ -149,7 +140,7 @@ impl WalletsContent {
|
||||||
self.wallets.add(wallet);
|
self.wallets.add(wallet);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
for mut wallet in self.wallets.list.clone() {
|
for mut wallet in wallets.clone() {
|
||||||
// Show content for selected wallet.
|
// Show content for selected wallet.
|
||||||
if self.wallets.is_selected(wallet.config.id) {
|
if self.wallets.is_selected(wallet.config.id) {
|
||||||
// Setup wallet content width.
|
// Setup wallet content width.
|
||||||
|
@ -205,13 +196,14 @@ impl WalletsContent {
|
||||||
|
|
||||||
// Setup flag to show wallet creation button if wallet is not showing
|
// Setup flag to show wallet creation button if wallet is not showing
|
||||||
// at non-dual panel mode or showing at dual panel mode.
|
// at non-dual panel mode or showing at dual panel mode.
|
||||||
let show_creation_btn
|
let show_creation_button
|
||||||
= (!show_wallet && !dual_panel) || (dual_panel && show_wallet);
|
= (!show_wallet && !dual_panel) || (dual_panel && show_wallet);
|
||||||
|
|
||||||
// Show list of wallets.
|
// Show list of wallets.
|
||||||
let scroll = self.wallet_list_ui(ui, dual_panel, show_creation_btn, cb);
|
let scroll =
|
||||||
|
self.wallet_list_ui(ui, wallets, dual_panel, show_creation_button, cb);
|
||||||
|
|
||||||
if show_creation_btn {
|
if show_creation_button {
|
||||||
// Setup right margin for button.
|
// Setup right margin for button.
|
||||||
let mut right_margin = if dual_panel { wallet_panel_width } else { 0.0 };
|
let mut right_margin = if dual_panel { wallet_panel_width } else { 0.0 };
|
||||||
if scroll { right_margin += 6.0 }
|
if scroll { right_margin += 6.0 }
|
||||||
|
@ -304,6 +296,7 @@ impl WalletsContent {
|
||||||
/// Draw list of wallets. Returns `true` if scroller is showing.
|
/// Draw list of wallets. Returns `true` if scroller is showing.
|
||||||
fn wallet_list_ui(&mut self,
|
fn wallet_list_ui(&mut self,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
|
wallets: Vec<Wallet>,
|
||||||
dual_panel: bool,
|
dual_panel: bool,
|
||||||
show_creation_btn: bool,
|
show_creation_btn: bool,
|
||||||
cb: &dyn PlatformCallbacks) -> bool {
|
cb: &dyn PlatformCallbacks) -> bool {
|
||||||
|
@ -331,7 +324,7 @@ impl WalletsContent {
|
||||||
rect.set_width(width);
|
rect.set_width(width);
|
||||||
|
|
||||||
ui.allocate_ui(rect.size(), |ui| {
|
ui.allocate_ui(rect.size(), |ui| {
|
||||||
for mut wallet in self.wallets.list.clone() {
|
for mut wallet in wallets {
|
||||||
// Draw wallet list item.
|
// Draw wallet list item.
|
||||||
self.wallet_item_ui(ui, &mut wallet, cb);
|
self.wallet_item_ui(ui, &mut wallet, cb);
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
|
@ -386,14 +379,17 @@ impl WalletsContent {
|
||||||
self.wallets.select(Some(id));
|
self.wallets.select(Some(id));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Show button to close opened wallet.
|
|
||||||
View::item_button(ui, if !is_selected {
|
// Show button to close opened wallet if wallet is not loading.
|
||||||
Rounding::none()
|
if !wallet.is_closing() {
|
||||||
} else {
|
View::item_button(ui, if !is_selected {
|
||||||
View::item_rounding(0, 1, true)
|
Rounding::none()
|
||||||
}, LOCK_KEY, None, || {
|
} else {
|
||||||
let _ = wallet.close();
|
View::item_rounding(0, 1, true)
|
||||||
});
|
}, LOCK_KEY, None, || {
|
||||||
|
wallet.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout_size = ui.available_size();
|
let layout_size = ui.available_size();
|
||||||
|
@ -420,7 +416,9 @@ impl WalletsContent {
|
||||||
|
|
||||||
// Setup wallet status text.
|
// Setup wallet status text.
|
||||||
let status_text = if wallet.is_open() {
|
let status_text = if wallet.is_open() {
|
||||||
if wallet.get_info().is_none() {
|
if wallet.is_closing() {
|
||||||
|
format!("{} {}", SPINNER, t!("wallets.wallet_closing"))
|
||||||
|
} else if wallet.get_data().is_none() {
|
||||||
if wallet.loading_error() {
|
if wallet.loading_error() {
|
||||||
format!("{} {}", WARNING_CIRCLE, t!("loading_error"))
|
format!("{} {}", WARNING_CIRCLE, t!("loading_error"))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -79,7 +79,7 @@ impl WalletContent {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Refresh content after 1 second for loaded wallet.
|
// Refresh content after 1 second for loaded wallet.
|
||||||
if wallet.get_info().is_some() {
|
if wallet.get_data().is_some() {
|
||||||
ui.ctx().request_repaint_after(Duration::from_millis(1000));
|
ui.ctx().request_repaint_after(Duration::from_millis(1000));
|
||||||
} else {
|
} else {
|
||||||
ui.ctx().request_repaint();
|
ui.ctx().request_repaint();
|
||||||
|
@ -123,10 +123,13 @@ impl WalletContent {
|
||||||
|
|
||||||
/// Content to draw when wallet is loading, returns `true` if wallet is not ready.
|
/// Content to draw when wallet is loading, returns `true` if wallet is not ready.
|
||||||
pub fn loading_ui(ui: &mut egui::Ui, frame: &mut eframe::Frame, wallet: &Wallet) -> bool {
|
pub fn loading_ui(ui: &mut egui::Ui, frame: &mut eframe::Frame, wallet: &Wallet) -> bool {
|
||||||
if wallet.config.ext_conn_id.is_none() {
|
if wallet.is_closing() {
|
||||||
|
Self::loading_progress_ui(ui, wallet);
|
||||||
|
return true;
|
||||||
|
} else if wallet.config.ext_conn_id.is_none() {
|
||||||
if !Node::is_running() || Node::is_stopping() {
|
if !Node::is_running() || Node::is_stopping() {
|
||||||
let dual_panel_root = Root::is_dual_panel_mode(frame);
|
let dual_panel_root = Root::is_dual_panel_mode(frame);
|
||||||
View::center_content(ui, if !dual_panel_root { 162.0 } else { 96.0 }, |ui| {
|
View::center_content(ui, 108.0, |ui| {
|
||||||
let text = t!("wallets.enable_node", "settings" => GEAR_FINE);
|
let text = t!("wallets.enable_node", "settings" => GEAR_FINE);
|
||||||
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
|
@ -141,41 +144,62 @@ impl WalletContent {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true
|
return true
|
||||||
} else if wallet.get_info().is_none() {
|
} else if wallet.loading_error()
|
||||||
Self::progress_ui(ui, wallet);
|
&& Node::get_sync_status() == Some(SyncStatus::NoSync) {
|
||||||
|
Self::loading_error_ui(ui, wallet);
|
||||||
|
return true;
|
||||||
|
} else if wallet.get_data().is_none() {
|
||||||
|
Self::loading_progress_ui(ui, wallet);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if wallet.get_info().is_none() {
|
} else if wallet.get_data().is_none() {
|
||||||
// Show error message with button to retry on wallet loading error or loading progress.
|
|
||||||
if wallet.loading_error() {
|
if wallet.loading_error() {
|
||||||
View::center_content(ui, 162.0, |ui| {
|
Self::loading_error_ui(ui, wallet);
|
||||||
let text = t!("wallets.wallet_loading_err", "settings" => GEAR_FINE);
|
|
||||||
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
|
||||||
ui.add_space(8.0);
|
|
||||||
let retry_text = format!("{} {}", REPEAT, t!("retry"));
|
|
||||||
View::button(ui, retry_text, Colors::GOLD, || {
|
|
||||||
wallet.set_loading_error(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
Self::progress_ui(ui, wallet);
|
Self::loading_progress_ui(ui, wallet);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw wallet loading progress.
|
/// Draw wallet loading error content.
|
||||||
fn progress_ui(ui: &mut egui::Ui, wallet: &Wallet) {
|
fn loading_error_ui(ui: &mut egui::Ui, wallet: &Wallet) {
|
||||||
|
View::center_content(ui, 108.0, |ui| {
|
||||||
|
let text = t!("wallets.wallet_loading_err", "settings" => GEAR_FINE);
|
||||||
|
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
||||||
|
ui.add_space(8.0);
|
||||||
|
let retry_text = format!("{} {}", REPEAT, t!("retry"));
|
||||||
|
View::button(ui, retry_text, Colors::GOLD, || {
|
||||||
|
wallet.set_loading_error(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw wallet loading progress content.
|
||||||
|
fn loading_progress_ui(ui: &mut egui::Ui, wallet: &Wallet) {
|
||||||
View::center_content(ui, 162.0, |ui| {
|
View::center_content(ui, 162.0, |ui| {
|
||||||
View::big_loading_spinner(ui);
|
View::big_loading_spinner(ui);
|
||||||
ui.add_space(18.0);
|
ui.add_space(18.0);
|
||||||
// Setup loading progress text.
|
// Setup loading progress text.
|
||||||
let progress = wallet.loading_progress();
|
let text = {
|
||||||
let text = if progress == 0 {
|
let info_progress = wallet.info_loading_progress();
|
||||||
t!("wallets.wallet_loading")
|
if wallet.is_closing() {
|
||||||
} else {
|
t!("wallets.wallet_closing")
|
||||||
format!("{}: {}%", t!("wallets.wallet_loading"), progress)
|
} else if info_progress != 100 {
|
||||||
|
if info_progress == 0 {
|
||||||
|
t!("wallets.wallet_loading")
|
||||||
|
} else {
|
||||||
|
format!("{}: {}%", t!("wallets.wallet_loading"), info_progress)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let tx_progress = wallet.txs_loading_progress();
|
||||||
|
if tx_progress == 0 {
|
||||||
|
t!("wallets.tx_loading")
|
||||||
|
} else {
|
||||||
|
format!("{}: {}%", t!("wallets.tx_loading"), tx_progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,6 +25,10 @@ impl WalletTab for WalletSettings {
|
||||||
WalletTabType::Settings
|
WalletTabType::Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
|
fn ui(&mut self,
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
frame: &mut eframe::Frame,
|
||||||
|
wallet: &Wallet,
|
||||||
|
cb: &dyn PlatformCallbacks) {
|
||||||
}
|
}
|
||||||
}
|
}
|
112
src/wallet/list.rs
Normal file
112
src/wallet/list.rs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright 2023 The Grim Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use grin_core::global::ChainTypes;
|
||||||
|
use grin_wallet_libwallet::Error;
|
||||||
|
|
||||||
|
use crate::AppConfig;
|
||||||
|
use crate::wallet::{Wallet, WalletConfig};
|
||||||
|
|
||||||
|
/// Wrapper for [`Wallet`] list.
|
||||||
|
pub struct WalletList {
|
||||||
|
/// List of wallets.
|
||||||
|
list: Vec<Wallet>,
|
||||||
|
/// Selected [`Wallet`] identifier.
|
||||||
|
selected_id: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WalletList {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
list: Self::init(),
|
||||||
|
selected_id: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WalletList {
|
||||||
|
/// Initialize [`Wallet`] list from base directory.
|
||||||
|
fn init() -> Vec<Wallet> {
|
||||||
|
let mut wallets = Vec::new();
|
||||||
|
let chain_types = vec![ChainTypes::Mainnet, ChainTypes::Testnet];
|
||||||
|
for chain in chain_types {
|
||||||
|
let wallets_dir = WalletConfig::get_base_path(chain);
|
||||||
|
// Load wallets from base directory.
|
||||||
|
for dir in wallets_dir.read_dir().unwrap() {
|
||||||
|
let wallet_dir = dir.unwrap().path();
|
||||||
|
if wallet_dir.is_dir() {
|
||||||
|
let wallet = Wallet::init(wallet_dir);
|
||||||
|
if let Some(w) = wallet {
|
||||||
|
wallets.push(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wallets
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get wallet list for current [`ChainTypes`].
|
||||||
|
pub fn list(&self) -> Vec<Wallet> {
|
||||||
|
let chain_type = AppConfig::chain_type();
|
||||||
|
self.list.iter().cloned()
|
||||||
|
.filter(|w| w.config.chain_type == chain_type)
|
||||||
|
.collect::<Vec<Wallet>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add created [`Wallet`] to the list.
|
||||||
|
pub fn add(&mut self, wallet: Wallet) {
|
||||||
|
self.selected_id = Some(wallet.config.id);
|
||||||
|
self.list.insert(0, wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select [`Wallet`] with provided identifier.
|
||||||
|
pub fn select(&mut self, id: Option<i64>) {
|
||||||
|
self.selected_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get selected [`Wallet`] name.
|
||||||
|
pub fn selected_name(&self) -> String {
|
||||||
|
for w in &self.list {
|
||||||
|
if Some(w.config.id) == self.selected_id {
|
||||||
|
return w.config.name.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t!("wallets.unlocked")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if [`Wallet`] is selected for provided identifier.
|
||||||
|
pub fn is_selected(&self, id: i64) -> bool {
|
||||||
|
return Some(id) == self.selected_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if selected [`Wallet`] is open.
|
||||||
|
pub fn is_selected_open(&self) -> bool {
|
||||||
|
for w in &self.list {
|
||||||
|
if Some(w.config.id) == self.selected_id {
|
||||||
|
return w.is_open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open selected [`Wallet`].
|
||||||
|
pub fn open_selected(&mut self, password: String) -> Result<(), Error> {
|
||||||
|
for w in self.list.iter_mut() {
|
||||||
|
if Some(w.config.id) == self.selected_id {
|
||||||
|
return w.open(password.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::GenericError("Wallet is not selected".to_string()))
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,4 +28,7 @@ mod wallets;
|
||||||
pub use wallets::*;
|
pub use wallets::*;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
|
|
||||||
|
mod list;
|
||||||
|
pub use list::*;
|
|
@ -16,7 +16,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use grin_keychain::ExtKeychain;
|
use grin_keychain::ExtKeychain;
|
||||||
use grin_wallet_impls::{DefaultLCProvider, HTTPNodeClient};
|
use grin_wallet_impls::{DefaultLCProvider, HTTPNodeClient};
|
||||||
use grin_wallet_libwallet::WalletInst;
|
use grin_wallet_libwallet::{TxLogEntry, WalletInfo, WalletInst};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
/// Mnemonic phrase setup mode.
|
/// Mnemonic phrase setup mode.
|
||||||
|
@ -64,6 +64,15 @@ impl PhraseSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wallet connection method.
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum ConnectionMethod {
|
||||||
|
/// Integrated node.
|
||||||
|
Integrated,
|
||||||
|
/// External node, contains connection identifier.
|
||||||
|
External(i64)
|
||||||
|
}
|
||||||
|
|
||||||
/// Wallet instance type.
|
/// Wallet instance type.
|
||||||
pub type WalletInstance = Arc<
|
pub type WalletInstance = Arc<
|
||||||
Mutex<
|
Mutex<
|
||||||
|
@ -78,11 +87,11 @@ pub type WalletInstance = Arc<
|
||||||
>,
|
>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/// Wallet node connection method type.
|
/// Contains wallet data to show.
|
||||||
#[derive(PartialEq)]
|
#[derive(Clone)]
|
||||||
pub enum ConnectionMethod {
|
pub struct WalletData {
|
||||||
/// Integrated node.
|
/// Wallet balance.
|
||||||
Integrated,
|
pub info: WalletInfo,
|
||||||
/// External node, contains connection identifier.
|
/// Transactions.
|
||||||
External(i64)
|
pub txs: Vec<TxLogEntry>
|
||||||
}
|
}
|
|
@ -14,115 +14,24 @@
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, mpsc, RwLock};
|
use std::sync::{Arc, mpsc, RwLock};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::thread::Thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use grin_chain::SyncStatus;
|
use grin_chain::SyncStatus;
|
||||||
use grin_core::global;
|
use grin_core::global;
|
||||||
use grin_core::global::ChainTypes;
|
|
||||||
use grin_keychain::{ExtKeychain, Keychain};
|
use grin_keychain::{ExtKeychain, Keychain};
|
||||||
use grin_util::secp::SecretKey;
|
use grin_util::secp::SecretKey;
|
||||||
use grin_util::types::ZeroingString;
|
use grin_util::types::ZeroingString;
|
||||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
|
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
|
||||||
use grin_wallet_libwallet::{Error, NodeClient, StatusMessage, WalletBackend, WalletInfo, WalletInst, WalletLCProvider};
|
use grin_wallet_libwallet::{Error, NodeClient, StatusMessage, WalletInst, WalletLCProvider};
|
||||||
use grin_wallet_libwallet::api_impl::owner::retrieve_summary_info;
|
use grin_wallet_libwallet::api_impl::owner::{retrieve_summary_info, retrieve_txs};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use crate::AppConfig;
|
|
||||||
use crate::node::{Node, NodeConfig};
|
use crate::node::{Node, NodeConfig};
|
||||||
use crate::wallet::{ConnectionsConfig, ExternalConnection, WalletConfig};
|
use crate::wallet::{ConnectionsConfig, ExternalConnection, WalletConfig};
|
||||||
use crate::wallet::types::{ConnectionMethod, WalletInstance};
|
use crate::wallet::types::{ConnectionMethod, WalletData, WalletInstance};
|
||||||
|
|
||||||
/// [`Wallet`] list wrapper.
|
|
||||||
pub struct Wallets {
|
|
||||||
/// List of wallets.
|
|
||||||
pub(crate) list: Vec<Wallet>,
|
|
||||||
/// Selected [`Wallet`] identifier.
|
|
||||||
selected_id: Option<i64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Wallets {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
list: Self::init(AppConfig::chain_type()),
|
|
||||||
selected_id: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wallets {
|
|
||||||
/// Initialize [`Wallet`] list from base directory for provided [`ChainType`].
|
|
||||||
fn init(chain_type: ChainTypes) -> Vec<Wallet> {
|
|
||||||
let mut wallets = Vec::new();
|
|
||||||
let wallets_dir = WalletConfig::get_base_path(chain_type);
|
|
||||||
// Load wallets from base directory.
|
|
||||||
for dir in wallets_dir.read_dir().unwrap() {
|
|
||||||
let wallet_dir = dir.unwrap().path();
|
|
||||||
if wallet_dir.is_dir() {
|
|
||||||
let wallet = Wallet::init(wallet_dir);
|
|
||||||
if let Some(w) = wallet {
|
|
||||||
wallets.push(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wallets
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reinitialize [`Wallet`] list for provided [`ChainTypes`].
|
|
||||||
pub fn reinit(&mut self, chain_type: ChainTypes) {
|
|
||||||
for w in self.list.iter_mut() {
|
|
||||||
let _ = w.close();
|
|
||||||
}
|
|
||||||
self.list = Self::init(chain_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add created [`Wallet`] to the list.
|
|
||||||
pub fn add(&mut self, wallet: Wallet) {
|
|
||||||
self.selected_id = Some(wallet.config.id);
|
|
||||||
self.list.insert(0, wallet);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select [`Wallet`] with provided identifier.
|
|
||||||
pub fn select(&mut self, id: Option<i64>) {
|
|
||||||
self.selected_id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get selected [`Wallet`] name.
|
|
||||||
pub fn selected_name(&self) -> String {
|
|
||||||
for w in &self.list {
|
|
||||||
if Some(w.config.id) == self.selected_id {
|
|
||||||
return w.config.name.to_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t!("wallets.unlocked")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if [`Wallet`] is selected for provided identifier.
|
|
||||||
pub fn is_selected(&self, id: i64) -> bool {
|
|
||||||
return Some(id) == self.selected_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if selected [`Wallet`] is open.
|
|
||||||
pub fn is_selected_open(&self) -> bool {
|
|
||||||
for w in &self.list {
|
|
||||||
if Some(w.config.id) == self.selected_id {
|
|
||||||
return w.is_open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open selected [`Wallet`].
|
|
||||||
pub fn open_selected(&mut self, password: String) -> Result<(), Error> {
|
|
||||||
for w in self.list.iter_mut() {
|
|
||||||
if Some(w.config.id) == self.selected_id {
|
|
||||||
return w.open(password.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(Error::GenericError("Wallet is not selected".to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Contains wallet instance and handles wallet commands.
|
/// Contains wallet instance and handles wallet commands.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -132,19 +41,25 @@ pub struct Wallet {
|
||||||
/// Wallet configuration.
|
/// Wallet configuration.
|
||||||
pub config: WalletConfig,
|
pub config: WalletConfig,
|
||||||
|
|
||||||
/// Identifier for launched wallet thread.
|
/// Wallet updating thread.
|
||||||
thread_id: Arc<AtomicI64>,
|
thread: Arc<RwLock<Option<Thread>>>,
|
||||||
|
|
||||||
/// Flag to check if wallet is open.
|
/// Flag to check if wallet is open.
|
||||||
is_open: Arc<AtomicBool>,
|
is_open: Arc<AtomicBool>,
|
||||||
|
/// Flag to check if wallet is loading.
|
||||||
|
closing: Arc<AtomicBool>,
|
||||||
|
|
||||||
/// Error on wallet loading.
|
/// Error on wallet loading.
|
||||||
loading_error: Arc<AtomicBool>,
|
loading_error: Arc<AtomicBool>,
|
||||||
/// Loading progress in percents
|
/// Info loading progress in percents
|
||||||
loading_progress: Arc<AtomicU8>,
|
info_loading_progress: Arc<AtomicU8>,
|
||||||
|
/// Transactions loading progress in percents
|
||||||
|
txs_loading_progress: Arc<AtomicU8>,
|
||||||
|
|
||||||
/// Wallet balance information.
|
/// Wallet data.
|
||||||
info: Arc<RwLock<Option<WalletInfo>>>
|
data: Arc<RwLock<Option<WalletData>>>,
|
||||||
|
/// Attempts amount to update wallet data.
|
||||||
|
data_update_attempts: Arc<AtomicU8>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
|
@ -153,11 +68,14 @@ impl Wallet {
|
||||||
Self {
|
Self {
|
||||||
instance,
|
instance,
|
||||||
config,
|
config,
|
||||||
thread_id: Arc::new(AtomicI64::new(0)),
|
thread: Arc::from(RwLock::new(None)),
|
||||||
is_open: Arc::from(AtomicBool::new(false)),
|
is_open: Arc::from(AtomicBool::new(false)),
|
||||||
|
closing: Arc::new(AtomicBool::new(false)),
|
||||||
loading_error: Arc::from(AtomicBool::new(false)),
|
loading_error: Arc::from(AtomicBool::new(false)),
|
||||||
loading_progress: Arc::new(AtomicU8::new(0)),
|
info_loading_progress: Arc::from(AtomicU8::new(0)),
|
||||||
info: Arc::new(RwLock::new(None)),
|
txs_loading_progress: Arc::from(AtomicU8::new(0)),
|
||||||
|
data: Arc::from(RwLock::new(None)),
|
||||||
|
data_update_attempts: Arc::new(AtomicU8::new(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +103,7 @@ impl Wallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize [`Wallet`] from provided data path.
|
/// Initialize [`Wallet`] from provided data path.
|
||||||
fn init(data_path: PathBuf) -> Option<Wallet> {
|
pub fn init(data_path: PathBuf) -> Option<Wallet> {
|
||||||
let wallet_config = WalletConfig::load(data_path.clone());
|
let wallet_config = WalletConfig::load(data_path.clone());
|
||||||
if let Some(config) = wallet_config {
|
if let Some(config) = wallet_config {
|
||||||
if let Ok(instance) = Self::create_wallet_instance(config.clone()) {
|
if let Ok(instance) = Self::create_wallet_instance(config.clone()) {
|
||||||
|
@ -196,10 +114,8 @@ impl Wallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reinitialize [`WalletInstance`] to apply new [`WalletConfig`].
|
/// Reinitialize [`WalletInstance`] to apply new [`WalletConfig`].
|
||||||
pub fn reinit(&mut self) -> Result<(), Error> {
|
pub fn reinit(&self) {
|
||||||
self.close()?;
|
//TODO: Reinit wallet.
|
||||||
self.instance = Self::create_wallet_instance(self.config.clone())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create [`WalletInstance`] from provided [`WalletConfig`].
|
/// Create [`WalletInstance`] from provided [`WalletConfig`].
|
||||||
|
@ -254,39 +170,38 @@ impl Wallet {
|
||||||
Ok(Arc::new(Mutex::new(wallet)))
|
Ok(Arc::new(Mutex::new(wallet)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open the wallet and start commands handling and updating info at separate thread.
|
/// Open the wallet and start update the data at separate thread.
|
||||||
pub fn open(&self, password: String) -> Result<(), Error> {
|
pub fn open(&self, password: String) -> Result<(), Error> {
|
||||||
let mut wallet_lock = self.instance.lock();
|
let mut wallet_lock = self.instance.lock();
|
||||||
let lc = wallet_lock.lc_provider()?;
|
let lc = wallet_lock.lc_provider()?;
|
||||||
lc.close_wallet(None)?;
|
|
||||||
|
|
||||||
// Open the wallet.
|
// Open the wallet.
|
||||||
match lc.open_wallet(None, ZeroingString::from(password), false, false) {
|
match lc.open_wallet(None, ZeroingString::from(password), false, false) {
|
||||||
Ok(keychain) => {
|
Ok(keychain) => {
|
||||||
|
// Start data updating if thread was not launched.
|
||||||
|
let mut thread_w = self.thread.write().unwrap();
|
||||||
|
if thread_w.is_none() {
|
||||||
|
println!("create new thread");
|
||||||
|
let thread = start_wallet(self.clone(), keychain.clone());
|
||||||
|
*thread_w = Some(thread);
|
||||||
|
} else {
|
||||||
|
println!("unfreeze thread");
|
||||||
|
thread_w.clone().unwrap().unpark();
|
||||||
|
}
|
||||||
self.is_open.store(true, Ordering::Relaxed);
|
self.is_open.store(true, Ordering::Relaxed);
|
||||||
// Start info updating and commands handling.
|
|
||||||
start_wallet(self.clone(), keychain);
|
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e)
|
Err(e) => return Err(e)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get wallet thread identifier.
|
/// Get wallet transactions loading progress.
|
||||||
fn get_thread_id(&self) -> i64 {
|
pub fn txs_loading_progress(&self) -> u8 {
|
||||||
self.thread_id.load(Ordering::Relaxed)
|
self.txs_loading_progress.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refresh wallet thread identifier.
|
/// Get wallet info loading progress.
|
||||||
fn new_thread_id(&self) -> i64 {
|
pub fn info_loading_progress(&self) -> u8 {
|
||||||
let id = chrono::Utc::now().timestamp();
|
self.info_loading_progress.load(Ordering::Relaxed)
|
||||||
self.thread_id.store(id, Ordering::Relaxed);
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get wallet loading progress after opening.
|
|
||||||
pub fn loading_progress(&self) -> u8 {
|
|
||||||
self.loading_progress.load(Ordering::Relaxed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if wallet had an error on loading.
|
/// Check if wallet had an error on loading.
|
||||||
|
@ -299,121 +214,261 @@ impl Wallet {
|
||||||
self.loading_error.store(error, Ordering::Relaxed);
|
self.loading_error.store(error, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get wallet data update attempts.
|
||||||
|
fn get_data_update_attempts(&self) -> u8 {
|
||||||
|
self.data_update_attempts.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment wallet data update attempts.
|
||||||
|
fn increment_data_update_attempts(&self) {
|
||||||
|
let mut attempts = self.get_data_update_attempts();
|
||||||
|
attempts += 1;
|
||||||
|
self.data_update_attempts.store(attempts, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset wallet data update attempts.
|
||||||
|
fn reset_data_update_attempts(&self) {
|
||||||
|
self.data_update_attempts.store(0, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if wallet was open.
|
/// Check if wallet was open.
|
||||||
pub fn is_open(&self) -> bool {
|
pub fn is_open(&self) -> bool {
|
||||||
self.is_open.load(Ordering::Relaxed)
|
self.is_open.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close the wallet.
|
/// Check if wallet is closing.
|
||||||
pub fn close(&mut self) -> Result<(), Error> {
|
pub fn is_closing(&self) -> bool {
|
||||||
if self.is_open() {
|
self.closing.load(Ordering::Relaxed)
|
||||||
let mut wallet_lock = self.instance.lock();
|
|
||||||
let lc = wallet_lock.lc_provider()?;
|
|
||||||
lc.close_wallet(None)?;
|
|
||||||
// Clear wallet info.
|
|
||||||
let mut w_info = self.info.write().unwrap();
|
|
||||||
*w_info = None;
|
|
||||||
// Mark wallet as not opened.
|
|
||||||
self.is_open.store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get wallet balance info.
|
/// Close the wallet.
|
||||||
pub fn get_info(&self) -> Option<WalletInfo> {
|
pub fn close(&self) {
|
||||||
let r_info = self.info.read().unwrap();
|
if !self.is_open() {
|
||||||
r_info.clone()
|
return;
|
||||||
|
}
|
||||||
|
self.closing.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Close wallet at separate thread.
|
||||||
|
let wallet_close = self.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
// Close the wallet.
|
||||||
|
let _ = Self::close_wallet(&wallet_close.instance);
|
||||||
|
|
||||||
|
// Clear wallet info.
|
||||||
|
let mut w_data = wallet_close.data.write().unwrap();
|
||||||
|
*w_data = None;
|
||||||
|
|
||||||
|
// Reset wallet loading values.
|
||||||
|
wallet_close.info_loading_progress.store(0, Ordering::Relaxed);
|
||||||
|
wallet_close.txs_loading_progress.store(0, Ordering::Relaxed);
|
||||||
|
wallet_close.set_loading_error(false);
|
||||||
|
wallet_close.reset_data_update_attempts();
|
||||||
|
|
||||||
|
// Mark wallet as not opened.
|
||||||
|
wallet_close.closing.store(false, Ordering::Relaxed);
|
||||||
|
wallet_close.is_open.store(false, Ordering::Relaxed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close provided [`WalletInstance`].
|
||||||
|
fn close_wallet(instance: &WalletInstance) -> Result<(), Error> {
|
||||||
|
let mut wallet_lock = instance.lock();
|
||||||
|
let lc = wallet_lock.lc_provider()?;
|
||||||
|
lc.close_wallet(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get wallet data.
|
||||||
|
pub fn get_data(&self) -> Option<WalletData> {
|
||||||
|
let r_data = self.data.read().unwrap();
|
||||||
|
r_data.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delay in seconds to update wallet info.
|
/// Delay in seconds to update wallet data every minute as average block time.
|
||||||
const INFO_UPDATE_DELAY: Duration = Duration::from_millis(20 * 1000);
|
const DATA_UPDATE_DELAY: Duration = Duration::from_millis(20 * 1000);
|
||||||
|
|
||||||
/// Launch thread for commands handling and info updating after [`INFO_UPDATE_DELAY`].
|
/// Number of attempts to update data after wallet opening before setting an error.
|
||||||
fn start_wallet(mut wallet: Wallet, keychain_mask: Option<SecretKey>) {
|
const DATA_UPDATE_ATTEMPTS: u8 = 10;
|
||||||
// Setup initial loading values.
|
|
||||||
wallet.loading_progress.store(0, Ordering::Relaxed);
|
|
||||||
wallet.set_loading_error(false);
|
|
||||||
|
|
||||||
// Launch loop at separate thread to update wallet info.
|
/// Launch thread to update wallet data.
|
||||||
let thread_id = wallet.new_thread_id();
|
fn start_wallet(wallet: Wallet, keychain: Option<SecretKey>) -> Thread {
|
||||||
|
let wallet_update = wallet.clone();
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
// Stop updating if wallet was closed or thread changed.
|
println!("start new cycle");
|
||||||
if !wallet.is_open() || thread_id != wallet.get_thread_id() {
|
// Stop updating if wallet was closed.
|
||||||
break;
|
if !wallet_update.is_open() {
|
||||||
|
println!("finishing thread at start");
|
||||||
|
let mut thread_w = wallet_update.thread.write().unwrap();
|
||||||
|
*thread_w = None;
|
||||||
|
println!("finish at start complete");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup error when required integrated node is not enabled or skip when it's not ready.
|
// Set an error when required integrated node is not enabled and
|
||||||
if wallet.config.ext_conn_id.is_none() {
|
// skip next cycle of update when node sync is not finished.
|
||||||
if !Node::is_running() {
|
if wallet_update.config.ext_conn_id.is_none() {
|
||||||
wallet.set_loading_error(true);
|
wallet_update.set_loading_error(!Node::is_running() || Node::is_stopping());
|
||||||
} else if Node::get_sync_status() != Some(SyncStatus::NoSync) {
|
if !Node::is_running() || Node::get_sync_status() != Some(SyncStatus::NoSync) {
|
||||||
wallet.set_loading_error(false);
|
println!("integrated node wait");
|
||||||
thread::sleep(Duration::from_millis(1000));
|
thread::park_timeout(Duration::from_millis(1000));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update wallet info if it was no loading error after several attempts.
|
// Update wallet data if there is no error.
|
||||||
if !wallet.loading_error() {
|
if !wallet_update.loading_error() {
|
||||||
let wallet_scan = wallet.clone();
|
update_wallet_data(&wallet_update, keychain.clone());
|
||||||
let (tx, rx) = mpsc::channel::<StatusMessage>();
|
|
||||||
// Update loading progress at separate thread.
|
|
||||||
thread::spawn(move || {
|
|
||||||
while let Ok(m) = rx.recv() {
|
|
||||||
// Stop updating if wallet was closed or maximum.
|
|
||||||
if !wallet_scan.is_open() || wallet_scan.get_thread_id() != thread_id {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
match m {
|
|
||||||
StatusMessage::UpdatingOutputs(_) => {}
|
|
||||||
StatusMessage::UpdatingTransactions(_) => {}
|
|
||||||
StatusMessage::FullScanWarn(_) => {}
|
|
||||||
StatusMessage::Scanning(_, progress) => {
|
|
||||||
wallet_scan.loading_progress.store(progress, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
StatusMessage::ScanningComplete(_) => {
|
|
||||||
wallet_scan.loading_progress.store(100, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
StatusMessage::UpdateWarning(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Retrieve wallet info.
|
|
||||||
match retrieve_summary_info(
|
|
||||||
wallet.instance.clone(),
|
|
||||||
keychain_mask.as_ref(),
|
|
||||||
&Some(tx),
|
|
||||||
true,
|
|
||||||
wallet.config.min_confirmations
|
|
||||||
) {
|
|
||||||
Ok(info) => {
|
|
||||||
if wallet.loading_progress() != 100 {
|
|
||||||
wallet.set_loading_error(true);
|
|
||||||
} else {
|
|
||||||
let mut w_info = wallet.info.write().unwrap();
|
|
||||||
*w_info = Some(info.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
wallet.set_loading_error(true);
|
|
||||||
wallet.loading_progress.store(0, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop updating if wallet was closed.
|
// Stop updating if wallet was closed.
|
||||||
if !wallet.is_open() || wallet.get_thread_id() != thread_id {
|
if !wallet_update.is_open() {
|
||||||
break;
|
println!("finishing thread after updating");
|
||||||
|
let mut thread_w = wallet_update.thread.write().unwrap();
|
||||||
|
*thread_w = None;
|
||||||
|
println!("finishing after updating complete");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeat after default delay or after 1 second if update was not complete.
|
// Repeat after default delay or after 1 second if update was not success.
|
||||||
let delay = if wallet.loading_progress() == 100 && !wallet.loading_error() {
|
let delay = if wallet_update.loading_error()
|
||||||
INFO_UPDATE_DELAY
|
|| wallet_update.get_data_update_attempts() != 0 {
|
||||||
} else {
|
|
||||||
Duration::from_millis(1000)
|
Duration::from_millis(1000)
|
||||||
|
} else {
|
||||||
|
DATA_UPDATE_DELAY
|
||||||
};
|
};
|
||||||
thread::sleep(delay);
|
println!("park for {}", delay.as_millis());
|
||||||
|
thread::park_timeout(delay);
|
||||||
|
}).thread().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle [`WalletCommand::UpdateData`] command to update [`WalletData`].
|
||||||
|
fn update_wallet_data(wallet: &Wallet, key: Option<SecretKey>) {
|
||||||
|
println!("UPDATE start, attempts: {}", wallet.get_data_update_attempts());
|
||||||
|
|
||||||
|
let wallet_scan = wallet.clone();
|
||||||
|
let (info_tx, info_rx) = mpsc::channel::<StatusMessage>();
|
||||||
|
// Update info loading progress at separate thread.
|
||||||
|
thread::spawn(move || {
|
||||||
|
while let Ok(m) = info_rx.recv() {
|
||||||
|
println!("UPDATE INFO MESSAGE");
|
||||||
|
match m {
|
||||||
|
StatusMessage::UpdatingOutputs(_) => {}
|
||||||
|
StatusMessage::UpdatingTransactions(_) => {}
|
||||||
|
StatusMessage::FullScanWarn(_) => {}
|
||||||
|
StatusMessage::Scanning(_, progress) => {
|
||||||
|
wallet_scan.info_loading_progress.store(progress, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
StatusMessage::ScanningComplete(_) => {
|
||||||
|
wallet_scan.info_loading_progress.store(100, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
StatusMessage::UpdateWarning(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Retrieve wallet info.
|
||||||
|
match retrieve_summary_info(
|
||||||
|
wallet.instance.clone(),
|
||||||
|
key.as_ref(),
|
||||||
|
&Some(info_tx),
|
||||||
|
true,
|
||||||
|
wallet.config.min_confirmations
|
||||||
|
) {
|
||||||
|
Ok(info) => {
|
||||||
|
// Do not retrieve txs if wallet was closed.
|
||||||
|
if !wallet.is_open() {
|
||||||
|
println!("UPDATE stop at retrieve_summary_info");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add attempt if scanning was not complete
|
||||||
|
// or set an error on initial request.
|
||||||
|
if wallet.info_loading_progress() != 100 {
|
||||||
|
println!("UPDATE retrieve_summary_info was not completed");
|
||||||
|
if wallet.get_data().is_none() {
|
||||||
|
wallet.set_loading_error(true);
|
||||||
|
} else {
|
||||||
|
wallet.increment_data_update_attempts();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("UPDATE before retrieve_txs");
|
||||||
|
|
||||||
|
let wallet_txs = wallet.clone();
|
||||||
|
let (txs_tx, txs_rx) = mpsc::channel::<StatusMessage>();
|
||||||
|
// Update txs loading progress at separate thread.
|
||||||
|
thread::spawn(move || {
|
||||||
|
while let Ok(m) = txs_rx.recv() {
|
||||||
|
println!("UPDATE TXS MESSAGE");
|
||||||
|
match m {
|
||||||
|
StatusMessage::UpdatingOutputs(_) => {}
|
||||||
|
StatusMessage::UpdatingTransactions(_) => {}
|
||||||
|
StatusMessage::FullScanWarn(_) => {}
|
||||||
|
StatusMessage::Scanning(_, progress) => {
|
||||||
|
wallet_txs.txs_loading_progress.store(progress, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
StatusMessage::ScanningComplete(_) => {
|
||||||
|
wallet_txs.txs_loading_progress.store(100, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
StatusMessage::UpdateWarning(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Retrieve txs.
|
||||||
|
match retrieve_txs(
|
||||||
|
wallet.instance.clone(),
|
||||||
|
key.as_ref(),
|
||||||
|
&Some(txs_tx),
|
||||||
|
true,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
) {
|
||||||
|
Ok(txs) => {
|
||||||
|
// Do not update data if wallet was closed.
|
||||||
|
if !wallet.is_open() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add attempt if retrieving was not complete
|
||||||
|
// or set an error on initial request.
|
||||||
|
if wallet.txs_loading_progress() != 100 {
|
||||||
|
if wallet.get_data().is_none() {
|
||||||
|
wallet.set_loading_error(true);
|
||||||
|
} else {
|
||||||
|
wallet.increment_data_update_attempts();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set wallet data.
|
||||||
|
let mut w_data = wallet.data.write().unwrap();
|
||||||
|
*w_data = Some(WalletData { info: info.1, txs: txs.1 });
|
||||||
|
|
||||||
|
// Reset attempts.
|
||||||
|
wallet.reset_data_update_attempts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// Increment attempts value in case of error.
|
||||||
|
wallet.increment_data_update_attempts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// Increment attempts value in case of error.
|
||||||
|
wallet.increment_data_update_attempts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset progress values.
|
||||||
|
wallet.info_loading_progress.store(0, Ordering::Relaxed);
|
||||||
|
wallet.txs_loading_progress.store(0, Ordering::Relaxed);
|
||||||
|
|
||||||
|
println!("UPDATE finish, attempts: {}", wallet.get_data_update_attempts());
|
||||||
|
|
||||||
|
// Set an error if maximum number of attempts was reached.
|
||||||
|
if wallet.get_data_update_attempts() >= DATA_UPDATE_ATTEMPTS {
|
||||||
|
wallet.reset_data_update_attempts();
|
||||||
|
wallet.set_loading_error(true);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue