feat: p2pool sync detection reworked using only API

This commit is contained in:
Cyrix126 2025-01-03 17:19:16 +01:00
parent ddd8f404af
commit 341002b047
6 changed files with 33 additions and 218 deletions

View file

@ -19,7 +19,7 @@ use crate::{
app::{Benchmark, eframe_impl::ProcessStatesGui},
disk::{gupax_p2pool_api::GupaxP2poolApi, state::Status, status::*},
helper::{
ProcessName, Sys,
ProcessName, ProcessState, Sys,
node::PubNodeApi,
p2pool::{ImgP2pool, PubP2poolApi},
xrig::{
@ -75,7 +75,7 @@ impl Status {
self.p2pool(
ui,
gupax_p2pool_api,
states.is_alive(ProcessName::P2pool),
states.find(ProcessName::P2pool).state == ProcessState::Alive,
p2pool_api,
);
//---------------------------------------------------------------------------------------------------- [Benchmarks]

View file

@ -366,11 +366,6 @@ impl Status {
});
});
});
// Tick bar
ui.vertical_centered(|ui| {
ui.label(api.calculate_tick_bar())
.on_hover_text(STATUS_SUBMENU_PROGRESS_BAR);
});
drop(api);
});
});

View file

@ -30,7 +30,6 @@ use crate::helper::signal_end;
use crate::helper::sleep_end_loop;
use crate::regex::P2POOL_REGEX;
use crate::regex::contains_end_status;
use crate::regex::contains_newchain_tip;
use crate::regex::contains_statuscommand;
use crate::regex::contains_yourhashrate;
use crate::regex::contains_yourshare;
@ -691,30 +690,21 @@ impl Helper {
// check if state must be changed based on local and p2p API
pub_api_lock.update_state(&mut process_lock);
// If more than 1 minute has passed, read the other API files.
let last_p2pool_request_expired =
last_p2pool_request.elapsed() >= Duration::from_secs(60);
// need to reload fast to get the first right values after syncing.
// check if value is 100k or under and request immediately if that's the case. fixed in release of p2pool including commit https://github.com/SChernykh/p2pool/commit/64a199be6dec7924b41f857a401086f25e1ec9be
if (last_p2pool_request_expired || pub_api_lock.p2pool_difficulty_u64 <= 100000)
&& process_lock.state == ProcessState::Alive
{
debug!("P2Pool Watchdog | Attempting [network] & [pool] API file read");
debug!("P2Pool Watchdog | Attempting [network] & [pool] API file read");
if let (Ok(network_api), Ok(pool_api)) = (
Self::path_to_string(&api_path_network, ProcessName::P2pool),
Self::path_to_string(&api_path_pool, ProcessName::P2pool),
) {
if let (Ok(network_api), Ok(pool_api)) = (
Self::path_to_string(&api_path_network, ProcessName::P2pool),
Self::path_to_string(&api_path_pool, ProcessName::P2pool),
PrivP2poolNetworkApi::from_str(&network_api),
PrivP2poolPoolApi::from_str(&pool_api),
) {
if let (Ok(network_api), Ok(pool_api)) = (
PrivP2poolNetworkApi::from_str(&network_api),
PrivP2poolPoolApi::from_str(&pool_api),
) {
PubP2poolApi::update_from_network_pool(
&mut pub_api_lock,
network_api,
pool_api,
);
last_p2pool_request = tokio::time::Instant::now();
}
PubP2poolApi::update_from_network_pool(
&mut pub_api_lock,
network_api,
pool_api,
);
last_p2pool_request = tokio::time::Instant::now();
}
}
@ -883,8 +873,7 @@ pub struct PubP2poolApi {
// from status
pub sidechain_shares: u32,
pub sidechain_ehr: f32,
// from height
pub synchronised: bool,
pub sidechain_height: u32,
// from local/p2p
pub p2p_connected: u32,
pub node_connected: bool,
@ -940,8 +929,8 @@ impl PubP2poolApi {
user_monero_percent: HumanNumber::unknown(),
sidechain_shares: 0,
sidechain_ehr: 0.0,
sidechain_height: 0,
p2p_connected: 0,
synchronised: false,
node_connected: false,
prefer_local_node: true,
}
@ -1007,33 +996,6 @@ impl PubP2poolApi {
// 2. Parse the full STDOUT
let mut output_parse = output_parse.lock().unwrap();
let (payouts_new, xmr_new) = Self::calc_payouts_and_xmr(&output_parse);
// Check for "SYNCHRONIZED" only if we aren't already. Works at level 0 and above.
if process.state == ProcessState::Syncing {
// How many times the word was captured.
let synchronized_captures = P2POOL_REGEX.synchronized.find_iter(&output_parse).count();
// If P2Pool receives shares before syncing, it will start mining on its own sidechain.
// In this instance, we technically are "synced" on block 1 and P2Pool will print "SYNCHRONIZED"
// although, that doesn't necessarily mean we're synced on main/mini-chain.
//
// So, if we find a `next block = 1`, that means we
// must look for at least 2 instances of "SYNCHRONIZED",
// one for the sidechain, one for main/mini.
if P2POOL_REGEX.next_height_1.is_match(&output_parse) {
if synchronized_captures > 1 {
process.state = ProcessState::Alive;
}
} else if synchronized_captures > 0 {
// if there is no `next block = 1`, fallback to
// just finding 1 instance of "SYNCHRONIZED".
process.state = ProcessState::Alive;
}
// if the p2pool node was synced but is not anymore due to faulty monero node and is synced again, the status must be alive again
// required log level 2 minimum
if contains_newchain_tip(&output_parse) {
process.state = ProcessState::Alive;
}
}
// if the node is offline, p2pool can not function properly. Requires at least p2pool log level 1
if process.state == ProcessState::Alive && contains_zmq_failure(&output_parse) {
process.state = ProcessState::Syncing;
@ -1120,8 +1082,8 @@ impl PubP2poolApi {
pub(super) fn update_from_p2p(public: &mut Self, p2p: PrivP2PoolP2PApi) {
*public = Self {
p2p_connected: p2p.connections,
// 30 seconds before concluding the monero node connection is lost
node_connected: p2p.zmq_last_active.is_some_and(|x| x < 30),
// 10 seconds before concluding the monero node connection is lost
node_connected: p2p.zmq_last_active.is_some_and(|x| x < 10),
..std::mem::take(&mut *public)
};
}
@ -1187,6 +1149,7 @@ impl PubP2poolApi {
p2pool_difficulty: HumanNumber::from_u64(p2pool_difficulty),
p2pool_hashrate: HumanNumber::from_u64_to_megahash_3_point(p2pool_hashrate),
miners: HumanNumber::from_u32(pool.pool_statistics.miners),
sidechain_height: pool.pool_statistics.sidechainHeight,
solo_block_mean,
p2pool_block_mean,
p2pool_share_mean,
@ -1196,16 +1159,19 @@ impl PubP2poolApi {
..std::mem::take(&mut *public)
};
}
/// Check if all conditions are met to be alive or if something is wrong
fn update_state(&self, process: &mut Process) {
if process.state == ProcessState::Syncing
&& self.synchronised
&& self.node_connected
&& self.p2p_connected > 1
&& self.height > 10
&& self.sidechain_height > 1000
{
process.state = ProcessState::Alive;
}
if process.state == ProcessState::Alive
&& (self.sidechain_height < 1000 || !self.node_connected || self.p2p_connected == 0)
{
process.state = ProcessState::Syncing;
}
}
#[inline]
@ -1226,77 +1192,6 @@ impl PubP2poolApi {
HumanNumber::from_f64_to_percent_6_point(f)
}
}
pub const fn calculate_tick_bar(&self) -> &'static str {
// The stars are reduced by one because it takes a frame to render the stats.
// We want 0 stars at the same time stats are rendered, so it looks a little off here.
// let stars = "*".repeat(self.tick - 1);
// let blanks = " ".repeat(60 - (self.tick - 1));
// [use crate::PubP2poolApi;use crate::PubP2poolApi;"[", &stars, &blanks, "]"].concat().as_str()
match self.tick {
0 => "[ ]",
1 => "[* ]",
2 => "[** ]",
3 => "[*** ]",
4 => "[**** ]",
5 => "[***** ]",
6 => "[****** ]",
7 => "[******* ]",
8 => "[******** ]",
9 => "[********* ]",
10 => "[********** ]",
11 => "[*********** ]",
12 => "[************ ]",
13 => "[************* ]",
14 => "[************** ]",
15 => "[*************** ]",
16 => "[**************** ]",
17 => "[***************** ]",
18 => "[****************** ]",
19 => "[******************* ]",
20 => "[******************** ]",
21 => "[********************* ]",
22 => "[********************** ]",
23 => "[*********************** ]",
24 => "[************************ ]",
25 => "[************************* ]",
26 => "[************************** ]",
27 => "[*************************** ]",
28 => "[**************************** ]",
29 => "[***************************** ]",
30 => "[****************************** ]",
31 => "[******************************* ]",
32 => "[******************************** ]",
33 => "[********************************* ]",
34 => "[********************************** ]",
35 => "[*********************************** ]",
36 => "[************************************ ]",
37 => "[************************************* ]",
38 => "[************************************** ]",
39 => "[*************************************** ]",
40 => "[**************************************** ]",
41 => "[***************************************** ]",
42 => "[****************************************** ]",
43 => "[******************************************* ]",
44 => "[******************************************** ]",
45 => "[********************************************* ]",
46 => "[********************************************** ]",
47 => "[*********************************************** ]",
48 => "[************************************************ ]",
49 => "[************************************************* ]",
50 => "[************************************************** ]",
51 => "[*************************************************** ]",
52 => "[**************************************************** ]",
53 => "[***************************************************** ]",
54 => "[****************************************************** ]",
55 => "[******************************************************* ]",
56 => "[******************************************************** ]",
57 => "[********************************************************* ]",
58 => "[********************************************************** ]",
59 => "[*********************************************************** ]",
_ => "[************************************************************]",
}
}
}
//---------------------------------------------------------------------------------------------------- Private P2Pool "Local" Api
@ -1419,6 +1314,7 @@ impl PrivP2poolPoolApi {
pub(super) struct PoolStatistics {
pub hashRate: u64,
pub miners: u32,
pub sidechainHeight: u32,
}
impl Default for PoolStatistics {
fn default() -> Self {
@ -1430,6 +1326,7 @@ impl PoolStatistics {
Self {
hashRate: 0,
miners: 0,
sidechainHeight: 0,
}
}
}

View file

@ -155,37 +155,6 @@ Uptime = 0h 2m 4s
assert_eq!(public.xmr_month, 648000.0000001296);
}
#[test]
fn set_p2pool_synchronized() {
use crate::helper::PubP2poolApi;
use std::sync::{Arc, Mutex};
let public = Arc::new(Mutex::new(PubP2poolApi::new()));
let output_parse = Arc::new(Mutex::new(String::from(
r#"payout of 5.000000000001 XMR in block 1111
NOTICE 2021-12-27 21:42:17.2008 SideChain SYNCHRONIZED
payout of 5.000000000001 XMR in block 1113"#,
)));
let output_pub = Arc::new(Mutex::new(String::new()));
let elapsed = std::time::Duration::from_secs(60);
let process = Arc::new(Mutex::new(Process::new(
ProcessName::P2pool,
"".to_string(),
PathBuf::new(),
)));
// It only gets checked if we're `Syncing`.
process.lock().unwrap().state = ProcessState::Syncing;
PubP2poolApi::update_from_output(
&mut public.lock().unwrap(),
&output_parse,
&output_pub,
elapsed,
&mut process.lock().unwrap(),
);
println!("{:#?}", process);
assert!(process.lock().unwrap().state == ProcessState::Alive);
}
#[test]
fn p2pool_synchronized_false_positive() {
use crate::helper::PubP2poolApi;
@ -221,43 +190,6 @@ Uptime = 0h 2m 4s
assert!(process.lock().unwrap().state == ProcessState::Syncing); // still syncing
}
#[test]
fn p2pool_synchronized_double_synchronized() {
use crate::helper::PubP2poolApi;
use std::sync::{Arc, Mutex};
let public = Arc::new(Mutex::new(PubP2poolApi::new()));
// The 1st SideChain that is "SYNCHRONIZED" in this output is
// the sidechain started on height 1, but there is another one
// which means the real main/mini is probably synced,
// so this _should_ trigger alive state.
let output_parse = Arc::new(Mutex::new(String::from(
r#"2024-11-02 17:39:02.6241 SideChain SYNCHRONIZED
2024-11-02 17:39:02.6242 StratumServer SHARE FOUND: mainchain height 3272685, sidechain height 0, diff 100000, client 127.0.0.1:40874, effort 100.001%
2024-11-02 17:39:02.6559 StratumServer SHARE FOUND: mainchain height 3272685, sidechain height 0, diff 100000, client 127.0.0.1:40874, effort 200.002%
2024-11-02 17:40:06.8562 SideChain SYNCHRONIZED"#,
)));
let output_pub = Arc::new(Mutex::new(String::new()));
let elapsed = std::time::Duration::from_secs(60);
let process = Arc::new(Mutex::new(Process::new(
ProcessName::P2pool,
"".to_string(),
PathBuf::new(),
)));
// It only gets checked if we're `Syncing`.
process.lock().unwrap().state = ProcessState::Syncing;
PubP2poolApi::update_from_output(
&mut public.lock().unwrap(),
&output_parse,
&output_pub,
elapsed,
&mut process.lock().unwrap(),
);
println!("{:#?}", process);
assert!(process.lock().unwrap().state == ProcessState::Alive);
}
#[test]
fn update_pub_p2pool_from_local_network_pool() {
use crate::helper::PubP2poolApi;
@ -287,6 +219,7 @@ Uptime = 0h 2m 4s
pool_statistics: PoolStatistics {
hashRate: 1_000_000, // 1 MH/s
miners: 1_000,
sidechainHeight: 10_000_000,
},
};
// Update Local
@ -430,7 +363,8 @@ Uptime = 0h 2m 4s
"totalHashes": 487463929193948,
"lastBlockFoundTime": 1670453228,
"lastBlockFound": 2756570,
"totalBlocksFound": 4
"totalBlocksFound": 4,
"sidechainHeight": 9000000
}
}"#;
let priv_api = crate::helper::p2pool::PrivP2poolPoolApi::from_str(data).unwrap();
@ -439,7 +373,8 @@ Uptime = 0h 2m 4s
let data_after_ser = r#"{
"pool_statistics": {
"hashRate": 10225772,
"miners": 713
"miners": 713,
"sidechainHeight": 9000000
}
}"#;
assert_eq!(data_after_ser, json)

View file

@ -308,7 +308,6 @@ pub const STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE: &str =
"The percent of hashrate you account for in P2Pool";
pub const STATUS_SUBMENU_YOUR_MONERO_DOMINANCE: &str =
"The percent of hashrate you account for in the entire Monero network";
pub const STATUS_SUBMENU_PROGRESS_BAR: &str = "The next time Gupaxx will update P2Pool stats.";
//-- Benchmarks
pub const STATUS_SUBMENU_YOUR_CPU: &str = "The CPU detected by Gupaxx";
pub const STATUS_SUBMENU_YOUR_BENCHMARKS: &str =

View file

@ -90,8 +90,6 @@ pub struct P2poolRegex {
pub block: Regex,
pub block_int: Regex,
pub block_comma: Regex,
pub synchronized: Regex,
pub next_height_1: Regex,
}
impl P2poolRegex {
@ -105,8 +103,6 @@ impl P2poolRegex {
block: Regex::new("block [0-9]{7}").unwrap(), // Monero blocks will be 7 digits for... the next 10,379 years
block_int: Regex::new("[0-9]{7}").unwrap(),
block_comma: Regex::new("[0-9],[0-9]{3},[0-9]{3}").unwrap(),
synchronized: Regex::new("SYNCHRONIZED").unwrap(),
next_height_1: Regex::new("sidechain height 0").unwrap(),
}
}
}
@ -286,11 +282,6 @@ pub fn contains_zmq_failure(l: &str) -> bool {
});
LINE_SHARE.is_match(l)
}
/// a way to detect that p2pool is alive
pub fn contains_newchain_tip(l: &str) -> bool {
static LINE_SHARE: Lazy<Regex> = Lazy::new(|| Regex::new(r"new chain tip").unwrap());
LINE_SHARE.is_match(l)
}
//---------------------------------------------------------------------------------------------------- TEST
#[cfg(test)]
@ -329,7 +320,6 @@ mod test {
let r = P2poolRegex::new();
let text = "NOTICE 2022-11-11 11:11:11.1111 P2Pool You received a payout of 0.111111111111 XMR in block 1111111";
let text2 = "2022-11-11 11:11:11.1111 | 0.111111111111 XMR | Block 1,111,111";
let text3 = "NOTICE 2020-12-11 12:35:41.3150 SideChain SYNCHRONIZED";
assert_eq!(
r.payout.find(text).unwrap().as_str(),
"payout of 0.111111111111 XMR"
@ -345,7 +335,6 @@ mod test {
assert_eq!(r.block.find(text).unwrap().as_str(), "block 1111111");
assert_eq!(r.block_int.find(text).unwrap().as_str(), "1111111");
assert_eq!(r.block_comma.find(text2).unwrap().as_str(), "1,111,111");
assert_eq!(r.synchronized.find(text3).unwrap().as_str(), "SYNCHRONIZED");
}
#[test]