From 94a598a923c50d7c4e692f030343cf2c7bd559a9 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 19 Jun 2023 01:29:15 +0300 Subject: [PATCH] config + ui: config refactoring, stratum server launch, mining ui (without worker list yet) --- locales/en.yml | 24 +++- locales/ru.yml | 24 +++- src/grim.rs | 2 +- src/gui/views/network.rs | 40 ++++--- src/gui/views/network_metrics.rs | 14 +-- src/gui/views/network_mining.rs | 180 ++++++++++++++++++++++++++++-- src/gui/views/network_node.rs | 8 +- src/gui/views/network_settings.rs | 7 +- src/gui/views/views.rs | 18 +-- src/node/config.rs | 76 +++++-------- src/node/node.rs | 46 ++++++-- src/settings.rs | 59 +++++++--- 12 files changed, 352 insertions(+), 146 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index cf384d8..06210b7 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -1,15 +1,16 @@ screen_accounts: title: Accounts network: + self: Network node: Integrated node - metrics: Metrics + metrics: Network metrics mining: Mining - settings: Server settings - enable: Enable + settings: Node settings + enable_node: Enable node disable: Disable restart: Restart autorun: Autorun - disabled_server: 'Enable server or choose another connection method by pressing %{dots} on top left corner of the screen.' + disabled_server: 'Enable integrated node or choose another connection method by pressing %{dots} in the top-left corner of the screen.' sync_status: server_restarting: Server is restarting server_down: Server is down @@ -51,7 +52,20 @@ network_metrics: supply: Supply block_time: Block time reward: Reward - difficulty_window: 'Difficulty at window %{size}' + difficulty_window: 'Difficulty window %{size}' +network_mining: + loading: Mining will be available after the synchronization + enable_server: Enable server + disabled_server: 'Enable stratum server at %{address} or change settings by selecting %{settings} at the bottom of the screen.' + starting: Stratum server is starting + info: 'Mining server is enabled, you can change settings by selecting %{settings} at the bottom of the screen. Data is updating when devices are connected.' + address: IP Address + wallet: Wallet Address + server: Stratum server + miners: Miners + devices: Devices + blocks_found: Blocks found + hashrate: Hashrate modal: cancel: Cancel modal_exit: diff --git a/locales/ru.yml b/locales/ru.yml index f2061a8..1881d51 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -1,15 +1,16 @@ screen_accounts: title: Аккаунты network: + self: Сеть node: Встроенный узел - metrics: Метрики + metrics: Показатели сети mining: Майнинг - settings: Настройки сервера - enable: Включить + settings: Настройки узла + enable_node: Включить узел disable: Выключить restart: Перезапустить autorun: Автозапуск - disabled_server: 'Включите сервер или выберите другой способ подключения, нажав %{dots} в левом верхнем углу экрана.' + disabled_server: 'Включите встроенный узел или выберите другой способ подключения, нажав %{dots} в левом-верхнем углу экрана.' sync_status: server_restarting: Сервер перезапускается server_down: Сервер выключен @@ -51,7 +52,20 @@ network_metrics: supply: Предложение block_time: Время блока reward: Награда - difficulty_window: 'Сложность в окне %{size}' + difficulty_window: 'Окно сложности %{size}' +network_mining: + loading: Майнинг будет доступен после синхронизации + enable_server: Включить сервер + disabled_server: 'Включите stratum-сервер по адресу %{address} или измените настройки, выбрав %{settings} внизу экрана.' + starting: Stratum-сервер запускается + info: 'Сервер майнинга запущен, вы можете изменить настройки, выбрав %{settings} внизу экрана. Данные обновляются, когда устройства подключены.' + address: IP Адрес + wallet: Адрес кошелька + server: Stratum-сервер + miners: Майнеры + devices: Устройства + found: Найдено + hashrate: Хешрэйт modal: cancel: Отмена modal_exit: diff --git a/src/grim.rs b/src/grim.rs index b5e3a0b..2a9eca3 100644 --- a/src/grim.rs +++ b/src/grim.rs @@ -74,7 +74,7 @@ fn start(mut options: NativeOptions, app_creator: AppCreator) { setup_i18n(); - if Settings::get_app_config().auto_start_node { + if Settings::app_config_to_read().auto_start_node { Node::start(); } diff --git a/src/gui/views/network.rs b/src/gui/views/network.rs index 4bd7443..145d2b0 100644 --- a/src/gui/views/network.rs +++ b/src/gui/views/network.rs @@ -20,7 +20,7 @@ use egui_extras::{Size, StripBuilder}; use grin_chain::SyncStatus; use crate::gui::{Colors, Navigator}; -use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, POWER}; +use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::network_metrics::NetworkMetrics; use crate::gui::views::network_mining::NetworkMining; @@ -32,7 +32,6 @@ use crate::Settings; pub trait NetworkTab { fn get_type(&self) -> NetworkTabType; - fn name(&self) -> String; fn ui(&mut self, ui: &mut egui::Ui); } @@ -44,6 +43,17 @@ pub enum NetworkTabType { Settings } +impl NetworkTabType { + pub fn name(&self) -> String { + match *self { + NetworkTabType::Node => { t!("network.node") } + NetworkTabType::Metrics => { t!("network.metrics") } + NetworkTabType::Mining => { t!("network.mining") } + NetworkTabType::Settings => { t!("network.settings") } + } + } +} + pub struct Network { current_tab: Box, } @@ -101,26 +111,22 @@ impl Network { ui.columns(4, |columns| { columns[0].vertical_centered_justified(|ui| { - View::tab_button(ui, DATABASE, - self.current_tab.get_type() == NetworkTabType::Node, || { + View::tab_button(ui, DATABASE, self.is_current_tab(NetworkTabType::Node), || { self.current_tab = Box::new(NetworkNode::default()); }); }); columns[1].vertical_centered_justified(|ui| { - View::tab_button(ui, GAUGE, - self.current_tab.get_type() == NetworkTabType::Metrics, || { + View::tab_button(ui, GAUGE, self.is_current_tab(NetworkTabType::Metrics), || { self.current_tab = Box::new(NetworkMetrics::default()); }); }); columns[2].vertical_centered_justified(|ui| { - View::tab_button(ui, FACTORY, - self.current_tab.get_type() == NetworkTabType::Mining, || { + View::tab_button(ui, FACTORY, self.is_current_tab(NetworkTabType::Mining), || { self.current_tab = Box::new(NetworkMining::default()); }); }); columns[3].vertical_centered_justified(|ui| { - View::tab_button(ui, FADERS, - self.current_tab.get_type() == NetworkTabType::Settings, || { + View::tab_button(ui, FADERS, self.is_current_tab(NetworkTabType::Settings), || { self.current_tab = Box::new(NetworkSettings::default()); }); }); @@ -128,6 +134,10 @@ impl Network { }); } + fn is_current_tab(&self, tab_type: NetworkTabType) -> bool { + self.current_tab.get_type() == tab_type + } + fn draw_title(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) { StripBuilder::new(ui) .size(Size::exact(52.0)) @@ -170,7 +180,7 @@ impl Network { strip.cell(|ui| { ui.add_space(2.0); ui.vertical_centered(|ui| { - ui.label(RichText::new(self.current_tab.name().to_uppercase()) + ui.label(RichText::new(self.current_tab.get_type().name().to_uppercase()) .size(18.0) .color(Colors::TITLE)); }); @@ -217,15 +227,15 @@ impl Network { ui.add_space(10.0); - View::button(ui, format!("{} {}", POWER, t!("network.enable")), Colors::GOLD, || { + View::button(ui, t!("network.enable_node"), Colors::GOLD, || { Node::start(); }); - ui.add_space(4.0); + ui.add_space(2.0); - let autostart: bool = Settings::get_app_config().auto_start_node; + let autostart: bool = Settings::app_config_to_read().auto_start_node; View::checkbox(ui, autostart, t!("network.autorun"), || { - let mut w_app_config = Settings::get_app_config_to_update(); + let mut w_app_config = Settings::app_config_to_update(); w_app_config.auto_start_node = !autostart; w_app_config.save(); }); diff --git a/src/gui/views/network_metrics.rs b/src/gui/views/network_metrics.rs index 6809034..2bb6057 100644 --- a/src/gui/views/network_metrics.rs +++ b/src/gui/views/network_metrics.rs @@ -34,23 +34,19 @@ impl NetworkTab for NetworkMetrics { NetworkTabType::Metrics } - fn name(&self) -> String { - t!("network.metrics") - } - fn ui(&mut self, ui: &mut egui::Ui) { let server_stats = Node::get_stats(); - // Show loading spinner when stats are not available or message when server is not enabled. + // Show message when node is not running or loading spinner when metrics are not available. if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 { if !Node::is_running() { Network::disabled_server_content(ui); } else { - View::center_content(ui, 160.0, |ui| { + View::center_content(ui, 162.0, |ui| { View::big_loading_spinner(ui); ui.add_space(18.0); ui.label(RichText::new(t!("network_metrics.loading")) .size(16.0) - .color(Colors::TEXT) + .color(Colors::INACTIVE_TEXT) ); }); } @@ -88,7 +84,7 @@ impl NetworkTab for NetworkMetrics { [false, true, false, true]); }); }); - ui.add_space(6.0); + ui.add_space(4.0); // Show difficulty adjustment window info ui.vertical_centered_justified(|ui| { @@ -119,7 +115,7 @@ impl NetworkTab for NetworkMetrics { [false, true, false, true]); }); }); - ui.add_space(6.0); + ui.add_space(4.0); // Show difficulty adjustment window blocks let blocks_size = stats.diff_stats.last_blocks.len(); diff --git a/src/gui/views/network_mining.rs b/src/gui/views/network_mining.rs index a87a3ac..ff21391 100644 --- a/src/gui/views/network_mining.rs +++ b/src/gui/views/network_mining.rs @@ -12,9 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +use egui::RichText; +use grin_chain::SyncStatus; + use crate::gui::Colors; +use crate::gui::icons::{COMPUTER_TOWER, CPU, FADERS, POLYGON}; use crate::gui::views::{Network, NetworkTab, NetworkTabType, View}; use crate::node::Node; +use crate::Settings; #[derive(Default)] pub struct NetworkMining; @@ -24,25 +29,184 @@ impl NetworkTab for NetworkMining { NetworkTabType::Mining } - fn name(&self) -> String { - t!("network.mining") - } - fn ui(&mut self, ui: &mut egui::Ui) { let server_stats = Node::get_stats(); - // Show loading spinner when stats are not available or message when server is not enabled. - if !server_stats.is_some() { + // Show message when node is not running or loading spinner when mining are not available. + if !server_stats.is_some() || Node::get_sync_status().unwrap() != SyncStatus::NoSync { if !Node::is_running() { Network::disabled_server_content(ui); } else { - ui.centered_and_justified(|ui| { + View::center_content(ui, 162.0, |ui| { View::big_loading_spinner(ui); + ui.add_space(18.0); + ui.label(RichText::new(t!("network_mining.loading")) + .size(16.0) + .color(Colors::INACTIVE_TEXT) + ); }); } return; } - let stats = server_stats.as_ref().unwrap(); + // Stratum mining server address. + let stratum_address = Settings::node_config_to_read() + .members.clone() + .server.stratum_mining_config.unwrap() + .stratum_server_addr.unwrap(); + let stratum_stats = &server_stats.as_ref().unwrap().stratum_stats; + if !stratum_stats.is_running && !Node::is_stratum_server_starting() { + // Show Stratum setup when mining server is not enabled. + View::center_content(ui, 162.0, |ui| { + let text = t!( + "network_mining.disabled_server", + "address" => stratum_address, + "settings" => FADERS + ); + ui.label(RichText::new(text) + .size(16.0) + .color(Colors::INACTIVE_TEXT) + ); + + ui.add_space(10.0); + + View::button(ui, t!("network_mining.enable_server"), Colors::GOLD, || { + Node::start_stratum_server(); + }); + + ui.add_space(2.0); + + // Check if stratum server is enabled at config + let stratum_enabled = Settings::node_config_to_read() + .members.clone() + .server.stratum_mining_config.unwrap() + .enable_stratum_server.unwrap(); + + View::checkbox(ui, stratum_enabled, t!("network.autorun"), || { + let mut w_node_config = Settings::node_config_to_update(); + w_node_config.members + .server.stratum_mining_config.as_mut().unwrap() + .enable_stratum_server = Some(!stratum_enabled); + w_node_config.save(); + }); + }); + return; + } else if Node::is_stratum_server_starting() { + // Show loading spinner when mining server is starting. + View::center_content(ui, 162.0, |ui| { + View::big_loading_spinner(ui); + ui.add_space(18.0); + ui.label(RichText::new(t!("network_mining.starting")) + .size(16.0) + .color(Colors::INACTIVE_TEXT) + ); + }); + return; + } + + // Show stratum mining server info. + ui.vertical_centered_justified(|ui| { + View::sub_header(ui, format!("{} {}", COMPUTER_TOWER, t!("network_mining.server"))); + }); + ui.add_space(4.0); + + ui.columns(2, |columns| { + columns[0].vertical_centered(|ui| { + View::rounded_box(ui, + stratum_address, + t!("network_mining.address"), + [true, false, true, false]); + }); + columns[1].vertical_centered(|ui| { + // Stratum mining wallet address. + let wallet_address = Settings::node_config_to_read() + .members.clone() + .server.stratum_mining_config.unwrap() + .wallet_listener_url; + View::rounded_box(ui, + wallet_address, + t!("network_mining.wallet"), + [false, true, false, true]); + }); + }); + ui.add_space(4.0); + + // Show network info. + ui.vertical_centered_justified(|ui| { + View::sub_header(ui, format!("{} {}", POLYGON, t!("network.self"))); + }); + ui.add_space(4.0); + + ui.columns(3, |columns| { + columns[0].vertical_centered(|ui| { + let difficulty = if stratum_stats.network_difficulty > 0 { + stratum_stats.network_difficulty.to_string() + } else { + "-".into() + }; + View::rounded_box(ui, + difficulty, + t!("network_node.difficulty"), + [true, false, true, false]); + }); + columns[1].vertical_centered(|ui| { + let block_height = if stratum_stats.block_height > 0 { + stratum_stats.block_height.to_string() + } else { + "-".into() + }; + View::rounded_box(ui, + block_height, + t!("network_node.header"), + [false, false, false, false]); + }); + columns[2].vertical_centered(|ui| { + let hashrate = if stratum_stats.network_hashrate > 0.0 { + stratum_stats.network_hashrate.to_string() + } else { + "-".into() + }; + View::rounded_box(ui, + hashrate, + t!("network_mining.hashrate"), + [false, true, false, true]); + }); + }); + ui.add_space(4.0); + + // Show mining info. + ui.vertical_centered_justified(|ui| { + View::sub_header(ui, format!("{} {}", CPU, t!("network_mining.miners"))); + }); + ui.add_space(4.0); + + ui.columns(2, |columns| { + columns[0].vertical_centered(|ui| { + View::rounded_box(ui, + stratum_stats.num_workers.to_string(), + t!("network_mining.devices"), + [true, false, true, false]); + }); + + columns[1].vertical_centered(|ui| { + View::rounded_box(ui, + stratum_stats.blocks_found.to_string(), + t!("network_mining.blocks_found"), + [false, true, false, true]); + }); + }); + ui.add_space(4.0); + + // Show miners info. + if !stratum_stats.worker_stats.is_empty() { + //TODO: miners workers + } else if ui.available_height() > 142.0 { + View::center_content(ui, 142.0, |ui| { + ui.label(RichText::new(t!("network_mining.info", "settings" => FADERS)) + .size(16.0) + .color(Colors::INACTIVE_TEXT) + ); + }); + } } } \ No newline at end of file diff --git a/src/gui/views/network_node.rs b/src/gui/views/network_node.rs index 61a962a..10824a4 100644 --- a/src/gui/views/network_node.rs +++ b/src/gui/views/network_node.rs @@ -26,16 +26,12 @@ pub struct NetworkNode; impl NetworkTab for NetworkNode { fn get_type(&self) -> NetworkTabType { - NetworkTabType::Metrics - } - - fn name(&self) -> String { - t!("network.node") + NetworkTabType::Node } fn ui(&mut self, ui: &mut egui::Ui) { let server_stats = Node::get_stats(); - // Show loading spinner when stats are not available or message when server is not enabled. + // Show message when node is not running or loading spinner when stats are not available. if !server_stats.is_some() { if !Node::is_running() { Network::disabled_server_content(ui); diff --git a/src/gui/views/network_settings.rs b/src/gui/views/network_settings.rs index 1bb394c..800db3a 100644 --- a/src/gui/views/network_settings.rs +++ b/src/gui/views/network_settings.rs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use grin_core::global::ChainTypes; use crate::gui::views::{NetworkTab, NetworkTabType}; +use crate::Settings; #[derive(Default)] pub struct NetworkSettings; @@ -22,11 +24,6 @@ impl NetworkTab for NetworkSettings { NetworkTabType::Settings } - fn name(&self) -> String { - t!("network.settings") - } - fn ui(&mut self, ui: &mut egui::Ui) { - } } \ No newline at end of file diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 75e0a5c..af935be 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -196,27 +196,19 @@ impl View { Spinner::new().size(48.0).color(Colors::GOLD).ui(ui); } - // let wt = RichText::new(text.to_uppercase()).size(18.0).color(Colors::BUTTON); - // let br = Button::new(wt) - // .stroke(Self::DEFAULT_STROKE) - // .fill(fill_color) - // .ui(ui).interact(Sense::click_and_drag()); - // - // Self::on_button_click(ui, br, action); - - /// Draw button that looks like checkbox with callback to change value. - pub fn checkbox(ui: &mut egui::Ui, value: bool, text: String, cb: impl FnOnce()) { - let text_value = match value { + /// Draw the button that looks like checkbox with callback after change. + pub fn checkbox(ui: &mut egui::Ui, checked: bool, text: String, callback: impl FnOnce()) { + let text_value = match checked { true => { format!("{} {}", CHECK_SQUARE, text)} false => { format!("{} {}", SQUARE, text)} }; - let wt = RichText::new(text_value).size(18.0).color(Colors::BUTTON); + let wt = RichText::new(text_value).size(19.0).color(Colors::BUTTON); let br = Button::new(wt) .frame(false) .stroke(Stroke::NONE) .fill(Colors::TRANSPARENT) .ui(ui).interact(Sense::click_and_drag()); - Self::on_button_click(ui, br, cb); + Self::on_button_click(ui, br, callback); } } \ No newline at end of file diff --git a/src/node/config.rs b/src/node/config.rs index 8065c14..37e90f5 100644 --- a/src/node/config.rs +++ b/src/node/config.rs @@ -12,11 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{fs, thread}; -use std::fs::File; -use std::io::Write; -use std::sync::atomic::{AtomicBool, Ordering}; - use grin_config::{config, ConfigError, ConfigMembers, GlobalConfig}; use grin_config::config::{API_SECRET_FILE_NAME, FOREIGN_API_SECRET_FILE_NAME, SERVER_CONFIG_FILE_NAME}; use grin_core::global::ChainTypes; @@ -24,70 +19,49 @@ use serde::{Deserialize, Serialize}; use crate::Settings; -/// Node config that contains [`GlobalConfig`] to be used by [`grin_servers::Server`]. +/// Wrapped node config to be used by [`grin_servers::Server`]. #[derive(Serialize, Deserialize)] pub struct NodeConfig { - pub global_config: GlobalConfig, - update_needed: AtomicBool, - updating: AtomicBool + pub members: ConfigMembers } impl NodeConfig { - /// Initialize node config with provided chain type from the disk. - pub fn init(chain_type: &ChainTypes) -> Self { + /// Initialize integrated node config. + pub fn init(chain_type: ChainTypes) -> Self { let _ = Self::check_api_secret_files(chain_type, API_SECRET_FILE_NAME); let _ = Self::check_api_secret_files(chain_type, FOREIGN_API_SECRET_FILE_NAME); - let config_path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, Some(chain_type)); - - // Create default config if it doesn't exist or has wrong format. - if !config_path.exists() || toml::from_str::( - fs::read_to_string(config_path.clone()).unwrap().as_str() - ).is_err() { - let mut default_config = GlobalConfig::for_chain(chain_type); - default_config.update_paths(&Settings::get_working_path(Some(chain_type))); - let _ = default_config.write_to_file(config_path.to_str().unwrap()); - } - - let config = GlobalConfig::new(config_path.to_str().unwrap()); - + let config_members = Self::for_chain_type(chain_type); Self { - global_config: config.unwrap(), - update_needed: AtomicBool::new(false), - updating: AtomicBool::new(false) + members: config_members } } - /// Write node config on disk. - pub fn save_config(&self) { - if self.updating.load(Ordering::Relaxed) { - self.update_needed.store(true, Ordering::Relaxed); - return; + /// Initialize config with provided [`ChainTypes`]. + pub fn for_chain_type(chain_type: ChainTypes) -> ConfigMembers { + let path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, Some(chain_type)); + let parsed = Settings::read_from_file::(path.clone()); + if !path.exists() || parsed.is_err() { + let mut default_config = GlobalConfig::for_chain(&chain_type); + default_config.update_paths(&Settings::get_working_path(Some(chain_type))); + let config = default_config.members.unwrap(); + Settings::write_to_file(&config, path); + config + } else { + parsed.unwrap() } + } - thread::spawn(move || loop { - let config = Settings::get_node_config(); - config.update_needed.store(false, Ordering::Relaxed); - config.updating.store(true, Ordering::Relaxed); - - let chain_type = &config.global_config.members.clone().unwrap().server.chain_type; - let config_path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, Some(chain_type)); - - // Write config to file. - let conf_out = toml::to_string(&config.global_config.members).unwrap(); - let mut file = File::create(config_path.to_str().unwrap()).unwrap(); - file.write_all(conf_out.as_bytes()).unwrap(); - - if !config.update_needed.load(Ordering::Relaxed) { - config.updating.store(false, Ordering::Relaxed); - break; - } - }); + /// Save node config to disk. + pub fn save(&mut self) { + let chain_type = self.members.server.chain_type; + let config_path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, Some(chain_type)); + Settings::write_to_file(&self.members, config_path); } /// Check that the api secret files exist and are valid. fn check_api_secret_files( - chain_type: &ChainTypes, + chain_type: ChainTypes, secret_file_name: &str, ) -> Result<(), ConfigError> { let grin_path = Settings::get_working_path(Some(chain_type)); diff --git a/src/node/node.rs b/src/node/node.rs index 69516b9..e5c5549 100644 --- a/src/node/node.rs +++ b/src/node/node.rs @@ -45,7 +45,9 @@ pub struct Node { /// Thread flag to stop the server. stop_needed: AtomicBool, /// Flag to check if app exit is needed after server stop. - exit_after_stop: AtomicBool + exit_after_stop: AtomicBool, + /// Thread flag to start stratum server at separate. + start_stratum_server: AtomicBool } impl Default for Node { @@ -55,7 +57,8 @@ impl Default for Node { starting: AtomicBool::new(false), restart_needed: AtomicBool::new(false), stop_needed: AtomicBool::new(false), - exit_after_stop: AtomicBool::new(false) + exit_after_stop: AtomicBool::new(false), + start_stratum_server: AtomicBool::new(false) } } } @@ -83,6 +86,16 @@ impl Node { } } + /// Start stratum server. + pub fn start_stratum_server() { + NODE_STATE.start_stratum_server.store(true, Ordering::Relaxed); + } + + /// Check if stratum server is starting. + pub fn is_stratum_server_starting() -> bool { + NODE_STATE.start_stratum_server.load(Ordering::Relaxed) + } + /// Check if node is starting. pub fn is_starting() -> bool { NODE_STATE.starting.load(Ordering::Relaxed) @@ -164,8 +177,20 @@ impl Node { NODE_STATE.starting.store(false, Ordering::Relaxed); NODE_STATE.stop_needed.store(false, Ordering::Relaxed); + NODE_STATE.start_stratum_server.store(false, Ordering::Relaxed); break; } else { + if Self::is_stratum_server_starting() { + // Start mining server. + let stratum_config = server.config.stratum_mining_config.clone().unwrap(); + server.start_stratum_server(stratum_config); + + // Wait for mining server to start and update status. + thread::sleep(Duration::from_millis(1000)); + + NODE_STATE.start_stratum_server.store(false, Ordering::Relaxed); + } + let stats = server.get_server_stats(); if stats.is_ok() { // Update server stats. @@ -308,8 +333,8 @@ impl Node { /// Start the node [`Server`]. fn start_server() -> Server { // Get current global config - let config = &Settings::get_node_config().global_config; - let server_config = config.members.as_ref().unwrap().server.clone(); + let config = &Settings::node_config_to_read().members; + let server_config = config.server.clone(); // Remove temporary file dir { @@ -328,7 +353,7 @@ fn start_server() -> Server { // accept_fee_base, and future_time_limit. // These are read via global and not read from config beyond this point. if !global::GLOBAL_CHAIN_TYPE.is_init() { - global::init_global_chain_type(config.members.as_ref().unwrap().server.chain_type); + global::init_global_chain_type(config.server.chain_type); } info!("Chain: {:?}", global::get_chain_type()); @@ -345,12 +370,12 @@ fn start_server() -> Server { } } if !global::GLOBAL_ACCEPT_FEE_BASE.is_init() { - let afb = config.members.as_ref().unwrap().server.pool_config.accept_fee_base; + let afb = config.server.pool_config.accept_fee_base; global::init_global_accept_fee_base(afb); info!("Accept Fee Base: {:?}", global::get_accept_fee_base()); } if !global::GLOBAL_FUTURE_TIME_LIMIT.is_init() { - let future_time_limit = config.members.as_ref().unwrap().server.future_time_limit; + let future_time_limit = config.server.future_time_limit; global::init_global_future_time_limit(future_time_limit); info!("Future Time Limit: {:?}", global::get_future_time_limit()); } @@ -417,13 +442,12 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncTitle( #[cfg(target_os = "android")] #[allow(non_snake_case)] #[no_mangle] -/// Check if app exit is needed after node stop. +/// Check if app exit is needed after node stop to finish Android app at background. pub extern "C" fn Java_mw_gri_android_BackgroundService_exitAppAfterNodeStop( _env: jni::JNIEnv, _class: jni::objects::JObject, _activity: jni::objects::JObject, ) -> jboolean { - let exit_after_stop = NODE_STATE.exit_after_stop.load(Ordering::Relaxed); - let is_app_exit_needed = !Node::is_running() && exit_after_stop; - return is_app_exit_needed as jboolean; + let exit_needed = !Node::is_running() && NODE_STATE.exit_after_stop.load(Ordering::Relaxed); + return exit_needed as jboolean; } \ No newline at end of file diff --git a/src/settings.rs b/src/settings.rs index 43a1e8b..c2e2a15 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -38,7 +38,7 @@ pub struct AppConfig { /// Run node server on startup. pub auto_start_node: bool, /// Chain type for node server. - pub node_chain_type: ChainTypes + node_chain_type: ChainTypes } impl Default for AppConfig { @@ -53,62 +53,87 @@ impl Default for AppConfig { impl AppConfig { /// Initialize application config from the disk. pub fn init() -> Self { - let config_path = Settings::get_config_path(APP_CONFIG_FILE_NAME, None); - let parsed = Settings::read_from_file::(config_path.clone()); - if !config_path.exists() || parsed.is_err() { + let path = Settings::get_config_path(APP_CONFIG_FILE_NAME, None); + let parsed = Settings::read_from_file::(path.clone()); + if !path.exists() || parsed.is_err() { let default_config = AppConfig::default(); - Settings::write_to_file(&default_config, config_path); + Settings::write_to_file(&default_config, path); default_config } else { parsed.unwrap() } } + /// Change chain type and load new [`NodeConfig`] accordingly. + pub fn change_chain_type(&mut self, chain_type: ChainTypes) { + if self.node_chain_type == chain_type { + return; + } else { + self.node_chain_type = chain_type; + self.save(); + + // Load config for selected chain type. + Settings::node_config_to_update().members = NodeConfig::for_chain_type(chain_type); + } + } + + /// Save app config to disk. pub fn save(&self) { Settings::write_to_file(self, Settings::get_config_path(APP_CONFIG_FILE_NAME, None)); } } +const WORKING_DIRECTORY_NAME: &'static str = ".grim"; + +/// Provides access to app and node configs. pub struct Settings { app_config: Arc>, node_config: Arc> } impl Settings { - /// Initialize settings with app and node configs from the disk. + /// Initialize settings with app and node configs. fn init() -> Self { let app_config = AppConfig::init(); let chain_type = app_config.node_chain_type; Self { app_config: Arc::new(RwLock::new(app_config)), - node_config: Arc::new(RwLock::new(NodeConfig::init(&chain_type))) + node_config: Arc::new(RwLock::new(NodeConfig::init(chain_type))) } } - pub fn get_node_config() -> RwLockReadGuard<'static, NodeConfig> { + /// Get node config to read values. + pub fn node_config_to_read() -> RwLockReadGuard<'static, NodeConfig> { SETTINGS_STATE.node_config.read().unwrap() } - pub fn get_app_config() -> RwLockReadGuard<'static, AppConfig> { + /// Get node config to update values. + pub fn node_config_to_update() -> RwLockWriteGuard<'static, NodeConfig> { + SETTINGS_STATE.node_config.write().unwrap() + } + + /// Get app config to read values. + pub fn app_config_to_read() -> RwLockReadGuard<'static, AppConfig> { SETTINGS_STATE.app_config.read().unwrap() } - pub fn get_app_config_to_update() -> RwLockWriteGuard<'static, AppConfig> { + /// Get app config to update values. + pub fn app_config_to_update() -> RwLockWriteGuard<'static, AppConfig> { SETTINGS_STATE.app_config.write().unwrap() } - /// Get working directory path for application. - pub fn get_working_path(chain_type: Option<&ChainTypes>) -> PathBuf { - // Check if dir exists + /// Get working directory path for the application. + pub fn get_working_path(chain_type: Option) -> PathBuf { + // Check if dir exists. let mut path = match dirs::home_dir() { Some(p) => p, None => PathBuf::new(), }; - path.push(".grim"); + path.push(WORKING_DIRECTORY_NAME); if chain_type.is_some() { path.push(chain_type.unwrap().shortname()); } - // Create if the default path doesn't exist + // Create if the default path doesn't exist. if !path.exists() { let _ = fs::create_dir_all(path.clone()); } @@ -116,14 +141,14 @@ impl Settings { } /// Get config file path from provided name and [`ChainTypes`] if needed. - pub fn get_config_path(config_name: &str, chain_type: Option<&ChainTypes>) -> PathBuf { + pub fn get_config_path(config_name: &str, chain_type: Option) -> PathBuf { let main_path = Self::get_working_path(chain_type); let mut settings_path = main_path.clone(); settings_path.push(config_name); settings_path } - /// Read config from file + /// Read config from a file pub fn read_from_file(config_path: PathBuf) -> Result { let file_content = fs::read_to_string(config_path.clone())?; let parsed = toml::from_str::(file_content.as_str());