mirror of
https://github.com/hinto-janai/gupax.git
synced 2024-12-23 11:29:38 +00:00
Status Submenu: add serde for p2pools [local/network/pool] API
This commit is contained in:
parent
acba71f157
commit
9245cc8a11
3 changed files with 177 additions and 52 deletions
|
@ -4,6 +4,7 @@
|
|||
* [Bootstrap](#Bootstrap)
|
||||
* [Scale](#Scale)
|
||||
* [Naming Scheme](#naming-scheme)
|
||||
* [Mining Stat Reference](#mining-stat-reference)
|
||||
* [Sudo](#Sudo)
|
||||
* [Why does Gupax need to be Admin? (on Windows)](#why-does-gupax-need-to-be-admin-on-windows)
|
||||
- [The issue](#the-issue)
|
||||
|
@ -124,6 +125,31 @@ Exceptions (there are always exceptions...):
|
|||
- XMRig separates the hash and signature
|
||||
- P2Pool hashes are in UPPERCASE
|
||||
|
||||
## Mining Stat Reference
|
||||
Some pseudo JSON for constants/equations needed for generating mining stats. They're here for easy reference, I was never good at math :)
|
||||
```
|
||||
block_time_in_seconds: {
|
||||
P2POOL_BLOCK_TIME: 10,
|
||||
MONERO_BLOCK_TIME: 120,
|
||||
}
|
||||
|
||||
difficulty: {
|
||||
P2POOL_DIFFICULTY: (current_p2pool_hashrate * P2POOL_BLOCK_TIME),
|
||||
MONERO_DIFFICULTY: (current_monero_hashrate * MONERO_BLOCK_TIME),
|
||||
}
|
||||
|
||||
hashrate_per_second: {
|
||||
P2POOL_HASHRATE: (P2POOL_DIFFICULTY / P2POOL_BLOCK_TIME),
|
||||
MONERO_HASHRATE: (MONERO_DIFFICULTY / MONERO_BLOCK_TIME),
|
||||
}
|
||||
|
||||
mean_in_seconds: {
|
||||
P2POOL_BLOCK_MEAN: (MONERO_DIFF / P2POOL_HASHRATE),
|
||||
MY_SOLO_BLOCK_MEAN: (MONERO_DIFF / my_hashrate),
|
||||
MY_P2POOL_SHARE_MEAN: (P2POOL_DIFF / my_hashrate),
|
||||
}
|
||||
```
|
||||
|
||||
## Sudo
|
||||
Unlike Windows, Unix (macOS/Linux) has a userland program that handles all the dirty details of privilege escalation: `sudo`.
|
||||
|
||||
|
@ -208,7 +234,7 @@ This was the solution I would have gone with, but alas, the abstracted `Command`
|
|||
---
|
||||
|
||||
### Windows vs Unix
|
||||
Unix (macOS/Linux) has have a super nice, easy, friendly, not-completely-garbage userland program called: `sudo`. It is so extremely simple to use `sudo` as a sort of wrapper around XMRig since `sudo` isn't completely backwards and actually has valuable flags! No legacy `Administrator`, no UAC prompt, no shells within shells, no low-level system APIs, no messing with the user Registry.
|
||||
Unix (macOS/Linux) has a super nice, easy, friendly, not-completely-garbage userland program called: `sudo`. It is so extremely simple to use `sudo` as a sort of wrapper around XMRig since `sudo` isn't completely backwards and actually has valuable flags! No legacy `Administrator`, no UAC prompt, no shells within shells, no low-level system APIs, no messing with the user Registry.
|
||||
|
||||
You get the user's password, you input it to `sudo` with `--stdin` and you execute XMRig with it. Simple, easy, nice. (Don't forget to zero the password memory, though).
|
||||
|
||||
|
|
|
@ -70,9 +70,17 @@ r#"*---------------------------------------*
|
|||
*---------------------------------------*"#;
|
||||
// P2Pool & XMRig default API stuff
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const P2POOL_API_PATH: &str = r"local\stats"; // The default relative FS path of P2Pool's local API
|
||||
pub const P2POOL_API_PATH_LOCAL: &str = r"local\stats";
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const P2POOL_API_PATH_NETWORK: &str = r"network\stats";
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const P2POOL_API_PATH_POOL: &str = r"pool\stats";
|
||||
#[cfg(target_family = "unix")]
|
||||
pub const P2POOL_API_PATH: &str = "local/stats";
|
||||
pub const P2POOL_API_PATH_LOCAL: &str = "local/stats";
|
||||
#[cfg(target_family = "unix")]
|
||||
pub const P2POOL_API_PATH_NETWORK: &str = "network/stats";
|
||||
#[cfg(target_family = "unix")]
|
||||
pub const P2POOL_API_PATH_POOL: &str = "pool/stats";
|
||||
pub const XMRIG_API_URI: &str = "1/summary"; // The default relative URI of XMRig's API
|
||||
|
||||
// Process state tooltips (online, offline, etc)
|
||||
|
@ -148,8 +156,8 @@ pub const STATUS_GUPAX_SYSTEM_MEMORY: &str = "How much memory your entire system
|
|||
pub const STATUS_GUPAX_SYSTEM_CPU_MODEL: &str = "The detected model of your system's CPU and its current frequency";
|
||||
//--
|
||||
pub const STATUS_P2POOL_UPTIME: &str = "How long P2Pool has been online";
|
||||
pub const STATUS_P2POOL_PAYOUTS: &str = "The total amount of payouts received and an extrapolated estimate of how many you will receive. Warning: these stats will be quite inaccurate if your P2Pool hasn't been running for a long time!";
|
||||
pub const STATUS_P2POOL_XMR: &str = "The total amount of XMR mined via P2Pool and an extrapolated estimate of how many you will mine in the future. Warning: these stats will be quite inaccurate if your P2Pool hasn't been running for a long time!";
|
||||
pub const STATUS_P2POOL_PAYOUTS: &str = "The total amount of payouts received in this instance of P2Pool and an extrapolated estimate of how many you will receive. Warning: these stats will be quite inaccurate if your P2Pool hasn't been running for a long time!";
|
||||
pub const STATUS_P2POOL_XMR: &str = "The total amount of XMR mined in this instance of P2Pool and an extrapolated estimate of how many you will mine in the future. Warning: these stats will be quite inaccurate if your P2Pool hasn't been running for a long time!";
|
||||
pub const STATUS_P2POOL_HASHRATE: &str = "The total amount of hashrate your P2Pool has pointed at it in 15 minute, 1 hour, and 24 hour averages";
|
||||
pub const STATUS_P2POOL_SHARES: &str = "The total amount of shares found on P2Pool";
|
||||
pub const STATUS_P2POOL_EFFORT: &str = "The average amount of effort needed to find a share, and the current effort";
|
||||
|
|
181
src/helper.rs
181
src/helper.rs
|
@ -75,8 +75,10 @@ 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)
|
||||
priv_api_p2pool: Arc<Mutex<PrivP2poolApi>>, // For "watchdog" thread
|
||||
priv_api_xmrig: Arc<Mutex<PrivXmrigApi>>, // For "watchdog" thread
|
||||
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>>,
|
||||
priv_api_xmrig: Arc<Mutex<PrivXmrigApi>>, // Serde struct for XMRig's HTTP API
|
||||
}
|
||||
|
||||
// The communication between the data here and the GUI thread goes as follows:
|
||||
|
@ -110,12 +112,7 @@ impl Sys {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Sys {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
impl Default for Sys { fn default() -> Self { Self::new() } }
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [Process] Struct
|
||||
// This holds all the state of a (child) process.
|
||||
|
@ -229,7 +226,9 @@ impl Helper {
|
|||
instant,
|
||||
pub_sys,
|
||||
uptime: HumanTime::into_human(instant.elapsed()),
|
||||
priv_api_p2pool: Arc::new(Mutex::new(PrivP2poolApi::new())),
|
||||
priv_api_p2pool_local: Arc::new(Mutex::new(PrivP2poolLocalApi::new())),
|
||||
priv_api_p2pool_network: Arc::new(Mutex::new(PrivP2poolNetworkApi::new())),
|
||||
priv_api_p2pool_pool: Arc::new(Mutex::new(PrivP2poolPoolApi::new())),
|
||||
priv_api_xmrig: Arc::new(Mutex::new(PrivXmrigApi::new())),
|
||||
pub_api_p2pool: Arc::new(Mutex::new(PubP2poolApi::new())),
|
||||
pub_api_xmrig: Arc::new(Mutex::new(PubXmrigApi::new())),
|
||||
|
@ -278,6 +277,14 @@ impl Helper {
|
|||
}
|
||||
}
|
||||
|
||||
// Read P2Pool/XMRig's API file to a [String].
|
||||
fn path_to_string(path: &std::path::PathBuf, name: ProcessName) -> std::result::Result<String, std::io::Error> {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(s) => Ok(s),
|
||||
Err(e) => { warn!("{} API | [{}] read error: {}", name, path.display(), e); Err(e) },
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- P2Pool specific
|
||||
// Just sets some signals for the watchdog thread to pick up on.
|
||||
pub fn stop_p2pool(helper: &Arc<Mutex<Self>>) {
|
||||
|
@ -313,19 +320,24 @@ impl Helper {
|
|||
pub fn start_p2pool(helper: &Arc<Mutex<Self>>, state: &crate::disk::P2pool, path: &std::path::PathBuf) {
|
||||
helper.lock().unwrap().p2pool.lock().unwrap().state = ProcessState::Middle;
|
||||
|
||||
let (args, api_path) = Self::build_p2pool_args_and_mutate_img(helper, state, path);
|
||||
let (args, api_path_local, api_path_network, api_path_pool) = Self::build_p2pool_args_and_mutate_img(helper, state, path);
|
||||
|
||||
// Print arguments & user settings to console
|
||||
crate::disk::print_dash(&format!("P2Pool | Launch arguments: {:#?} | API Path: {:#?}", args, api_path));
|
||||
crate::disk::print_dash(&format!(
|
||||
"P2Pool | Launch arguments: {:#?} | Local API Path: {:#?} | Network API Path: {:#?} | Pool API Path: {:#?}",
|
||||
args,
|
||||
api_path_local,
|
||||
api_path_network,
|
||||
api_path_pool,
|
||||
));
|
||||
|
||||
// Spawn watchdog thread
|
||||
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 priv_api = Arc::clone(&helper.lock().unwrap().priv_api_p2pool);
|
||||
let path = path.clone();
|
||||
thread::spawn(move || {
|
||||
Self::spawn_p2pool_watchdog(process, gui_api, pub_api, priv_api, args, path, api_path);
|
||||
Self::spawn_p2pool_watchdog(process, gui_api, pub_api, args, path, api_path_local, api_path_network, api_path_pool);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -341,7 +353,7 @@ impl Helper {
|
|||
// Takes in some [State/P2pool] and parses it to build the actual command arguments.
|
||||
// Returns the [Vec] of actual arguments, and mutates the [ImgP2pool] for the main GUI thread
|
||||
// It returns a value... and mutates a deeply nested passed argument... this is some pretty bad code...
|
||||
pub fn build_p2pool_args_and_mutate_img(helper: &Arc<Mutex<Self>>, state: &crate::disk::P2pool, path: &std::path::PathBuf) -> (Vec<String>, PathBuf) {
|
||||
pub fn build_p2pool_args_and_mutate_img(helper: &Arc<Mutex<Self>>, state: &crate::disk::P2pool, path: &std::path::PathBuf) -> (Vec<String>, PathBuf, PathBuf, PathBuf) {
|
||||
let mut args = Vec::with_capacity(500);
|
||||
let path = path.clone();
|
||||
let mut api_path = path;
|
||||
|
@ -419,12 +431,17 @@ impl Helper {
|
|||
};
|
||||
}
|
||||
}
|
||||
api_path.push(P2POOL_API_PATH);
|
||||
(args, api_path)
|
||||
let mut api_path_local = api_path.clone();
|
||||
let mut api_path_network = api_path.clone();
|
||||
let mut api_path_pool = api_path.clone();
|
||||
api_path_local.push(P2POOL_API_PATH_LOCAL);
|
||||
api_path_network.push(P2POOL_API_PATH_NETWORK);
|
||||
api_path_pool.push(P2POOL_API_PATH_POOL);
|
||||
(args, api_path_local, api_path_network, api_path_pool)
|
||||
}
|
||||
|
||||
// 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>>, _priv_api: Arc<Mutex<PrivP2poolApi>>, args: Vec<String>, path: std::path::PathBuf, api_path: 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) {
|
||||
// 1a. Create PTY
|
||||
debug!("P2Pool | Creating PTY...");
|
||||
let pty = portable_pty::native_pty_system();
|
||||
|
@ -464,17 +481,17 @@ impl Helper {
|
|||
let output_parse = Arc::clone(&process.lock().unwrap().output_parse);
|
||||
let output_pub = Arc::clone(&process.lock().unwrap().output_pub);
|
||||
|
||||
debug!("P2Pool | Cleaning old API files...");
|
||||
debug!("P2Pool | Cleaning old [local] API files...");
|
||||
// Attempt to remove stale API file
|
||||
match std::fs::remove_file(&api_path) {
|
||||
match std::fs::remove_file(&api_path_local) {
|
||||
Ok(_) => info!("P2Pool | Attempting to remove stale API file ... OK"),
|
||||
Err(e) => warn!("P2Pool | Attempting to remove stale API file ... FAIL ... {}", e),
|
||||
}
|
||||
// Attempt to create a default empty one.
|
||||
use std::io::Write;
|
||||
if std::fs::File::create(&api_path).is_ok() {
|
||||
if std::fs::File::create(&api_path_local).is_ok() {
|
||||
let text = r#"{"hashrate_15m":0,"hashrate_1h":0,"hashrate_24h":0,"shares_found":0,"average_effort":0.0,"current_effort":0.0,"connections":0}"#;
|
||||
match std::fs::write(&api_path, text) {
|
||||
match std::fs::write(&api_path_local, text) {
|
||||
Ok(_) => info!("P2Pool | Creating default empty API file ... OK"),
|
||||
Err(e) => warn!("P2Pool | Creating default empty API file ... FAIL ... {}", e),
|
||||
}
|
||||
|
@ -600,13 +617,28 @@ impl Helper {
|
|||
debug!("P2Pool Watchdog | Starting [update_from_output()]");
|
||||
PubP2poolApi::update_from_output(&pub_api, &output_parse, &output_pub, start.elapsed(), ®ex);
|
||||
|
||||
// Read API file into string
|
||||
debug!("P2Pool Watchdog | Attempting API file read");
|
||||
if let Ok(string) = PrivP2poolApi::read_p2pool_api(&api_path) {
|
||||
// Read [local] API
|
||||
debug!("P2Pool Watchdog | Attempting [local] API file read");
|
||||
if let Ok(string) = Self::path_to_string(&api_path_local, ProcessName::P2pool) {
|
||||
// Deserialize
|
||||
if let Ok(s) = PrivP2poolApi::str_to_priv_p2pool_api(&string) {
|
||||
if let Ok(s) = PrivP2poolLocalApi::from_str(&string) {
|
||||
// Update the structs.
|
||||
PubP2poolApi::update_from_priv(&pub_api, s);
|
||||
PubP2poolApi::update_from_local(&pub_api, s);
|
||||
}
|
||||
}
|
||||
// If more than 1 minute has passed, read the other API files.
|
||||
if now.elapsed().as_secs() >= 60 {
|
||||
debug!("P2Pool Watchdog | Attempting [network] API file read");
|
||||
if let Ok(string) = Self::path_to_string(&api_path_network, ProcessName::P2pool) {
|
||||
if let Ok(s) = PrivP2poolNetworkApi::from_str(&string) {
|
||||
// PubP2poolApi::update_from_network(&pub_api, s);
|
||||
}
|
||||
}
|
||||
debug!("P2Pool Watchdog | Attempting [pool] API file read");
|
||||
if let Ok(string) = Self::path_to_string(&api_path_pool, ProcessName::P2pool) {
|
||||
if let Ok(s) = PrivP2poolPoolApi::from_str(&string) {
|
||||
// PubP2poolApi::update_from_network(&pub_api, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1388,7 +1420,7 @@ pub struct PubP2poolApi {
|
|||
pub xmr_hour: f64,
|
||||
pub xmr_day: f64,
|
||||
pub xmr_month: f64,
|
||||
// The rest are serialized from the API, then turned into [HumanNumber]s
|
||||
// Local API
|
||||
pub hashrate_15m: HumanNumber,
|
||||
pub hashrate_1h: HumanNumber,
|
||||
pub hashrate_24h: HumanNumber,
|
||||
|
@ -1396,6 +1428,8 @@ pub struct PubP2poolApi {
|
|||
pub average_effort: HumanNumber,
|
||||
pub current_effort: HumanNumber,
|
||||
pub connections: HumanNumber,
|
||||
// Network API
|
||||
// Pool API
|
||||
}
|
||||
|
||||
impl Default for PubP2poolApi {
|
||||
|
@ -1498,8 +1532,8 @@ impl PubP2poolApi {
|
|||
};
|
||||
}
|
||||
|
||||
// Mutate [PubP2poolApi] with data from a [PrivP2poolApi] and the process output.
|
||||
fn update_from_priv(public: &Arc<Mutex<Self>>, private: PrivP2poolApi) {
|
||||
// Mutate [PubP2poolApi] with data from a [PrivP2poolLocalApi] and the process output.
|
||||
fn update_from_local(public: &Arc<Mutex<Self>>, private: PrivP2poolLocalApi) {
|
||||
// priv -> pub conversion
|
||||
let mut public = public.lock().unwrap();
|
||||
*public = Self {
|
||||
|
@ -1530,12 +1564,11 @@ impl PubP2poolApi {
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Private P2Pool API
|
||||
// This is the data the "watchdog" threads mutate.
|
||||
// It matches directly to P2Pool's [local/stats] JSON API file (excluding a few stats).
|
||||
//---------------------------------------------------------------------------------------------------- Private P2Pool "Local" Api
|
||||
// This matches directly to P2Pool's [local/stats] JSON API file (excluding a few stats).
|
||||
// P2Pool seems to initialize all stats at 0 (or 0.0), so no [Option] wrapper seems needed.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
struct PrivP2poolApi {
|
||||
struct PrivP2poolLocalApi {
|
||||
hashrate_15m: u128,
|
||||
hashrate_1h: u128,
|
||||
hashrate_24h: u128,
|
||||
|
@ -1545,7 +1578,9 @@ struct PrivP2poolApi {
|
|||
connections: u16, // No one will have more than 65535 connections... right?
|
||||
}
|
||||
|
||||
impl PrivP2poolApi {
|
||||
impl Default for PrivP2poolLocalApi { fn default() -> Self { Self::new() } }
|
||||
|
||||
impl PrivP2poolLocalApi {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
hashrate_15m: 0,
|
||||
|
@ -1558,23 +1593,79 @@ impl PrivP2poolApi {
|
|||
}
|
||||
}
|
||||
|
||||
// Read P2Pool's API file to a [String].
|
||||
fn read_p2pool_api(path: &std::path::PathBuf) -> std::result::Result<String, std::io::Error> {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(s) => Ok(s),
|
||||
Err(e) => { warn!("P2Pool API | [{}] read error: {}", path.display(), e); Err(e) },
|
||||
// Deserialize the above [String] into a [PrivP2poolApi]
|
||||
fn from_str(string: &str) -> std::result::Result<Self, serde_json::Error> {
|
||||
match serde_json::from_str::<Self>(string) {
|
||||
Ok(a) => Ok(a),
|
||||
Err(e) => { warn!("P2Pool Local API | Could not deserialize API data: {}", e); Err(e) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize the above [String] into a [PrivP2poolApi]
|
||||
fn str_to_priv_p2pool_api(string: &str) -> std::result::Result<Self, serde_json::Error> {
|
||||
//---------------------------------------------------------------------------------------------------- Private P2Pool "Network" API
|
||||
// This matches P2Pool's [network/stats] JSON API file.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
struct PrivP2poolNetworkApi {
|
||||
difficulty: u128,
|
||||
hash: String,
|
||||
height: u32,
|
||||
reward: u128,
|
||||
timestamp: u32,
|
||||
}
|
||||
|
||||
impl Default for PrivP2poolNetworkApi { fn default() -> Self { Self::new() } }
|
||||
|
||||
impl PrivP2poolNetworkApi {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
difficulty: 0,
|
||||
hash: String::from("???"),
|
||||
height: 0,
|
||||
reward: 0,
|
||||
timestamp: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_str(string: &str) -> std::result::Result<Self, serde_json::Error> {
|
||||
match serde_json::from_str::<Self>(string) {
|
||||
Ok(a) => Ok(a),
|
||||
Err(e) => { warn!("P2Pool API | Could not deserialize API data: {}", e); Err(e) },
|
||||
Err(e) => { warn!("P2Pool Network API | Could not deserialize API data: {}", e); Err(e) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Private P2Pool "Pool" API
|
||||
// This matches P2Pool's [pool/stats] JSON API file.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
struct PrivP2poolPoolApi {
|
||||
pool_statistics: PoolStatistics,
|
||||
}
|
||||
|
||||
impl Default for PrivP2poolPoolApi { fn default() -> Self { Self::new() } }
|
||||
|
||||
impl PrivP2poolPoolApi {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
pool_statistics: PoolStatistics::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_str(string: &str) -> std::result::Result<Self, serde_json::Error> {
|
||||
match serde_json::from_str::<Self>(string) {
|
||||
Ok(a) => Ok(a),
|
||||
Err(e) => { warn!("P2Pool Pool API | Could not deserialize API data: {}", e); Err(e) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
struct PoolStatistics {
|
||||
hashrate: u128,
|
||||
miners: u32,
|
||||
}
|
||||
impl Default for PoolStatistics { fn default() -> Self { Self::new() } }
|
||||
impl PoolStatistics { fn new() -> Self { Self { hashrate: 0, miners: 0 } } }
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [ImgXmrig]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImgXmrig {
|
||||
|
@ -1928,7 +2019,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn serde_priv_p2pool_api() {
|
||||
fn serde_priv_p2pool_local_api() {
|
||||
let data =
|
||||
r#"{
|
||||
"hashrate_15m": 12,
|
||||
|
@ -1941,8 +2032,8 @@ mod test {
|
|||
"connections": 123,
|
||||
"incoming_connections": 96
|
||||
}"#;
|
||||
use crate::helper::PrivP2poolApi;
|
||||
let priv_api = PrivP2poolApi::str_to_priv_p2pool_api(data).unwrap();
|
||||
use crate::helper::PrivP2poolLocalApi;
|
||||
let priv_api = PrivP2poolLocalApi::from_str(data).unwrap();
|
||||
let json = serde_json::ser::to_string_pretty(&priv_api).unwrap();
|
||||
println!("{}", json);
|
||||
let data_after_ser =
|
||||
|
|
Loading…
Reference in a new issue