config + ui + node: all server config values, stratum config ui refactoring, global modal position fix, modal id refactoring, remove chain data on node launch error

This commit is contained in:
ardocrat 2023-06-23 22:12:30 +03:00
parent 4ad134c5e3
commit 1346b04bc9
20 changed files with 906 additions and 241 deletions

View file

@ -19,7 +19,7 @@ sync_status:
awaiting_peers: Waiting for peers
header_sync: Downloading headers
header_sync_percent: 'Downloading headers: %{percent}%'
tx_hashset_pibd: 'Downloading state (PIBD)'
tx_hashset_pibd: Downloading state (PIBD)
tx_hashset_pibd_percent: 'Downloading state (PIBD): %{percent}%'
tx_hashset_download: Downloading chain state
tx_hashset_download_percent: 'Downloading chain state: %{percent}%'
@ -57,22 +57,28 @@ network_mining:
loading: Mining will be available after the synchronization
server_setup: Stratum server setup
enable_server: Enable server
server_setting: 'Enable stratum server or change more settings by selecting %{settings} at the bottom of the screen.'
server_setting: 'Enable stratum server or change more settings by selecting %{settings} at the bottom of the screen. App restart is required to change settings of the running server.'
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.'
no_ip_addresses: There are no available IP addresses on your system, the server cannot be started, check your network connectivity.
choose_ip_address: 'Choose IP Address:'
change_port: 'Change port:'
ip_address: IP Address
no_ip_addresses: There are no available IP addresses on your system, stratum server cannot be started, check your network connectivity.
port_unavailable: Specified port is unavailable
rewards_wallet: Wallet for rewards
server: Stratum server
address: Address
miners: Miners
devices: Devices
blocks_found: Blocks found
hashrate: 'Hashrate (C%{bits})'
connected: Connected
disconnected: Disconnected
network_settings:
server: Server
port: Port
ip_address: IP Address
change_port: Change port
enter_value: 'Enter value:'
modal:
cancel: Cancel
save: Save
modal_exit:
description: Are you sure you want to quit the application?
exit: Exit

View file

@ -19,7 +19,7 @@ sync_status:
awaiting_peers: Ожидание пиров
header_sync: Загрузка заголовков
header_sync_percent: 'Загрузка заголовков: %{percent}%'
tx_hashset_pibd: 'Загрузка состояния (PIBD)'
tx_hashset_pibd: Загрузка состояния (PIBD)
tx_hashset_pibd_percent: 'Загрузка состояния (PIBD): %{percent}%'
tx_hashset_download: Загрузка состояния цепи
tx_hashset_download_percent: 'Загрузка состояния цепи: %{percent}%'
@ -57,22 +57,28 @@ network_mining:
loading: Майнинг будет доступен после синхронизации
server_setup: Настройка stratum-сервера
enable_server: Включить сервер
server_setting: 'Включите stratum-сервер или измените больше настроек, выбрав %{settings} внизу экрана.'
server_setting: 'Включите stratum-сервер или измените больше настроек, выбрав %{settings} внизу экрана. Для изменения настроек запущенного сервера потребуется перезапуск приложения.'
info: 'Сервер майнинга запущен, вы можете изменить настройки, выбрав %{settings} внизу экрана. Данные обновляются, когда устройства подключены.'
no_ip_addresses: В вашей системе отсутствуют доступные IP адреса, запуск сервера невозможен, проверьте ваше подключение к сети.
choose_ip_address: 'Выберите IP адрес:'
change_port: 'Измените порт:'
ip_address: IP Адрес
no_ip_addresses: В вашей системе отсутствуют доступные IP адреса, запуск stratum-сервера невозможен, проверьте ваше подключение к сети.
port_unavailable: Указанный порт недоступен
rewards_wallet: Кошелёк для наград
server: Stratum-сервер
address: Адрес
miners: Майнеры
devices: Устройства
found: Найдено
hashrate: 'Хешрэйт (C%{bits})'
connected: Подключен
disconnected: Отключен
network_settings:
server: Сервер
ip_address: IP Адрес
port: Порт
change_port: Изменить порт
enter_value: 'Введите значение:'
modal:
cancel: Отмена
save: Сохранить
modal_exit:
description: Вы уверены, что хотите выйти из приложения?
exit: Выход

View file

