config + ui: config refactoring, stratum server launch, mining ui (without worker list yet)

This commit is contained in:
ardocrat 2023-06-19 01:29:15 +03:00
parent d34889a801
commit 94a598a923
12 changed files with 352 additions and 146 deletions

View file

@ -1,15 +1,16 @@
screen_accounts: screen_accounts:
title: Accounts title: Accounts
network: network:
self: Network
node: Integrated node node: Integrated node
metrics: Metrics metrics: Network metrics
mining: Mining mining: Mining
settings: Server settings settings: Node settings
enable: Enable enable_node: Enable node
disable: Disable disable: Disable
restart: Restart restart: Restart
autorun: Autorun 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: sync_status:
server_restarting: Server is restarting server_restarting: Server is restarting
server_down: Server is down server_down: Server is down
@ -51,7 +52,20 @@ network_metrics:
supply: Supply supply: Supply
block_time: Block time block_time: Block time
reward: Reward 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: modal:
cancel: Cancel cancel: Cancel
modal_exit: modal_exit:

View file

@ -1,15 +1,16 @@
screen_accounts: screen_accounts:
title: Аккаунты title: Аккаунты
network: network:
self: Сеть
node: Встроенный узел node: Встроенный узел
metrics: Метрики metrics: Показатели сети
mining: Майнинг mining: Майнинг
settings: Настройки сервера settings: Настройки узла
enable: Включить enable_node: Включить узел
disable: Выключить disable: Выключить
restart: Перезапустить restart: Перезапустить
autorun: Автозапуск autorun: Автозапуск
disabled_server: 'Включите сервер или выберите другой способ подключения, нажав %{dots} в левом верхнем углу экрана.' disabled_server: 'Включите встроенный узел или выберите другой способ подключения, нажав %{dots} в левом-верхнем углу экрана.'
sync_status: sync_status:
server_restarting: Сервер перезапускается server_restarting: Сервер перезапускается
server_down: Сервер выключен server_down: Сервер выключен
@ -51,7 +52,20 @@ network_metrics:
supply: Предложение supply: Предложение
block_time: Время блока block_time: Время блока
reward: Награда 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: modal:
cancel: Отмена cancel: Отмена
modal_exit: modal_exit:

View file

