2023-06-15 23:54:31 +03:00
|
|
|
// 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::fs::{self, File};
|
|
|
|
use std::io::Write;
|
|
|
|
use std::path::PathBuf;
|
2023-06-17 13:23:31 +03:00
|
|
|
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
2023-06-15 23:54:31 +03:00
|
|
|
|
|
|
|
use grin_config::ConfigError;
|
|
|
|
use grin_core::global::ChainTypes;
|
|
|
|
use lazy_static::lazy_static;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use serde::de::DeserializeOwned;
|
|
|
|
|
|
|
|
use crate::node::NodeConfig;
|
2023-08-01 02:13:35 +03:00
|
|
|
use crate::wallet::{ExternalConnection, Wallets};
|
2023-06-15 23:54:31 +03:00
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
/// Static settings state to be accessible globally.
|
|
|
|
static ref SETTINGS_STATE: Arc<Settings> = Arc::new(Settings::init());
|
|
|
|
}
|
|
|
|
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Application configuration file name.
|
2023-06-15 23:54:31 +03:00
|
|
|
const APP_CONFIG_FILE_NAME: &'static str = "app.toml";
|
|
|
|
|
2023-07-11 03:02:44 +03:00
|
|
|
/// Common application settings.
|
2023-06-15 23:54:31 +03:00
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
pub struct AppConfig {
|
|
|
|
/// Run node server on startup.
|
|
|
|
pub auto_start_node: bool,
|
2023-06-23 22:12:30 +03:00
|
|
|
/// Chain type for node and wallets.
|
2023-07-25 03:42:52 +03:00
|
|
|
chain_type: ChainTypes,
|
2023-08-01 02:13:35 +03:00
|
|
|
/// URLs of external connections for wallets.
|
|
|
|
external_connections: Vec<ExternalConnection>
|
2023-06-15 23:54:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for AppConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
auto_start_node: false,
|
2023-06-23 22:12:30 +03:00
|
|
|
chain_type: ChainTypes::default(),
|
2023-08-01 02:13:35 +03:00
|
|
|
external_connections: vec![
|
|
|
|
ExternalConnection::default()
|
2023-07-25 03:42:52 +03:00
|
|
|
],
|
2023-06-15 23:54:31 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AppConfig {
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Initialize application config from the file.
|
2023-06-15 23:54:31 +03:00
|
|
|
pub fn init() -> Self {
|
2023-06-19 01:29:15 +03:00
|
|
|
let path = Settings::get_config_path(APP_CONFIG_FILE_NAME, None);
|
|
|
|
let parsed = Settings::read_from_file::<AppConfig>(path.clone());
|
|
|
|
if !path.exists() || parsed.is_err() {
|
2023-06-15 23:54:31 +03:00
|
|
|
let default_config = AppConfig::default();
|
2023-06-19 01:29:15 +03:00
|
|
|
Settings::write_to_file(&default_config, path);
|
2023-06-15 23:54:31 +03:00
|
|
|
default_config
|
|
|
|
} else {
|
|
|
|
parsed.unwrap()
|
|
|
|
}
|
|
|
|
}
|
2023-06-17 13:23:31 +03:00
|
|
|
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Save app config to file.
|
2023-07-01 03:29:05 +03:00
|
|
|
pub fn save(&self) {
|
|
|
|
Settings::write_to_file(self, Settings::get_config_path(APP_CONFIG_FILE_NAME, None));
|
|
|
|
}
|
2023-06-19 01:29:15 +03:00
|
|
|
|
2023-07-11 03:02:44 +03:00
|
|
|
/// Change chain type and load new [`NodeConfig`].
|
2023-07-01 03:29:05 +03:00
|
|
|
pub fn change_chain_type(chain_type: &ChainTypes) {
|
|
|
|
let current_chain_type = Self::chain_type();
|
|
|
|
if current_chain_type != *chain_type {
|
2023-07-31 01:04:41 +03:00
|
|
|
// Save chain type at app config.
|
|
|
|
{
|
|
|
|
let mut w_app_config = Settings::app_config_to_update();
|
|
|
|
w_app_config.chain_type = *chain_type;
|
|
|
|
w_app_config.save();
|
|
|
|
}
|
2023-07-11 03:02:44 +03:00
|
|
|
// Load node config for selected chain type.
|
2023-07-31 01:04:41 +03:00
|
|
|
{
|
|
|
|
let mut w_node_config = Settings::node_config_to_update();
|
|
|
|
let node_config = NodeConfig::for_chain_type(chain_type);
|
|
|
|
w_node_config.node = node_config.node;
|
|
|
|
w_node_config.peers = node_config.peers;
|
|
|
|
}
|
2023-07-25 03:42:52 +03:00
|
|
|
// Reload wallets.
|
2023-07-29 00:17:54 +03:00
|
|
|
Wallets::reload(chain_type);
|
2023-06-19 01:29:15 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-01 03:29:05 +03:00
|
|
|
/// Get current [`ChainTypes`] for node and wallets.
|
|
|
|
pub fn chain_type() -> ChainTypes {
|
|
|
|
let r_config = Settings::app_config_to_read();
|
|
|
|
r_config.chain_type
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if integrated node is starting with application.
|
|
|
|
pub fn autostart_node() -> bool {
|
|
|
|
let r_config = Settings::app_config_to_read();
|
|
|
|
r_config.auto_start_node
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Toggle integrated node autostart.
|
|
|
|
pub fn toggle_node_autostart() {
|
|
|
|
let autostart = Self::autostart_node();
|
|
|
|
let mut w_app_config = Settings::app_config_to_update();
|
|
|
|
w_app_config.auto_start_node = !autostart;
|
|
|
|
w_app_config.save();
|
2023-06-17 13:23:31 +03:00
|
|
|
}
|
2023-07-25 03:42:52 +03:00
|
|
|
|
2023-08-01 02:13:35 +03:00
|
|
|
/// Get external connections for the wallet.
|
|
|
|
pub fn external_connections() -> Vec<ExternalConnection> {
|
2023-07-25 03:42:52 +03:00
|
|
|
let r_config = Settings::app_config_to_read();
|
2023-08-01 02:13:35 +03:00
|
|
|
r_config.external_connections.clone()
|
2023-07-25 03:42:52 +03:00
|
|
|
}
|
|
|
|
|
2023-08-01 02:13:35 +03:00
|
|
|
/// Save external connection for the wallet in app config.
|
|
|
|
pub fn add_external_connection(conn: ExternalConnection) {
|
2023-07-25 03:42:52 +03:00
|
|
|
let mut w_config = Settings::app_config_to_update();
|
2023-08-01 02:13:35 +03:00
|
|
|
let mut exists = false;
|
|
|
|
for mut c in w_config.external_connections.iter_mut() {
|
|
|
|
// Update connection if URL exists.
|
|
|
|
if c.url == conn.url {
|
|
|
|
c.secret = conn.secret.clone();
|
|
|
|
exists = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Create new connection if URL not exists.
|
|
|
|
if !exists {
|
|
|
|
w_config.external_connections.insert(0, conn);
|
|
|
|
}
|
2023-07-25 03:42:52 +03:00
|
|
|
w_config.save();
|
|
|
|
}
|
|
|
|
|
2023-08-01 02:13:35 +03:00
|
|
|
/// Get external node connection secret from provided URL.
|
|
|
|
pub fn get_external_connection_secret(url: String) -> Option<String> {
|
|
|
|
let r_config = Settings::app_config_to_read();
|
|
|
|
for c in &r_config.external_connections {
|
|
|
|
if c.url == url {
|
|
|
|
return c.secret.clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2023-06-15 23:54:31 +03:00
|
|
|
}
|
|
|
|
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Main application directory name.
|
|
|
|
const MAIN_DIR_NAME: &'static str = ".grim";
|
2023-06-19 01:29:15 +03:00
|
|
|
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Provides access to application, integrated node and wallets configs.
|
2023-06-15 23:54:31 +03:00
|
|
|
pub struct Settings {
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Application config instance.
|
2023-06-15 23:54:31 +03:00
|
|
|
app_config: Arc<RwLock<AppConfig>>,
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Integrated node config instance.
|
|
|
|
node_config: Arc<RwLock<NodeConfig>>,
|
2023-06-15 23:54:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Settings {
|
2023-06-19 01:29:15 +03:00
|
|
|
/// Initialize settings with app and node configs.
|
2023-06-15 23:54:31 +03:00
|
|
|
fn init() -> Self {
|
|
|
|
let app_config = AppConfig::init();
|
|
|
|
Self {
|
2023-07-11 03:02:44 +03:00
|
|
|
node_config: Arc::new(RwLock::new(NodeConfig::for_chain_type(&app_config.chain_type))),
|
2023-07-25 03:42:52 +03:00
|
|
|
app_config: Arc::new(RwLock::new(app_config)),
|
2023-06-15 23:54:31 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-19 01:29:15 +03:00
|
|
|
/// Get node config to read values.
|
|
|
|
pub fn node_config_to_read() -> RwLockReadGuard<'static, NodeConfig> {
|
2023-06-15 23:54:31 +03:00
|
|
|
SETTINGS_STATE.node_config.read().unwrap()
|
|
|
|
}
|
|
|
|
|
2023-06-19 01:29:15 +03:00
|
|
|
/// 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> {
|
2023-06-15 23:54:31 +03:00
|
|
|
SETTINGS_STATE.app_config.read().unwrap()
|
|
|
|
}
|
|
|
|
|
2023-06-19 01:29:15 +03:00
|
|
|
/// Get app config to update values.
|
|
|
|
pub fn app_config_to_update() -> RwLockWriteGuard<'static, AppConfig> {
|
2023-06-17 13:23:31 +03:00
|
|
|
SETTINGS_STATE.app_config.write().unwrap()
|
|
|
|
}
|
|
|
|
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Get base directory path for config.
|
|
|
|
pub fn get_base_path(sub_dir: Option<String>) -> PathBuf {
|
2023-06-19 01:29:15 +03:00
|
|
|
// Check if dir exists.
|
2023-06-15 23:54:31 +03:00
|
|
|
let mut path = match dirs::home_dir() {
|
|
|
|
Some(p) => p,
|
|
|
|
None => PathBuf::new(),
|
|
|
|
};
|
2023-07-25 03:42:52 +03:00
|
|
|
path.push(MAIN_DIR_NAME);
|
|
|
|
if sub_dir.is_some() {
|
|
|
|
path.push(sub_dir.unwrap());
|
2023-06-15 23:54:31 +03:00
|
|
|
}
|
2023-06-19 01:29:15 +03:00
|
|
|
// Create if the default path doesn't exist.
|
2023-06-15 23:54:31 +03:00
|
|
|
if !path.exists() {
|
|
|
|
let _ = fs::create_dir_all(path.clone());
|
|
|
|
}
|
|
|
|
path
|
|
|
|
}
|
|
|
|
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Get config file path from provided name and sub-directory if needed.
|
|
|
|
pub fn get_config_path(config_name: &str, sub_dir: Option<String>) -> PathBuf {
|
|
|
|
let mut settings_path = Self::get_base_path(sub_dir);
|
2023-06-15 23:54:31 +03:00
|
|
|
settings_path.push(config_name);
|
|
|
|
settings_path
|
|
|
|
}
|
|
|
|
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Read config from the file.
|
2023-06-15 23:54:31 +03:00
|
|
|
pub fn read_from_file<T: DeserializeOwned>(config_path: PathBuf) -> Result<T, ConfigError> {
|
|
|
|
let file_content = fs::read_to_string(config_path.clone())?;
|
|
|
|
let parsed = toml::from_str::<T>(file_content.as_str());
|
|
|
|
match parsed {
|
2023-07-01 03:29:05 +03:00
|
|
|
Ok(cfg) => Ok(cfg),
|
2023-06-15 23:54:31 +03:00
|
|
|
Err(e) => {
|
|
|
|
return Err(ConfigError::ParseError(
|
|
|
|
config_path.to_str().unwrap().to_string(),
|
|
|
|
format!("{}", e),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-25 03:42:52 +03:00
|
|
|
/// Write config to the file.
|
2023-06-17 13:23:31 +03:00
|
|
|
pub fn write_to_file<T: Serialize>(config: &T, path: PathBuf) {
|
2023-06-15 23:54:31 +03:00
|
|
|
let conf_out = toml::to_string(config).unwrap();
|
2023-06-17 13:23:31 +03:00
|
|
|
let mut file = File::create(path.to_str().unwrap()).unwrap();
|
2023-06-15 23:54:31 +03:00
|
|
|
file.write_all(conf_out.as_bytes()).unwrap();
|
|
|
|
}
|
|
|
|
}
|