@ -64,7 +64,6 @@ impl App {
/// Setup application styles.
pub fn setup_visuals(ctx: &Context) {
// Setup style
let mut style = (*ctx.style()).clone();
// Setup spacing for buttons.
style.spacing.button_padding = egui::vec2(12.0, 8.0);
@ -76,14 +75,16 @@ impl App {
style.spacing.icon_width = 24.0;
style.spacing.icon_width_inner = 14.0;
style.spacing.icon_spacing = 10.0;
// Setup style
ctx.set_style(style);
// Setup visuals
let mut visuals = egui::Visuals::light();
// Setup selection color.
visuals.selection.stroke = Stroke { width: 1.0, color: Colors::TEXT };
visuals.selection.bg_fill = Colors::GOLD;
// Disable stroke around panels by default
visuals.widgets.noninteractive.bg_stroke = Stroke::NONE;
// Setup visuals
ctx.set_visuals(visuals);
}

View file

@ -25,10 +25,12 @@ impl Colors {
pub const GOLD: Color32 = Color32::from_rgb(255, 215, 0);
pub const GREEN: Color32 = Color32::from_rgb(0, 0x64, 0);
pub const RED: Color32 = Color32::from_rgb(0x8B, 0, 0);
pub const FILL: Color32 = Color32::from_gray(240);
pub const FILL: Color32 = Color32::from_gray(244);
pub const FILL_DARK: Color32 = Color32::from_gray(232);
pub const TITLE: Color32 = Color32::from_gray(60);
pub const TEXT: Color32 = Color32::from_gray(80);
pub const BUTTON: Color32 = Color32::from_gray(70);
pub const TEXT_BUTTON: Color32 = Color32::from_gray(70);
pub const BUTTON: Color32 = Color32::from_gray(249);
pub const GRAY: Color32 = Color32::from_gray(120);
pub const STROKE: Color32 = Color32::from_gray(190);
pub const INACTIVE_TEXT: Color32 = Color32::from_gray(150);

View file

@ -19,7 +19,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use lazy_static::lazy_static;
use crate::gui::screens::ScreenId;
use crate::gui::views::{Modal, ModalId, ModalLocation};
use crate::gui::views::{Modal, ModalLocation};
lazy_static! {
/// Static [`Navigator`] state to be accessible from anywhere.
@ -53,6 +53,9 @@ impl Default for Navigator {
}
impl Navigator {
/// Identifier for exit [`Modal`].
pub const EXIT_MODAL: &'static str = "exit";
/// Initialize navigation from provided [`ScreenId`].
pub fn init(from: ScreenId) {
let mut w_nav = NAVIGATOR_STATE.write().unwrap();
@ -118,7 +121,7 @@ impl Navigator {
/// Open exit confirmation [`Modal`] with provided [NAVIGATOR_STATE] lock.
fn open_exit_modal_nav(mut w_nav: RwLockWriteGuard<Navigator>) {
let m = Modal::new(ModalId::Exit, ModalLocation::Global).title(t!("modal_exit.exit"));
let m = Modal::new(Self::EXIT_MODAL, ModalLocation::Global).title(t!("modal_exit.exit"));
w_nav.global_modal = Some(m);
}

View file

@ -15,7 +15,7 @@
use egui::Frame;
use crate::gui::icons::{ARROW_CIRCLE_LEFT, GLOBE, PLUS};
use crate::gui::Navigator;
use crate::gui::{Colors, Navigator};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::screens::{Screen, ScreenId};
use crate::gui::views::{TitlePanel, TitlePanelAction, View};
@ -43,6 +43,7 @@ impl Screen for Accounts {
egui::CentralPanel::default()
.frame(Frame {
stroke: View::DEFAULT_STROKE,
fill: Colors::FILL_DARK,
..Default::default()
})
.show_inside(ui, |ui| {

View file

@ -17,7 +17,7 @@ use std::cmp::min;
use crate::gui::{App, Colors, Navigator};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::screens::{Account, Accounts, Screen, ScreenId};
use crate::gui::views::{ModalId, ModalLocation, Network, View};
use crate::gui::views::{ModalLocation, Network, View};
use crate::node::Node;
pub struct Root {
@ -67,10 +67,9 @@ impl Root {
ui: &mut egui::Ui,
frame: &mut eframe::Frame,
cb: &dyn PlatformCallbacks) {
let location = ModalLocation::Global;
Navigator::modal_ui(ui, location, |ui, modal| {
Navigator::modal_ui(ui, ModalLocation::Global, |ui, modal| {
match modal.id {
ModalId::Exit => {
Navigator::EXIT_MODAL => {
if self.show_exit_progress {
if !Node::is_running() {
App::exit(frame, cb);
@ -89,30 +88,36 @@ impl Root {
ui.label(t!("modal_exit.description"));
});
ui.add_space(10.0);
// Setup spacing between buttons
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || {
if !Node::is_running() {
App::exit(frame, cb);
// Show modal buttons.
ui.scope(|ui| {
// Setup spacing between buttons.
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || {
if !Node::is_running() {
App::exit(frame, cb);
modal.close();
} else {
Node::stop(true);
modal.disable_closing();
self.show_exit_progress = true;
}
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
modal.close();
} else {
Node::stop(true);
modal.disable_closing();
self.show_exit_progress = true;
}
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
modal.close();
});
});
});
ui.add_space(6.0);
});
ui.add_space(6.0);
}
}
_ => {}
}
});
}

View file

@ -28,3 +28,5 @@ mod network_node;
mod network_settings;
mod network_metrics;
mod network_mining;
mod settings_stratum;
mod settings_node_server;

View file

@ -21,11 +21,6 @@ use egui::epaint::RectShape;
use crate::gui::Colors;
use crate::gui::views::View;
/// Identifier for [`Modal`] content to draw at [`Modal::ui`].
pub enum ModalId {
Exit
}
/// Location for [`Modal`] at application UI.
pub enum ModalLocation {
/// To draw globally above side panel and screen.
@ -46,8 +41,8 @@ pub enum ModalPosition {
/// Stores data to draw dialog box/popup at UI, powered by [`egui::Window`].
pub struct Modal {
/// Identifier for content.
pub(crate) id: ModalId,
/// Identifier for modal.
pub(crate) id: &'static str,
/// Location at UI.
pub(crate) location: ModalLocation,
/// Position on the screen.
@ -65,7 +60,7 @@ impl Modal {
const DEFAULT_WIDTH: i64 = 380;
/// Create open and closeable Modal with center position.
pub fn new(id: ModalId, location: ModalLocation) -> Self {
pub fn new(id: &'static str, location: ModalLocation) -> Self {
Self {
id,
location,
@ -116,9 +111,7 @@ impl Modal {
/// Show Modal with provided content.
pub fn ui(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
let width = min(ui.available_width() as i64 - 20, Self::DEFAULT_WIDTH) as f32;
// Show background Window at full available size
// Show background Window at full available size.
egui::Window::new(self.window_id(true))
.title_bar(false)
.resizable(false)
@ -133,13 +126,17 @@ impl Modal {
ui.set_min_size(ui.available_size());
});
// Show main content Window at given position
// Choose width of modal content.
let width = min(ui.available_width() as i64 - 20, Self::DEFAULT_WIDTH) as f32;
// Show main content Window at given position.
let (content_align, content_offset) = self.modal_position();
let layer_id = egui::Window::new(self.window_id(false))
.title_bar(false)
.resizable(false)
.collapsible(false)
.default_width(width)
.anchor(self.modal_position(), Vec2::default())
.anchor(content_align, content_offset)
.frame(egui::Frame {
rounding: Rounding::same(8.0),
fill: Colors::YELLOW,
@ -152,7 +149,7 @@ impl Modal {
self.draw_content(ui, add_content);
}).unwrap().response.layer_id;
// Always show main content Window above background Window
// Always show main content Window above background Window.
ui.ctx().move_to_top(layer_id);
}
@ -173,11 +170,16 @@ impl Modal {
}
/// Get [`egui::Window`] position based on [`ModalPosition`].
fn modal_position(&self) -> Align2 {
match self.position {
fn modal_position(&self) -> (Align2, Vec2) {
let align = match self.position {
ModalPosition::CenterTop => { Align2::CENTER_TOP }
ModalPosition::Center => { Align2::CENTER_CENTER }
}
};
let offset = match self.position {
ModalPosition::CenterTop => { Vec2::new(0.0, 20.0) }
ModalPosition::Center => { Vec2::new(0.0, 0.0) }
};
(align, offset)
}
/// Draw provided content.

View file

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::net::{IpAddr, Ipv4Addr, SocketAddrV4, TcpListener};
use std::str::FromStr;
use std::time::Duration;
use egui::{Color32, lerp, Rgba, RichText, Stroke};
@ -22,17 +24,18 @@ use grin_chain::SyncStatus;
use crate::gui::{Colors, Navigator};
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, ModalLocation, View};
use crate::gui::views::network_metrics::NetworkMetrics;
use crate::gui::views::network_mining::NetworkMining;
use crate::gui::views::network_node::NetworkNode;
use crate::gui::views::network_settings::NetworkSettings;
use crate::gui::views::View;
use crate::node::Node;
use crate::Settings;
pub trait NetworkTab {
fn get_type(&self) -> NetworkTabType;
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks);
fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks);
}
#[derive(PartialEq)]
@ -68,13 +71,19 @@ impl Default for Network {
impl Network {
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
// Show modal if it's opened.
if Navigator::is_modal_open(ModalLocation::SidePanel) {
Navigator::modal_ui(ui, ModalLocation::SidePanel, |ui, modal| {
self.current_tab.as_mut().on_modal_ui(ui, modal, cb);
});
}
egui::TopBottomPanel::top("network_title")
.resizable(false)
.frame(egui::Frame {
fill: Colors::YELLOW,
inner_margin: Margin::same(0.0),
outer_margin: Margin::same(0.0),
stroke: Stroke::NONE,
..Default::default()
})
.show_inside(ui, |ui| {
@ -217,7 +226,8 @@ impl Network {
});
}
pub fn disabled_server_content(ui: &mut egui::Ui) {
/// Content to draw when node is disabled.
pub fn disabled_node_ui(ui: &mut egui::Ui) {
View::center_content(ui, 162.0, |ui| {
let text = t!("network.disabled_server", "dots" => DOTS_THREE_OUTLINE_VERTICAL);
ui.label(RichText::new(text)
@ -241,5 +251,25 @@ impl Network {
});
});
}
/// List of available IP addresses.
pub fn get_ip_list() -> Vec<IpAddr> {
let mut addresses = Vec::new();
for net_if in pnet::datalink::interfaces() {
for ip in net_if.ips {
if ip.is_ipv4() {
addresses.push(ip.ip());
}
}
}
addresses
}
/// Check whether a port is available on the provided host.
pub fn is_port_available(host: &str, port: u16) -> bool {
let ip_addr = Ipv4Addr::from_str(host).unwrap();
let ipv4 = SocketAddrV4::new(ip_addr, port);
TcpListener::bind(ipv4).is_ok()
}
}

View file

@ -13,13 +13,13 @@
// limitations under the License.
use chrono::{DateTime, NaiveDateTime, Utc};
use egui::{RichText, Rounding, ScrollArea, Stroke};
use egui::{RichText, Rounding, ScrollArea, Stroke, Ui};
use grin_servers::DiffBlock;
use crate::gui::Colors;
use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
use crate::gui::views::{Modal, Network, NetworkTab, NetworkTabType, View};
use crate::node::Node;
#[derive(Default)]
@ -39,7 +39,7 @@ impl NetworkTab for NetworkMetrics {
// 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);
Network::disabled_node_ui(ui);
} else {
View::center_content(ui, 162.0, |ui| {
View::big_loading_spinner(ui);
@ -113,9 +113,9 @@ impl NetworkTab for NetworkMetrics {
// Show difficulty adjustment window blocks
let blocks_size = stats.diff_stats.last_blocks.len();
ScrollArea::vertical()
.id_source("difficulty_scroll")
.auto_shrink([false; 2])
.stick_to_bottom(true)
.id_source("difficulty_scroll")
.show_rows(
ui,
DIFF_BLOCK_UI_HEIGHT,
@ -137,6 +137,8 @@ impl NetworkTab for NetworkMetrics {
},
);
}
fn on_modal_ui(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {}
}
const DIFF_BLOCK_UI_HEIGHT: f32 = 76.60;

View file

@ -12,24 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fmt::format;
use std::net::IpAddr;
use std::str::FromStr;
use chrono::{DateTime, NaiveDateTime, Utc};
use egui::{RichText, Rounding, ScrollArea, Stroke};
use egui::{Response, RichText, Rounding, ScrollArea, Stroke, TextStyle, Widget};
use grin_chain::SyncStatus;
use grin_servers::WorkerStats;
use pnet::ipnetwork::IpNetwork;
use crate::gui::Colors;
use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, COMPUTER_TOWER, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_NOTCH_MINUS, FOLDER_NOTCH_PLUS, PLUGS, PLUGS_CONNECTED, POLYGON, WRENCH};
use crate::gui::{Colors, Navigator};
use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, COMPUTER_TOWER, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_NOTCH_MINUS, FOLDER_NOTCH_PLUS, PLUGS, PLUGS_CONNECTED, POLYGON};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
use crate::node::Node;
use crate::gui::views::{Modal, Network, NetworkTab, NetworkTabType, View};
use crate::gui::views::settings_stratum::StratumServerSetup;
use crate::node::{Node, NodeConfig};
use crate::Settings;
#[derive(Default)]
pub struct NetworkMining;
pub struct NetworkMining {
stratum_server_setup: StratumServerSetup
}
impl NetworkTab for NetworkMining {
fn get_type(&self) -> NetworkTabType {
@ -42,7 +41,7 @@ impl NetworkTab for NetworkMining {
// 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);
Network::disabled_node_ui(ui);
} else {
View::center_content(ui, 162.0, |ui| {
View::big_loading_spinner(ui);
@ -58,137 +57,31 @@ impl NetworkTab for NetworkMining {
let stratum_stats = &server_stats.as_ref().unwrap().stratum_stats;
// Stratum server address + port from config.
let saved_stratum_addr = Settings::node_config_to_read()
.members.clone()
.server.stratum_mining_config.unwrap()
.stratum_server_addr.unwrap();
let (stratum_addr, stratum_port) = saved_stratum_addr.split_once(":").unwrap();
// List of available ip addresses.
let mut addresses = Vec::new();
for net_if in pnet::datalink::interfaces() {
for ip in net_if.ips {
if ip.is_ipv4() {
addresses.push(ip.ip());
}
}
}
// Show error message when IP addresses are not available on the system.
if addresses.is_empty() {
View::center_content(ui, 52.0, |ui| {
ui.label(RichText::new(t!("network_mining.no_ip_addresses"))
.size(16.0)
.color(Colors::INACTIVE_TEXT)
);
});
return;
}
// Show stratum server setup when mining server is not enabled.
// Show stratum server setup when mining server is not running.
if !stratum_stats.is_running && !Node::is_stratum_server_starting() {
ScrollArea::vertical()
.id_source("stratum_server_setup")
.auto_shrink([false; 2])
.show(ui, |ui| {
ui.add_space(6.0);
View::sub_title(ui,
format!("{} {}", WRENCH, t!("network_mining.server_setup")));
ui.add_space(4.0);
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(6.0);
self.stratum_server_setup.ui(ui, cb);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_mining.choose_ip_address"))
.size(16.0)
.color(Colors::GRAY)
);
ui.add_space(10.0);
if addresses.len() != 0 {
let saved_ip_addr = &IpAddr::from_str(stratum_addr).unwrap();
let mut selected_addr = saved_ip_addr;
// Set first IP address as current if saved is not present at system.
if !addresses.contains(selected_addr) {
selected_addr = addresses.get(0).unwrap();
}
// Show available IP addresses on the system.
let _ = addresses.chunks(2).map(|x| {
if x.len() == 2 {
ui.columns(2, |columns| {
let addr0 = x.get(0).unwrap();
columns[0].vertical_centered(|ui| {
View::radio_value(ui,
&mut selected_addr,
addr0,
addr0.to_string());
});
let addr1 = x.get(1).unwrap();
columns[1].vertical_centered(|ui| {
View::radio_value(ui,
&mut selected_addr,
addr1,
addr1.to_string());
})
});
ui.add_space(12.0);
} else {
let addr = x.get(0).unwrap();
View::radio_value(ui,
&mut selected_addr,
addr,
addr.to_string());
ui.add_space(4.0);
}
}).collect::<Vec<_>>();
// Save stratum server address at config if it was changed.
if saved_ip_addr != selected_addr {
let addr_to_save = format!("{}:{}", selected_addr, stratum_port);
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members
.server.stratum_mining_config.as_mut().unwrap()
.stratum_server_addr = Some(addr_to_save);
w_node_config.save();
}
}
ui.label(RichText::new(t!("network_mining.change_port"))
.size(16.0)
.color(Colors::GRAY)
);
// Show button to choose server port.
ui.add_space(6.0);
View::button(ui, stratum_port.to_string(), Colors::WHITE, || {
//TODO: Open modal to change value
cb.show_keyboard();
});
ui.add_space(14.0);
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(6.0);
// Show message about stratum server config.
let text = t!(
"network_mining.server_setting",
"address" => saved_stratum_addr,
"settings" => FADERS
);
let text = t!("network_mining.server_setting", "settings" => FADERS);
ui.label(RichText::new(text)
.size(16.0)
.color(Colors::INACTIVE_TEXT)
);
ui.add_space(8.0);
ui.add_space(4.0);
// Show button to enable server.
View::button(ui, t!("network_mining.enable_server"), Colors::GOLD, || {
Node::start_stratum_server();
});
ui.add_space(2.0);
// Show button to enable stratum server if port is available.
if self.stratum_server_setup.stratum_port_available {
ui.add_space(6.0);
View::button(ui, t!("network_mining.enable_server"), Colors::GOLD, || {
Node::start_stratum_server();
});
ui.add_space(2.0);
}
let stratum_enabled = Settings::node_config_to_read()
.members.clone()
@ -218,9 +111,10 @@ impl NetworkTab for NetworkMining {
View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network_mining.server")));
ui.columns(2, |columns| {
columns[0].vertical_centered(|ui| {
let (stratum_addr, stratum_port) = NodeConfig::get_stratum_address_port();
View::rounded_box(ui,
saved_stratum_addr,
t!("network_mining.ip_address"),
format!("{}:{}", stratum_addr, stratum_port),
t!("network_mining.address"),
[true, false, true, false]);
});
columns[1].vertical_centered(|ui| {
@ -302,7 +196,6 @@ impl NetworkTab for NetworkMining {
ui.add_space(4.0);
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(4.0);
ScrollArea::vertical()
.auto_shrink([false; 2])
.id_source("stratum_workers_scroll")
@ -322,7 +215,7 @@ impl NetworkTab for NetworkMining {
} else {
[false, false]
};
draw_worker_stats(ui, worker, rounding)
draw_workers_stats(ui, worker, rounding)
}
},
);
@ -335,11 +228,20 @@ impl NetworkTab for NetworkMining {
});
}
}
fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
match modal.id {
StratumServerSetup::STRATUM_PORT_MODAL => {
self.stratum_server_setup.stratum_port_modal_ui(ui, modal, cb);
},
_ => {}
}
}
}
const WORKER_UI_HEIGHT: f32 = 77.0;
fn draw_worker_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
fn draw_workers_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
// Add space before the first item.
if rounding[0] {
ui.add_space(4.0);

View file

@ -13,13 +13,13 @@
// limitations under the License.
use eframe::epaint::Stroke;
use egui::{RichText, Rounding, ScrollArea};
use egui::{RichText, Rounding, ScrollArea, Ui};
use grin_servers::PeerStats;
use crate::gui::Colors;
use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
use crate::gui::views::{Modal, Network, NetworkTab, NetworkTabType, View};
use crate::node::Node;
#[derive(Default)]
@ -35,7 +35,7 @@ impl NetworkTab for NetworkNode {
// 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);
Network::disabled_node_ui(ui);
} else {
ui.centered_and_justified(|ui| {
View::big_loading_spinner(ui);
@ -47,6 +47,7 @@ impl NetworkTab for NetworkNode {
let stats = server_stats.as_ref().unwrap();
ScrollArea::vertical()
.id_source("integrated_node")
.auto_shrink([false; 2])
.show(ui, |ui| {
// Show header info.
@ -180,6 +181,8 @@ impl NetworkTab for NetworkNode {
}
});
}
fn on_modal_ui(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {}
}
fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {

View file

@ -12,9 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use egui::Ui;
use grin_core::global::ChainTypes;
use crate::gui::Colors;
use crate::gui::icons::COMPUTER_TOWER;
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{NetworkTab, NetworkTabType};
use crate::gui::views::{Modal, NetworkTab, NetworkTabType, View};
use crate::Settings;
#[derive(Default)]
@ -26,5 +29,13 @@ impl NetworkTab for NetworkSettings {
}
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network_settings.server")));
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(4.0);
}
fn on_modal_ui(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
}
}

View file

@ -0,0 +1,16 @@
// 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.
// /// Integrated node server setup ui section.
// struct

View file

@ -0,0 +1,247 @@
// 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 std::net::IpAddr;
use std::str::FromStr;
use egui::{RichText, TextStyle, Widget};
use crate::gui::{Colors, Navigator};
use crate::gui::icons::WRENCH;
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, ModalLocation, ModalPosition, Network, View};
use crate::node::NodeConfig;
/// Stratum server setup ui section.
pub struct StratumServerSetup {
/// Stratum address to be used inside edit modal.
stratum_address_edit: String,
/// Stratum port to be used inside edit modal.
stratum_port_edit: String,
/// Flag to check if stratum port is available inside edit modal.
port_available_edit: bool,
/// Flag to check if stratum port is available from saved config value.
pub(crate) stratum_port_available: bool
}
impl Default for StratumServerSetup {
fn default() -> Self {
let (stratum_address, stratum_port) = NodeConfig::get_stratum_address_port();
let is_port_available = Network::is_port_available(stratum_address.as_str(), stratum_port);
Self {
stratum_address_edit: stratum_address,
stratum_port_edit: stratum_port.to_string(),
port_available_edit: is_port_available,
stratum_port_available: is_port_available
}
}
}
impl StratumServerSetup {
/// Identifier for stratum port [`Modal`].
pub const STRATUM_PORT_MODAL: &'static str = "stratum_port";
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", WRENCH, t!("network_mining.server_setup")));
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(4.0);
// Show error message when IP addresses are not available on the system.
let mut addresses = Network::get_ip_list();
if addresses.is_empty() {
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_mining.no_ip_addresses"))
.size(16.0)
.color(Colors::INACTIVE_TEXT)
);
ui.add_space(6.0);
});
return;
}
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.ip_address"))
.size(16.0)
.color(Colors::GRAY)
);
ui.add_space(6.0);
// Show stratum IP address setup.
Self::ip_address_setup_ui(ui, addresses);
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(6.0);
ui.label(RichText::new(t!("network_settings.port"))
.size(16.0)
.color(Colors::GRAY)
);
// Show button to choose stratum server port.
ui.add_space(6.0);
let (stratum_address, stratum_port) = NodeConfig::get_stratum_address_port();
View::button(ui, stratum_port.to_string(), Colors::BUTTON, || {
// Setup values for modal.
self.stratum_address_edit = stratum_address.clone();
self.stratum_port_edit = stratum_port.to_string();
self.port_available_edit = Network::is_port_available(
stratum_address.as_str(),
stratum_port
);
// Show stratum port modal.
let port_modal = Modal::new(Self::STRATUM_PORT_MODAL,
ModalLocation::SidePanel)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_port"));
Navigator::open_modal(port_modal);
cb.show_keyboard();
});
ui.add_space(12.0);
// Show error when stratum server port is unavailable.
if !self.stratum_port_available {
ui.label(RichText::new(t!("network_mining.port_unavailable"))
.size(16.0)
.color(Colors::RED));
ui.add_space(12.0);
}
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(6.0);
});
}
/// Draw stratum port [`Modal`] content.
pub fn stratum_port_modal_ui(&mut self,
ui: &mut egui::Ui,
modal: &Modal,
cb: &dyn PlatformCallbacks) {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.enter_value"))
.size(16.0)
.color(Colors::GRAY));
ui.add_space(8.0);
// Draw stratum port text edit.
let text_edit_resp = egui::TextEdit::singleline(&mut self.stratum_port_edit)
.font(TextStyle::Button)
.desired_width(48.0)
.cursor_at_end(true)
.ui(ui);
text_edit_resp.request_focus();
if text_edit_resp.clicked() {
cb.show_keyboard();
}
// Show error when specified port is unavailable.
if !self.port_available_edit {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_mining.port_unavailable"))
.size(16.0)
.color(Colors::RED));
}
ui.add_space(12.0);
// Show modal buttons.
ui.scope(|ui| {
// Setup spacing between buttons.
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.save"), Colors::WHITE, || {
// Check if port is available.
let port_parse = self.stratum_port_edit.parse::<u16>();
let is_available = port_parse.is_ok() && Network::is_port_available(
self.stratum_address_edit.as_str(),
port_parse.unwrap()
);
self.port_available_edit = is_available;
// Save port at config if it's available.
if self.port_available_edit {
NodeConfig::save_stratum_address_port(
self.stratum_address_edit.clone(),
self.stratum_port_edit.clone()
);
self.stratum_port_available = true;
cb.hide_keyboard();
modal.close();
}
});
});
});
ui.add_space(6.0);
});
});
}
/// Show stratum IP address setup.
fn ip_address_setup_ui(ui: &mut egui::Ui, addresses: Vec<IpAddr>) {
let (addr, port) = NodeConfig::get_stratum_address_port();
let saved_ip_addr = &IpAddr::from_str(addr.as_str()).unwrap();
let mut selected_addr = saved_ip_addr;
// Set first IP address as current if saved is not present at system.
if !addresses.contains(selected_addr) {
selected_addr = addresses.get(0).unwrap();
}
// Show available IP addresses on the system.
let _ = addresses.chunks(2).map(|x| {
if x.len() == 2 {
ui.columns(2, |columns| {
let addr0 = x.get(0).unwrap();
columns[0].vertical_centered(|ui| {
View::radio_value(ui,
&mut selected_addr,
addr0,
addr0.to_string());
});
let addr1 = x.get(1).unwrap();
columns[1].vertical_centered(|ui| {
View::radio_value(ui,
&mut selected_addr,
addr1,
addr1.to_string());
})
});
} else {
let addr = x.get(0).unwrap();
View::radio_value(ui,
&mut selected_addr,
addr,
addr.to_string());
}
ui.add_space(10.0);
}).collect::<Vec<_>>();
// Save stratum server address at config if it was changed.
if saved_ip_addr != selected_addr {
NodeConfig::save_stratum_address_port(selected_addr.to_string(), port.to_string());
}
}
}

View file

@ -115,7 +115,7 @@ impl View {
/// Draw [`Button`] with specified background fill color.
pub fn button(ui: &mut egui::Ui, text: String, fill_color: Color32, action: impl FnOnce()) {
let br = Button::new(RichText::new(text.to_uppercase()).size(18.0).color(Colors::BUTTON))
let br = Button::new(RichText::new(text.to_uppercase()).size(18.0).color(Colors::TEXT_BUTTON))
.stroke(Self::DEFAULT_STROKE)
.fill(fill_color)
.ui(ui);
@ -207,7 +207,7 @@ impl View {
/// Draw the button that looks like checkbox with callback on check.
pub fn checkbox(ui: &mut egui::Ui, checked: bool, text: String, callback: impl FnOnce()) {
let (text_value, color) = match checked {
true => { (format!("{} {}", CHECK_SQUARE, text), Colors::BUTTON) }
true => { (format!("{} {}", CHECK_SQUARE, text), Colors::TEXT_BUTTON) }
false => { (format!("{} {}", SQUARE, text), Colors::TEXT) }
};
let br = Button::new(RichText::new(text_value).size(18.0).color(color))

View file

@ -12,9 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::net::IpAddr;
use std::str::FromStr;
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;
use grin_p2p::msg::PeerAddrs;
use grin_p2p::{PeerAddr, Seeding};
use grin_servers::common::types::ChainValidationMode;
use serde::{Deserialize, Serialize};
use crate::Settings;
@ -73,4 +81,429 @@ impl NodeConfig {
config::check_api_secret(&api_secret_path)
}
}
/// Get stratum server IP address and port.
pub fn get_stratum_address_port() -> (String, u16) {
let r_config = Settings::node_config_to_read();
let saved_stratum_addr = r_config
.members
.server
.stratum_mining_config
.as_ref()
.unwrap()
.stratum_server_addr
.as_ref()
.unwrap();
let (addr, port) = saved_stratum_addr.split_once(":").unwrap();
(addr.to_string(), port.parse().unwrap())
}
/// Save stratum server IP address and port.
pub fn save_stratum_address_port(addr: String, port: String) {
let addr_to_save = format!("{}:{}", addr, port);
let mut w_node_config = Settings::node_config_to_update();
w_node_config
.members
.server
.stratum_mining_config
.as_mut()
.unwrap()
.stratum_server_addr = Some(addr_to_save);
w_node_config.save();
}
/// Get API server IP address and port.
pub fn get_api_address_port() -> (String, u16) {
let r_config = Settings::node_config_to_read();
let saved_api_addr = r_config
.members
.server
.api_http_addr
.as_str();
let (addr, port) = saved_api_addr.split_once(":").unwrap();
(addr.to_string(), port.parse().unwrap())
}
/// Save API server IP address and port.
pub fn save_api_server_address_port(addr: String, port: String) {
let addr_to_save = format!("{}:{}", addr, port);
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.api_http_addr = addr_to_save;
w_node_config.save();
}
/// Get API secret text.
pub fn get_api_secret() -> String {
let r_config = Settings::node_config_to_read();
let api_secret_path = r_config
.members
.server
.api_secret_path
.as_ref()
.unwrap();
let api_secret_file = File::open(api_secret_path).unwrap();
let buf_reader = BufReader::new(api_secret_file);
let mut lines_iter = buf_reader.lines();
let first_line = lines_iter.next().unwrap();
first_line.unwrap()
}
/// Save API secret text.
pub fn save_api_secret(api_secret: String) {
if api_secret.is_empty() {
return;
}
let r_config = Settings::node_config_to_read();
let api_secret_path = r_config
.members
.server
.api_secret_path
.as_ref()
.unwrap();
let mut api_secret_file = File::create(api_secret_path).unwrap();
api_secret_file.write_all(api_secret.as_bytes()).unwrap();
}
/// Get Foreign API secret text.
pub fn get_foreign_api_secret() -> String {
let r_config = Settings::node_config_to_read();
let foreign_api_secret_path = r_config
.members
.server
.foreign_api_secret_path
.as_ref()
.unwrap();
let foreign_api_secret_file = File::open(foreign_api_secret_path).unwrap();
let buf_reader = BufReader::new(foreign_api_secret_file);
let mut lines_iter = buf_reader.lines();
let first_line = lines_iter.next().unwrap();
first_line.unwrap()
}
/// Save Foreign API secret text.
pub fn save_foreign_api_secret(api_secret: String) {
if api_secret.is_empty() {
return;
}
let r_config = Settings::node_config_to_read();
let foreign_api_secret_path = r_config
.members
.server
.foreign_api_secret_path
.as_ref()
.unwrap();
let mut foreign_api_secret_file = File::create(foreign_api_secret_path).unwrap();
foreign_api_secret_file.write_all(api_secret.as_bytes()).unwrap();
}
/// Get Future Time Limit.
pub fn get_ftl() -> u64 {
Settings::node_config_to_read().members.server.future_time_limit
}
/// Save Future Time Limit.
pub fn save_ftl(ftl: u64) {
let mut w_config = Settings::node_config_to_update();
w_config.members.server.future_time_limit = ftl;
w_config.save();
}
/// Check if full chain validation mode is enabled.
pub fn is_full_chain_validation() -> bool {
let mode = Settings::node_config_to_read().members.clone().server.chain_validation_mode;
mode == ChainValidationMode::EveryBlock
}
/// Toggle full chain validation.
pub fn toggle_chain_validation() {
let mode = Settings::node_config_to_read().members.clone().server.chain_validation_mode;
let new_mode = if mode == ChainValidationMode::Disabled {
ChainValidationMode::Disabled
} else {
ChainValidationMode::EveryBlock
};
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.chain_validation_mode = new_mode;
w_node_config.save();
}
/// Check if node is running in archive mode.
pub fn is_archive_mode() -> bool {
let archive_mode = Settings::node_config_to_read().members.clone().server.archive_mode;
archive_mode.is_some() && archive_mode.unwrap()
}
/// Toggle archive node mode.
pub fn toggle_archive_mode() {
let archive_mode = Self::is_archive_mode();
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.archive_mode = Some(!archive_mode);
w_node_config.save();
}
// P2P settings
/// Get P2P server port.
pub fn get_p2p_port() -> u16 {
Settings::node_config_to_read().members.server.p2p_config.port
}
/// Get P2P server port.
pub fn save_p2p_port(port: u16) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.port = port;
w_node_config.save();
}
/// Get peers seeding type.
pub fn get_peers_seeding_type() -> Seeding {
Settings::node_config_to_read().members.server.p2p_config.seeding_type
}
/// Get seeds for [`Seeding::List`] type.
pub fn get_seeds() -> PeerAddrs {
let r_config = Settings::node_config_to_read();
r_config.members.server.p2p_config.seeds.clone().unwrap_or(PeerAddrs::default())
}
/// Save peers seeding type, with list of peers for [`Seeding::List`] type.
pub fn save_peers_seeding_type(seeding_type: Seeding, peers: Option<PeerAddrs>) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.seeding_type = seeding_type;
if seeding_type == Seeding::List {
w_node_config.members.server.p2p_config.seeds = peers;
}
w_node_config.save();
}
/// Get denied peer list.
pub fn get_denied_peers() -> PeerAddrs {
let r_config = Settings::node_config_to_read();
r_config.members.server.p2p_config.peers_deny.clone().unwrap_or(PeerAddrs::default())
}
/// Add peer at denied list.
pub fn deny_peer(peer: String) {
let ip_addr = IpAddr::from_str(peer.as_str()).unwrap();
let peer_addr = PeerAddr::from_ip(ip_addr);
let mut deny_peers = Self::get_denied_peers();
deny_peers.peers.insert(0, peer_addr);
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.peers_deny = Some(deny_peers);
w_node_config.save();
}
/// Save denied peer list.
pub fn save_denied_peers(peers: Option<PeerAddrs>) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.peers_deny = peers;
w_node_config.save();
}
/// Get allowed peer list.
pub fn get_allowed_peers() -> PeerAddrs {
let r_config = Settings::node_config_to_read();
r_config.members.server.p2p_config.peers_allow.clone().unwrap_or(PeerAddrs::default())
}
/// Save allowed peer list.
pub fn save_allowed_peers(peers: Option<PeerAddrs>) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.peers_allow = peers;
w_node_config.save();
}
/// Get preferred peer list.
pub fn get_preferred_peers() -> PeerAddrs {
let r_config = Settings::node_config_to_read();
r_config.members.server.p2p_config.peers_preferred.clone().unwrap_or(PeerAddrs::default())
}
/// Add peer at preferred list.
pub fn prefer_peer(peer: String) {
let ip_addr = IpAddr::from_str(peer.as_str()).unwrap();
let peer_addr = PeerAddr::from_ip(ip_addr);
let mut prefer_peers = Self::get_preferred_peers();
prefer_peers.peers.insert(0, peer_addr);
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.peers_preferred = Some(prefer_peers);
w_node_config.save();
}
/// Save preferred peer list.
pub fn save_preferred_peers(peers: Option<PeerAddrs>) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.peers_preferred = peers;
w_node_config.save();
}
/// How long a banned peer should stay banned in ms.
pub fn get_ban_window() -> i64 {
Settings::node_config_to_read().members.server.p2p_config.ban_window()
}
/// Set how long a banned peer should stay banned in ms.
pub fn set_ban_window(time: i64) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.ban_window = Some(time);
w_node_config.save();
}
/// Maximum number of inbound peer connections.
pub fn get_max_inbound_count() -> u32 {
Settings::node_config_to_read().members.server.p2p_config.peer_max_inbound_count()
}
/// Set maximum number of inbound peer connections.
pub fn set_max_inbound_count(count: u32) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.peer_max_inbound_count = Some(count);
w_node_config.save();
}
/// Maximum number of outbound peer connections.
pub fn get_max_outbound_count() -> u32 {
Settings::node_config_to_read().members.server.p2p_config.peer_max_outbound_count()
}
/// Set maximum number of outbound peer connections.
pub fn set_max_outbound_count(count: u32) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.peer_max_outbound_count = Some(count);
w_node_config.save();
}
/// Minimum number of outbound peer connections.
pub fn get_min_outbound_count() -> u32 {
Settings::node_config_to_read()
.members
.server
.p2p_config
.peer_min_preferred_outbound_count()
}
/// Set minimum number of outbound peer connections.
pub fn set_min_outbound_count(count: u32) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.p2p_config.peer_min_preferred_outbound_count = Some(count);
w_node_config.save();
}
// Pool settings
/// Base fee that's accepted into the pool.
pub fn get_base_fee() -> u64 {
Settings::node_config_to_read().members.server.pool_config.accept_fee_base
}
/// Set base fee that's accepted into the pool.
pub fn set_base_fee(fee: u64) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.pool_config.accept_fee_base = fee;
w_node_config.save();
}
/// Reorg cache retention period in minute.
pub fn get_reorg_cache_period() -> u32 {
Settings::node_config_to_read().members.server.pool_config.reorg_cache_period
}
/// Set reorg cache retention period in minute.
pub fn set_reorg_cache_period(period: u32) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.pool_config.reorg_cache_period = period;
w_node_config.save();
}
/// Max amount of transactions at pool.
pub fn get_max_pool_size() -> usize {
Settings::node_config_to_read().members.server.pool_config.max_pool_size
}
/// Set max amount of transactions at pool.
pub fn set_max_pool_size(amount: usize) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.pool_config.max_pool_size = amount;
w_node_config.save();
}
/// Max amount of transactions at stem pool.
pub fn get_max_stempool_size() -> usize {
Settings::node_config_to_read().members.server.pool_config.max_stempool_size
}
/// Set max amount of transactions at stem pool.
pub fn set_max_stempool_size(amount: usize) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.pool_config.max_stempool_size = amount;
w_node_config.save();
}
/// Max total weight of transactions that can get selected to build a block.
pub fn get_mineable_max_weight() -> u64 {
Settings::node_config_to_read().members.server.pool_config.mineable_max_weight
}
/// Set max total weight of transactions that can get selected to build a block.
pub fn set_mineable_max_weight(weight: u64) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.pool_config.mineable_max_weight = weight;
w_node_config.save();
}
// Dandelion settings
/// Dandelion epoch duration in secs.
pub fn get_epoch() -> u16 {
Settings::node_config_to_read().members.server.dandelion_config.epoch_secs
}
/// Set Dandelion epoch duration in secs.
pub fn set_epoch(secs: u16) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.dandelion_config.epoch_secs = secs;
w_node_config.save();
}
/// Dandelion embargo timer in secs.
/// Fluff and broadcast after embargo expires if tx not seen on network.
pub fn get_embargo() -> u16 {
Settings::node_config_to_read().members.server.dandelion_config.embargo_secs
}
/// Set Dandelion embargo timer.
pub fn set_embargo(secs: u16) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.dandelion_config.embargo_secs = secs;
w_node_config.save();
}
/// Dandelion stem probability (default: stem 90% of the time, fluff 10% of the time).
pub fn get_stem_probability() -> u8 {
Settings::node_config_to_read().members.server.dandelion_config.stem_probability
}
/// Set Dandelion stem probability.
pub fn set_stem_probability(percent: u8) {
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.dandelion_config.stem_probability = percent;
w_node_config.save();
}
/// Default to always stem our txs as described in Dandelion++ paper.
pub fn always_stem_our_txs() -> bool {
Settings::node_config_to_read().members.server.dandelion_config.always_stem_our_txs
}
/// Toggle stem of our txs.
pub fn toggle_always_stem_our_txs() {
let stem_txs = Self::always_stem_our_txs();
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members.server.dandelion_config.always_stem_our_txs = stem_txs;
w_node_config.save();
}
}