@ -74,7 +74,7 @@ fn start(mut options: NativeOptions, app_creator: AppCreator) {
setup_i18n(); setup_i18n();
if Settings::get_app_config().auto_start_node { if Settings::app_config_to_read().auto_start_node {
Node::start(); Node::start();
} }

View file

@ -20,7 +20,7 @@ use egui_extras::{Size, StripBuilder};
use grin_chain::SyncStatus; use grin_chain::SyncStatus;
use crate::gui::{Colors, Navigator}; 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::platform::PlatformCallbacks;
use crate::gui::views::network_metrics::NetworkMetrics; use crate::gui::views::network_metrics::NetworkMetrics;
use crate::gui::views::network_mining::NetworkMining; use crate::gui::views::network_mining::NetworkMining;
@ -32,7 +32,6 @@ use crate::Settings;
pub trait NetworkTab { pub trait NetworkTab {
fn get_type(&self) -> NetworkTabType; fn get_type(&self) -> NetworkTabType;
fn name(&self) -> String;
fn ui(&mut self, ui: &mut egui::Ui); fn ui(&mut self, ui: &mut egui::Ui);
} }
@ -44,6 +43,17 @@ pub enum NetworkTabType {
Settings 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 { pub struct Network {
current_tab: Box<dyn NetworkTab>, current_tab: Box<dyn NetworkTab>,
} }
@ -101,26 +111,22 @@ impl Network {
ui.columns(4, |columns| { ui.columns(4, |columns| {
columns[0].vertical_centered_justified(|ui| { columns[0].vertical_centered_justified(|ui| {
View::tab_button(ui, DATABASE, View::tab_button(ui, DATABASE, self.is_current_tab(NetworkTabType::Node), || {
self.current_tab.get_type() == NetworkTabType::Node, || {
self.current_tab = Box::new(NetworkNode::default()); self.current_tab = Box::new(NetworkNode::default());
}); });
}); });
columns[1].vertical_centered_justified(|ui| { columns[1].vertical_centered_justified(|ui| {
View::tab_button(ui, GAUGE, View::tab_button(ui, GAUGE, self.is_current_tab(NetworkTabType::Metrics), || {
self.current_tab.get_type() == NetworkTabType::Metrics, || {
self.current_tab = Box::new(NetworkMetrics::default()); self.current_tab = Box::new(NetworkMetrics::default());
}); });
}); });
columns[2].vertical_centered_justified(|ui| { columns[2].vertical_centered_justified(|ui| {
View::tab_button(ui, FACTORY, View::tab_button(ui, FACTORY, self.is_current_tab(NetworkTabType::Mining), || {
self.current_tab.get_type() == NetworkTabType::Mining, || {
self.current_tab = Box::new(NetworkMining::default()); self.current_tab = Box::new(NetworkMining::default());
}); });
}); });
columns[3].vertical_centered_justified(|ui| { columns[3].vertical_centered_justified(|ui| {
View::tab_button(ui, FADERS, View::tab_button(ui, FADERS, self.is_current_tab(NetworkTabType::Settings), || {
self.current_tab.get_type() == NetworkTabType::Settings, || {
self.current_tab = Box::new(NetworkSettings::default()); 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) { fn draw_title(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
StripBuilder::new(ui) StripBuilder::new(ui)
.size(Size::exact(52.0)) .size(Size::exact(52.0))
@ -170,7 +180,7 @@ impl Network {
strip.cell(|ui| { strip.cell(|ui| {
ui.add_space(2.0); ui.add_space(2.0);
ui.vertical_centered(|ui| { 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) .size(18.0)
.color(Colors::TITLE)); .color(Colors::TITLE));
}); });
@ -217,15 +227,15 @@ impl Network {
ui.add_space(10.0); 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(); 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"), || { 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.auto_start_node = !autostart;
w_app_config.save(); w_app_config.save();
}); });

View file

@ -34,23 +34,19 @@ impl NetworkTab for NetworkMetrics {
NetworkTabType::Metrics NetworkTabType::Metrics
} }
fn name(&self) -> String {
t!("network.metrics")
}
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
let server_stats = Node::get_stats(); 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 server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
if !Node::is_running() { if !Node::is_running() {
Network::disabled_server_content(ui); Network::disabled_server_content(ui);
} else { } else {
View::center_content(ui, 160.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);
ui.label(RichText::new(t!("network_metrics.loading")) ui.label(RichText::new(t!("network_metrics.loading"))
.size(16.0) .size(16.0)
.color(Colors::TEXT) .color(Colors::INACTIVE_TEXT)
); );
}); });
} }
@ -88,7 +84,7 @@ impl NetworkTab for NetworkMetrics {
[false, true, false, true]); [false, true, false, true]);
}); });
}); });
ui.add_space(6.0); ui.add_space(4.0);
// Show difficulty adjustment window info // Show difficulty adjustment window info
ui.vertical_centered_justified(|ui| { ui.vertical_centered_justified(|ui| {
@ -119,7 +115,7 @@ impl NetworkTab for NetworkMetrics {
[false, true, false, true]); [false, true, false, true]);
}); });
}); });
ui.add_space(6.0); ui.add_space(4.0);
// Show difficulty adjustment window blocks // Show difficulty adjustment window blocks
let blocks_size = stats.diff_stats.last_blocks.len(); let blocks_size = stats.diff_stats.last_blocks.len();

View file

@ -12,9 +12,14 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use egui::RichText;
use grin_chain::SyncStatus;
use crate::gui::Colors; use crate::gui::Colors;
use crate::gui::icons::{COMPUTER_TOWER, CPU, FADERS, POLYGON};
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View}; use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
use crate::node::Node; use crate::node::Node;
use crate::Settings;
#[derive(Default)] #[derive(Default)]
pub struct NetworkMining; pub struct NetworkMining;
@ -24,25 +29,184 @@ impl NetworkTab for NetworkMining {
NetworkTabType::Mining NetworkTabType::Mining
} }
fn name(&self) -> String {
t!("network.mining")
}
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
let server_stats = Node::get_stats(); 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 mining are not available.
if !server_stats.is_some() { if !server_stats.is_some() || Node::get_sync_status().unwrap() != SyncStatus::NoSync {
if !Node::is_running() { if !Node::is_running() {
Network::disabled_server_content(ui); Network::disabled_server_content(ui);
} else { } else {
ui.centered_and_justified(|ui| { View::center_content(ui, 162.0, |ui| {
View::big_loading_spinner(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; 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)
);
});
}
} }
} }

View file

@ -26,16 +26,12 @@ pub struct NetworkNode;
impl NetworkTab for NetworkNode { impl NetworkTab for NetworkNode {
fn get_type(&self) -> NetworkTabType { fn get_type(&self) -> NetworkTabType {
NetworkTabType::Metrics NetworkTabType::Node
}
fn name(&self) -> String {
t!("network.node")
} }
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
let server_stats = Node::get_stats(); 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 !server_stats.is_some() {
if !Node::is_running() { if !Node::is_running() {
Network::disabled_server_content(ui); Network::disabled_server_content(ui);

View file

@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use grin_core::global::ChainTypes;
use crate::gui::views::{NetworkTab, NetworkTabType}; use crate::gui::views::{NetworkTab, NetworkTabType};
use crate::Settings;
#[derive(Default)] #[derive(Default)]
pub struct NetworkSettings; pub struct NetworkSettings;
@ -22,11 +24,6 @@ impl NetworkTab for NetworkSettings {
NetworkTabType::Settings NetworkTabType::Settings
} }
fn name(&self) -> String {
t!("network.settings")
}
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
} }
} }

View file

@ -196,27 +196,19 @@ impl View {
Spinner::new().size(48.0).color(Colors::GOLD).ui(ui); Spinner::new().size(48.0).color(Colors::GOLD).ui(ui);
} }
// let wt = RichText::new(text.to_uppercase()).size(18.0).color(Colors::BUTTON); /// Draw the button that looks like checkbox with callback after change.
// let br = Button::new(wt) pub fn checkbox(ui: &mut egui::Ui, checked: bool, text: String, callback: impl FnOnce()) {
// .stroke(Self::DEFAULT_STROKE) let text_value = match checked {
// .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 {
true => { format!("{} {}", CHECK_SQUARE, text)} true => { format!("{} {}", CHECK_SQUARE, text)}
false => { format!("{} {}", 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) let br = Button::new(wt)
.frame(false) .frame(false)
.stroke(Stroke::NONE) .stroke(Stroke::NONE)
.fill(Colors::TRANSPARENT) .fill(Colors::TRANSPARENT)
.ui(ui).interact(Sense::click_and_drag()); .ui(ui).interact(Sense::click_and_drag());
Self::on_button_click(ui, br, cb); Self::on_button_click(ui, br, callback);
} }
} }

View file

