mirror of
https://github.com/Cyrix126/gupaxx.git
synced 2025-03-29 10:38:59 +00:00
411 lines
15 KiB
Rust
411 lines
15 KiB
Rust
// 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 [u64] 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.
|
|
|
|
// [u64] can hold max: 18_446_744_073_709_551_615 which equals to 18,446,744,073 XMR (18 billion).
|
|
// Given the constant XMR tail emission of (0.3 per minute|18 per hour|432 per day|157,680 per year)
|
|
// this would take: 116,976~ years to overflow.
|
|
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
|
|
pub struct AtomicUnit(u64);
|
|
|
|
impl AtomicUnit {
|
|
pub const fn new() -> Self {
|
|
Self(0)
|
|
}
|
|
|
|
pub const fn from_u64(u: u64) -> Self {
|
|
Self(u)
|
|
}
|
|
|
|
pub const fn add_u64(self, u: u64) -> Self {
|
|
Self(self.0 + u)
|
|
}
|
|
|
|
pub const fn add_self(self, atomic_unit: Self) -> Self {
|
|
Self(self.0 + atomic_unit.0)
|
|
}
|
|
|
|
pub const fn to_u64(self) -> u64 {
|
|
self.0
|
|
}
|
|
|
|
pub fn to_string(self) -> String {
|
|
self.0.to_string()
|
|
}
|
|
|
|
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 u64)
|
|
}
|
|
|
|
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 const fn from_vec(vec: Vec<(String, AtomicUnit, HumanNumber)>) -> Self {
|
|
Self(vec)
|
|
}
|
|
|
|
pub fn is_same(a: &Self, b: &Self) -> bool {
|
|
if a.0.is_empty() && b.0.is_empty() { return true }
|
|
if a.0.len() != b.0.len() { return false }
|
|
let mut n = 0;
|
|
for (date, atomic_unit, block) in &a.0 {
|
|
if *date != b.0[n].0 { return false }
|
|
if *atomic_unit != b.0[n].1 { return false }
|
|
if *block != b.0[n].2 { return false }
|
|
n += 1;
|
|
}
|
|
true
|
|
}
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
self.0.is_empty()
|
|
}
|
|
|
|
// Expected input: "NOTICE 2022-01-27 01:30:23.1377 P2Pool You received a payout of 0.000000000001 XMR in block 2642816"
|
|
pub fn parse_raw_payout_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.payout_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 {
|
|
error!("P2Pool | AtomicUnit parse error: [{}]", line);
|
|
AtomicUnit::new()
|
|
}
|
|
} else {
|
|
error!("P2Pool | AtomicUnit parse error: [{}]", line);
|
|
AtomicUnit::new()
|
|
};
|
|
// Block
|
|
let block = if let Some(word) = regex.block.find(line) {
|
|
if let Some(word) = regex.block_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 {
|
|
error!("P2Pool | Block parse error: [{}]", line);
|
|
HumanNumber::unknown()
|
|
}
|
|
} else {
|
|
error!("P2Pool | Block parse error: [{}]", line);
|
|
HumanNumber::unknown()
|
|
};
|
|
(date, atomic_unit, block)
|
|
}
|
|
|
|
// Expected input: "2022-01-27 01:30:23.1377 | 0.000000000001 XMR | Block 2,642,816"
|
|
pub fn parse_formatted_payout_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_float.find(line) {
|
|
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 {
|
|
error!("P2Pool | AtomicUnit parse error: [{}]", line);
|
|
AtomicUnit::new()
|
|
};
|
|
// Block
|
|
let block = match regex.block_comma.find(line) {
|
|
Some(b) => HumanNumber::from_str(b.as_str()),
|
|
None => { error!("P2Pool | Block parse error: [{}]", line); HumanNumber::unknown() },
|
|
};
|
|
(date, atomic_unit, block)
|
|
}
|
|
|
|
// Takes in input of ONLY P2Pool payout logs and converts it into a usable [PayoutOrd]
|
|
// It expects formatted log lines like this: "2022-04-11 00:20:17.2571 | 0.001371623621 XMR | Block 2,562,511"
|
|
// 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) + (SPACES, PIPES, MISC WORDS) * amount_of_lines
|
|
// The first three are more or less constants (monero block 10m is in 10,379 years...): [23, 14, 7] (sum: 44)
|
|
// Spaces, pipes, commas and words (XMR, Block): [19]
|
|
// Add 7 more bytes for wrapper type overhead and it's an even [70] 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(70 * amount_of_lines);
|
|
for line in log.lines() {
|
|
debug!("PayoutOrd | Parsing line: [{}]", line);
|
|
vec.push(Self::parse_formatted_payout_line(line, ®ex));
|
|
}
|
|
*self = Self(vec);
|
|
}
|
|
|
|
// Takes the wrapper types, and pushes to existing [Self]
|
|
pub fn push(&mut self, date: String, atomic_unit: AtomicUnit, block: HumanNumber) {
|
|
self.0.push((date, atomic_unit, block));
|
|
}
|
|
|
|
// Takes the raw components (no wrapper types), convert them and pushes to existing [Self]
|
|
pub fn push_raw(&mut self, date: &str, atomic_unit: u64, 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: u64 = 0;
|
|
for (_, atomic_unit, _) in &self.0 {
|
|
sum += atomic_unit.to_u64();
|
|
}
|
|
AtomicUnit::from_u64(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 [u64] 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));
|
|
}
|
|
|
|
// These sorting functions take around [0.0035~] seconds on a Ryzen 5950x
|
|
// given a Vec filled with 1_000_000 elements, not bad.
|
|
pub fn sort_payout_low_to_high(&mut self) {
|
|
self.0.sort_by(|a, b| a.1.0.cmp(&b.1.0));
|
|
}
|
|
|
|
// Returns a reversed [Iter] of the [PayoutOrd]
|
|
// This is obviously faster than actually reordering the Vec.
|
|
pub fn rev_iter(&self) -> std::iter::Rev<std::slice::Iter<'_, (String, AtomicUnit, HumanNumber)>> {
|
|
self.0.iter().rev()
|
|
}
|
|
|
|
// 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#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1,234,567
|
|
2021-12-21 02:01:01.1111 | 0.002000000000 XMR | Block 2,345,678
|
|
2021-12-21 03:01:01.1111 | 0.003000000000 XMR | Block 3,456,789
|
|
"#;
|
|
let mut payout_ord = PayoutOrd::new();
|
|
println!("BEFORE: {}", payout_ord);
|
|
PayoutOrd::update_from_payout_log(&mut payout_ord, log);
|
|
println!("AFTER: {}", payout_ord);
|
|
assert_eq!(payout_ord.to_string(), log);
|
|
}
|
|
|
|
#[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_raw("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_u64(1), HumanNumber::from_u64(2654321)),
|
|
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u64(1), HumanNumber::from_u64(2654322)),
|
|
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u64(1), HumanNumber::from_u64(2654323)),
|
|
]);
|
|
println!("OG: {:#?}", payout_ord);
|
|
let sum = PayoutOrd::atomic_unit_sum(&payout_ord);
|
|
println!("SUM: {}", sum.to_u64());
|
|
assert_eq!(sum.to_u64(), 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_u64(1000000000), HumanNumber::from_u64(2654321)),
|
|
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u64(2000000000), HumanNumber::from_u64(2654322)),
|
|
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u64(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 payout_ord_is_same() {
|
|
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_u64(1000000000), HumanNumber::from_u64(2654321)),
|
|
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u64(2000000000), HumanNumber::from_u64(2654322)),
|
|
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u64(3000000000), HumanNumber::from_u64(2654323)),
|
|
]);
|
|
let payout_ord_2 = payout_ord.clone();
|
|
println!("1: {:#?}", payout_ord);
|
|
println!("2: {:#?}", payout_ord);
|
|
|
|
assert!(PayoutOrd::is_same(&payout_ord, &payout_ord_2) == true);
|
|
payout_ord.push_raw("2022-09-08 18:42:55.4636", 1000000000, 2654321);
|
|
println!("1: {:#?}", payout_ord);
|
|
println!("2: {:#?}", payout_ord);
|
|
assert!(PayoutOrd::is_same(&payout_ord, &payout_ord_2) == false);
|
|
}
|
|
|
|
#[test]
|
|
fn view_reverse_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_u64(1000000000), HumanNumber::from_u64(2654321)),
|
|
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u64(2000000000), HumanNumber::from_u64(2654322)),
|
|
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u64(3000000000), HumanNumber::from_u64(2654323)),
|
|
]);
|
|
println!("OG: {:#?}", payout_ord);
|
|
|
|
for (_, atomic_unit, _) in payout_ord.rev_iter() {
|
|
if atomic_unit.to_u64() == 3000000000 {
|
|
break
|
|
} else {
|
|
println!("expected: 3000000000, found: {}", atomic_unit);
|
|
panic!("not reversed");
|
|
}
|
|
}
|
|
}
|
|
}
|