View file

@ -355,7 +355,6 @@ fn start_server() -> Server {
if !global::GLOBAL_CHAIN_TYPE.is_init() {
global::init_global_chain_type(config.server.chain_type);
}
info!("Chain: {:?}", global::get_chain_type());
if !global::GLOBAL_NRD_FEATURE_ENABLED.is_init() {
match global::get_chain_type() {
@ -372,39 +371,33 @@ fn start_server() -> Server {
if !global::GLOBAL_ACCEPT_FEE_BASE.is_init() {
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.server.future_time_limit;
global::init_global_future_time_limit(future_time_limit);
info!("Future Time Limit: {:?}", global::get_future_time_limit());
}
let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) =
Box::leak(Box::new(oneshot::channel::<()>()));
let server_result = Server::new(server_config, None, api_chan);
//TODO: handle server errors
//
// if server_result.is_err() {
// let mut db_path = PathBuf::from(&server_config.db_root);
// db_path.push("grin.lock");
// fs::remove_file(db_path).unwrap();
//
// // Remove chain data on server start error
// let dirs_to_remove: Vec<&str> = vec!["header", "lmdb", "txhashset"];
// for dir in dirs_to_remove {
// let mut path = PathBuf::from(&server_config.db_root);
// path.push(dir);
// fs::remove_dir_all(path).unwrap();
// }
//
// // Recreate server
// let config = node_config.clone().unwrap();
// let server_config = config.members.as_ref().unwrap().server.clone();
// let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) =
// Box::leak(Box::new(oneshot::channel::<()>()));
// server_result = Server::new(server_config.clone(), None, api_chan);
// }
let mut server_result = Server::new(server_config.clone(), None, api_chan);
if server_result.is_err() {
let mut db_path = PathBuf::from(&server_config.db_root);
db_path.push("grin.lock");
fs::remove_file(db_path).unwrap();
// Remove chain data on server start error
let dirs_to_remove: Vec<&str> = vec!["header", "lmdb", "txhashset"];
for dir in dirs_to_remove {
let mut path = PathBuf::from(&server_config.db_root);
path.push(dir);
fs::remove_dir_all(path).unwrap();
}
// Recreate server
let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) =
Box::leak(Box::new(oneshot::channel::<()>()));
server_result = Server::new(server_config.clone(), None, api_chan);
}
server_result.unwrap()
}

View file

@ -37,15 +37,15 @@ const APP_CONFIG_FILE_NAME: &'static str = "app.toml";
pub struct AppConfig {
/// Run node server on startup.
pub auto_start_node: bool,
/// Chain type for node server.
node_chain_type: ChainTypes
/// Chain type for node and wallets.
chain_type: ChainTypes
}
impl Default for AppConfig {
fn default() -> Self {
Self {
auto_start_node: false,
node_chain_type: ChainTypes::default(),
chain_type: ChainTypes::default(),
}
}
}
@ -66,10 +66,10 @@ impl AppConfig {
/// 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 {
if self.chain_type == chain_type {
return;
} else {
self.node_chain_type = chain_type;
self.chain_type = chain_type;
self.save();
// Load config for selected chain type.
@ -95,7 +95,7 @@ impl Settings {
/// Initialize settings with app and node configs.
fn init() -> Self {
let app_config = AppConfig::init();
let chain_type = app_config.node_chain_type;
let chain_type = app_config.chain_type;
Self {
app_config: Arc::new(RwLock::new(app_config)),
node_config: Arc::new(RwLock::new(NodeConfig::init(chain_type)))