@ -12,11 +12,6 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // 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, ConfigError, ConfigMembers, GlobalConfig};
use grin_config::config::{API_SECRET_FILE_NAME, FOREIGN_API_SECRET_FILE_NAME, SERVER_CONFIG_FILE_NAME}; use grin_config::config::{API_SECRET_FILE_NAME, FOREIGN_API_SECRET_FILE_NAME, SERVER_CONFIG_FILE_NAME};
use grin_core::global::ChainTypes; use grin_core::global::ChainTypes;
@ -24,70 +19,49 @@ use serde::{Deserialize, Serialize};
use crate::Settings; 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)] #[derive(Serialize, Deserialize)]
pub struct NodeConfig { pub struct NodeConfig {
pub global_config: GlobalConfig, pub members: ConfigMembers
update_needed: AtomicBool,
updating: AtomicBool
} }
impl NodeConfig { impl NodeConfig {
/// Initialize node config with provided chain type from the disk. /// Initialize integrated node config.
pub fn init(chain_type: &ChainTypes) -> Self { 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, API_SECRET_FILE_NAME);
let _ = Self::check_api_secret_files(chain_type, FOREIGN_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)); let config_members = Self::for_chain_type(chain_type);
// Create default config if it doesn't exist or has wrong format.
if !config_path.exists() || toml::from_str::<ConfigMembers>(
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());
Self { Self {
global_config: config.unwrap(), members: config_members
update_needed: AtomicBool::new(false),
updating: AtomicBool::new(false)
} }
} }
/// Write node config on disk. /// Initialize config with provided [`ChainTypes`].
pub fn save_config(&self) { pub fn for_chain_type(chain_type: ChainTypes) -> ConfigMembers {
if self.updating.load(Ordering::Relaxed) { let path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, Some(chain_type));
self.update_needed.store(true, Ordering::Relaxed); let parsed = Settings::read_from_file::<ConfigMembers>(path.clone());
return; 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 { /// Save node config to disk.
let config = Settings::get_node_config(); pub fn save(&mut self) {
config.update_needed.store(false, Ordering::Relaxed); let chain_type = self.members.server.chain_type;
config.updating.store(true, Ordering::Relaxed); let config_path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, Some(chain_type));
Settings::write_to_file(&self.members, config_path);
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;
}
});
} }
/// Check that the api secret files exist and are valid. /// Check that the api secret files exist and are valid.
fn check_api_secret_files( fn check_api_secret_files(
chain_type: &ChainTypes, chain_type: ChainTypes,
secret_file_name: &str, secret_file_name: &str,
) -> Result<(), ConfigError> { ) -> Result<(), ConfigError> {
let grin_path = Settings::get_working_path(Some(chain_type)); let grin_path = Settings::get_working_path(Some(chain_type));

View file

@ -45,7 +45,9 @@ pub struct Node {
/// Thread flag to stop the server. /// Thread flag to stop the server.
stop_needed: AtomicBool, stop_needed: AtomicBool,
/// Flag to check if app exit is needed after server stop. /// 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 { impl Default for Node {
@ -55,7 +57,8 @@ impl Default for Node {
starting: AtomicBool::new(false), starting: AtomicBool::new(false),
restart_needed: AtomicBool::new(false), restart_needed: AtomicBool::new(false),
stop_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. /// Check if node is starting.
pub fn is_starting() -> bool { pub fn is_starting() -> bool {
NODE_STATE.starting.load(Ordering::Relaxed) NODE_STATE.starting.load(Ordering::Relaxed)
@ -164,8 +177,20 @@ impl Node {
NODE_STATE.starting.store(false, Ordering::Relaxed); NODE_STATE.starting.store(false, Ordering::Relaxed);
NODE_STATE.stop_needed.store(false, Ordering::Relaxed); NODE_STATE.stop_needed.store(false, Ordering::Relaxed);
NODE_STATE.start_stratum_server.store(false, Ordering::Relaxed);
break; break;
} else { } 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(); let stats = server.get_server_stats();
if stats.is_ok() { if stats.is_ok() {
// Update server stats. // Update server stats.
@ -308,8 +333,8 @@ impl Node {
/// Start the node [`Server`]. /// Start the node [`Server`].
fn start_server() -> Server { fn start_server() -> Server {
// Get current global config // Get current global config
let config = &Settings::get_node_config().global_config; let config = &Settings::node_config_to_read().members;
let server_config = config.members.as_ref().unwrap().server.clone(); let server_config = config.server.clone();
// Remove temporary file dir // Remove temporary file dir
{ {
@ -328,7 +353,7 @@ fn start_server() -> Server {
// accept_fee_base, and future_time_limit. // accept_fee_base, and future_time_limit.
// These are read via global and not read from config beyond this point. // These are read via global and not read from config beyond this point.
if !global::GLOBAL_CHAIN_TYPE.is_init() { 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()); info!("Chain: {:?}", global::get_chain_type());
@ -345,12 +370,12 @@ fn start_server() -> Server {
} }
} }
if !global::GLOBAL_ACCEPT_FEE_BASE.is_init() { 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); global::init_global_accept_fee_base(afb);
info!("Accept Fee Base: {:?}", global::get_accept_fee_base()); info!("Accept Fee Base: {:?}", global::get_accept_fee_base());
} }
if !global::GLOBAL_FUTURE_TIME_LIMIT.is_init() { 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); global::init_global_future_time_limit(future_time_limit);
info!("Future Time Limit: {:?}", global::get_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")] #[cfg(target_os = "android")]
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[no_mangle] #[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( pub extern "C" fn Java_mw_gri_android_BackgroundService_exitAppAfterNodeStop(
_env: jni::JNIEnv, _env: jni::JNIEnv,
_class: jni::objects::JObject, _class: jni::objects::JObject,
_activity: jni::objects::JObject, _activity: jni::objects::JObject,
) -> jboolean { ) -> jboolean {
let exit_after_stop = NODE_STATE.exit_after_stop.load(Ordering::Relaxed); let exit_needed = !Node::is_running() && NODE_STATE.exit_after_stop.load(Ordering::Relaxed);
let is_app_exit_needed = !Node::is_running() && exit_after_stop; return exit_needed as jboolean;
return is_app_exit_needed as jboolean;
} }

View file

@ -38,7 +38,7 @@ pub struct AppConfig {
/// Run node server on startup. /// Run node server on startup.
pub auto_start_node: bool, pub auto_start_node: bool,
/// Chain type for node server. /// Chain type for node server.
pub node_chain_type: ChainTypes node_chain_type: ChainTypes
} }
impl Default for AppConfig { impl Default for AppConfig {
@ -53,62 +53,87 @@ impl Default for AppConfig {
impl AppConfig { impl AppConfig {
/// Initialize application config from the disk. /// Initialize application config from the disk.
pub fn init() -> Self { pub fn init() -> Self {
let config_path = Settings::get_config_path(APP_CONFIG_FILE_NAME, None); let path = Settings::get_config_path(APP_CONFIG_FILE_NAME, None);
let parsed = Settings::read_from_file::<AppConfig>(config_path.clone()); let parsed = Settings::read_from_file::<AppConfig>(path.clone());
if !config_path.exists() || parsed.is_err() { if !path.exists() || parsed.is_err() {
let default_config = AppConfig::default(); let default_config = AppConfig::default();
Settings::write_to_file(&default_config, config_path); Settings::write_to_file(&default_config, path);
default_config default_config
} else { } else {
parsed.unwrap() 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) { pub fn save(&self) {
Settings::write_to_file(self, Settings::get_config_path(APP_CONFIG_FILE_NAME, None)); 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 { pub struct Settings {
app_config: Arc<RwLock<AppConfig>>, app_config: Arc<RwLock<AppConfig>>,
node_config: Arc<RwLock<NodeConfig>> node_config: Arc<RwLock<NodeConfig>>
} }
impl Settings { impl Settings {
/// Initialize settings with app and node configs from the disk. /// Initialize settings with app and node configs.
fn init() -> Self { fn init() -> Self {
let app_config = AppConfig::init(); let app_config = AppConfig::init();
let chain_type = app_config.node_chain_type; let chain_type = app_config.node_chain_type;
Self { Self {
app_config: Arc::new(RwLock::new(app_config)), 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() 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() 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() SETTINGS_STATE.app_config.write().unwrap()
} }
/// Get working directory path for application. /// Get working directory path for the application.
pub fn get_working_path(chain_type: Option<&ChainTypes>) -> PathBuf { pub fn get_working_path(chain_type: Option<ChainTypes>) -> PathBuf {
// Check if dir exists // Check if dir exists.
let mut path = match dirs::home_dir() { let mut path = match dirs::home_dir() {
Some(p) => p, Some(p) => p,
None => PathBuf::new(), None => PathBuf::new(),
}; };
path.push(".grim"); path.push(WORKING_DIRECTORY_NAME);
if chain_type.is_some() { if chain_type.is_some() {
path.push(chain_type.unwrap().shortname()); 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() { if !path.exists() {
let _ = fs::create_dir_all(path.clone()); let _ = fs::create_dir_all(path.clone());
} }
@ -116,14 +141,14 @@ impl Settings {
} }
/// Get config file path from provided name and [`ChainTypes`] if needed. /// 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<ChainTypes>) -> PathBuf {
let main_path = Self::get_working_path(chain_type); let main_path = Self::get_working_path(chain_type);
let mut settings_path = main_path.clone(); let mut settings_path = main_path.clone();
settings_path.push(config_name); settings_path.push(config_name);
settings_path settings_path
} }
/// Read config from file /// Read config from a file
pub fn read_from_file<T: DeserializeOwned>(config_path: PathBuf) -> Result<T, ConfigError> { pub fn read_from_file<T: DeserializeOwned>(config_path: PathBuf) -> Result<T, ConfigError> {
let file_content = fs::read_to_string(config_path.clone())?; let file_content = fs::read_to_string(config_path.clone())?;
let parsed = toml::from_str::<T>(file_content.as_str()); let parsed = toml::from_str::<T>(file_content.as_str());