Status Submenu: add [GupaxP2poolApi] to [disk.rs], add to [App]

This commit is contained in:
hinto-janaiyo 2022-12-26 17:37:45 -05:00
parent 3175824a2b
commit 2846441049
No known key found for this signature in database
GPG key ID: B1C5A64B80691E45
4 changed files with 276 additions and 12 deletions

View file

@ -482,7 +482,7 @@ You need [`cargo`](https://www.rust-lang.org/learn/get-started), Rust's build to
The `--release` profile in Gupax is set to prefer code performance & small binary sizes over compilation speed (see [`Cargo.toml`](https://github.com/hinto-janaiyo/gupax/blob/main/Cargo.toml)). Gupax itself (with all dependencies already built) takes around 1m30s to build (vs 10s on a normal `--release`) with a Ryzen 5950x.
There are `21` unit tests throughout the codebase files, you should probably run:
There are `24` unit tests throughout the codebase files, you should probably run:
```
cargo test
```

View file

@ -42,6 +42,7 @@ use serde::{Serialize,Deserialize};
use figment::Figment;
use figment::providers::{Format,Toml};
use crate::{
human::*,
constants::*,
gupax::Ratio,
Tab,
@ -59,6 +60,37 @@ const DIRECTORY: &str = "Gupax/";
#[cfg(target_os = "linux")]
const DIRECTORY: &str = "gupax/";
// File names
pub const STATE_TOML: &str = "state.toml";
pub const NODE_TOML: &str = "node.toml";
pub const POOL_TOML: &str = "pool.toml";
// P2Pool API
// Lives within the Gupax OS data directory.
// ~/.local/share/gupax/p2pool/
// ├─ payout // Raw log lines of payouts received
// ├─ xmr // Raw log lines of XMR mined
// ├─ block // Raw log lines of actual blocks found
// ├─ total_payout // Single [u128] representing total payouts
// ├─ total_xmr // Single [u128] representing total XMR mined in atomic units
// ├─ total_block // Single [u128] representing total blocks foundpub const GUPAX_P2POOL_API_: &str = "state.toml";
#[cfg(target_os = "windows")]
pub const GUPAX_P2POOL_API_DIRECTORY: &str = r"p2pool\";
#[cfg(target_family = "unix")]
pub const GUPAX_P2POOL_API_DIRECTORY: &str = "p2pool/";
pub const GUPAX_P2POOL_API_PAYOUT: &str = "payout";
pub const GUPAX_P2POOL_API_BLOCK: &str = "block";
pub const GUPAX_P2POOL_API_TOTAL_PAYOUT: &str = "total_payout";
pub const GUPAX_P2POOL_API_TOTAL_XMR: &str = "total_xmr";
pub const GUPAX_P2POOL_API_TOTAL_BLOCK: &str = "total_block";
pub const GUPAX_P2POOL_API_FILE_ARRAY: [&str; 5] = [
GUPAX_P2POOL_API_PAYOUT,
GUPAX_P2POOL_API_BLOCK,
GUPAX_P2POOL_API_TOTAL_PAYOUT,
GUPAX_P2POOL_API_TOTAL_XMR,
GUPAX_P2POOL_API_TOTAL_BLOCK,
];
#[cfg(target_os = "windows")]
pub const DEFAULT_P2POOL_PATH: &str = r"P2Pool\p2pool.exe";
#[cfg(target_os = "macos")]
@ -96,20 +128,37 @@ pub fn get_gupax_data_path() -> Result<PathBuf, TomlError> {
path.push(DIRECTORY);
info!("OS | Data path ... {}", path.display());
create_gupax_dir(&path)?;
let mut gupax_p2pool_dir = path.clone();
gupax_p2pool_dir.push(GUPAX_P2POOL_API_DIRECTORY);
create_gupax_p2pool_dir(&gupax_p2pool_dir)?;
Ok(path)
},
None => { error!("OS | Data path ... FAIL"); Err(TomlError::Path(PATH_ERROR.to_string())) },
}
}
pub fn get_gupax_p2pool_path(os_data_path: &PathBuf) -> PathBuf {
let mut gupax_p2pool_dir = os_data_path.clone();
gupax_p2pool_dir.push(GUPAX_P2POOL_API_DIRECTORY);
gupax_p2pool_dir
}
pub fn create_gupax_dir(path: &PathBuf) -> Result<(), TomlError> {
// Create directory
// Create Gupax directory
match fs::create_dir_all(path) {
Ok(_) => { info!("OS | Create data path ... OK"); Ok(()) },
Err(e) => { error!("OS | Create data path ... FAIL ... {}", e); Err(TomlError::Io(e)) },
}
}
pub fn create_gupax_p2pool_dir(path: &PathBuf) -> Result<(), TomlError> {
// Create Gupax directory
match fs::create_dir_all(path) {
Ok(_) => { info!("OS | Create Gupax-P2Pool API path ... OK"); Ok(()) },
Err(e) => { error!("OS | Create Gupax-P2Pool API path ... FAIL ... {}", e); Err(TomlError::Io(e)) },
}
}
// Convert a [File] path to a [String]
pub fn read_to_string(file: File, path: &PathBuf) -> Result<String, TomlError> {
match fs::read_to_string(path) {
@ -511,6 +560,138 @@ impl Pool {
}
}
//---------------------------------------------------------------------------------------------------- Gupax-P2Pool API
#[derive(Clone,Eq,PartialEq,Debug)]
pub struct GupaxP2poolApi {
pub payout: HumanNumber,
pub xmr: HumanNumber,
pub block: HumanNumber,
pub int_payout: u128,
pub int_xmr: u128,
pub int_block: u128,
pub log_payout: String,
pub log_block: String,
pub path_int_payout: PathBuf,
pub path_int_xmr: PathBuf,
pub path_int_block: PathBuf,
pub path_log_payout: PathBuf,
pub path_log_block: PathBuf,
}
impl Default for GupaxP2poolApi { fn default() -> Self { Self::new() } }
impl GupaxP2poolApi {
pub fn new() -> Self {
Self {
payout: HumanNumber::unknown(),
xmr: HumanNumber::unknown(),
block: HumanNumber::unknown(),
int_payout: 0,
int_xmr: 0,
int_block: 0,
log_payout: String::new(),
log_block: String::new(),
path_int_payout: PathBuf::new(),
path_int_xmr: PathBuf::new(),
path_int_block: PathBuf::new(),
path_log_payout: PathBuf::new(),
path_log_block: PathBuf::new(),
}
}
pub fn fill_paths(&mut self, gupax_p2pool_dir: &PathBuf) {
let mut path_int_payout = gupax_p2pool_dir.clone();
let mut path_int_xmr = gupax_p2pool_dir.clone();
let mut path_int_block = gupax_p2pool_dir.clone();
let mut path_log_payout = gupax_p2pool_dir.clone();
let mut path_log_block = gupax_p2pool_dir.clone();
path_int_payout.push(GUPAX_P2POOL_API_TOTAL_PAYOUT);
path_int_xmr.push(GUPAX_P2POOL_API_TOTAL_XMR);
path_int_block.push(GUPAX_P2POOL_API_TOTAL_BLOCK);
path_log_payout.push(GUPAX_P2POOL_API_PAYOUT);
path_log_block.push(GUPAX_P2POOL_API_BLOCK);
*self = Self {
path_int_payout,
path_int_xmr,
path_int_block,
path_log_payout,
path_log_block,
..std::mem::take(self)
};
}
pub fn create_all_files(gupax_p2pool_dir: &PathBuf) -> Result<(), TomlError> {
use std::io::Write;
for file in GUPAX_P2POOL_API_FILE_ARRAY {
let mut path = gupax_p2pool_dir.clone();
path.push(file);
if path.exists() {
info!("GupaxP2poolApi | [{}] already exists, skipping...", path.display());
continue
}
match std::fs::File::create(&path) {
Ok(mut f) => {
match file {
GUPAX_P2POOL_API_TOTAL_PAYOUT|GUPAX_P2POOL_API_TOTAL_XMR|GUPAX_P2POOL_API_TOTAL_BLOCK => f.write_all(b"0")?,
_ => (),
}
info!("GupaxP2poolApi | [{}] create ... OK", path.display());
},
Err(e) => { warn!("GupaxP2poolApi | [{}] create ... FAIL: {}", path.display(), e); return Err(TomlError::Io(e)) },
}
}
Ok(())
}
pub fn read_all_files_and_update(&mut self) -> Result<(), TomlError> {
let int_payout = match read_to_string(File::IntPayout, &self.path_int_payout)?.as_str().parse::<u128>() {
Ok(o) => o,
Err(e) => { warn!("GupaxP2poolApi | [int_payout] parse error: {}", e); return Err(TomlError::Parse("int_payout")) }
};
let int_xmr = match read_to_string(File::IntXmr, &self.path_int_xmr)?.as_str().parse::<u128>() {
Ok(o) => o,
Err(e) => { warn!("GupaxP2poolApi | [int_xmr] parse error: {}", e); return Err(TomlError::Parse("int_xmr")) }
};
let int_block = match read_to_string(File::IntBlock, &self.path_int_block)?.as_str().parse::<u128>() {
Ok(o) => o,
Err(e) => { warn!("GupaxP2poolApi | [int_block] parse error: {}", e); return Err(TomlError::Parse("int_block")) }
};
let payout = HumanNumber::from_u128(int_payout);
let xmr = HumanNumber::from_u128(int_xmr);
let block = HumanNumber::from_u128(int_block);
let log_payout = read_to_string(File::LogPayout, &self.path_log_payout)?;
let log_block = read_to_string(File::LogBlock, &self.path_log_block)?;
*self = Self {
payout,
xmr,
block,
int_payout,
int_xmr,
int_block,
log_payout,
log_block,
..std::mem::take(self)
};
Ok(())
}
pub fn write_to_all_files(&self) -> Result<(), TomlError> {
Self::save(&self.int_payout.to_string(), &self.path_int_payout)?;
Self::save(&self.int_xmr.to_string(), &self.path_int_xmr)?;
Self::save(&self.int_block.to_string(), &self.path_int_block)?;
Self::save(&self.log_payout, &self.path_log_payout)?;
Self::save(&self.log_block, &self.path_log_block)?;
Ok(())
}
pub fn save(string: &str, path: &PathBuf) -> Result<(), TomlError> {
match fs::write(path, string) {
Ok(_) => { info!("GupaxP2poolApi | Save [{}] ... OK", path.display()); Ok(()) },
Err(e) => { error!("GupaxP2poolApi | Save [{}] ... FAIL: {}", path.display(), e); Err(TomlError::Io(e)) },
}
}
}
//---------------------------------------------------------------------------------------------------- Custom Error [TomlError]
#[derive(Debug)]
pub enum TomlError {
@ -553,9 +734,17 @@ impl From<std::fmt::Error> for TomlError {
//---------------------------------------------------------------------------------------------------- [File] Enum (for matching which file)
#[derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize)]
pub enum File {
State, // state.toml -> Gupax state
Node, // node.toml -> P2Pool manual node selector
Pool, // pool.toml -> XMRig manual pool selector
// State files
State, // state.toml | Gupax state
Node, // node.toml | P2Pool manual node selector
Pool, // pool.toml | XMRig manual pool selector
// Gupax-P2Pool API
LogPayout, // payout | Raw log lines of P2Pool payouts received
LogBlock, // block | Raw log lines of actual blocks found via P2Pool
IntPayout, // total_payout | Single [u128] representing total payouts
IntXmr, // total_xmr | Single [u128] representing total XMR mined in atomic units
IntBlock, // total_block | Single [u128] representing total blocks found
}
//---------------------------------------------------------------------------------------------------- [Node] Struct
@ -973,4 +1162,43 @@ mod test {
assert!(!merged_state.contains("SETTING_THAT_DOESNT_EXIST_ANYMORE"));
assert!(merged_state.contains("44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW"));
}
#[test]
fn create_and_serde_gupax_p2pool_api() {
use crate::disk::GupaxP2poolApi;
// Get API dir, fill paths.
let mut api = GupaxP2poolApi::new();
let mut path = crate::disk::get_gupax_data_path().unwrap();
path.push(crate::disk::GUPAX_P2POOL_API_DIRECTORY);
GupaxP2poolApi::fill_paths(&mut api, &path);
println!("{:#?}", api);
// Create and read all files, write some fake data.
GupaxP2poolApi::create_all_files(&path).unwrap();
GupaxP2poolApi::read_all_files_and_update(&mut api).unwrap();
api.int_payout = 1;
api.int_xmr = 2;
api.int_block = 3;
api.log_payout = "P2Pool You received a payout of 0.000000000001 XMR in block 2642816".to_string();
api.log_block = "client 127.0.0.1:51111 user asdf found a mainchain block at height 2642816, submitting it".to_string();
GupaxP2poolApi::write_to_all_files(&api).unwrap();
println!("AFTER WRITE: {:#?}", api);
// Reset internal stats, read file data.
api.int_payout = 0;
api.int_xmr = 0;
api.int_block = 0;
api.log_payout.clear();
api.log_block.clear();
GupaxP2poolApi::read_all_files_and_update(&mut api).unwrap();
println!("AFTER READ: {:#?}", api);
// Assert that the file read mutated the internal struct correctly.
assert_eq!(api.int_payout, 1);
assert_eq!(api.int_xmr, 2);
assert_eq!(api.int_block, 3);
assert_eq!(api.log_payout, "P2Pool You received a payout of 0.000000000001 XMR in block 2642816");
assert_eq!(api.log_block, "client 127.0.0.1:51111 user asdf found a mainchain block at height 2642816, submitting it");
}
}

View file

@ -53,8 +53,6 @@ use sysinfo::{CpuExt,ProcessExt};
use log::*;
//---------------------------------------------------------------------------------------------------- Constants
// The locale numbers are formatting in is English, which looks like: [1,000]
const LOCALE: num_format::Locale = num_format::Locale::en;
// The max amount of bytes of process output we are willing to
// hold in memory before it's too much and we need to reset.
const MAX_GUI_OUTPUT_BYTES: usize = 500_000;
@ -1486,6 +1484,7 @@ impl PrivP2poolPoolApi {
}
}
#[allow(non_snake_case)]
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
struct PoolStatistics {
hashRate: u128,

View file

@ -129,6 +129,12 @@ pub struct App {
sudo: Arc<Mutex<SudoState>>, // This is just a dummy struct on [Windows].
// State from [--flags]
no_startup: bool,
// Gupax-P2Pool API
// Gupax's P2Pool API (e.g: ~/.local/share/gupax/p2pool/)
// This is a file-based API that contains data for permanent stats.
// The below struct holds everything needed for it, the paths, the
// actual stats, and all the functions needed to mutate them.
gupax_p2pool_api: GupaxP2poolApi,
// Static stuff
pid: sysinfo::Pid, // Gupax's PID
max_threads: usize, // Max amount of detected system threads
@ -138,7 +144,8 @@ pub struct App {
resolution: Vec2, // Frame resolution
os: &'static str, // OS
admin: bool, // Are we admin? (for Windows)
os_data_path: PathBuf, // OS data path (e.g: ~/.local/share/gupax)
os_data_path: PathBuf, // OS data path (e.g: ~/.local/share/gupax/)
gupax_p2pool_api_path: PathBuf, // Gupax-P2Pool API path (e.g: ~/.local/share/gupax/p2pool/)
state_path: PathBuf, // State file path
node_path: PathBuf, // Node file path
pool_path: PathBuf, // Pool file path
@ -219,6 +226,7 @@ impl App {
resizing: false,
alpha: 0,
no_startup: false,
gupax_p2pool_api: GupaxP2poolApi::new(),
pub_sys,
pid,
max_threads: num_cpus::get(),
@ -229,6 +237,7 @@ impl App {
resolution: Vec2::new(APP_DEFAULT_HEIGHT, APP_DEFAULT_WIDTH),
os: OS,
os_data_path: PathBuf::new(),
gupax_p2pool_api_path: PathBuf::new(),
state_path: PathBuf::new(),
node_path: PathBuf::new(),
pool_path: PathBuf::new(),
@ -259,11 +268,11 @@ impl App {
debug!("App Init | Setting TOML path...");
// Set [*.toml] path
app.state_path = app.os_data_path.clone();
app.state_path.push("state.toml");
app.state_path.push(STATE_TOML);
app.node_path = app.os_data_path.clone();
app.node_path.push("node.toml");
app.node_path.push(NODE_TOML);
app.pool_path = app.os_data_path.clone();
app.pool_path.push("pool.toml");
app.pool_path.push(POOL_TOML);
// Apply arg state
// It's not safe to [--reset] if any of the previous variables
@ -334,6 +343,34 @@ impl App {
debug!("Pool Vec:");
debug!("{:#?}", app.pool_vec);
//----------------------------------------------------------------------------------------------------
// Read [GupaxP2poolApi] disk files
app.gupax_p2pool_api_path = crate::disk::get_gupax_p2pool_path(&app.os_data_path);
app.gupax_p2pool_api.fill_paths(&app.gupax_p2pool_api_path);
GupaxP2poolApi::create_all_files(&app.gupax_p2pool_api_path);
debug!("App Init | Reading Gupax-P2Pool API files...");
match app.gupax_p2pool_api.read_all_files_and_update() {
Ok(_) => {
info!(
"GupaxP2poolApi ... Payouts: {} | XMR (atomic-units): {} | Blocks: {}",
app.gupax_p2pool_api.payout,
app.gupax_p2pool_api.xmr,
app.gupax_p2pool_api.block,
);
},
Err(err) => {
error!("GupaxP2poolApi ... {}", err);
match err {
Io(e) => app.error_state.set(format!("GupaxP2poolApi: {}", e), ErrorFerris::Panic, ErrorButtons::Quit),
Path(e) => app.error_state.set(format!("GupaxP2poolApi: {}", e), ErrorFerris::Panic, ErrorButtons::Quit),
Serialize(e) => app.error_state.set(format!("GupaxP2poolApi: {}", e), ErrorFerris::Panic, ErrorButtons::Quit),
Deserialize(e) => app.error_state.set(format!("GupaxP2poolApi: {}", e), ErrorFerris::Panic, ErrorButtons::Quit),
Format(e) => app.error_state.set(format!("GupaxP2poolApi: {}", e), ErrorFerris::Panic, ErrorButtons::Quit),
Merge(e) => app.error_state.set(format!("GupaxP2poolApi: {}", e), ErrorFerris::Error, ErrorButtons::ResetState),
Parse(e) => app.error_state.set(format!("GupaxP2poolApi: {}", e), ErrorFerris::Panic, ErrorButtons::Quit),
};
},
};
//----------------------------------------------------------------------------------------------------
let mut og = app.og.lock().unwrap(); // Lock [og]
@ -930,7 +967,7 @@ fn main() {
Ok(_) => info!("Temporary folder cleanup ... OK"),
Err(e) => warn!("Could not cleanup [gupax_tmp] folders: {}", e),
}
info!("Init ... DONE");
info!("/*************************************/ Init ... OK /*************************************/");
eframe::run_native(&app.name_version.clone(), options, Box::new(|cc| Box::new(App::cc(cc, app))),);
}