mirror of
https://github.com/Cyrix126/gupaxx.git
synced 2025-01-18 20:04:30 +00:00
Status Submenu: separate [Regex], [AtomicUnit], [PayoutOrd]
This fixes some funcs, tests and separates some structs into separate files.
This commit is contained in:
parent
fd6398fb4d
commit
46b528ecbe
7 changed files with 533 additions and 386 deletions
|
@ -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 `28` unit tests throughout the codebase files, you should probably run:
|
||||
There are `30` unit tests throughout the codebase files, you should probably run:
|
||||
```
|
||||
cargo test
|
||||
```
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
## Structure
|
||||
| File/Folder | Purpose |
|
||||
|--------------|---------|
|
||||
| constants.rs | General constants needed in Gupax
|
||||
| constants.rs | General constants used in Gupax
|
||||
| disk.rs | Code for writing to disk: `state.toml/node.toml/pool.toml`; This holds the structs for the [State] struct
|
||||
| ferris.rs | Cute crab bytes
|
||||
| gupax.rs | `Gupax` tab
|
||||
|
@ -26,9 +26,11 @@
|
|||
| main.rs | The main `App` struct that holds all data + misc data/functions
|
||||
| node.rs | Community node ping code for the `P2Pool` simple tab
|
||||
| p2pool.rs | `P2Pool` tab
|
||||
| regex.rs | General regexes used in Gupax
|
||||
| status.rs | `Status` tab
|
||||
| sudo.rs | Code for handling `sudo` escalation for XMRig on Unix
|
||||
| update.rs | Update code for the `Gupax` tab
|
||||
| xmr.rs | Code for handling actual XMR, `AtomicUnit` & `PayoutOrd`
|
||||
| xmrig.rs | `XMRig` tab
|
||||
|
||||
## Thread Model
|
||||
|
|
142
src/disk.rs
142
src/disk.rs
|
@ -46,6 +46,7 @@ use crate::{
|
|||
constants::*,
|
||||
gupax::Ratio,
|
||||
Tab,
|
||||
xmr::*,
|
||||
};
|
||||
use log::*;
|
||||
|
||||
|
@ -78,17 +79,13 @@ pub const POOL_TOML: &str = "pool.toml";
|
|||
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] = [
|
||||
pub const GUPAX_P2POOL_API_PAYOUT: &str = "payout_log";
|
||||
pub const GUPAX_P2POOL_API_TOTAL_PAYOUT: &str = "payout";
|
||||
pub const GUPAX_P2POOL_API_TOTAL_XMR: &str = "xmr";
|
||||
pub const GUPAX_P2POOL_API_FILE_ARRAY: [&str; 3] = [
|
||||
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")]
|
||||
|
@ -562,21 +559,16 @@ impl Pool {
|
|||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Gupax-P2Pool API
|
||||
#[derive(Clone,Eq,PartialEq,Debug)]
|
||||
#[derive(Clone,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 xmr: AtomicUnit,
|
||||
pub int_payout: u64,
|
||||
pub payout_ord: PayoutOrd, // Ordered Vec of payouts
|
||||
pub log_payout: String,
|
||||
pub log_block: String,
|
||||
pub path_xmr: PathBuf,
|
||||
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() } }
|
||||
|
@ -585,38 +577,27 @@ impl GupaxP2poolApi {
|
|||
pub fn new() -> Self {
|
||||
Self {
|
||||
payout: HumanNumber::unknown(),
|
||||
xmr: HumanNumber::unknown(),
|
||||
block: HumanNumber::unknown(),
|
||||
xmr: AtomicUnit::new(),
|
||||
int_payout: 0,
|
||||
int_xmr: 0,
|
||||
int_block: 0,
|
||||
log_payout: String::new(),
|
||||
log_block: String::new(),
|
||||
payout_ord: PayoutOrd::new(),
|
||||
path_xmr: PathBuf::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_xmr = gupax_p2pool_dir.clone();
|
||||
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_xmr.push(GUPAX_P2POOL_API_TOTAL_XMR);
|
||||
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_xmr,
|
||||
path_log_payout,
|
||||
path_log_block,
|
||||
..std::mem::take(self)
|
||||
};
|
||||
}
|
||||
|
@ -633,7 +614,7 @@ impl GupaxP2poolApi {
|
|||
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")?,
|
||||
GUPAX_P2POOL_API_TOTAL_PAYOUT|GUPAX_P2POOL_API_TOTAL_XMR => writeln!(f, "0")?,
|
||||
_ => (),
|
||||
}
|
||||
info!("GupaxP2poolApi | [{}] create ... OK", path.display());
|
||||
|
@ -645,50 +626,63 @@ impl GupaxP2poolApi {
|
|||
}
|
||||
|
||||
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>() {
|
||||
let int_payout = match read_to_string(File::IntPayout, &self.path_int_payout)?.trim().parse::<u64>() {
|
||||
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 xmr = match read_to_string(File::IntXmr, &self.path_xmr)?.trim().parse::<u128>() {
|
||||
Ok(o) => AtomicUnit::from_u128(o),
|
||||
Err(e) => { warn!("GupaxP2poolApi | [xmr] parse error: {}", e); return Err(TomlError::Parse("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 payout = HumanNumber::from_u64(int_payout);
|
||||
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.payout_ord.update_from_payout_log(&log_payout);
|
||||
*self = Self {
|
||||
payout,
|
||||
xmr,
|
||||
block,
|
||||
int_payout,
|
||||
int_xmr,
|
||||
int_block,
|
||||
log_payout,
|
||||
log_block,
|
||||
..std::mem::take(self)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Takes raw int and raw log line and appends it.
|
||||
pub fn add_payout(&mut self, xmr: u128, log_payout_line: &str) {
|
||||
self.log_payout.push_str(log_payout_line);
|
||||
self.int_payout += 1;
|
||||
self.payout = HumanNumber::from_u64(self.int_payout);
|
||||
self.xmr = self.xmr.add_u128(xmr);
|
||||
}
|
||||
|
||||
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)?;
|
||||
Self::disk_overwrite(&self.int_payout.to_string(), &self.path_int_payout)?;
|
||||
Self::disk_overwrite(&self.xmr.to_u128().to_string(), &self.path_xmr)?;
|
||||
Self::disk_append(&self.log_payout, &self.path_log_payout)?;
|
||||
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)) },
|
||||
pub fn disk_append(string: &str, path: &PathBuf) -> Result<(), TomlError> {
|
||||
use std::io::Write;
|
||||
let mut file = match fs::OpenOptions::new().append(true).open(path) {
|
||||
Ok(f) => f,
|
||||
Err(e) => { error!("GupaxP2poolApi | Append [{}] ... FAIL: {}", path.display(), e); return Err(TomlError::Io(e)) },
|
||||
};
|
||||
match writeln!(file, "{}", string) {
|
||||
Ok(_) => { debug!("GupaxP2poolApi | Append [{}] ... OK", path.display()); Ok(()) },
|
||||
Err(e) => { error!("GupaxP2poolApi | Append [{}] ... FAIL: {}", path.display(), e); Err(TomlError::Io(e)) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disk_overwrite(string: &str, path: &PathBuf) -> Result<(), TomlError> {
|
||||
use std::io::Write;
|
||||
let mut file = match fs::OpenOptions::new().write(true).open(path) {
|
||||
Ok(f) => f,
|
||||
Err(e) => { error!("GupaxP2poolApi | Overwrite [{}] ... FAIL: {}", path.display(), e); return Err(TomlError::Io(e)) },
|
||||
};
|
||||
match writeln!(file, "{}", string) {
|
||||
Ok(_) => { debug!("GupaxP2poolApi | Overwrite [{}] ... OK", path.display()); Ok(()) },
|
||||
Err(e) => { error!("GupaxP2poolApi | Overwrite [{}] ... FAIL: {}", path.display(), e); Err(TomlError::Io(e)) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -742,10 +736,8 @@ pub enum File {
|
|||
|
||||
// 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
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [Submenu] enum for [Status] tab
|
||||
|
@ -1284,6 +1276,7 @@ mod test {
|
|||
#[test]
|
||||
fn create_and_serde_gupax_p2pool_api() {
|
||||
use crate::disk::GupaxP2poolApi;
|
||||
use crate::xmr::AtomicUnit;
|
||||
|
||||
// Get API dir, fill paths.
|
||||
let mut api = GupaxP2poolApi::new();
|
||||
|
@ -1292,32 +1285,23 @@ mod test {
|
|||
GupaxP2poolApi::fill_paths(&mut api, &path);
|
||||
println!("{:#?}", api);
|
||||
|
||||
// Create and read all files, write some fake data.
|
||||
// Create, write some fake data.
|
||||
GupaxP2poolApi::create_all_files(&path).unwrap();
|
||||
GupaxP2poolApi::read_all_files_and_update(&mut api).unwrap();
|
||||
api.log_payout = "NOTICE 2022-01-27 01:30:23.1377 P2Pool You received a payout of 0.000000000001 XMR in block 2642816".to_string();
|
||||
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();
|
||||
api.xmr = AtomicUnit::from_u128(2);
|
||||
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();
|
||||
// Read
|
||||
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");
|
||||
assert_eq!(api.xmr.to_u128(), 2);
|
||||
assert!(!api.payout_ord.is_empty());
|
||||
assert!(api.log_payout.contains("2022-01-27 01:30:23.1377 P2Pool You received a payout of 0.000000000001 XMR in block 2642816\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
296
src/helper.rs
296
src/helper.rs
|
@ -46,6 +46,9 @@ use crate::{
|
|||
constants::*,
|
||||
SudoState,
|
||||
human::*,
|
||||
GupaxP2poolApi,
|
||||
P2poolRegex,
|
||||
xmr::*,
|
||||
};
|
||||
use sysinfo::SystemExt;
|
||||
use serde::{Serialize,Deserialize};
|
||||
|
@ -77,6 +80,7 @@ pub struct Helper {
|
|||
pub img_xmrig: Arc<Mutex<ImgXmrig>>, // A static "image" of the data XMRig started with
|
||||
pub_api_p2pool: Arc<Mutex<PubP2poolApi>>, // P2Pool API state (for Helper/P2Pool thread)
|
||||
pub_api_xmrig: Arc<Mutex<PubXmrigApi>>, // XMRig API state (for Helper/XMRig thread)
|
||||
pub gupax_p2pool_api: Arc<Mutex<GupaxP2poolApi>>, //
|
||||
priv_api_p2pool_local: Arc<Mutex<PrivP2poolLocalApi>>, // Serde struct(s) for P2Pool's API files
|
||||
priv_api_p2pool_network: Arc<Mutex<PrivP2poolNetworkApi>>,
|
||||
priv_api_p2pool_pool: Arc<Mutex<PrivP2poolPoolApi>>,
|
||||
|
@ -223,7 +227,7 @@ impl std::fmt::Display for ProcessName {
|
|||
//---------------------------------------------------------------------------------------------------- [Helper]
|
||||
impl Helper {
|
||||
//---------------------------------------------------------------------------------------------------- General Functions
|
||||
pub fn new(instant: std::time::Instant, pub_sys: Arc<Mutex<Sys>>, p2pool: Arc<Mutex<Process>>, xmrig: Arc<Mutex<Process>>, gui_api_p2pool: Arc<Mutex<PubP2poolApi>>, gui_api_xmrig: Arc<Mutex<PubXmrigApi>>, img_p2pool: Arc<Mutex<ImgP2pool>>, img_xmrig: Arc<Mutex<ImgXmrig>>) -> Self {
|
||||
pub fn new(instant: std::time::Instant, pub_sys: Arc<Mutex<Sys>>, p2pool: Arc<Mutex<Process>>, xmrig: Arc<Mutex<Process>>, gui_api_p2pool: Arc<Mutex<PubP2poolApi>>, gui_api_xmrig: Arc<Mutex<PubXmrigApi>>, img_p2pool: Arc<Mutex<ImgP2pool>>, img_xmrig: Arc<Mutex<ImgXmrig>>, gupax_p2pool_api: Arc<Mutex<GupaxP2poolApi>>) -> Self {
|
||||
Self {
|
||||
instant,
|
||||
pub_sys,
|
||||
|
@ -241,26 +245,33 @@ impl Helper {
|
|||
gui_api_xmrig,
|
||||
img_p2pool,
|
||||
img_xmrig,
|
||||
gupax_p2pool_api,
|
||||
}
|
||||
}
|
||||
|
||||
// Reads a PTY which combines STDOUT/STDERR for me, yay
|
||||
fn read_pty(output_parse: Arc<Mutex<String>>, output_pub: Arc<Mutex<String>>, reader: Box<dyn std::io::Read + Send>, name: ProcessName) {
|
||||
fn read_pty_xmrig(output_parse: Arc<Mutex<String>>, output_pub: Arc<Mutex<String>>, reader: Box<dyn std::io::Read + Send>) {
|
||||
use std::io::BufRead;
|
||||
let mut stdout = std::io::BufReader::new(reader).lines();
|
||||
// We don't need to write twice for XMRig, since we dont parse it... yet.
|
||||
if name == ProcessName::Xmrig {
|
||||
while let Some(Ok(line)) = stdout.next() {
|
||||
// println!("{}", line); // For debugging.
|
||||
// if let Err(e) = writeln!(output_parse.lock().unwrap(), "{}", line) { error!("PTY | Output error: {}", e); }
|
||||
if let Err(e) = writeln!(output_pub.lock().unwrap(), "{}", line) { error!("PTY | Output error: {}", e); }
|
||||
}
|
||||
} else {
|
||||
while let Some(Ok(line)) = stdout.next() {
|
||||
// println!("{}", line); // For debugging.
|
||||
if let Err(e) = writeln!(output_parse.lock().unwrap(), "{}", line) { error!("PTY | Output error: {}", e); }
|
||||
if let Err(e) = writeln!(output_pub.lock().unwrap(), "{}", line) { error!("PTY | Output error: {}", e); }
|
||||
while let Some(Ok(line)) = stdout.next() {
|
||||
// println!("{}", line); // For debugging.
|
||||
if let Err(e) = writeln!(output_pub.lock().unwrap(), "{}", line) { error!("XMRig PTY | Output error: {}", e); }
|
||||
}
|
||||
}
|
||||
|
||||
fn read_pty_p2pool(output_parse: Arc<Mutex<String>>, output_pub: Arc<Mutex<String>>, reader: Box<dyn std::io::Read + Send>, regex: P2poolRegex, gupax_p2pool_api: Arc<Mutex<GupaxP2poolApi>>) {
|
||||
use std::io::BufRead;
|
||||
let mut stdout = std::io::BufReader::new(reader).lines();
|
||||
while let Some(Ok(line)) = stdout.next() {
|
||||
// println!("{}", line); // For debugging.
|
||||
if regex.payout.is_match(&line) {
|
||||
debug!("P2Pool PTY | Found payout, attempting write: {}", line);
|
||||
let (date, atomic_unit, block) = PayoutOrd::parse_line(&line, ®ex);
|
||||
GupaxP2poolApi::add_payout(&mut gupax_p2pool_api.lock().unwrap(), atomic_unit.to_u128(), &line);
|
||||
if let Err(e) = GupaxP2poolApi::write_to_all_files(&gupax_p2pool_api.lock().unwrap()) { error!("P2Pool PTY GupaxP2poolApi | Write error: {}", e); }
|
||||
}
|
||||
if let Err(e) = writeln!(output_parse.lock().unwrap(), "{}", line) { error!("P2Pool PTY Parse | Output error: {}", e); }
|
||||
if let Err(e) = writeln!(output_pub.lock().unwrap(), "{}", line) { error!("P2Pool PTY Pub | Output error: {}", e); }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -337,9 +348,10 @@ impl Helper {
|
|||
let process = Arc::clone(&helper.lock().unwrap().p2pool);
|
||||
let gui_api = Arc::clone(&helper.lock().unwrap().gui_api_p2pool);
|
||||
let pub_api = Arc::clone(&helper.lock().unwrap().pub_api_p2pool);
|
||||
let gupax_p2pool_api = Arc::clone(&helper.lock().unwrap().gupax_p2pool_api);
|
||||
let path = path.clone();
|
||||
thread::spawn(move || {
|
||||
Self::spawn_p2pool_watchdog(process, gui_api, pub_api, args, path, api_path_local, api_path_network, api_path_pool);
|
||||
Self::spawn_p2pool_watchdog(process, gui_api, pub_api, args, path, api_path_local, api_path_network, api_path_pool, gupax_p2pool_api);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -347,7 +359,7 @@ impl Helper {
|
|||
// 6 characters separated with dots like so: [4abcde...abcdef]
|
||||
fn head_tail_of_monero_address(address: &str) -> String {
|
||||
if address.len() < 95 { return "???".to_string() }
|
||||
let head = &address[0..5];
|
||||
let head = &address[0..6];
|
||||
let tail = &address[89..95];
|
||||
head.to_owned() + "..." + tail
|
||||
}
|
||||
|
@ -443,7 +455,7 @@ impl Helper {
|
|||
}
|
||||
|
||||
// The P2Pool watchdog. Spawns 1 OS thread for reading a PTY (STDOUT+STDERR), and combines the [Child] with a PTY so STDIN actually works.
|
||||
fn spawn_p2pool_watchdog(process: Arc<Mutex<Process>>, gui_api: Arc<Mutex<PubP2poolApi>>, pub_api: Arc<Mutex<PubP2poolApi>>, args: Vec<String>, path: std::path::PathBuf, api_path_local: std::path::PathBuf, api_path_network: std::path::PathBuf, api_path_pool: std::path::PathBuf) {
|
||||
fn spawn_p2pool_watchdog(process: Arc<Mutex<Process>>, gui_api: Arc<Mutex<PubP2poolApi>>, pub_api: Arc<Mutex<PubP2poolApi>>, args: Vec<String>, path: std::path::PathBuf, api_path_local: std::path::PathBuf, api_path_network: std::path::PathBuf, api_path_pool: std::path::PathBuf, gupax_p2pool_api: Arc<Mutex<GupaxP2poolApi>>) {
|
||||
// 1a. Create PTY
|
||||
debug!("P2Pool | Creating PTY...");
|
||||
let pty = portable_pty::native_pty_system();
|
||||
|
@ -473,12 +485,16 @@ impl Helper {
|
|||
lock.stdin = Some(pair.master);
|
||||
drop(lock);
|
||||
|
||||
let regex = P2poolRegex::new();
|
||||
|
||||
// 3. Spawn PTY read thread
|
||||
debug!("P2Pool | Spawning PTY read thread...");
|
||||
let output_parse = Arc::clone(&process.lock().unwrap().output_parse);
|
||||
let output_pub = Arc::clone(&process.lock().unwrap().output_pub);
|
||||
let gupax_p2pool_api = Arc::clone(&gupax_p2pool_api);
|
||||
let regex_clone = regex.clone();
|
||||
thread::spawn(move || {
|
||||
Self::read_pty(output_parse, output_pub, reader, ProcessName::P2pool);
|
||||
Self::read_pty_p2pool(output_parse, output_pub, reader, regex_clone, gupax_p2pool_api);
|
||||
});
|
||||
let output_parse = Arc::clone(&process.lock().unwrap().output_parse);
|
||||
let output_pub = Arc::clone(&process.lock().unwrap().output_pub);
|
||||
|
@ -498,7 +514,6 @@ impl Helper {
|
|||
Err(e) => warn!("P2Pool | Creating default empty API file ... FAIL ... {}", e),
|
||||
}
|
||||
}
|
||||
let regex = P2poolRegex::new();
|
||||
let start = process.lock().unwrap().start;
|
||||
|
||||
// Reset stats before loop
|
||||
|
@ -875,7 +890,7 @@ impl Helper {
|
|||
let output_parse = Arc::clone(&process.lock().unwrap().output_parse);
|
||||
let output_pub = Arc::clone(&process.lock().unwrap().output_pub);
|
||||
thread::spawn(move || {
|
||||
Self::read_pty(output_parse, output_pub, reader, ProcessName::Xmrig);
|
||||
Self::read_pty_xmrig(output_parse, output_pub, reader);
|
||||
});
|
||||
// We don't parse anything in XMRigs output... yet.
|
||||
// let output_parse = Arc::clone(&process.lock().unwrap().output_parse);
|
||||
|
@ -1161,189 +1176,6 @@ impl Helper {
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Regexes
|
||||
// Not to be confused with the [Regexes] struct in [main.rs], this one is meant
|
||||
// for parsing the output of P2Pool and finding payouts and total XMR found.
|
||||
// Why Regex instead of the standard library?
|
||||
// 1. I'm already using Regex
|
||||
// 2. It's insanely faster
|
||||
//
|
||||
// The following STDLIB implementation takes [0.003~] seconds to find all matches given a [String] with 30k lines:
|
||||
// let mut n = 0;
|
||||
// for line in P2POOL_OUTPUT.lines() {
|
||||
// if line.contains("payout of [0-9].[0-9]+ XMR") { n += 1; }
|
||||
// }
|
||||
//
|
||||
// This regex function takes [0.0003~] seconds (10x faster):
|
||||
// let regex = Regex::new("payout of [0-9].[0-9]+ XMR").unwrap();
|
||||
// let n = regex.find_iter(P2POOL_OUTPUT).count();
|
||||
//
|
||||
// Both are nominally fast enough where it doesn't matter too much but meh, why not use regex.
|
||||
struct P2poolRegex {
|
||||
payout: regex::Regex,
|
||||
float: regex::Regex,
|
||||
date: regex::Regex,
|
||||
block: regex::Regex,
|
||||
int: regex::Regex,
|
||||
}
|
||||
|
||||
impl P2poolRegex {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
payout: regex::Regex::new("payout of [0-9].[0-9]+ XMR").unwrap(),
|
||||
float: regex::Regex::new("[0-9].[0-9]+").unwrap(),
|
||||
date: regex::Regex::new("[0-9]+-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+.[0-9]+").unwrap(),
|
||||
block: regex::Regex::new("block [0-9]+").unwrap(),
|
||||
int: regex::Regex::new("[0-9]+").unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- XMR AtomicUnit
|
||||
#[derive(Debug, Clone)]
|
||||
struct AtomicUnit(u128);
|
||||
|
||||
impl AtomicUnit {
|
||||
fn new() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
fn sum_vec(vec: &Vec<Self>) -> Self {
|
||||
let mut sum = 0;
|
||||
for int in vec {
|
||||
sum += int.0;
|
||||
}
|
||||
Self(sum)
|
||||
}
|
||||
|
||||
fn from_f64(f: f64) -> Self {
|
||||
Self((f * 1_000_000_000_000.0) as u128)
|
||||
}
|
||||
|
||||
fn to_f64(&self) -> f64 {
|
||||
self.0 as f64 / 1_000_000_000_000.0
|
||||
}
|
||||
|
||||
fn to_human_number_12_point(&self) -> HumanNumber {
|
||||
let f = self.0 as f64 / 1_000_000_000_000.0;
|
||||
HumanNumber::from_f64_12_point(f)
|
||||
}
|
||||
|
||||
fn to_human_number_no_fmt(&self) -> HumanNumber {
|
||||
let f = self.0 as f64 / 1_000_000_000_000.0;
|
||||
HumanNumber::from_f64_no_fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// Displays AtomicUnit as a real XMR floating point.
|
||||
impl std::fmt::Display for AtomicUnit {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", Self::to_human_number_12_point(self))
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [PayoutOrd]
|
||||
// This is the struct for ordering P2Pool payout lines into a structured and ordered vector of elements.
|
||||
// The structure goes as follows:
|
||||
//
|
||||
// Vec<(String, AtomicUnit, u64)>
|
||||
// "2022-08-17 12:16:11.8662" | 0.002382256231 XMR | Block 2573821
|
||||
// [0] = DATE
|
||||
// [1] = XMR IN ATOMIC-UNITS
|
||||
// [2] = MONERO BLOCK
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PayoutOrd(Vec<(String, AtomicUnit, HumanNumber)>);
|
||||
|
||||
impl PayoutOrd {
|
||||
fn new() -> Self {
|
||||
Self(vec![(String::from("????-??-?? ??:??:??.????"), AtomicUnit::new(), HumanNumber::unknown())])
|
||||
}
|
||||
|
||||
// Takes in input of ONLY P2Pool payout logs and
|
||||
// converts it into a usable [PayoutOrd]
|
||||
// It expects log lines like this:
|
||||
// "NOTICE 2022-04-11 00:20:17.2571 P2Pool You received a payout of 0.001371623621 XMR in block 2562511"
|
||||
// For efficiency reasons, I'd like to know the byte size
|
||||
// we should allocate for the vector so we aren't adding every loop.
|
||||
// Given a log [str], the equation for how many bytes the final vec will be is:
|
||||
// (BYTES_OF_DATE + BYTES OF XMR + BYTES OF BLOCK) * amount_of_lines
|
||||
// The first three are more or less constants (monero block 10m is in 10,379 years...): [23, 14, 7] (sum: 44)
|
||||
// Add 16 more bytes for wrapper type overhead and it's an even [60] bytes per line.
|
||||
fn update_from_payout_log(&mut self, log: &str, regex: &P2poolRegex) {
|
||||
let amount_of_lines = log.lines().count();
|
||||
let mut vec: Vec<(String, AtomicUnit, HumanNumber)> = Vec::with_capacity(60 * amount_of_lines);
|
||||
for line in log.lines() {
|
||||
// Date
|
||||
let date = match regex.date.find(line) {
|
||||
Some(date) => date.as_str().to_string(),
|
||||
None => { error!("P2Pool | Date parse error: [{}]", line); "????-??-?? ??:??:??.????".to_string() },
|
||||
};
|
||||
// AtomicUnit
|
||||
let atomic_unit = if let Some(word) = regex.payout.find(line) {
|
||||
if let Some(word) = regex.float.find(word.as_str()) {
|
||||
match word.as_str().parse::<f64>() {
|
||||
Ok(au) => AtomicUnit::from_f64(au),
|
||||
Err(e) => { error!("P2Pool | AtomicUnit parse error: [{}] on [{}]", e, line); AtomicUnit::new() },
|
||||
}
|
||||
} else {
|
||||
AtomicUnit::new()
|
||||
}
|
||||
} else {
|
||||
AtomicUnit::new()
|
||||
};
|
||||
// Block
|
||||
let block = if let Some(word) = regex.block.find(line) {
|
||||
if let Some(word) = regex.int.find(word.as_str()) {
|
||||
match word.as_str().parse::<u64>() {
|
||||
Ok(b) => HumanNumber::from_u64(b),
|
||||
Err(e) => { error!("P2Pool | Block parse error: [{}] on [{}]", e, line); HumanNumber::unknown() },
|
||||
}
|
||||
} else {
|
||||
HumanNumber::unknown()
|
||||
}
|
||||
} else {
|
||||
HumanNumber::unknown()
|
||||
};
|
||||
vec.push((date, atomic_unit, block));
|
||||
}
|
||||
*self = Self(vec);
|
||||
}
|
||||
|
||||
// Takes the raw components (no wrapper types), convert them and pushes to existing [Self]
|
||||
fn push(&mut self, date: String, atomic_unit: u128, block: u64) {
|
||||
let atomic_unit = AtomicUnit(atomic_unit);
|
||||
let block = HumanNumber::from_u64(block);
|
||||
self.0.push((date, atomic_unit, block));
|
||||
}
|
||||
|
||||
// Sort [Self] from highest payout to lowest
|
||||
fn sort_payout_high_to_low(&mut self) {
|
||||
// This is a little confusing because wrapper types are basically 1 element tuples so:
|
||||
// self.0 = The [Vec] within [PayoutOrd]
|
||||
// b.1.0 = [b] is [(String, AtomicUnit, HumanNumber)], [.1] is the [AtomicUnit] inside it, [.0] is the [u128] inside that
|
||||
// a.1.0 = Same deal, but we compare it with the previous value (b)
|
||||
self.0.sort_by(|a, b| b.1.0.cmp(&a.1.0));
|
||||
}
|
||||
|
||||
fn sort_payout_low_to_high(&mut self) {
|
||||
self.0.sort_by(|a, b| a.1.0.cmp(&b.1.0));
|
||||
}
|
||||
|
||||
// Recent <-> Oldest relies on the line order.
|
||||
// The raw log lines will be shown instead of this struct.
|
||||
}
|
||||
|
||||
impl Default for PayoutOrd { fn default() -> Self { Self::new() } }
|
||||
|
||||
impl std::fmt::Display for PayoutOrd {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for i in &self.0 {
|
||||
writeln!(f, "{} | {} XMR | Block {}", i.0, i.1, i.2)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [ImgP2pool]
|
||||
// A static "image" of data that P2Pool started with.
|
||||
// This is just a snapshot of the user data when they initially started P2Pool.
|
||||
|
@ -1946,64 +1778,6 @@ struct Result {
|
|||
//---------------------------------------------------------------------------------------------------- TESTS
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn update_p2pool_payout_log() {
|
||||
use crate::helper::PayoutOrd;
|
||||
use crate::helper::P2poolRegex;
|
||||
let log =
|
||||
r#"NOTICE 2021-12-21 01:01:01.1111 P2Pool You received a payout of 0.001000000000 XMR in block 1
|
||||
NOTICE 2021-12-21 02:01:01.1111 P2Pool You received a payout of 0.002000000000 XMR in block 2
|
||||
NOTICE 2021-12-21 03:01:01.1111 P2Pool You received a payout of 0.003000000000 XMR in block 3
|
||||
"#;
|
||||
let mut payout_ord = PayoutOrd::new();
|
||||
println!("BEFORE: {}", payout_ord);
|
||||
PayoutOrd::update_from_payout_log(&mut payout_ord, log, &P2poolRegex::new());
|
||||
println!("AFTER: {}", payout_ord);
|
||||
let should_be =
|
||||
r#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1
|
||||
2021-12-21 02:01:01.1111 | 0.002000000000 XMR | Block 2
|
||||
2021-12-21 03:01:01.1111 | 0.003000000000 XMR | Block 3
|
||||
"#;
|
||||
assert_eq!(payout_ord.to_string(), should_be)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sort_p2pool_payout_ord() {
|
||||
use crate::helper::PayoutOrd;
|
||||
use crate::helper::AtomicUnit;
|
||||
use crate::helper::HumanNumber;
|
||||
let mut payout_ord = PayoutOrd(vec![
|
||||
("2022-09-08 18:42:55.4636".to_string(), AtomicUnit(1000000000), HumanNumber::from_u64(2654321)),
|
||||
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit(2000000000), HumanNumber::from_u64(2654322)),
|
||||
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit(3000000000), HumanNumber::from_u64(2654323)),
|
||||
]);
|
||||
println!("OG: {:#?}", payout_ord);
|
||||
|
||||
// High to Low
|
||||
PayoutOrd::sort_payout_high_to_low(&mut payout_ord);
|
||||
println!("AFTER PAYOUT HIGH TO LOW: {:#?}", payout_ord);
|
||||
let should_be =
|
||||
r#"2022-09-10 11:15:21.1272 | 0.003000000000 XMR | Block 2,654,323
|
||||
2022-09-09 16:18:26.7582 | 0.002000000000 XMR | Block 2,654,322
|
||||
2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
||||
"#;
|
||||
println!("SHOULD_BE:\n{}", should_be);
|
||||
println!("IS:\n{}", payout_ord);
|
||||
assert_eq!(payout_ord.to_string(), should_be);
|
||||
|
||||
// Low to High
|
||||
PayoutOrd::sort_payout_low_to_high(&mut payout_ord);
|
||||
println!("AFTER PAYOUT LOW TO HIGH: {:#?}", payout_ord);
|
||||
let should_be =
|
||||
r#"2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
||||
2022-09-09 16:18:26.7582 | 0.002000000000 XMR | Block 2,654,322
|
||||
2022-09-10 11:15:21.1272 | 0.003000000000 XMR | Block 2,654,323
|
||||
"#;
|
||||
println!("SHOULD_BE:\n{}", should_be);
|
||||
println!("IS:\n{}", payout_ord);
|
||||
assert_eq!(payout_ord.to_string(), should_be);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_gui_output() {
|
||||
let max = crate::helper::GUI_OUTPUT_LEEWAY;
|
||||
|
|
76
src/main.rs
76
src/main.rs
|
@ -37,7 +37,7 @@ use eframe::{egui,NativeOptions};
|
|||
use log::*;
|
||||
use env_logger::{Builder,WriteStyle};
|
||||
// Regex
|
||||
use regex::Regex;
|
||||
use ::regex::Regex;
|
||||
// Serde
|
||||
use serde::{Serialize,Deserialize};
|
||||
// std
|
||||
|
@ -63,7 +63,9 @@ mod xmrig;
|
|||
mod update;
|
||||
mod helper;
|
||||
mod human;
|
||||
use {ferris::*,constants::*,node::*,disk::*,update::*,gupax::*,helper::*};
|
||||
mod regex;
|
||||
mod xmr;
|
||||
use {crate::regex::*,ferris::*,constants::*,node::*,disk::*,update::*,gupax::*,helper::*};
|
||||
|
||||
// Sudo (dummy values for Windows)
|
||||
mod sudo;
|
||||
|
@ -134,7 +136,7 @@ pub struct App {
|
|||
// 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,
|
||||
gupax_p2pool_api: Arc<Mutex<GupaxP2poolApi>>,
|
||||
// Static stuff
|
||||
pid: sysinfo::Pid, // Gupax's PID
|
||||
max_threads: usize, // Max amount of detected system threads
|
||||
|
@ -213,7 +215,7 @@ impl App {
|
|||
restart: Arc::new(Mutex::new(Restart::No)),
|
||||
diff: false,
|
||||
error_state: ErrorState::new(),
|
||||
helper: Arc::new(Mutex::new(Helper::new(now, pub_sys.clone(), p2pool.clone(), xmrig.clone(), p2pool_api.clone(), xmrig_api.clone(), p2pool_img.clone(), xmrig_img.clone()))),
|
||||
helper: Arc::new(Mutex::new(Helper::new(now, pub_sys.clone(), p2pool.clone(), xmrig.clone(), p2pool_api.clone(), xmrig_api.clone(), p2pool_img.clone(), xmrig_img.clone(), Arc::new(Mutex::new(GupaxP2poolApi::new()))))),
|
||||
p2pool,
|
||||
xmrig,
|
||||
p2pool_api,
|
||||
|
@ -226,7 +228,7 @@ impl App {
|
|||
resizing: false,
|
||||
alpha: 0,
|
||||
no_startup: false,
|
||||
gupax_p2pool_api: GupaxP2poolApi::new(),
|
||||
gupax_p2pool_api: Arc::new(Mutex::new(GupaxP2poolApi::new())),
|
||||
pub_sys,
|
||||
pid,
|
||||
max_threads: num_cpus::get(),
|
||||
|
@ -346,16 +348,30 @@ impl App {
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
// 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);
|
||||
let mut gupax_p2pool_api = app.gupax_p2pool_api.lock().unwrap();
|
||||
gupax_p2pool_api.fill_paths(&app.gupax_p2pool_api_path);
|
||||
match GupaxP2poolApi::create_all_files(&app.gupax_p2pool_api_path) {
|
||||
Ok(_) => debug!("App Init | Creating Gupax-P2Pool API files ... OK"),
|
||||
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),
|
||||
};
|
||||
},
|
||||
}
|
||||
debug!("App Init | Reading Gupax-P2Pool API files...");
|
||||
match app.gupax_p2pool_api.read_all_files_and_update() {
|
||||
match 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,
|
||||
"GupaxP2poolApi ... Payouts: {} | XMR (atomic-units): {}",
|
||||
gupax_p2pool_api.payout,
|
||||
gupax_p2pool_api.xmr,
|
||||
);
|
||||
},
|
||||
Err(err) => {
|
||||
|
@ -371,6 +387,8 @@ impl App {
|
|||
};
|
||||
},
|
||||
};
|
||||
drop(gupax_p2pool_api);
|
||||
app.helper.lock().unwrap().gupax_p2pool_api = Arc::clone(&app.gupax_p2pool_api);
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
let mut og = app.og.lock().unwrap(); // Lock [og]
|
||||
|
@ -606,33 +624,6 @@ impl Images {
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [Regexes] struct
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Regexes {
|
||||
pub name: Regex,
|
||||
pub address: Regex,
|
||||
pub ipv4: Regex,
|
||||
pub domain: Regex,
|
||||
pub port: Regex,
|
||||
}
|
||||
|
||||
impl Regexes {
|
||||
fn new() -> Self {
|
||||
Regexes {
|
||||
name: Regex::new("^[A-Za-z0-9-_.]+( [A-Za-z0-9-_.]+)*$").unwrap(),
|
||||
address: Regex::new("^4[A-Za-z1-9]+$").unwrap(), // This still needs to check for (l, I, o, 0)
|
||||
ipv4: Regex::new(r#"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$"#).unwrap(),
|
||||
domain: Regex::new(r#"^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$"#).unwrap(),
|
||||
port: Regex::new(r#"^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$"#).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a Monero address is correct.
|
||||
pub fn addr_ok(&self, address: &str) -> bool {
|
||||
address.len() == 95 && Regex::is_match(&self.address, address) && !address.contains('0') && !address.contains('O') && !address.contains('l')
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [Pressed] enum
|
||||
// These represent the keys pressed during the frame.
|
||||
// I could use egui's [Key] but there is no option for
|
||||
|
@ -1682,9 +1673,7 @@ XMRig console byte length: {}\n
|
|||
{:#?}\n
|
||||
------------------------------------------ XMRIG IMAGE ------------------------------------------
|
||||
{:#?}\n
|
||||
------------------------------------------ P2POOL GUI API ------------------------------------------
|
||||
{:#?}\n
|
||||
------------------------------------------ XMRIG GUI API ------------------------------------------
|
||||
------------------------------------------ GUPAX-P2POOL API ------------------------------------------
|
||||
{:#?}\n
|
||||
------------------------------------------ WORKING STATE ------------------------------------------
|
||||
{:#?}\n
|
||||
|
@ -1717,8 +1706,7 @@ XMRig console byte length: {}\n
|
|||
xmrig_gui_len,
|
||||
self.p2pool_img.lock().unwrap(),
|
||||
self.xmrig_img.lock().unwrap(),
|
||||
self.p2pool_api.lock().unwrap(),
|
||||
self.xmrig_api.lock().unwrap(),
|
||||
self.gupax_p2pool_api.lock().unwrap(),
|
||||
self.state,
|
||||
self.og.lock().unwrap(),
|
||||
);
|
||||
|
|
89
src/regex.rs
Normal file
89
src/regex.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Gupax - GUI Uniting P2Pool And XMRig
|
||||
//
|
||||
// Copyright (c) 2022 hinto-janaiyo
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Some regexes used throughout Gupax.
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [Regexes] struct
|
||||
// General purpose Regexes, mostly used in the GUI.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Regexes {
|
||||
pub name: Regex,
|
||||
pub address: Regex,
|
||||
pub ipv4: Regex,
|
||||
pub domain: Regex,
|
||||
pub port: Regex,
|
||||
}
|
||||
|
||||
impl Regexes {
|
||||
pub fn new() -> Self {
|
||||
Regexes {
|
||||
name: Regex::new("^[A-Za-z0-9-_.]+( [A-Za-z0-9-_.]+)*$").unwrap(),
|
||||
address: Regex::new("^4[A-Za-z1-9]+$").unwrap(), // This still needs to check for (l, I, o, 0)
|
||||
ipv4: Regex::new(r#"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$"#).unwrap(),
|
||||
domain: Regex::new(r#"^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$"#).unwrap(),
|
||||
port: Regex::new(r#"^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$"#).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a Monero address is correct.
|
||||
// This actually only checks for length & Base58, and doesn't do any checksum validation
|
||||
// (the last few bytes of a Monero address are a Keccak hash checksum) so some invalid addresses can trick this function.
|
||||
pub fn addr_ok(&self, address: &str) -> bool {
|
||||
address.len() == 95 && Regex::is_match(&self.address, address) && !address.contains('0') && !address.contains('O') && !address.contains('l')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [P2poolRegex]
|
||||
// Meant for parsing the output of P2Pool and finding payouts and total XMR found.
|
||||
// Why Regex instead of the standard library?
|
||||
// 1. I'm already using Regex
|
||||
// 2. It's insanely faster
|
||||
//
|
||||
// The following STDLIB implementation takes [0.003~] seconds to find all matches given a [String] with 30k lines:
|
||||
// let mut n = 0;
|
||||
// for line in P2POOL_OUTPUT.lines() {
|
||||
// if line.contains("payout of [0-9].[0-9]+ XMR") { n += 1; }
|
||||
// }
|
||||
//
|
||||
// This regex function takes [0.0003~] seconds (10x faster):
|
||||
// let regex = Regex::new("payout of [0-9].[0-9]+ XMR").unwrap();
|
||||
// let n = regex.find_iter(P2POOL_OUTPUT).count();
|
||||
//
|
||||
// Both are nominally fast enough where it doesn't matter too much but meh, why not use regex.
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct P2poolRegex {
|
||||
pub payout: regex::Regex,
|
||||
pub float: regex::Regex,
|
||||
pub date: regex::Regex,
|
||||
pub block: regex::Regex,
|
||||
pub int: regex::Regex,
|
||||
}
|
||||
|
||||
impl P2poolRegex {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
payout: regex::Regex::new("payout of [0-9].[0-9]+ XMR").unwrap(),
|
||||
float: regex::Regex::new("[0-9].[0-9]+").unwrap(),
|
||||
date: regex::Regex::new("[0-9]+-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+.[0-9]+").unwrap(),
|
||||
block: regex::Regex::new("block [0-9]+").unwrap(),
|
||||
int: regex::Regex::new("[0-9]+").unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
310
src/xmr.rs
Normal file
310
src/xmr.rs
Normal file
|
@ -0,0 +1,310 @@
|
|||
// Gupax - GUI Uniting P2Pool And XMRig
|
||||
//
|
||||
// Copyright (c) 2022 hinto-janaiyo
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// This file is for handling actual XMR integers/floats using [AtomicUnit] & [PayoutOrd]
|
||||
// AtomicUnit is just a wrapper around a [u128] implementing common XMR Atomic Unit functions.
|
||||
// PayoutOrd is a wrapper around a [Vec] for sorting P2Pool payouts with this type signature:
|
||||
// "Vec<(String, AtomicUnit, HumanNumber)>"
|
||||
// These represent:
|
||||
// "(DATE, ATOMIC_UNIT, MONERO_BLOCK)"
|
||||
|
||||
use crate::{
|
||||
human::*,
|
||||
P2poolRegex,
|
||||
};
|
||||
|
||||
use log::*;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- XMR AtomicUnit
|
||||
// After I initially wrote this struct, I forgot why I even needed it.
|
||||
// I get the XMR received as a float, I display it as a float and it wouldn't be
|
||||
// too bad if I wrote it to disk as a float, but then I realized [.cmp()] doesn't
|
||||
// work on [f64] and also that Rust makes sorting floats a pain so instead of deleting
|
||||
// this code and making some float sorter, I might as well use it.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AtomicUnit(u128);
|
||||
|
||||
impl AtomicUnit {
|
||||
pub fn new() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
pub fn from_u128(u: u128) -> Self {
|
||||
Self(u)
|
||||
}
|
||||
|
||||
pub fn add_u128(&mut self, u: u128) -> Self {
|
||||
Self(self.0 + u)
|
||||
}
|
||||
|
||||
pub fn add_self(&mut self, atomic_unit: &Self) -> Self {
|
||||
Self(self.0 + atomic_unit.0)
|
||||
}
|
||||
|
||||
pub fn to_u128(&self) -> u128 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn sum_vec(vec: &Vec<Self>) -> Self {
|
||||
let mut sum = 0;
|
||||
for int in vec {
|
||||
sum += int.0;
|
||||
}
|
||||
Self(sum)
|
||||
}
|
||||
|
||||
pub fn from_f64(f: f64) -> Self {
|
||||
Self((f * 1_000_000_000_000.0) as u128)
|
||||
}
|
||||
|
||||
pub fn to_f64(&self) -> f64 {
|
||||
self.0 as f64 / 1_000_000_000_000.0
|
||||
}
|
||||
|
||||
pub fn to_human_number_12_point(&self) -> HumanNumber {
|
||||
let f = self.0 as f64 / 1_000_000_000_000.0;
|
||||
HumanNumber::from_f64_12_point(f)
|
||||
}
|
||||
|
||||
pub fn to_human_number_no_fmt(&self) -> HumanNumber {
|
||||
let f = self.0 as f64 / 1_000_000_000_000.0;
|
||||
HumanNumber::from_f64_no_fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// Displays AtomicUnit as a real XMR floating point.
|
||||
impl std::fmt::Display for AtomicUnit {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", Self::to_human_number_12_point(self))
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [PayoutOrd]
|
||||
// This is the struct for ordering P2Pool payout lines into a structured and ordered vector of elements.
|
||||
// The structure goes as follows:
|
||||
// "Vec<(String, AtomicUnit, HumanNumber)>"
|
||||
// Which displays as:
|
||||
// "2022-08-17 12:16:11.8662" | 0.002382256231 XMR | Block 2573821
|
||||
//
|
||||
// [0] = DATE
|
||||
// [1] = XMR IN ATOMIC-UNITS
|
||||
// [2] = MONERO BLOCK
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct PayoutOrd(Vec<(String, AtomicUnit, HumanNumber)>);
|
||||
|
||||
impl PayoutOrd {
|
||||
pub fn new() -> Self {
|
||||
Self(vec![(String::from("????-??-?? ??:??:??.????"), AtomicUnit::new(), HumanNumber::unknown())])
|
||||
}
|
||||
|
||||
pub fn from_vec(vec: Vec<(String, AtomicUnit, HumanNumber)>) -> Self {
|
||||
Self(vec)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn parse_line(line: &str, regex: &P2poolRegex) -> (String, AtomicUnit, HumanNumber) {
|
||||
// Date
|
||||
let date = match regex.date.find(line) {
|
||||
Some(date) => date.as_str().to_string(),
|
||||
None => { error!("P2Pool | Date parse error: [{}]", line); "????-??-?? ??:??:??.????".to_string() },
|
||||
};
|
||||
// AtomicUnit
|
||||
let atomic_unit = if let Some(word) = regex.payout.find(line) {
|
||||
if let Some(word) = regex.float.find(word.as_str()) {
|
||||
match word.as_str().parse::<f64>() {
|
||||
Ok(au) => AtomicUnit::from_f64(au),
|
||||
Err(e) => { error!("P2Pool | AtomicUnit parse error: [{}] on [{}]", e, line); AtomicUnit::new() },
|
||||
}
|
||||
} else {
|
||||
AtomicUnit::new()
|
||||
}
|
||||
} else {
|
||||
AtomicUnit::new()
|
||||
};
|
||||
// Block
|
||||
let block = if let Some(word) = regex.block.find(line) {
|
||||
if let Some(word) = regex.int.find(word.as_str()) {
|
||||
match word.as_str().parse::<u64>() {
|
||||
Ok(b) => HumanNumber::from_u64(b),
|
||||
Err(e) => { error!("P2Pool | Block parse error: [{}] on [{}]", e, line); HumanNumber::unknown() },
|
||||
}
|
||||
} else {
|
||||
HumanNumber::unknown()
|
||||
}
|
||||
} else {
|
||||
HumanNumber::unknown()
|
||||
};
|
||||
(date, atomic_unit, block)
|
||||
}
|
||||
|
||||
// Takes in input of ONLY P2Pool payout logs and
|
||||
// converts it into a usable [PayoutOrd]
|
||||
// It expects log lines like this:
|
||||
// "NOTICE 2022-04-11 00:20:17.2571 P2Pool You received a payout of 0.001371623621 XMR in block 2562511"
|
||||
// For efficiency reasons, I'd like to know the byte size
|
||||
// we should allocate for the vector so we aren't adding every loop.
|
||||
// Given a log [str], the equation for how many bytes the final vec will be is:
|
||||
// (BYTES_OF_DATE + BYTES OF XMR + BYTES OF BLOCK) * amount_of_lines
|
||||
// The first three are more or less constants (monero block 10m is in 10,379 years...): [23, 14, 7] (sum: 44)
|
||||
// Add 16 more bytes for wrapper type overhead and it's an even [60] bytes per line.
|
||||
pub fn update_from_payout_log(&mut self, log: &str) {
|
||||
let regex = P2poolRegex::new();
|
||||
let amount_of_lines = log.lines().count();
|
||||
let mut vec: Vec<(String, AtomicUnit, HumanNumber)> = Vec::with_capacity(60 * amount_of_lines);
|
||||
for line in log.lines() {
|
||||
debug!("PayoutOrg | Parsing line: [{}]", line);
|
||||
vec.push(Self::parse_line(line, ®ex));
|
||||
}
|
||||
*self = Self(vec);
|
||||
}
|
||||
|
||||
// Takes the raw components (no wrapper types), convert them and pushes to existing [Self]
|
||||
pub fn push(&mut self, date: &str, atomic_unit: u128, block: u64) {
|
||||
let atomic_unit = AtomicUnit(atomic_unit);
|
||||
let block = HumanNumber::from_u64(block);
|
||||
self.0.push((date.to_string(), atomic_unit, block));
|
||||
}
|
||||
|
||||
pub fn atomic_unit_sum(&self) -> AtomicUnit {
|
||||
let mut sum: u128 = 0;
|
||||
for (_, atomic_unit, _) in &self.0 {
|
||||
sum += atomic_unit.to_u128();
|
||||
}
|
||||
AtomicUnit::from_u128(sum)
|
||||
}
|
||||
|
||||
// Sort [Self] from highest payout to lowest
|
||||
pub fn sort_payout_high_to_low(&mut self) {
|
||||
// This is a little confusing because wrapper types are basically 1 element tuples so:
|
||||
// self.0 = The [Vec] within [PayoutOrd]
|
||||
// b.1.0 = [b] is [(String, AtomicUnit, HumanNumber)], [.1] is the [AtomicUnit] inside it, [.0] is the [u128] inside that
|
||||
// a.1.0 = Same deal, but we compare it with the previous value (b)
|
||||
self.0.sort_by(|a, b| b.1.0.cmp(&a.1.0));
|
||||
}
|
||||
|
||||
pub fn sort_payout_low_to_high(&mut self) {
|
||||
self.0.sort_by(|a, b| a.1.0.cmp(&b.1.0));
|
||||
}
|
||||
|
||||
// Recent <-> Oldest relies on the line order.
|
||||
// The raw log lines will be shown instead of this struct.
|
||||
}
|
||||
|
||||
impl Default for PayoutOrd { fn default() -> Self { Self::new() } }
|
||||
|
||||
impl std::fmt::Display for PayoutOrd {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for i in &self.0 {
|
||||
writeln!(f, "{} | {} XMR | Block {}", i.0, i.1, i.2)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- TESTS
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn update_p2pool_payout_log() {
|
||||
use crate::xmr::PayoutOrd;
|
||||
let log =
|
||||
r#"NOTICE 2021-12-21 01:01:01.1111 P2Pool You received a payout of 0.001000000000 XMR in block 1
|
||||
NOTICE 2021-12-21 02:01:01.1111 P2Pool You received a payout of 0.002000000000 XMR in block 2
|
||||
NOTICE 2021-12-21 03:01:01.1111 P2Pool You received a payout of 0.003000000000 XMR in block 3
|
||||
"#;
|
||||
let mut payout_ord = PayoutOrd::new();
|
||||
println!("BEFORE: {}", payout_ord);
|
||||
PayoutOrd::update_from_payout_log(&mut payout_ord, log);
|
||||
println!("AFTER: {}", payout_ord);
|
||||
let should_be =
|
||||
r#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1
|
||||
2021-12-21 02:01:01.1111 | 0.002000000000 XMR | Block 2
|
||||
2021-12-21 03:01:01.1111 | 0.003000000000 XMR | Block 3
|
||||
"#;
|
||||
assert_eq!(payout_ord.to_string(), should_be)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_to_payout_ord() {
|
||||
use crate::xmr::PayoutOrd;
|
||||
use crate::xmr::AtomicUnit;
|
||||
use crate::human::HumanNumber;
|
||||
let mut payout_ord = PayoutOrd::from_vec(vec![]);
|
||||
let should_be = "2022-09-08 18:42:55.4636 | 0.000000000001 XMR | Block 2,654,321\n";
|
||||
println!("BEFORE: {:#?}", payout_ord);
|
||||
payout_ord.push("2022-09-08 18:42:55.4636", 1, 2654321);
|
||||
println!("AFTER: {}", payout_ord);
|
||||
println!("SHOULD_BE: {}", should_be);
|
||||
assert_eq!(payout_ord.to_string(), should_be);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sum_payout_ord_atomic_unit() {
|
||||
use crate::xmr::PayoutOrd;
|
||||
use crate::xmr::AtomicUnit;
|
||||
use crate::human::HumanNumber;
|
||||
let mut payout_ord = PayoutOrd::from_vec(vec![
|
||||
("2022-09-08 18:42:55.4636".to_string(), AtomicUnit::from_u128(1), HumanNumber::from_u64(2654321)),
|
||||
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u128(1), HumanNumber::from_u64(2654322)),
|
||||
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u128(1), HumanNumber::from_u64(2654323)),
|
||||
]);
|
||||
println!("OG: {:#?}", payout_ord);
|
||||
let sum = PayoutOrd::atomic_unit_sum(&payout_ord);
|
||||
println!("SUM: {}", sum.to_u128());
|
||||
assert_eq!(sum.to_u128(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sort_p2pool_payout_ord() {
|
||||
use crate::xmr::PayoutOrd;
|
||||
use crate::xmr::AtomicUnit;
|
||||
use crate::human::HumanNumber;
|
||||
let mut payout_ord = PayoutOrd::from_vec(vec![
|
||||
("2022-09-08 18:42:55.4636".to_string(), AtomicUnit::from_u128(1000000000), HumanNumber::from_u64(2654321)),
|
||||
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u128(2000000000), HumanNumber::from_u64(2654322)),
|
||||
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u128(3000000000), HumanNumber::from_u64(2654323)),
|
||||
]);
|
||||
println!("OG: {:#?}", payout_ord);
|
||||
|
||||
// High to Low
|
||||
PayoutOrd::sort_payout_high_to_low(&mut payout_ord);
|
||||
println!("AFTER PAYOUT HIGH TO LOW: {:#?}", payout_ord);
|
||||
let should_be =
|
||||
r#"2022-09-10 11:15:21.1272 | 0.003000000000 XMR | Block 2,654,323
|
||||
2022-09-09 16:18:26.7582 | 0.002000000000 XMR | Block 2,654,322
|
||||
2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
||||
"#;
|
||||
println!("SHOULD_BE:\n{}", should_be);
|
||||
println!("IS:\n{}", payout_ord);
|
||||
assert_eq!(payout_ord.to_string(), should_be);
|
||||
|
||||
// Low to High
|
||||
PayoutOrd::sort_payout_low_to_high(&mut payout_ord);
|
||||
println!("AFTER PAYOUT LOW TO HIGH: {:#?}", payout_ord);
|
||||
let should_be =
|
||||
r#"2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
||||
2022-09-09 16:18:26.7582 | 0.002000000000 XMR | Block 2,654,322
|
||||
2022-09-10 11:15:21.1272 | 0.003000000000 XMR | Block 2,654,323
|
||||
"#;
|
||||
println!("SHOULD_BE:\n{}", should_be);
|
||||
println!("IS:\n{}", payout_ord);
|
||||
assert_eq!(payout_ord.to_string(), should_be);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue